Merge remote-tracking branch 'origin/master' into sheet
[pspp] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014,
3    2016, 2017  Free Software Foundation
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 #include <gtk/gtk.h>
21 #include <stdlib.h>
22
23 #include "data/dataset.h"
24 #include "data/session.h"
25 #include "language/lexer/lexer.h"
26 #include "libpspp/message.h"
27 #include "libpspp/str.h"
28 #include "ui/gui/builder-wrapper.h"
29 #include "ui/gui/entry-dialog.h"
30 #include "ui/gui/executor.h"
31 #include "ui/gui/help-menu.h"
32 #include "ui/gui/helper.h"
33 #include "ui/gui/helper.h"
34 #include "ui/gui/psppire-import-assistant.h"
35 #include "ui/gui/psppire-data-window.h"
36 #include "ui/gui/psppire-data-editor.h"
37 #include "ui/gui/psppire-dialog-action.h"
38 #include "ui/gui/psppire-encoding-selector.h"
39 #include "ui/gui/psppire-syntax-window.h"
40 #include "ui/gui/psppire-window.h"
41 #include "ui/gui/windows-menu.h"
42 #include "ui/gui/goto-case-dialog.h"
43 #include "ui/gui/psppire.h"
44 #include "ui/syntax-gen.h"
45
46 #include "gl/c-strcase.h"
47 #include "gl/c-strcasestr.h"
48 #include "gl/xvasprintf.h"
49
50 #include "ui/gui/efficient-sheet/jmd-sheet.h"
51
52 #include "find-dialog.h"
53 #include "options-dialog.h"
54 #include "psppire-dialog-action-1sks.h"
55 #include "psppire-dialog-action-aggregate.h"
56 #include "psppire-dialog-action-autorecode.h"
57 #include "psppire-dialog-action-barchart.h"
58 #include "psppire-dialog-action-binomial.h"
59 #include "psppire-dialog-action-chisquare.h"
60 #include "psppire-dialog-action-comments.h"
61 #include "psppire-dialog-action-compute.h"
62 #include "psppire-dialog-action-correlation.h"
63 #include "psppire-dialog-action-count.h"
64 #include "psppire-dialog-action-crosstabs.h"
65 #include "psppire-dialog-action-descriptives.h"
66 #include "psppire-dialog-action-examine.h"
67 #include "psppire-dialog-action-factor.h"
68 #include "psppire-dialog-action-flip.h"
69 #include "psppire-dialog-action-frequencies.h"
70 #include "psppire-dialog-action-histogram.h"
71 #include "psppire-dialog-action-indep-samps.h"
72 #include "psppire-dialog-action-k-related.h"
73 #include "psppire-dialog-action-kmeans.h"
74 #include "psppire-dialog-action-logistic.h"
75 #include "psppire-dialog-action-means.h"
76 #include "psppire-dialog-action-oneway.h"
77 #include "psppire-dialog-action-paired.h"
78 #include "psppire-dialog-action-rank.h"
79 #include "psppire-dialog-action-recode-same.h"
80 #include "psppire-dialog-action-recode-different.h"
81 #include "psppire-dialog-action-regression.h"
82 #include "psppire-dialog-action-reliability.h"
83 #include "psppire-dialog-action-roc.h"
84 #include "psppire-dialog-action-runs.h"
85 #include "psppire-dialog-action-scatterplot.h"
86 #include "psppire-dialog-action-select.h"
87 #include "psppire-dialog-action-sort.h"
88 #include "psppire-dialog-action-split.h"
89 #include "psppire-dialog-action-tt1s.h"
90 #include "psppire-dialog-action-two-sample.h"
91 #include "psppire-dialog-action-univariate.h"
92 #include "psppire-dialog-action-var-info.h"
93 #include "psppire-dialog-action-weight.h"
94
95
96 #include <gettext.h>
97 #define _(msgid) gettext (msgid)
98 #define N_(msgid) msgid
99
100 struct session *the_session;
101 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
102
103 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
104 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
105
106
107 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
108
109 static void psppire_data_window_dispose (GObject *object);
110 static void psppire_data_window_finalize (GObject *object);
111 static void psppire_data_window_set_property (GObject         *object,
112                                               guint            prop_id,
113                                               const GValue    *value,
114                                               GParamSpec      *pspec);
115 static void psppire_data_window_get_property (GObject         *object,
116                                               guint            prop_id,
117                                               GValue          *value,
118                                               GParamSpec      *pspec);
119
120 GType
121 psppire_data_window_get_type (void)
122 {
123   static GType psppire_data_window_type = 0;
124
125   if (!psppire_data_window_type)
126     {
127       static const GTypeInfo psppire_data_window_info =
128         {
129           sizeof (PsppireDataWindowClass),
130           NULL,
131           NULL,
132           (GClassInitFunc)psppire_data_window_class_init,
133           (GClassFinalizeFunc) NULL,
134           NULL,
135           sizeof (PsppireDataWindow),
136           0,
137           (GInstanceInitFunc) psppire_data_window_init,
138         };
139
140       static const GInterfaceInfo window_interface_info =
141         {
142           (GInterfaceInitFunc) psppire_data_window_iface_init,
143           NULL,
144           NULL
145         };
146
147       psppire_data_window_type =
148         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
149                                 &psppire_data_window_info, 0);
150
151
152       g_type_add_interface_static (psppire_data_window_type,
153                                    PSPPIRE_TYPE_WINDOW_MODEL,
154                                    &window_interface_info);
155     }
156
157   return psppire_data_window_type;
158 }
159
160 static GObjectClass *parent_class ;
161
162 enum {
163   PROP_DATASET = 1
164 };
165
166 static void
167 psppire_data_window_class_init (PsppireDataWindowClass *class)
168 {
169   GObjectClass *object_class = G_OBJECT_CLASS (class);
170
171   parent_class = g_type_class_peek_parent (class);
172
173   object_class->dispose = psppire_data_window_dispose;
174   object_class->finalize = psppire_data_window_finalize;
175   object_class->set_property = psppire_data_window_set_property;
176   object_class->get_property = psppire_data_window_get_property;
177
178   g_object_class_install_property (
179                                    object_class, PROP_DATASET,
180                                    g_param_spec_pointer ("dataset", "Dataset",
181                                                          "'struct datset *' represented by the window",
182                                                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
183 }
184
185 \f
186
187 /* Run the EXECUTE command. */
188 static void
189 execute (PsppireDataWindow *dw)
190 {
191   execute_const_syntax_string (dw, "EXECUTE.");
192 }
193
194 static void
195 transformation_change_callback (bool transformations_pending,
196                                 gpointer data)
197 {
198   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
199
200   GtkWidget *status_label  =
201     get_widget_assert (de->builder, "case-counter-area");
202
203   {
204     GAction *action = g_action_map_lookup_action (G_ACTION_MAP (de),
205                                                   "transform-pending");
206
207     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
208                                  transformations_pending);
209   }
210
211   if ( transformations_pending)
212     gtk_label_set_text (GTK_LABEL (status_label),
213                         _("Transformations Pending"));
214   else
215     gtk_label_set_text (GTK_LABEL (status_label), "");
216 }
217
218 /* Callback for when the dictionary changes its filter variable */
219 static void
220 on_filter_change (GObject *o, gint filter_index, gpointer data)
221 {
222   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
223
224   GtkWidget *filter_status_area =
225     get_widget_assert (de->builder, "filter-use-status-area");
226
227   if ( filter_index == -1 )
228     {
229       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
230     }
231   else
232     {
233       PsppireDict *dict = NULL;
234       struct variable *var ;
235       gchar *text ;
236
237       g_object_get (de->data_editor, "dictionary", &dict, NULL);
238
239       var = psppire_dict_get_variable (dict, filter_index);
240
241       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
242
243       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
244
245       g_free (text);
246     }
247 }
248
249 /* Callback for when the dictionary changes its split variables */
250 static void
251 on_split_change (PsppireDict *dict, gpointer data)
252 {
253   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
254
255   size_t n_split_vars = dict_get_split_cnt (dict->dict);
256
257   GtkWidget *split_status_area =
258     get_widget_assert (de->builder, "split-file-status-area");
259
260   if ( n_split_vars == 0 )
261     {
262       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
263     }
264   else
265     {
266       gint i;
267       GString *text;
268       const struct variable *const * split_vars =
269         dict_get_split_vars (dict->dict);
270
271       text = g_string_new (_("Split by "));
272
273       for (i = 0 ; i < n_split_vars - 1; ++i )
274         {
275           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
276         }
277       g_string_append (text, var_get_name (split_vars[i]));
278
279       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
280
281       g_string_free (text, TRUE);
282     }
283 }
284
285
286
287
288 /* Callback for when the dictionary changes its weights */
289 static void
290 on_weight_change (GObject *o, gint weight_index, gpointer data)
291 {
292   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
293
294   GtkWidget *weight_status_area =
295     get_widget_assert (de->builder, "weight-status-area");
296
297   if ( weight_index == -1 )
298     {
299       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
300     }
301   else
302     {
303       struct variable *var ;
304       PsppireDict *dict = NULL;
305       gchar *text;
306
307       g_object_get (de->data_editor, "dictionary", &dict, NULL);
308
309       var = psppire_dict_get_variable (dict, weight_index);
310
311       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
312
313       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
314
315       g_free (text);
316     }
317 }
318
319 #if 0
320 static void
321 dump_rm (GtkRecentManager *rm)
322 {
323   GList *items = gtk_recent_manager_get_items (rm);
324
325   GList *i;
326
327   g_print ("Recent Items:\n");
328   for (i = items; i; i = i->next)
329     {
330       GtkRecentInfo *ri = i->data;
331
332       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
333                gtk_recent_info_get_short_name (ri),
334                gtk_recent_info_get_mime_type (ri),
335                gtk_recent_info_get_description (ri),
336                gtk_recent_info_get_uri (ri)
337                );
338
339
340       gtk_recent_info_unref (ri);
341     }
342
343   g_list_free (items);
344 }
345 #endif
346
347 static gboolean
348 has_suffix (const gchar *name, const gchar *suffix)
349 {
350   size_t name_len = strlen (name);
351   size_t suffix_len = strlen (suffix);
352   return (name_len > suffix_len
353           && !c_strcasecmp (&name[name_len - suffix_len], suffix));
354 }
355
356 static gboolean
357 name_has_por_suffix (const gchar *name)
358 {
359   return has_suffix (name, ".por");
360 }
361
362 static gboolean
363 name_has_sav_suffix (const gchar *name)
364 {
365   return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
366 }
367
368 /* Returns true if NAME has a suffix which might denote a PSPP file */
369 static gboolean
370 name_has_suffix (const gchar *name)
371 {
372   return name_has_por_suffix (name) || name_has_sav_suffix (name);
373 }
374
375 static gboolean
376 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
377            gpointer syn)
378 {
379   const char *mime_type = NULL;
380   gchar *syntax = NULL;
381   bool ok;
382
383   if (syn == NULL)
384     {
385       gchar *utf8_file_name;
386       struct string filename;
387
388       utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
389
390       if (NULL == utf8_file_name)
391         return FALSE;
392
393       ds_init_empty (&filename);
394       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
395
396       g_free (utf8_file_name);
397
398       if (encoding && encoding[0])
399         syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
400                                   ds_cstr (&filename), encoding);
401       else
402         syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
403       ds_destroy (&filename);
404     }
405   else
406     {
407       syntax = syn;
408     }
409
410   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
411                        lex_reader_for_string (syntax, "UTF-8"));
412   g_free (syntax);
413
414   if (ok && syn == NULL)
415     {
416       if (name_has_por_suffix (file_name))
417         mime_type = "application/x-spss-por";
418       else if (name_has_sav_suffix (file_name))
419         mime_type = "application/x-spss-sav";
420
421       add_most_recent (file_name, mime_type, encoding);
422     }
423
424   return ok;
425 }
426
427 static const char *
428 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
429 {
430   if (format == PSPPIRE_DATA_WINDOW_SAV)
431     return ".sav";
432   else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
433     return ".zsav";
434   else
435     return ".por";
436 }
437
438 /* Save DE to file */
439 static void
440 save_file (PsppireWindow *w)
441 {
442   const gchar *file_name = NULL;
443   gchar *utf8_file_name = NULL;
444   GString *fnx;
445   struct string filename ;
446   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
447   gchar *syntax;
448
449   file_name = psppire_window_get_filename (w);
450
451   fnx = g_string_new (file_name);
452
453   if ( ! name_has_suffix (fnx->str))
454     g_string_append (fnx, psppire_data_window_format_to_string (de->format));
455
456   ds_init_empty (&filename);
457
458   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
459
460   g_string_free (fnx, TRUE);
461
462   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
463   g_free (utf8_file_name);
464
465   if (de->format == PSPPIRE_DATA_WINDOW_SAV)
466     syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
467   else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
468     syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
469                               ds_cstr (&filename));
470   else
471     syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
472
473   ds_destroy (&filename);
474
475   g_free (execute_syntax_string (de, syntax));
476 }
477
478
479 static void
480 display_dict (PsppireDataWindow *de)
481 {
482   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
483 }
484
485 static void
486 sysfile_info (PsppireDataWindow *de)
487 {
488   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
489
490   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
491     {
492       struct string filename;
493       gchar *file_name =
494         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
495       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
496                                                   NULL);
497
498       const gchar *encoding = psppire_encoding_selector_get_encoding (
499                                                                       gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
500
501       gchar *syntax;
502
503       ds_init_empty (&filename);
504
505       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
506
507       g_free (utf8_file_name);
508
509       if (encoding)
510         syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
511                                   ds_cstr (&filename), encoding);
512       else
513         syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
514       g_free (execute_syntax_string (de, syntax));
515     }
516
517   gtk_widget_destroy (dialog);
518 }
519
520
521 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
522 static void
523 data_pick_filename (PsppireWindow *window)
524 {
525   GtkListStore *list_store;
526   GtkWidget *combo_box;
527
528   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
529   GtkFileFilter *filter;
530   GtkWidget *dialog =
531     gtk_file_chooser_dialog_new (_("Save"),
532                                  GTK_WINDOW (de),
533                                  GTK_FILE_CHOOSER_ACTION_SAVE,
534                                  _("Cancel"), GTK_RESPONSE_CANCEL,
535                                  _("Save"), GTK_RESPONSE_ACCEPT,
536                                  NULL);
537
538   g_object_set (dialog, "local-only", FALSE, NULL);
539
540   filter = gtk_file_filter_new ();
541   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
542   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
543   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
544
545   filter = gtk_file_filter_new ();
546   gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
547   gtk_file_filter_add_pattern (filter, "*.zsav");
548   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
549
550   filter = gtk_file_filter_new ();
551   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
552   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
553   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
554
555   filter = gtk_file_filter_new ();
556   gtk_file_filter_set_name (filter, _("All Files"));
557   gtk_file_filter_add_pattern (filter, "*");
558   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
559   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
560
561   {
562     GtkCellRenderer *cell;
563     GtkWidget *label;
564     GtkTreeIter iter;
565     GtkWidget *hbox;
566
567     list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
568     combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
569
570     gtk_list_store_append (list_store, &iter);
571     gtk_list_store_set (list_store, &iter,
572                         0, PSPPIRE_DATA_WINDOW_SAV,
573                         1, _("System File"),
574                         -1);
575     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
576
577     gtk_list_store_append (list_store, &iter);
578     gtk_list_store_set (list_store, &iter,
579                         0, PSPPIRE_DATA_WINDOW_ZSAV,
580                         1, _("Compressed System File"),
581                         -1);
582
583     gtk_list_store_append (list_store, &iter);
584     gtk_list_store_set (list_store, &iter,
585                         0, PSPPIRE_DATA_WINDOW_POR,
586                         1, _("Portable File"),
587                         -1);
588
589     label = gtk_label_new (_("Format:"));
590
591     cell = gtk_cell_renderer_text_new ();
592     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
593     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
594                                    "text", 1);
595
596     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
597     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
598     gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
599     gtk_widget_show_all (hbox);
600
601     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
602   }
603
604   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
605                                                   TRUE);
606
607   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
608     {
609     case GTK_RESPONSE_ACCEPT:
610       {
611         GString *filename =
612           g_string_new
613           (
614            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
615            );
616
617         GtkTreeIter iter;
618         int format;
619
620         gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
621         gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
622                             0, &format,
623                             -1);
624         de->format = format;
625
626         if ( ! name_has_suffix (filename->str))
627           g_string_append (filename,
628                            psppire_data_window_format_to_string (format));
629
630         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
631
632         g_string_free (filename, TRUE);
633       }
634       break;
635     default:
636       break;
637     }
638
639   gtk_widget_destroy (dialog);
640 }
641
642 static bool
643 confirm_delete_dataset (PsppireDataWindow *de,
644                         const char *old_dataset,
645                         const char *new_dataset,
646                         const char *existing_dataset)
647 {
648   GtkWidget *dialog;
649   int result;
650
651   dialog = gtk_message_dialog_new (
652                                    GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
653                                    _("Delete Existing Dataset?"));
654
655   gtk_message_dialog_format_secondary_text (
656                                             GTK_MESSAGE_DIALOG (dialog),
657                                             _("Renaming \"%s\" to \"%s\" will destroy the existing "
658                                               "dataset named \"%s\".  Are you sure that you want to do this?"),
659                                             old_dataset, new_dataset, existing_dataset);
660
661   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
662                           _("Cancel"), GTK_RESPONSE_CANCEL,
663                           _("Delete"), GTK_RESPONSE_OK,
664                           NULL);
665
666   g_object_set (dialog, "icon-name", "pspp", NULL);
667
668   result = gtk_dialog_run (GTK_DIALOG (dialog));
669
670   gtk_widget_destroy (dialog);
671
672   return result == GTK_RESPONSE_OK;
673 }
674
675 static void
676 on_rename_dataset (PsppireDataWindow *de)
677 {
678   struct dataset *ds = de->dataset;
679   struct session *session = dataset_session (ds);
680   const char *old_name = dataset_name (ds);
681   struct dataset *existing_dataset;
682   char *new_name;
683   char *prompt;
684
685   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
686                       old_name);
687   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
688                                old_name);
689   free (prompt);
690
691   if (new_name == NULL)
692     return;
693
694   existing_dataset = session_lookup_dataset (session, new_name);
695   if (existing_dataset == NULL || existing_dataset == ds
696       || confirm_delete_dataset (de, old_name, new_name,
697                                  dataset_name (existing_dataset)))
698     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
699                                                         new_name)));
700
701   free (new_name);
702 }
703
704
705 static void
706 status_bar_activate (GAction *action, GVariant *param,  PsppireDataWindow  *de)
707 {
708   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
709
710   GVariant *state = g_action_get_state (action);
711   const gboolean visible = g_variant_get_boolean (state);
712   g_action_change_state (action, g_variant_new_boolean (!visible));
713
714   gtk_widget_set_visible (statusbar, !visible);
715 }
716
717
718 static void
719 grid_lines_activate (GAction *action, GVariant *param,  PsppireDataWindow  *de)
720 {
721   GVariant *state = g_action_get_state (action);
722   const gboolean grid_visible = g_variant_get_boolean (state);
723   g_action_change_state (action, g_variant_new_boolean (!grid_visible));
724
725   psppire_data_editor_show_grid (de->data_editor, !grid_visible);
726 }
727
728
729 static void
730 on_switch_page (GtkNotebook *notebook, GtkWidget *page, guint pn, gpointer ud)
731 {
732   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (ud);
733
734   GAction *action = g_action_map_lookup_action (G_ACTION_MAP (de), "view_dv");
735
736   switch (pn)
737     {
738     case 0:
739       g_action_change_state (action, g_variant_new_string ("DATA"));
740       gtk_widget_show (GTK_WIDGET (de->ti_insert_case));
741       gtk_widget_show (GTK_WIDGET (de->ti_jump_to_case));
742       gtk_widget_show (GTK_WIDGET (de->ti_find));
743
744       gtk_widget_show (GTK_WIDGET (de->mi_go_to_case));
745       gtk_widget_show (GTK_WIDGET (de->mi_insert_case));
746       gtk_widget_show (GTK_WIDGET (de->mi_find));
747       gtk_widget_show (GTK_WIDGET (de->mi_find_separator));
748       gtk_widget_show (GTK_WIDGET (de->mi_clear_cases));
749
750       break;
751
752     case 1:
753       g_action_change_state (action, g_variant_new_string ("VARS"));
754       gtk_widget_hide (GTK_WIDGET (de->ti_insert_case));
755       gtk_widget_hide (GTK_WIDGET (de->ti_jump_to_case));
756       gtk_widget_hide (GTK_WIDGET (de->ti_find));
757
758       gtk_widget_hide (GTK_WIDGET (de->mi_go_to_case));
759       gtk_widget_hide (GTK_WIDGET (de->mi_insert_case));
760       gtk_widget_hide (GTK_WIDGET (de->mi_find));
761       gtk_widget_hide (GTK_WIDGET (de->mi_find_separator));
762       gtk_widget_hide (GTK_WIDGET (de->mi_clear_cases));
763
764       break;
765     }
766 }
767
768
769 static void
770 activate_change_view (GAction *action, GVariant *param, PsppireDataWindow  *de)
771 {
772   g_action_change_state (action, param);
773   GVariant *new_state = g_action_get_state (action);
774
775   const gchar *what = g_variant_get_string (new_state, NULL);
776   if (0 == g_strcmp0 (what, "DATA"))
777     {
778       gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
779     }
780   else if (0 == g_strcmp0 (what, "VARS"))
781     {
782       gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
783     }
784 }
785
786
787
788 static void
789 fonts_activate (PsppireDataWindow  *de)
790 {
791   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
792   GtkWidget *dialog =  gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
793   GtkStyleContext *style = gtk_widget_get_style_context (GTK_WIDGET(de->data_editor));
794   const PangoFontDescription *current_font ;
795
796   gtk_style_context_get (style, GTK_STATE_FLAG_NORMAL, "font", &current_font, NULL);
797
798   gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
799
800   gtk_window_set_transient_for (GTK_WINDOW (dialog),
801                                 GTK_WINDOW (toplevel));
802
803   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
804     {
805       PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
806
807       psppire_data_editor_set_font (de->data_editor, font_desc);
808     }
809
810   gtk_widget_hide (dialog);
811 }
812
813
814
815 /* Callback for the value labels action */
816
817 static void
818 value_labels_activate (GAction *action, GVariant *param,  PsppireDataWindow  *de)
819 {
820   GVariant *v = g_action_get_state (action);
821   gboolean labels_active = g_variant_get_boolean (v);
822   g_action_change_state (action, g_variant_new_boolean (!labels_active));
823
824   GVariant *new_state  = g_action_get_state (action);
825   labels_active = g_variant_get_boolean (new_state);
826   g_object_set (de->data_editor, "value-labels", labels_active, NULL);
827
828   gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (de->ti_value_labels_button),
829                                      labels_active);
830 }
831
832 static void
833 on_labels_button_toggle (GtkToggleToolButton *ttb, PsppireDataWindow *de)
834 {
835   GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de), "value_labels");
836   g_assert (a);
837   gboolean labels_active = gtk_toggle_tool_button_get_active (ttb);
838
839   g_action_change_state (a, g_variant_new_boolean (labels_active));
840
841   GVariant *new_state  = g_action_get_state (a);
842   labels_active = g_variant_get_boolean (new_state);
843   g_object_set (de->data_editor, "value-labels", labels_active, NULL);
844 }
845
846 static void
847 on_recent_data_select (GtkMenuShell *menushell,
848                        PsppireWindow *window)
849 {
850   gchar *file;
851
852   gchar *uri =
853     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
854
855   file = g_filename_from_uri (uri, NULL, NULL);
856
857   g_free (uri);
858
859   open_data_window (window, file, NULL, NULL);
860
861   g_free (file);
862 }
863
864 static char *
865 charset_from_mime_type (const char *mime_type)
866 {
867   const char *charset;
868   struct string s;
869   const char *p;
870
871   if (mime_type == NULL)
872     return NULL;
873
874   charset = c_strcasestr (mime_type, "charset=");
875   if (charset == NULL)
876     return NULL;
877
878   ds_init_empty (&s);
879   p = charset + 8;
880   if (*p == '"')
881     {
882       /* Parse a "quoted-string" as defined by RFC 822. */
883       for (p++; *p != '\0' && *p != '"'; p++)
884         {
885           if (*p != '\\')
886             ds_put_byte (&s, *p);
887           else if (*++p != '\0')
888             ds_put_byte (&s, *p);
889         }
890     }
891   else
892     {
893       /* Parse a "token" as defined by RFC 2045. */
894       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
895         ds_put_byte (&s, *p++);
896     }
897   if (!ds_is_empty (&s))
898     return ds_steal_cstr (&s);
899
900   ds_destroy (&s);
901   return NULL;
902 }
903
904 static void
905 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
906 {
907   GtkRecentInfo *item;
908   char *encoding;
909   GtkWidget *se;
910   gchar *file;
911
912   /* Get the file name and its encoding. */
913   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
914   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
915   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
916   gtk_recent_info_unref (item);
917
918   se = psppire_syntax_window_new (encoding);
919
920   free (encoding);
921
922   if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
923     gtk_widget_show (se);
924   else
925     gtk_widget_destroy (se);
926
927   g_free (file);
928 }
929
930 static void
931 set_unsaved (gpointer w)
932 {
933   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
934 }
935
936
937 /* Only a data file with at least one variable can be saved. */
938 static void
939 enable_save (PsppireDataWindow *dw)
940 {
941   gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
942
943   GAction *save_as = g_action_map_lookup_action (G_ACTION_MAP (dw), "save-as");
944   GAction *save = g_action_map_lookup_action (G_ACTION_MAP (dw), "save");
945
946   if (save)
947     g_object_set (save, "enabled", enable, NULL);
948
949   if (save_as)
950     g_object_set (save_as, "enabled", enable, NULL);
951 }
952
953 /* Initializes as much of a PsppireDataWindow as we can and must before the
954    dataset has been set.
955
956    In particular, the 'menu' member is required in case the "filename" property
957    is set before the "dataset" property: otherwise PsppireWindow will try to
958    modify the menu as part of the "filename" property_set() function and end up
959    with a Gtk-CRITICAL since 'menu' is NULL.  */
960 static void
961 psppire_data_window_init (PsppireDataWindow *de)
962 {
963   de->builder = builder_new ("data-editor.ui");
964 }
965
966 static void
967 file_import (PsppireDataWindow *dw)
968 {
969   GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
970   PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
971   gtk_widget_show_all (w);
972
973   asst->main_loop = g_main_loop_new (NULL, TRUE);
974   g_main_loop_run (asst->main_loop);
975   g_main_loop_unref (asst->main_loop);
976
977 #if TEXT_FILE
978   if (!asst->file_name)
979     goto end;
980
981
982   switch (asst->response)
983     {
984     case GTK_RESPONSE_APPLY:
985       {
986         gchar *fn = g_path_get_basename (asst->file_name);
987         open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
988         g_free (fn);
989       }
990       break;
991     case PSPPIRE_RESPONSE_PASTE:
992       free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
993       break;
994     default:
995       break;
996     }
997 #endif
998  end:
999   gtk_widget_destroy (GTK_WIDGET (asst));
1000 }
1001
1002
1003
1004 static void
1005 connect_dialog_action (GType type, PsppireDataWindow *de)
1006 {
1007   GAction *act = g_object_new (type,
1008                                "top-level", de,
1009                                NULL);
1010
1011   g_action_map_add_action (G_ACTION_MAP (de), act);
1012 }
1013
1014 static void
1015 g_action_activate_null (GAction *a)
1016 {
1017   g_action_activate (a, NULL);
1018 }
1019
1020 static void
1021 connect_action_to_menuitem (GActionMap *map, const gchar *action_name, GtkWidget *w, const gchar *accel)
1022 {
1023   GAction *a = g_action_map_lookup_action (map, action_name);
1024
1025   if (NULL == a)
1026     g_error ("Action \"%s\" not found in map", action_name);
1027
1028   if (accel)
1029     {
1030       GtkApplication *app = GTK_APPLICATION (g_application_get_default());
1031
1032       /* First set the label for the accellerator so that it appears
1033          on the menuitem */
1034       GtkWidget *child = gtk_bin_get_child (GTK_BIN (w));
1035       guint key;
1036       GdkModifierType modifier;
1037       gtk_accelerator_parse (accel, &key, &modifier);
1038       gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child), key, modifier);
1039
1040       /* Now tell the application that it must do something when that
1041          key combination is pressed */
1042       const gchar *accels[2];
1043       accels[0] = accel;
1044       accels[1] = NULL;
1045
1046       gchar *detailed_action_name = NULL;
1047       if (GTK_IS_WINDOW (map))
1048         detailed_action_name = g_strdup_printf ("win.%s", action_name);
1049       else if (GTK_IS_APPLICATION (map))
1050         detailed_action_name = g_strdup_printf ("app.%s", action_name);
1051
1052       gtk_application_set_accels_for_action (app,
1053                                              detailed_action_name,
1054                                              accels);
1055       free (detailed_action_name);
1056     }
1057
1058   g_signal_connect_swapped (w, "activate", G_CALLBACK (g_action_activate_null), a);
1059  }
1060
1061
1062 static void
1063 set_data_page (PsppireDataWindow *dw)
1064 {
1065   gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 1);
1066   gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 0);
1067 }
1068
1069
1070 static void
1071 on_cut (PsppireDataWindow *dw)
1072 {
1073   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1074   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1075   {
1076       PsppireDict *dict = NULL;
1077       g_object_get (dw->data_editor, "dictionary", &dict, NULL);
1078
1079       gint x, y;
1080       JmdSheet *sheet = JMD_SHEET (dw->data_editor->data_sheet);
1081       JmdRange sel = *sheet->selection;
1082
1083       GtkClipboard *clip =
1084         gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)),
1085                                        GDK_SELECTION_CLIPBOARD);
1086
1087       /* Save the selected area to a string */
1088       GString *str = g_string_new ("");
1089       for (y = sel.start_y ; y <= sel.end_y; ++y)
1090         {
1091           for (x = sel.start_x ; x <= sel.end_x; ++x)
1092             {
1093               const struct variable * var = psppire_dict_get_variable (dict, x);
1094               gchar *s = psppire_data_store_get_string (dw->data_editor->data_store,
1095                                                           y, var, FALSE);
1096               g_string_append (str, s);
1097               g_string_append (str, "\t");
1098               g_free (s);
1099             }
1100           g_string_append (str, "\n");
1101         }
1102
1103       gtk_clipboard_set_text (clip, str->str, str->len);
1104       g_string_free (str, TRUE);
1105
1106       /* Now fill the selected area with SYSMIS */
1107       union value sm ;
1108       sm.f = SYSMIS;
1109       for (x = sel.start_x ; x <= sel.end_x; ++x)
1110         {
1111           const struct variable * var = psppire_dict_get_variable (dict, x);
1112           for (y = sel.start_y ; y <= sel.end_y; ++y)
1113             {
1114               psppire_data_store_set_value (dw->data_editor->data_store,
1115                                             y,
1116                                             var, &sm);
1117             }
1118         }
1119
1120     }
1121 }
1122
1123 static void
1124 on_copy (PsppireDataWindow *dw)
1125 {
1126   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1127   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1128     {
1129       GtkClipboard *clip =
1130         gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)),
1131                                    GDK_SELECTION_CLIPBOARD);
1132
1133       jmd_sheet_set_clip (JMD_SHEET (dw->data_editor->data_sheet), clip);
1134     }
1135 }
1136
1137 static void
1138 on_paste (PsppireDataWindow *dw)
1139 {
1140   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1141   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1142     {
1143       psppire_data_editor_paste (dw->data_editor);
1144     }
1145 }
1146
1147 static void
1148 on_clear_cases (PsppireDataWindow *dw)
1149 {
1150   PsppireDataEditor *de = dw->data_editor;
1151   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1152   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1153     {
1154       JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1155       psppire_data_store_delete_cases (de->data_store, range->start_y,
1156                                        range->end_y - range->start_y + 1);
1157       gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet));
1158     }
1159 }
1160
1161 static void
1162 on_clear_variables (PsppireDataWindow *dw)
1163 {
1164   PsppireDataEditor *de = dw->data_editor;
1165   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1166   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1167     {
1168       psppire_data_editor_data_delete_variables (de);
1169     }
1170   else
1171     {
1172       psppire_data_editor_var_delete_variables (de);
1173     }
1174 }
1175
1176 static void
1177 insert_variable (PsppireDataWindow *dw)
1178 {
1179   PsppireDataEditor *de = dw->data_editor;
1180   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1181
1182   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1183     {
1184       JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1185       psppire_data_editor_insert_new_variable_at_posn (de, range->start_x);
1186     }
1187   else
1188     {
1189       JmdRange *range = JMD_SHEET(de->var_sheet)->selection;
1190       psppire_data_editor_insert_new_variable_at_posn (de, range->start_y);
1191     }
1192 }
1193
1194 static void
1195 insert_case_at_row (PsppireDataWindow *dw)
1196 {
1197   PsppireDataEditor *de = dw->data_editor;
1198   JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1199   psppire_data_editor_insert_new_case_at_posn (de, range->start_y);
1200 }
1201
1202 static void
1203 goto_case (PsppireDataWindow *dw)
1204 {
1205   PsppireDataEditor *de = dw->data_editor;
1206   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1207   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1208     {
1209       goto_case_dialog (JMD_SHEET (de->data_sheet));
1210     }
1211 }
1212
1213 static GtkWidget *
1214 create_file_menu (PsppireDataWindow *dw)
1215 {
1216   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_File"));
1217   GtkWidget *menu = gtk_menu_new ();
1218
1219   {
1220     GtkWidget *new = gtk_menu_item_new_with_mnemonic (_("_New"));
1221     gtk_menu_attach (GTK_MENU (menu), new,        0, 1, 0, 1);
1222
1223     GtkWidget *new_menu = gtk_menu_new ();
1224
1225     g_object_set (new, "submenu", new_menu, NULL);
1226
1227     GtkWidget *syntax  = gtk_menu_item_new_with_mnemonic (_("_Syntax"));
1228     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-syntax", syntax, 0);
1229
1230     GtkWidget *data = gtk_menu_item_new_with_mnemonic (_("_Data"));
1231     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-data", data, 0);
1232
1233     gtk_menu_attach (GTK_MENU (new_menu), syntax,    0, 1, 0, 1);
1234     gtk_menu_attach (GTK_MENU (new_menu), data,      0, 1, 1, 2);
1235   }
1236
1237   GtkWidget *open = gtk_menu_item_new_with_mnemonic (_("_Open"));
1238   connect_action_to_menuitem (G_ACTION_MAP (dw), "open", open, "<Ctrl>O");
1239
1240   GtkWidget *import = gtk_menu_item_new_with_mnemonic (_("_Import Data..."));
1241   connect_action_to_menuitem (G_ACTION_MAP (dw), "file-import", import, 0);
1242
1243   gtk_menu_attach (GTK_MENU (menu), open,       0, 1, 1, 2);
1244   gtk_menu_attach (GTK_MENU (menu), import,     0, 1, 2, 3);
1245
1246   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 3, 4);
1247
1248   GtkWidget *save = gtk_menu_item_new_with_mnemonic (_("_Save..."));
1249   connect_action_to_menuitem (G_ACTION_MAP (dw), "save", save, "<Ctrl>S");
1250
1251   GtkWidget *save_as = gtk_menu_item_new_with_mnemonic (_("Save _As..."));
1252   connect_action_to_menuitem (G_ACTION_MAP (dw), "save-as", save_as, "<Shift><Ctrl>S");
1253
1254   GtkWidget *rename_dataset = gtk_menu_item_new_with_mnemonic (_("_Rename Dataset..."));
1255   connect_action_to_menuitem (G_ACTION_MAP (dw), "rename-dataset", rename_dataset, 0);
1256
1257
1258   gtk_menu_attach (GTK_MENU (menu), save,        0, 1, 4, 5);
1259   gtk_menu_attach (GTK_MENU (menu), save_as,     0, 1, 5, 6);
1260   gtk_menu_attach (GTK_MENU (menu), rename_dataset,     0, 1, 6, 7);
1261
1262   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 7, 8);
1263
1264   {
1265     GtkWidget *display_data = gtk_menu_item_new_with_mnemonic (_("_Display Data File Information"));
1266     gtk_menu_attach (GTK_MENU (menu), display_data,     0, 1, 8, 9);
1267
1268     GtkWidget *dd_menu = gtk_menu_new ();
1269
1270     g_object_set (display_data, "submenu", dd_menu, NULL);
1271
1272     GtkWidget *working_file  = gtk_menu_item_new_with_mnemonic (_("Working File"));
1273     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-working", working_file, 0);
1274     GtkWidget *external_file = gtk_menu_item_new_with_mnemonic (_("_External File..."));
1275     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-external", external_file, 0);
1276
1277     gtk_menu_attach (GTK_MENU (dd_menu), working_file,    0, 1, 0, 1);
1278     gtk_menu_attach (GTK_MENU (dd_menu), external_file,   0, 1, 1, 2);
1279   }
1280
1281   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 9, 10);
1282
1283   {
1284     GtkWidget *mi_data = gtk_menu_item_new_with_mnemonic (_("_Recently Used Data"));
1285     GtkWidget *mi_files = gtk_menu_item_new_with_mnemonic (_("Recently Used _Files"));
1286
1287     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1288       gtk_recent_manager_get_default ());
1289
1290     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1291       gtk_recent_manager_get_default ());
1292
1293     gtk_menu_attach (GTK_MENU (menu), mi_data,       0, 1, 10, 11);
1294     gtk_menu_attach (GTK_MENU (menu), mi_files,      0, 1, 11, 12);
1295
1296     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1297     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1298
1299     g_object_set (mi_data, "submenu",  menu_data, NULL);
1300     g_object_set (mi_files, "submenu", menu_files, NULL);
1301
1302     {
1303       GtkRecentFilter *filter = gtk_recent_filter_new ();
1304
1305       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1306       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1307
1308       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1309
1310       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1311     }
1312
1313     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), dw);
1314
1315     {
1316       GtkRecentFilter *filter = gtk_recent_filter_new ();
1317
1318       gtk_recent_filter_add_pattern (filter, "*.sps");
1319       gtk_recent_filter_add_pattern (filter, "*.SPS");
1320
1321       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1322
1323       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1324     }
1325
1326     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), dw);
1327   }
1328
1329   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 12, 13);
1330
1331   {
1332     GtkWidget *quit = gtk_menu_item_new_with_mnemonic (_("_Quit"));
1333     gtk_menu_attach (GTK_MENU (menu), quit,     0, 1, 13, 14);
1334
1335     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()),
1336                                 "quit", quit, "<Ctrl>Q");
1337   }
1338
1339   g_object_set (menuitem, "submenu", menu, NULL);
1340   gtk_widget_show_all (menuitem);
1341
1342   return menuitem;
1343 }
1344
1345
1346 static GtkWidget *
1347 create_edit_menu (PsppireDataWindow *dw)
1348 {
1349   int i = 0;
1350   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit"));
1351
1352   GtkWidget *menu = gtk_menu_new ();
1353
1354   dw->mi_insert_var = gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
1355   dw->mi_insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
1356   GtkWidget *go_to_variable = gtk_menu_item_new_with_mnemonic (_("_Go To Variable..."));
1357   dw->mi_go_to_case = gtk_menu_item_new_with_mnemonic (_("_Go To Case..."));
1358
1359   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_var,        0, 1, i, i + 1); ++i;
1360   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_case,     0, 1, i, i + 1); ++i;
1361
1362   g_signal_connect_swapped (dw->mi_insert_case, "activate", G_CALLBACK (insert_case_at_row), dw);
1363   g_signal_connect_swapped (dw->mi_go_to_case, "activate", G_CALLBACK (goto_case), dw);
1364   g_signal_connect_swapped (dw->mi_insert_var, "activate", G_CALLBACK (insert_variable), dw);
1365
1366   GAction *a = g_action_map_lookup_action (G_ACTION_MAP (dw),  "PsppireDialogActionVarInfo");
1367   g_assert (a);
1368   g_signal_connect_swapped (go_to_variable, "activate", G_CALLBACK (psppire_dialog_action_activate_null), a);
1369
1370   gtk_menu_attach (GTK_MENU (menu), go_to_variable,         0, 1, i, i + 1); ++i;
1371   gtk_menu_attach (GTK_MENU (menu), dw->mi_go_to_case,      0, 1, i, i + 1); ++i;
1372
1373   {
1374     GtkAccelGroup *ag = gtk_accel_group_new ();
1375
1376     dw->mi_edit_separator = gtk_separator_menu_item_new ();
1377     gtk_menu_attach (GTK_MENU (menu), dw->mi_edit_separator, 0, 1, i, i + 1); ++i;
1378
1379     dw->mi_cut = gtk_menu_item_new_with_mnemonic (_("Cu_t"));
1380     gtk_menu_attach (GTK_MENU (menu), dw->mi_cut,     0, 1, i, i + 1); ++i;
1381     g_signal_connect_swapped (dw->mi_cut, "activate", G_CALLBACK (on_cut), dw);
1382
1383     gtk_window_add_accel_group (GTK_WINDOW (dw), ag);
1384     gtk_widget_add_accelerator (dw->mi_cut, "activate", ag,
1385                                 'X', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1386
1387     dw->mi_copy = gtk_menu_item_new_with_mnemonic (_("_Copy"));
1388     gtk_menu_attach (GTK_MENU (menu), dw->mi_copy,     0, 1, i, i + 1); ++i;
1389     g_signal_connect_swapped (dw->mi_copy, "activate", G_CALLBACK (on_copy), dw);
1390     gtk_widget_add_accelerator (dw->mi_copy, "activate", ag,
1391                                 'C', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1392
1393     dw->mi_paste = gtk_menu_item_new_with_mnemonic (_("_Paste"));
1394     gtk_menu_attach (GTK_MENU (menu), dw->mi_paste,     0, 1, i, i + 1); ++i;
1395     g_signal_connect_swapped (dw->mi_paste, "activate", G_CALLBACK (on_paste), dw);
1396     gtk_widget_add_accelerator (dw->mi_paste, "activate", ag,
1397                                 'V', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1398
1399     dw->mi_clear_variables = gtk_menu_item_new_with_mnemonic (_("Clear _Variables"));
1400     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_variables,     0, 1, i, i + 1); ++i;
1401     g_signal_connect_swapped (dw->mi_clear_variables, "activate", G_CALLBACK (on_clear_variables), dw);
1402
1403     dw->mi_clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
1404     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_cases,     0, 1, i, i + 1); ++i;
1405     g_signal_connect_swapped (dw->mi_clear_cases, "activate", G_CALLBACK (on_clear_cases), dw);
1406   }
1407
1408   {
1409     dw->mi_find_separator = gtk_separator_menu_item_new ();
1410     gtk_menu_attach (GTK_MENU (menu), dw->mi_find_separator, 0, 1, i, i + 1); ++i;
1411
1412     dw->mi_find = gtk_menu_item_new_with_mnemonic (_("_Find..."));
1413     g_signal_connect_swapped (dw->mi_find, "activate", G_CALLBACK (find_dialog), dw);
1414     gtk_menu_attach (GTK_MENU (menu), dw->mi_find,    0, 1,  i, i + 1); ++i;
1415   }
1416
1417   {
1418     dw->mi_options = gtk_menu_item_new_with_mnemonic (_("_Options..."));
1419     g_signal_connect_swapped (dw->mi_options, "activate",
1420                               G_CALLBACK (options_dialog), dw);
1421     gtk_menu_attach (GTK_MENU (menu), dw->mi_options, 0, 1,  i, i + 1); ++i;
1422   }
1423
1424   g_object_set (menuitem, "submenu", menu, NULL);
1425
1426   gtk_widget_show_all (menuitem);
1427
1428   return menuitem;
1429 }
1430
1431 static void
1432 psppire_data_window_finish_init (PsppireDataWindow *de,
1433                                  struct dataset *ds)
1434 {
1435   static const struct dataset_callbacks cbs =
1436     {
1437       set_unsaved,                    /* changed */
1438       transformation_change_callback, /* transformations_changed */
1439     };
1440
1441   GtkWidget *menubar;
1442   GtkWidget *hb ;
1443   GtkWidget *sb ;
1444
1445   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1446
1447   de->dataset = ds;
1448   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
1449   de->data_store = psppire_data_store_new (de->dict);
1450   psppire_data_store_set_reader (de->data_store, NULL);
1451
1452   GObject *menu = get_object_assert (de->builder, "data-editor-menu", G_TYPE_MENU);
1453   menubar = gtk_menu_bar_new_from_model (G_MENU_MODEL (menu));
1454   gtk_widget_show (menubar);
1455
1456   hb = gtk_toolbar_new ();
1457   sb = get_widget_assert (de->builder, "status-bar");
1458
1459   de->data_editor =
1460     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
1461
1462   g_signal_connect (de, "realize",
1463                     G_CALLBACK (set_data_page), de);
1464
1465   g_signal_connect_swapped (de->data_store, "case-changed",
1466                             G_CALLBACK (set_unsaved), de);
1467
1468   dataset_set_callbacks (de->dataset, &cbs, de);
1469
1470   connect_help (de->builder);
1471
1472   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1473   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1474   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1475   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1476
1477   gtk_container_add (GTK_CONTAINER (de), box);
1478
1479   g_signal_connect (de->dict, "weight-changed",
1480                     G_CALLBACK (on_weight_change),
1481                     de);
1482
1483   g_signal_connect (de->dict, "filter-changed",
1484                     G_CALLBACK (on_filter_change),
1485                     de);
1486
1487   g_signal_connect (de->dict, "split-changed",
1488                     G_CALLBACK (on_split_change),
1489                     de);
1490
1491   g_signal_connect_swapped (de->dict, "items-changed",
1492                             G_CALLBACK (enable_save), de);
1493   g_signal_connect_swapped (de->dict, "variable-inserted",
1494                             G_CALLBACK (enable_save), de);
1495   g_signal_connect_swapped (de->dict, "variable-deleted",
1496                             G_CALLBACK (enable_save), de);
1497   enable_save (de);
1498
1499   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SORT,  de);
1500   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SPLIT,  de);
1501   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FLIP,  de);
1502   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AGGREGATE,  de);
1503   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_WEIGHT,  de);
1504
1505   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMPUTE,  de);
1506   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COUNT,  de);
1507   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AUTORECODE,  de);
1508   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RANK,  de);
1509   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SELECT,  de);
1510   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME,  de);
1511   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT,  de);
1512
1513
1514   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_DESCRIPTIVES,  de);
1515   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FREQUENCIES,  de);
1516   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_EXAMINE,  de);
1517   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CROSSTABS,  de);
1518
1519   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_INDEP_SAMPS,  de);
1520   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_PAIRED,  de);
1521
1522   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_MEANS,  de);
1523   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TT1S,  de);
1524
1525   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ONEWAY, de);
1526   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_UNIVARIATE, de);
1527   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_KMEANS, de);
1528   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FACTOR, de);
1529   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CORRELATION, de);
1530   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RELIABILITY, de);
1531   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_REGRESSION, de);
1532   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_LOGISTIC, de);
1533   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ROC, de);
1534
1535   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMMENTS, de);
1536   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_VAR_INFO, de);
1537
1538   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, de);
1539   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, de);
1540   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, de);
1541
1542   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CHISQUARE, de);
1543   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BINOMIAL, de);
1544   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RUNS, de);
1545   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_1SKS, de);
1546   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TWO_SAMPLE, de);
1547   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_RELATED, de);
1548
1549   {
1550     GSimpleAction *file_import_action = g_simple_action_new ("file-import", NULL);
1551     g_signal_connect_swapped (file_import_action, "activate", G_CALLBACK (file_import), de);
1552     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (file_import_action));
1553   }
1554
1555   {
1556     GSimpleAction *save = g_simple_action_new ("save", NULL);
1557     g_signal_connect_swapped (save, "activate", G_CALLBACK (psppire_window_save), de);
1558     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save));
1559   }
1560
1561   {
1562     GSimpleAction *open = g_simple_action_new ("open", NULL);
1563     g_signal_connect_swapped (open, "activate", G_CALLBACK (psppire_window_open), de);
1564     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (open));
1565   }
1566
1567   {
1568     GSimpleAction *save_as = g_simple_action_new ("save-as", NULL);
1569     g_signal_connect_swapped (save_as, "activate", G_CALLBACK (psppire_window_save_as), de);
1570     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save_as));
1571   }
1572
1573   {
1574     GSimpleAction *rename_dataset_act = g_simple_action_new ("rename-dataset", NULL);
1575     g_signal_connect_swapped (rename_dataset_act, "activate",
1576                               G_CALLBACK (on_rename_dataset), de);
1577     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (rename_dataset_act));
1578   }
1579
1580   {
1581     GSimpleAction *info_working = g_simple_action_new ("info-working", NULL);
1582     g_signal_connect_swapped (info_working, "activate", G_CALLBACK (display_dict), de);
1583     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_working));
1584   }
1585   {
1586     GSimpleAction *info_external = g_simple_action_new ("info-external", NULL);
1587     g_signal_connect_swapped (info_external, "activate", G_CALLBACK (sysfile_info), de);
1588     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_external));
1589   }
1590
1591   {
1592     GSimpleAction *act_statusbar = g_simple_action_new_stateful ("statusbar", NULL, g_variant_new_boolean (TRUE));
1593     g_signal_connect (act_statusbar, "activate", G_CALLBACK (status_bar_activate), de);
1594     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_statusbar));
1595   }
1596
1597   {
1598     GSimpleAction *act_gridlines = g_simple_action_new_stateful ("gridlines", NULL, g_variant_new_boolean (TRUE));
1599     g_signal_connect (act_gridlines, "activate", G_CALLBACK (grid_lines_activate), de);
1600     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_gridlines));
1601   }
1602
1603
1604   {
1605     GSimpleAction *act_view_data = g_simple_action_new_stateful ("view_dv", G_VARIANT_TYPE_STRING,
1606                                                                  g_variant_new_string ("DATA"));
1607     g_signal_connect (act_view_data, "activate", G_CALLBACK (activate_change_view), de);
1608     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_view_data));
1609   }
1610
1611   {
1612     GSimpleAction *act_fonts = g_simple_action_new ("fonts", NULL);
1613     g_signal_connect_swapped (act_fonts, "activate", G_CALLBACK (fonts_activate), de);
1614     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_fonts));
1615   }
1616
1617   {
1618     GSimpleAction *act_value_labels =
1619       g_simple_action_new_stateful ("value_labels", NULL,
1620                                     g_variant_new_boolean (FALSE));
1621     g_signal_connect (act_value_labels, "activate", G_CALLBACK (value_labels_activate), de);
1622     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_value_labels));
1623   }
1624
1625   {
1626     GSimpleAction *act_transform_pending = g_simple_action_new ("transform-pending", NULL);
1627     g_signal_connect_swapped (act_transform_pending, "activate", G_CALLBACK (execute), de);
1628     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_transform_pending));
1629   }
1630
1631   {
1632     GSimpleAction *act_jump_to_variable = g_simple_action_new ("jump-to-variable", NULL);
1633     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_variable));
1634   }
1635
1636   {
1637     GSimpleAction *act_insert_variable = g_simple_action_new ("insert-variable", NULL);
1638     g_signal_connect_swapped (act_insert_variable, "activate", G_CALLBACK (insert_variable), de);
1639     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_variable));
1640   }
1641
1642   {
1643     GSimpleAction *act_jump_to_case = g_simple_action_new ("jump-to-case", NULL);
1644     g_signal_connect_swapped (act_jump_to_case, "activate", G_CALLBACK (goto_case), de);
1645     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_case));
1646   }
1647
1648   {
1649     GSimpleAction *act_insert_case = g_simple_action_new ("insert-case", NULL);
1650     g_signal_connect_swapped (act_insert_case, "activate", G_CALLBACK (insert_case_at_row), de);
1651     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_case));
1652   }
1653
1654   {
1655     GSimpleAction *find = g_simple_action_new ("find", NULL);
1656     g_signal_connect_swapped (find, "activate", G_CALLBACK (find_dialog), de);
1657     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (find));
1658   }
1659
1660   {
1661     int idx = 0;
1662     {
1663       GtkToolItem *ti = gtk_tool_button_new (NULL, "Open");
1664       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_open), de);
1665       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1666       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-open-data");
1667     }
1668
1669     {
1670       GtkToolItem *ti = gtk_tool_button_new (NULL, "Save");
1671       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_save), de);
1672       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1673       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-save-data");
1674     }
1675
1676     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1677
1678     {
1679       de->ti_jump_to_variable = gtk_tool_button_new (NULL, "Goto Var");
1680
1681       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "PsppireDialogActionVarInfo");
1682       g_assert (a);
1683       g_signal_connect_swapped (de->ti_jump_to_variable, "clicked",
1684                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1685
1686       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_variable, idx++);
1687       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_variable), "edit-go-to-variable");
1688       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_variable), _("Jump to variable"));
1689     }
1690
1691     {
1692       de->ti_jump_to_case = gtk_tool_button_new (NULL, "Jump to Case");
1693
1694       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "jump-to-case");
1695       g_assert (a);
1696       g_signal_connect_swapped (de->ti_jump_to_case, "clicked",
1697                                 G_CALLBACK (g_action_activate_null), a);
1698
1699       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_case, idx++);
1700       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_case), "edit-go-to-case");
1701       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_case), _("Jump to a case in the data sheet"));
1702     }
1703
1704     {
1705       de->ti_find = gtk_tool_button_new (NULL, "Find");
1706
1707       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "find");
1708       g_assert (a);
1709       g_signal_connect_swapped (de->ti_find, "clicked",
1710                                 G_CALLBACK (g_action_activate_null), a);
1711
1712
1713       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_find, idx++);
1714       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_find), "edit-find");
1715       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_find), _("Search for values in the data"));
1716     }
1717
1718     {
1719       de->ti_insert_case = gtk_tool_button_new (NULL, "Create Case");
1720       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-case");
1721       g_assert (a);
1722       g_signal_connect_swapped (de->ti_insert_case, "clicked",
1723                                 G_CALLBACK (g_action_activate_null), a);
1724
1725       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_case, idx++);
1726       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_case), "edit-insert-case");
1727       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_case), _("Create a new case at the current position"));
1728     }
1729
1730     {
1731       de->ti_insert_variable = gtk_tool_button_new (NULL, "Create Variable");
1732       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-variable");
1733       g_assert (a);
1734       g_signal_connect_swapped (de->ti_insert_variable, "clicked",
1735                                 G_CALLBACK (g_action_activate_null), a);
1736
1737       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_variable, idx++);
1738       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_variable), "edit-insert-variable");
1739       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_variable), _("Create a new variable at the current position"));
1740     }
1741
1742     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1743
1744     {
1745       GtkToolItem *ti = gtk_tool_button_new (NULL, "Split");
1746       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1747                                                "PsppireDialogActionSplit");
1748       g_assert (a);
1749       g_signal_connect_swapped (ti, "clicked",
1750                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1751       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1752       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-split-file");
1753       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Split the active dataset"));
1754     }
1755
1756     {
1757       GtkToolItem *ti = gtk_tool_button_new (NULL, "Weight");
1758       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1759                                                "PsppireDialogActionWeight");
1760       g_assert (a);
1761       g_signal_connect_swapped (ti, "clicked",
1762                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1763       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1764       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-weight-cases");
1765       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Weight cases by variable"));
1766     }
1767
1768     {
1769       de->ti_value_labels_button = gtk_toggle_tool_button_new ();
1770       gtk_tool_button_set_label (GTK_TOOL_BUTTON (de->ti_value_labels_button),
1771                                  "Value Labels");
1772       g_signal_connect (de->ti_value_labels_button, "toggled",
1773                         G_CALLBACK (on_labels_button_toggle), de);
1774       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_value_labels_button, idx++);
1775       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_value_labels_button), "view-value-labels");
1776       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_value_labels_button), _("Show/hide value labels"));
1777     }
1778   }
1779
1780
1781   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1782   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1783
1784   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_file_menu (de), 0);
1785   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_edit_menu (de), 1);
1786   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_windows_menu (GTK_WINDOW (de)));
1787   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1788
1789   g_signal_connect (de->data_editor, "switch-page",
1790                     G_CALLBACK (on_switch_page), de);
1791
1792   gtk_widget_show (GTK_WIDGET (de->data_editor));
1793   gtk_widget_show_all (box);
1794
1795   ll_push_head (&all_data_windows, &de->ll);
1796 }
1797
1798
1799 static void
1800 psppire_data_window_dispose (GObject *object)
1801 {
1802   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1803
1804   if (dw->builder != NULL)
1805     {
1806       g_object_unref (dw->builder);
1807       dw->builder = NULL;
1808     }
1809
1810   if (dw->dict)
1811     {
1812       g_signal_handlers_disconnect_by_func (dw->dict,
1813                                             G_CALLBACK (enable_save), dw);
1814       g_signal_handlers_disconnect_by_func (dw->dict,
1815                                             G_CALLBACK (on_weight_change), dw);
1816       g_signal_handlers_disconnect_by_func (dw->dict,
1817                                             G_CALLBACK (on_filter_change), dw);
1818       g_signal_handlers_disconnect_by_func (dw->dict,
1819                                             G_CALLBACK (on_split_change), dw);
1820
1821       g_object_unref (dw->dict);
1822       dw->dict = NULL;
1823     }
1824
1825   if (dw->data_store)
1826     {
1827       g_object_unref (dw->data_store);
1828       dw->data_store = NULL;
1829     }
1830
1831   if (dw->ll.next != NULL)
1832     {
1833       ll_remove (&dw->ll);
1834       dw->ll.next = NULL;
1835     }
1836
1837   if (G_OBJECT_CLASS (parent_class)->dispose)
1838     G_OBJECT_CLASS (parent_class)->dispose (object);
1839 }
1840
1841 static void
1842 psppire_data_window_finalize (GObject *object)
1843 {
1844   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1845
1846   if (dw->dataset)
1847     {
1848       struct dataset *dataset = dw->dataset;
1849       struct session *session = dataset_session (dataset);
1850
1851       dw->dataset = NULL;
1852
1853       dataset_set_callbacks (dataset, NULL, NULL);
1854       session_set_active_dataset (session, NULL);
1855       dataset_destroy (dataset);
1856     }
1857
1858   if (G_OBJECT_CLASS (parent_class)->finalize)
1859     G_OBJECT_CLASS (parent_class)->finalize (object);
1860 }
1861
1862 static void
1863 psppire_data_window_set_property (GObject         *object,
1864                                   guint            prop_id,
1865                                   const GValue    *value,
1866                                   GParamSpec      *pspec)
1867 {
1868   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1869
1870   switch (prop_id)
1871     {
1872     case PROP_DATASET:
1873       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1874       break;
1875     default:
1876       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1877       break;
1878     };
1879 }
1880
1881 static void
1882 psppire_data_window_get_property (GObject         *object,
1883                                   guint            prop_id,
1884                                   GValue          *value,
1885                                   GParamSpec      *pspec)
1886 {
1887   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1888
1889   switch (prop_id)
1890     {
1891     case PROP_DATASET:
1892       g_value_set_pointer (value, window->dataset);
1893       break;
1894     default:
1895       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1896       break;
1897     };
1898 }
1899
1900
1901
1902 GtkWidget*
1903 psppire_data_window_new (struct dataset *ds)
1904 {
1905   GtkWidget *dw;
1906
1907   if (the_session == NULL)
1908     the_session = session_create (NULL);
1909
1910   if (ds == NULL)
1911     {
1912       char *dataset_name = session_generate_dataset_name (the_session);
1913       ds = dataset_create (the_session, dataset_name);
1914       free (dataset_name);
1915     }
1916   assert (dataset_session (ds) == the_session);
1917
1918   dw = GTK_WIDGET (
1919                    g_object_new (
1920                                  psppire_data_window_get_type (),
1921                                  "description", _("Data Editor"),
1922                                  "dataset", ds,
1923                                  NULL));
1924
1925   if (dataset_name (ds) != NULL)
1926     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1927
1928
1929   GApplication *app = g_application_get_default ();
1930   gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (dw));
1931
1932   return dw;
1933 }
1934
1935
1936
1937 bool
1938 psppire_data_window_is_empty (PsppireDataWindow *dw)
1939 {
1940   return psppire_dict_get_var_cnt (dw->dict) == 0;
1941 }
1942
1943
1944 static void
1945 psppire_data_window_iface_init (PsppireWindowIface *iface)
1946 {
1947   iface->save = save_file;
1948   iface->pick_filename = data_pick_filename;
1949   iface->load = load_file;
1950 }
1951
1952 \f
1953
1954
1955 PsppireDataWindow *
1956 psppire_default_data_window (void)
1957 {
1958   if (ll_is_empty (&all_data_windows))
1959     create_data_window ();
1960   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1961 }
1962
1963
1964
1965 void
1966 psppire_data_window_set_default (PsppireDataWindow *pdw)
1967 {
1968   ll_remove (&pdw->ll);
1969   ll_push_head (&all_data_windows, &pdw->ll);
1970 }
1971
1972 void
1973 psppire_data_window_undefault (PsppireDataWindow *pdw)
1974 {
1975   ll_remove (&pdw->ll);
1976   ll_push_tail (&all_data_windows, &pdw->ll);
1977 }
1978
1979
1980
1981 PsppireDataWindow *
1982 psppire_data_window_for_dataset (struct dataset *ds)
1983 {
1984   PsppireDataWindow *pdw;
1985
1986   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1987     if (pdw->dataset == ds)
1988       return pdw;
1989
1990   return NULL;
1991 }
1992
1993 PsppireDataWindow *
1994 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1995 {
1996   PsppireDataWindow *pdw;
1997
1998   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1999     if (pdw->data_store == data_store)
2000       return pdw;
2001
2002   return NULL;
2003 }
2004
2005 GtkWindow *
2006 create_data_window (void)
2007 {
2008   GtkWidget *w = psppire_data_window_new (NULL);
2009
2010   gtk_widget_show (w);
2011
2012   return GTK_WINDOW (w);
2013 }
2014
2015 GtkWindow *
2016 open_data_window (PsppireWindow *victim, const char *file_name,
2017                   const char *encoding, gpointer hint)
2018 {
2019   GtkWidget *window;
2020
2021   if (PSPPIRE_IS_DATA_WINDOW (victim)
2022       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
2023     {
2024       window = GTK_WIDGET (victim);
2025       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
2026     }
2027   else
2028     window = psppire_data_window_new (NULL);
2029
2030   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
2031   gtk_widget_show_all (window);
2032   return GTK_WINDOW (window);
2033 }