(Re)enable the pasting of the importer syntax
[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 (!asst->file_name)
978     goto end;
979
980   switch (asst->response)
981     {
982     case GTK_RESPONSE_APPLY:
983       {
984         gchar *fn = g_path_get_basename (asst->file_name);
985         open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
986         g_free (fn);
987       }
988       break;
989     case PSPPIRE_RESPONSE_PASTE:
990       free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
991       break;
992     default:
993       break;
994     }
995
996  end:
997   gtk_widget_destroy (GTK_WIDGET (asst));
998 }
999
1000
1001
1002 static void
1003 connect_dialog_action (GType type, PsppireDataWindow *de)
1004 {
1005   GAction *act = g_object_new (type,
1006                                "top-level", de,
1007                                NULL);
1008
1009   g_action_map_add_action (G_ACTION_MAP (de), act);
1010 }
1011
1012 static void
1013 g_action_activate_null (GAction *a)
1014 {
1015   g_action_activate (a, NULL);
1016 }
1017
1018 static void
1019 connect_action_to_menuitem (GActionMap *map, const gchar *action_name, GtkWidget *w, const gchar *accel)
1020 {
1021   GAction *a = g_action_map_lookup_action (map, action_name);
1022
1023   if (NULL == a)
1024     g_error ("Action \"%s\" not found in map", action_name);
1025
1026   if (accel)
1027     {
1028       GtkApplication *app = GTK_APPLICATION (g_application_get_default());
1029
1030       /* First set the label for the accellerator so that it appears
1031          on the menuitem */
1032       GtkWidget *child = gtk_bin_get_child (GTK_BIN (w));
1033       guint key;
1034       GdkModifierType modifier;
1035       gtk_accelerator_parse (accel, &key, &modifier);
1036       gtk_accel_label_set_accel (GTK_ACCEL_LABEL (child), key, modifier);
1037
1038       /* Now tell the application that it must do something when that
1039          key combination is pressed */
1040       const gchar *accels[2];
1041       accels[0] = accel;
1042       accels[1] = NULL;
1043
1044       gchar *detailed_action_name = NULL;
1045       if (GTK_IS_WINDOW (map))
1046         detailed_action_name = g_strdup_printf ("win.%s", action_name);
1047       else if (GTK_IS_APPLICATION (map))
1048         detailed_action_name = g_strdup_printf ("app.%s", action_name);
1049
1050       gtk_application_set_accels_for_action (app,
1051                                              detailed_action_name,
1052                                              accels);
1053       free (detailed_action_name);
1054     }
1055
1056   g_signal_connect_swapped (w, "activate", G_CALLBACK (g_action_activate_null), a);
1057  }
1058
1059
1060 static void
1061 set_data_page (PsppireDataWindow *dw)
1062 {
1063   gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 1);
1064   gtk_notebook_set_current_page (GTK_NOTEBOOK (dw->data_editor), 0);
1065 }
1066
1067
1068 static void
1069 on_cut (PsppireDataWindow *dw)
1070 {
1071   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1072   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1073   {
1074       PsppireDict *dict = NULL;
1075       g_object_get (dw->data_editor, "dictionary", &dict, NULL);
1076
1077       gint x, y;
1078       JmdSheet *sheet = JMD_SHEET (dw->data_editor->data_sheet);
1079       JmdRange sel = *sheet->selection;
1080
1081       GtkClipboard *clip =
1082         gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)),
1083                                        GDK_SELECTION_CLIPBOARD);
1084
1085       /* Save the selected area to a string */
1086       GString *str = g_string_new ("");
1087       for (y = sel.start_y ; y <= sel.end_y; ++y)
1088         {
1089           for (x = sel.start_x ; x <= sel.end_x; ++x)
1090             {
1091               const struct variable * var = psppire_dict_get_variable (dict, x);
1092               gchar *s = psppire_data_store_get_string (dw->data_editor->data_store,
1093                                                           y, var, FALSE);
1094               g_string_append (str, s);
1095               g_string_append (str, "\t");
1096               g_free (s);
1097             }
1098           g_string_append (str, "\n");
1099         }
1100
1101       gtk_clipboard_set_text (clip, str->str, str->len);
1102       g_string_free (str, TRUE);
1103
1104       /* Now fill the selected area with SYSMIS */
1105       union value sm ;
1106       sm.f = SYSMIS;
1107       for (x = sel.start_x ; x <= sel.end_x; ++x)
1108         {
1109           const struct variable * var = psppire_dict_get_variable (dict, x);
1110           for (y = sel.start_y ; y <= sel.end_y; ++y)
1111             {
1112               psppire_data_store_set_value (dw->data_editor->data_store,
1113                                             y,
1114                                             var, &sm);
1115             }
1116         }
1117
1118     }
1119 }
1120
1121 static void
1122 on_copy (PsppireDataWindow *dw)
1123 {
1124   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1125   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1126     {
1127       GtkClipboard *clip =
1128         gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)),
1129                                    GDK_SELECTION_CLIPBOARD);
1130
1131       jmd_sheet_set_clip (JMD_SHEET (dw->data_editor->data_sheet), clip);
1132     }
1133 }
1134
1135 static void
1136 on_paste (PsppireDataWindow *dw)
1137 {
1138   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1139   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1140     {
1141       psppire_data_editor_paste (dw->data_editor);
1142     }
1143 }
1144
1145 static void
1146 on_clear_cases (PsppireDataWindow *dw)
1147 {
1148   PsppireDataEditor *de = dw->data_editor;
1149   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1150   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1151     {
1152       JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1153       psppire_data_store_delete_cases (de->data_store, range->start_y,
1154                                        range->end_y - range->start_y + 1);
1155       gtk_widget_queue_draw (GTK_WIDGET (de->data_sheet));
1156     }
1157 }
1158
1159 static void
1160 on_clear_variables (PsppireDataWindow *dw)
1161 {
1162   PsppireDataEditor *de = dw->data_editor;
1163   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1164   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1165     {
1166       psppire_data_editor_data_delete_variables (de);
1167     }
1168   else
1169     {
1170       psppire_data_editor_var_delete_variables (de);
1171     }
1172 }
1173
1174 static void
1175 insert_variable (PsppireDataWindow *dw)
1176 {
1177   PsppireDataEditor *de = dw->data_editor;
1178   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1179
1180   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1181     {
1182       JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1183       psppire_data_editor_insert_new_variable_at_posn (de, range->start_x);
1184     }
1185   else
1186     {
1187       JmdRange *range = JMD_SHEET(de->var_sheet)->selection;
1188       psppire_data_editor_insert_new_variable_at_posn (de, range->start_y);
1189     }
1190 }
1191
1192 static void
1193 insert_case_at_row (PsppireDataWindow *dw)
1194 {
1195   PsppireDataEditor *de = dw->data_editor;
1196   JmdRange *range = JMD_SHEET(de->data_sheet)->selection;
1197   psppire_data_editor_insert_new_case_at_posn (de, range->start_y);
1198 }
1199
1200 static void
1201 goto_case (PsppireDataWindow *dw)
1202 {
1203   PsppireDataEditor *de = dw->data_editor;
1204   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (de));
1205   if (p == PSPPIRE_DATA_EDITOR_DATA_VIEW)
1206     {
1207       goto_case_dialog (JMD_SHEET (de->data_sheet));
1208     }
1209 }
1210
1211 static GtkWidget *
1212 create_file_menu (PsppireDataWindow *dw)
1213 {
1214   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_File"));
1215   GtkWidget *menu = gtk_menu_new ();
1216
1217   {
1218     GtkWidget *new = gtk_menu_item_new_with_mnemonic (_("_New"));
1219     gtk_menu_attach (GTK_MENU (menu), new,        0, 1, 0, 1);
1220
1221     GtkWidget *new_menu = gtk_menu_new ();
1222
1223     g_object_set (new, "submenu", new_menu, NULL);
1224
1225     GtkWidget *syntax  = gtk_menu_item_new_with_mnemonic (_("_Syntax"));
1226     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-syntax", syntax, 0);
1227
1228     GtkWidget *data = gtk_menu_item_new_with_mnemonic (_("_Data"));
1229     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-data", data, 0);
1230
1231     gtk_menu_attach (GTK_MENU (new_menu), syntax,    0, 1, 0, 1);
1232     gtk_menu_attach (GTK_MENU (new_menu), data,      0, 1, 1, 2);
1233   }
1234
1235   GtkWidget *open = gtk_menu_item_new_with_mnemonic (_("_Open"));
1236   connect_action_to_menuitem (G_ACTION_MAP (dw), "open", open, "<Ctrl>O");
1237
1238   GtkWidget *import = gtk_menu_item_new_with_mnemonic (_("_Import Data..."));
1239   connect_action_to_menuitem (G_ACTION_MAP (dw), "file-import", import, 0);
1240
1241   gtk_menu_attach (GTK_MENU (menu), open,       0, 1, 1, 2);
1242   gtk_menu_attach (GTK_MENU (menu), import,     0, 1, 2, 3);
1243
1244   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 3, 4);
1245
1246   GtkWidget *save = gtk_menu_item_new_with_mnemonic (_("_Save..."));
1247   connect_action_to_menuitem (G_ACTION_MAP (dw), "save", save, "<Ctrl>S");
1248
1249   GtkWidget *save_as = gtk_menu_item_new_with_mnemonic (_("Save _As..."));
1250   connect_action_to_menuitem (G_ACTION_MAP (dw), "save-as", save_as, "<Shift><Ctrl>S");
1251
1252   GtkWidget *rename_dataset = gtk_menu_item_new_with_mnemonic (_("_Rename Dataset..."));
1253   connect_action_to_menuitem (G_ACTION_MAP (dw), "rename-dataset", rename_dataset, 0);
1254
1255
1256   gtk_menu_attach (GTK_MENU (menu), save,        0, 1, 4, 5);
1257   gtk_menu_attach (GTK_MENU (menu), save_as,     0, 1, 5, 6);
1258   gtk_menu_attach (GTK_MENU (menu), rename_dataset,     0, 1, 6, 7);
1259
1260   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 7, 8);
1261
1262   {
1263     GtkWidget *display_data = gtk_menu_item_new_with_mnemonic (_("_Display Data File Information"));
1264     gtk_menu_attach (GTK_MENU (menu), display_data,     0, 1, 8, 9);
1265
1266     GtkWidget *dd_menu = gtk_menu_new ();
1267
1268     g_object_set (display_data, "submenu", dd_menu, NULL);
1269
1270     GtkWidget *working_file  = gtk_menu_item_new_with_mnemonic (_("Working File"));
1271     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-working", working_file, 0);
1272     GtkWidget *external_file = gtk_menu_item_new_with_mnemonic (_("_External File..."));
1273     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-external", external_file, 0);
1274
1275     gtk_menu_attach (GTK_MENU (dd_menu), working_file,    0, 1, 0, 1);
1276     gtk_menu_attach (GTK_MENU (dd_menu), external_file,   0, 1, 1, 2);
1277   }
1278
1279   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 9, 10);
1280
1281   {
1282     GtkWidget *mi_data = gtk_menu_item_new_with_mnemonic (_("_Recently Used Data"));
1283     GtkWidget *mi_files = gtk_menu_item_new_with_mnemonic (_("Recently Used _Files"));
1284
1285     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1286       gtk_recent_manager_get_default ());
1287
1288     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1289       gtk_recent_manager_get_default ());
1290
1291     gtk_menu_attach (GTK_MENU (menu), mi_data,       0, 1, 10, 11);
1292     gtk_menu_attach (GTK_MENU (menu), mi_files,      0, 1, 11, 12);
1293
1294     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1295     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1296
1297     g_object_set (mi_data, "submenu",  menu_data, NULL);
1298     g_object_set (mi_files, "submenu", menu_files, NULL);
1299
1300     {
1301       GtkRecentFilter *filter = gtk_recent_filter_new ();
1302
1303       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1304       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1305
1306       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1307
1308       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1309     }
1310
1311     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), dw);
1312
1313     {
1314       GtkRecentFilter *filter = gtk_recent_filter_new ();
1315
1316       gtk_recent_filter_add_pattern (filter, "*.sps");
1317       gtk_recent_filter_add_pattern (filter, "*.SPS");
1318
1319       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1320
1321       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1322     }
1323
1324     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), dw);
1325   }
1326
1327   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 12, 13);
1328
1329   {
1330     GtkWidget *quit = gtk_menu_item_new_with_mnemonic (_("_Quit"));
1331     gtk_menu_attach (GTK_MENU (menu), quit,     0, 1, 13, 14);
1332
1333     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()),
1334                                 "quit", quit, "<Ctrl>Q");
1335   }
1336
1337   g_object_set (menuitem, "submenu", menu, NULL);
1338   gtk_widget_show_all (menuitem);
1339
1340   return menuitem;
1341 }
1342
1343
1344 static GtkWidget *
1345 create_edit_menu (PsppireDataWindow *dw)
1346 {
1347   int i = 0;
1348   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit"));
1349
1350   GtkWidget *menu = gtk_menu_new ();
1351
1352   dw->mi_insert_var = gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
1353   dw->mi_insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
1354   GtkWidget *go_to_variable = gtk_menu_item_new_with_mnemonic (_("_Go To Variable..."));
1355   dw->mi_go_to_case = gtk_menu_item_new_with_mnemonic (_("_Go To Case..."));
1356
1357   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_var,        0, 1, i, i + 1); ++i;
1358   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_case,     0, 1, i, i + 1); ++i;
1359
1360   g_signal_connect_swapped (dw->mi_insert_case, "activate", G_CALLBACK (insert_case_at_row), dw);
1361   g_signal_connect_swapped (dw->mi_go_to_case, "activate", G_CALLBACK (goto_case), dw);
1362   g_signal_connect_swapped (dw->mi_insert_var, "activate", G_CALLBACK (insert_variable), dw);
1363
1364   GAction *a = g_action_map_lookup_action (G_ACTION_MAP (dw),  "PsppireDialogActionVarInfo");
1365   g_assert (a);
1366   g_signal_connect_swapped (go_to_variable, "activate", G_CALLBACK (psppire_dialog_action_activate_null), a);
1367
1368   gtk_menu_attach (GTK_MENU (menu), go_to_variable,         0, 1, i, i + 1); ++i;
1369   gtk_menu_attach (GTK_MENU (menu), dw->mi_go_to_case,      0, 1, i, i + 1); ++i;
1370
1371   {
1372     GtkAccelGroup *ag = gtk_accel_group_new ();
1373
1374     dw->mi_edit_separator = gtk_separator_menu_item_new ();
1375     gtk_menu_attach (GTK_MENU (menu), dw->mi_edit_separator, 0, 1, i, i + 1); ++i;
1376
1377     dw->mi_cut = gtk_menu_item_new_with_mnemonic (_("Cu_t"));
1378     gtk_menu_attach (GTK_MENU (menu), dw->mi_cut,     0, 1, i, i + 1); ++i;
1379     g_signal_connect_swapped (dw->mi_cut, "activate", G_CALLBACK (on_cut), dw);
1380
1381     gtk_window_add_accel_group (GTK_WINDOW (dw), ag);
1382     gtk_widget_add_accelerator (dw->mi_cut, "activate", ag,
1383                                 'X', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1384
1385     dw->mi_copy = gtk_menu_item_new_with_mnemonic (_("_Copy"));
1386     gtk_menu_attach (GTK_MENU (menu), dw->mi_copy,     0, 1, i, i + 1); ++i;
1387     g_signal_connect_swapped (dw->mi_copy, "activate", G_CALLBACK (on_copy), dw);
1388     gtk_widget_add_accelerator (dw->mi_copy, "activate", ag,
1389                                 'C', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1390
1391     dw->mi_paste = gtk_menu_item_new_with_mnemonic (_("_Paste"));
1392     gtk_menu_attach (GTK_MENU (menu), dw->mi_paste,     0, 1, i, i + 1); ++i;
1393     g_signal_connect_swapped (dw->mi_paste, "activate", G_CALLBACK (on_paste), dw);
1394     gtk_widget_add_accelerator (dw->mi_paste, "activate", ag,
1395                                 'V', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1396
1397     dw->mi_clear_variables = gtk_menu_item_new_with_mnemonic (_("Clear _Variables"));
1398     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_variables,     0, 1, i, i + 1); ++i;
1399     g_signal_connect_swapped (dw->mi_clear_variables, "activate", G_CALLBACK (on_clear_variables), dw);
1400
1401     dw->mi_clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
1402     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_cases,     0, 1, i, i + 1); ++i;
1403     g_signal_connect_swapped (dw->mi_clear_cases, "activate", G_CALLBACK (on_clear_cases), dw);
1404   }
1405
1406   {
1407     dw->mi_find_separator = gtk_separator_menu_item_new ();
1408     gtk_menu_attach (GTK_MENU (menu), dw->mi_find_separator, 0, 1, i, i + 1); ++i;
1409
1410     dw->mi_find = gtk_menu_item_new_with_mnemonic (_("_Find..."));
1411     g_signal_connect_swapped (dw->mi_find, "activate", G_CALLBACK (find_dialog), dw);
1412     gtk_menu_attach (GTK_MENU (menu), dw->mi_find,    0, 1,  i, i + 1); ++i;
1413   }
1414
1415   {
1416     dw->mi_options = gtk_menu_item_new_with_mnemonic (_("_Options..."));
1417     g_signal_connect_swapped (dw->mi_options, "activate",
1418                               G_CALLBACK (options_dialog), dw);
1419     gtk_menu_attach (GTK_MENU (menu), dw->mi_options, 0, 1,  i, i + 1); ++i;
1420   }
1421
1422   g_object_set (menuitem, "submenu", menu, NULL);
1423
1424   gtk_widget_show_all (menuitem);
1425
1426   return menuitem;
1427 }
1428
1429 static void
1430 psppire_data_window_finish_init (PsppireDataWindow *de,
1431                                  struct dataset *ds)
1432 {
1433   static const struct dataset_callbacks cbs =
1434     {
1435       set_unsaved,                    /* changed */
1436       transformation_change_callback, /* transformations_changed */
1437     };
1438
1439   GtkWidget *menubar;
1440   GtkWidget *hb ;
1441   GtkWidget *sb ;
1442
1443   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1444
1445   de->dataset = ds;
1446   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
1447   de->data_store = psppire_data_store_new (de->dict);
1448   psppire_data_store_set_reader (de->data_store, NULL);
1449
1450   GObject *menu = get_object_assert (de->builder, "data-editor-menu", G_TYPE_MENU);
1451   menubar = gtk_menu_bar_new_from_model (G_MENU_MODEL (menu));
1452   gtk_widget_show (menubar);
1453
1454   hb = gtk_toolbar_new ();
1455   sb = get_widget_assert (de->builder, "status-bar");
1456
1457   de->data_editor =
1458     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
1459
1460   g_signal_connect (de, "realize",
1461                     G_CALLBACK (set_data_page), de);
1462
1463   g_signal_connect_swapped (de->data_store, "case-changed",
1464                             G_CALLBACK (set_unsaved), de);
1465
1466   dataset_set_callbacks (de->dataset, &cbs, de);
1467
1468   connect_help (de->builder);
1469
1470   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1471   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1472   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1473   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1474
1475   gtk_container_add (GTK_CONTAINER (de), box);
1476
1477   g_signal_connect (de->dict, "weight-changed",
1478                     G_CALLBACK (on_weight_change),
1479                     de);
1480
1481   g_signal_connect (de->dict, "filter-changed",
1482                     G_CALLBACK (on_filter_change),
1483                     de);
1484
1485   g_signal_connect (de->dict, "split-changed",
1486                     G_CALLBACK (on_split_change),
1487                     de);
1488
1489   g_signal_connect_swapped (de->dict, "items-changed",
1490                             G_CALLBACK (enable_save), de);
1491   g_signal_connect_swapped (de->dict, "variable-inserted",
1492                             G_CALLBACK (enable_save), de);
1493   g_signal_connect_swapped (de->dict, "variable-deleted",
1494                             G_CALLBACK (enable_save), de);
1495   enable_save (de);
1496
1497   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SORT,  de);
1498   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SPLIT,  de);
1499   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FLIP,  de);
1500   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AGGREGATE,  de);
1501   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_WEIGHT,  de);
1502
1503   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMPUTE,  de);
1504   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COUNT,  de);
1505   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AUTORECODE,  de);
1506   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RANK,  de);
1507   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SELECT,  de);
1508   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME,  de);
1509   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT,  de);
1510
1511
1512   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_DESCRIPTIVES,  de);
1513   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FREQUENCIES,  de);
1514   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_EXAMINE,  de);
1515   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CROSSTABS,  de);
1516
1517   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_INDEP_SAMPS,  de);
1518   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_PAIRED,  de);
1519
1520   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_MEANS,  de);
1521   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TT1S,  de);
1522
1523   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ONEWAY, de);
1524   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_UNIVARIATE, de);
1525   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_KMEANS, de);
1526   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FACTOR, de);
1527   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CORRELATION, de);
1528   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RELIABILITY, de);
1529   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_REGRESSION, de);
1530   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_LOGISTIC, de);
1531   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ROC, de);
1532
1533   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMMENTS, de);
1534   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_VAR_INFO, de);
1535
1536   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, de);
1537   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, de);
1538   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, de);
1539
1540   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CHISQUARE, de);
1541   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BINOMIAL, de);
1542   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RUNS, de);
1543   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_1SKS, de);
1544   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TWO_SAMPLE, de);
1545   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_RELATED, de);
1546
1547   {
1548     GSimpleAction *file_import_action = g_simple_action_new ("file-import", NULL);
1549     g_signal_connect_swapped (file_import_action, "activate", G_CALLBACK (file_import), de);
1550     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (file_import_action));
1551   }
1552
1553   {
1554     GSimpleAction *save = g_simple_action_new ("save", NULL);
1555     g_signal_connect_swapped (save, "activate", G_CALLBACK (psppire_window_save), de);
1556     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save));
1557   }
1558
1559   {
1560     GSimpleAction *open = g_simple_action_new ("open", NULL);
1561     g_signal_connect_swapped (open, "activate", G_CALLBACK (psppire_window_open), de);
1562     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (open));
1563   }
1564
1565   {
1566     GSimpleAction *save_as = g_simple_action_new ("save-as", NULL);
1567     g_signal_connect_swapped (save_as, "activate", G_CALLBACK (psppire_window_save_as), de);
1568     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save_as));
1569   }
1570
1571   {
1572     GSimpleAction *rename_dataset_act = g_simple_action_new ("rename-dataset", NULL);
1573     g_signal_connect_swapped (rename_dataset_act, "activate",
1574                               G_CALLBACK (on_rename_dataset), de);
1575     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (rename_dataset_act));
1576   }
1577
1578   {
1579     GSimpleAction *info_working = g_simple_action_new ("info-working", NULL);
1580     g_signal_connect_swapped (info_working, "activate", G_CALLBACK (display_dict), de);
1581     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_working));
1582   }
1583   {
1584     GSimpleAction *info_external = g_simple_action_new ("info-external", NULL);
1585     g_signal_connect_swapped (info_external, "activate", G_CALLBACK (sysfile_info), de);
1586     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_external));
1587   }
1588
1589   {
1590     GSimpleAction *act_statusbar = g_simple_action_new_stateful ("statusbar", NULL, g_variant_new_boolean (TRUE));
1591     g_signal_connect (act_statusbar, "activate", G_CALLBACK (status_bar_activate), de);
1592     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_statusbar));
1593   }
1594
1595   {
1596     GSimpleAction *act_gridlines = g_simple_action_new_stateful ("gridlines", NULL, g_variant_new_boolean (TRUE));
1597     g_signal_connect (act_gridlines, "activate", G_CALLBACK (grid_lines_activate), de);
1598     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_gridlines));
1599   }
1600
1601
1602   {
1603     GSimpleAction *act_view_data = g_simple_action_new_stateful ("view_dv", G_VARIANT_TYPE_STRING,
1604                                                                  g_variant_new_string ("DATA"));
1605     g_signal_connect (act_view_data, "activate", G_CALLBACK (activate_change_view), de);
1606     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_view_data));
1607   }
1608
1609   {
1610     GSimpleAction *act_fonts = g_simple_action_new ("fonts", NULL);
1611     g_signal_connect_swapped (act_fonts, "activate", G_CALLBACK (fonts_activate), de);
1612     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_fonts));
1613   }
1614
1615   {
1616     GSimpleAction *act_value_labels =
1617       g_simple_action_new_stateful ("value_labels", NULL,
1618                                     g_variant_new_boolean (FALSE));
1619     g_signal_connect (act_value_labels, "activate", G_CALLBACK (value_labels_activate), de);
1620     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_value_labels));
1621   }
1622
1623   {
1624     GSimpleAction *act_transform_pending = g_simple_action_new ("transform-pending", NULL);
1625     g_signal_connect_swapped (act_transform_pending, "activate", G_CALLBACK (execute), de);
1626     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_transform_pending));
1627   }
1628
1629   {
1630     GSimpleAction *act_jump_to_variable = g_simple_action_new ("jump-to-variable", NULL);
1631     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_variable));
1632   }
1633
1634   {
1635     GSimpleAction *act_insert_variable = g_simple_action_new ("insert-variable", NULL);
1636     g_signal_connect_swapped (act_insert_variable, "activate", G_CALLBACK (insert_variable), de);
1637     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_variable));
1638   }
1639
1640   {
1641     GSimpleAction *act_jump_to_case = g_simple_action_new ("jump-to-case", NULL);
1642     g_signal_connect_swapped (act_jump_to_case, "activate", G_CALLBACK (goto_case), de);
1643     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_case));
1644   }
1645
1646   {
1647     GSimpleAction *act_insert_case = g_simple_action_new ("insert-case", NULL);
1648     g_signal_connect_swapped (act_insert_case, "activate", G_CALLBACK (insert_case_at_row), de);
1649     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_case));
1650   }
1651
1652   {
1653     GSimpleAction *find = g_simple_action_new ("find", NULL);
1654     g_signal_connect_swapped (find, "activate", G_CALLBACK (find_dialog), de);
1655     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (find));
1656   }
1657
1658   {
1659     int idx = 0;
1660     {
1661       GtkToolItem *ti = gtk_tool_button_new (NULL, "Open");
1662       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_open), de);
1663       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1664       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-open-data");
1665     }
1666
1667     {
1668       GtkToolItem *ti = gtk_tool_button_new (NULL, "Save");
1669       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_save), de);
1670       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1671       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-save-data");
1672     }
1673
1674     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1675
1676     {
1677       de->ti_jump_to_variable = gtk_tool_button_new (NULL, "Goto Var");
1678
1679       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "PsppireDialogActionVarInfo");
1680       g_assert (a);
1681       g_signal_connect_swapped (de->ti_jump_to_variable, "clicked",
1682                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1683
1684       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_variable, idx++);
1685       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_variable), "edit-go-to-variable");
1686       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_variable), _("Jump to variable"));
1687     }
1688
1689     {
1690       de->ti_jump_to_case = gtk_tool_button_new (NULL, "Jump to Case");
1691
1692       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "jump-to-case");
1693       g_assert (a);
1694       g_signal_connect_swapped (de->ti_jump_to_case, "clicked",
1695                                 G_CALLBACK (g_action_activate_null), a);
1696
1697       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_case, idx++);
1698       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_case), "edit-go-to-case");
1699       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_case), _("Jump to a case in the data sheet"));
1700     }
1701
1702     {
1703       de->ti_find = gtk_tool_button_new (NULL, "Find");
1704
1705       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "find");
1706       g_assert (a);
1707       g_signal_connect_swapped (de->ti_find, "clicked",
1708                                 G_CALLBACK (g_action_activate_null), a);
1709
1710
1711       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_find, idx++);
1712       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_find), "edit-find");
1713       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_find), _("Search for values in the data"));
1714     }
1715
1716     {
1717       de->ti_insert_case = gtk_tool_button_new (NULL, "Create Case");
1718       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-case");
1719       g_assert (a);
1720       g_signal_connect_swapped (de->ti_insert_case, "clicked",
1721                                 G_CALLBACK (g_action_activate_null), a);
1722
1723       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_case, idx++);
1724       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_case), "edit-insert-case");
1725       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_case), _("Create a new case at the current position"));
1726     }
1727
1728     {
1729       de->ti_insert_variable = gtk_tool_button_new (NULL, "Create Variable");
1730       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-variable");
1731       g_assert (a);
1732       g_signal_connect_swapped (de->ti_insert_variable, "clicked",
1733                                 G_CALLBACK (g_action_activate_null), a);
1734
1735       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_variable, idx++);
1736       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_variable), "edit-insert-variable");
1737       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_variable), _("Create a new variable at the current position"));
1738     }
1739
1740     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1741
1742     {
1743       GtkToolItem *ti = gtk_tool_button_new (NULL, "Split");
1744       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1745                                                "PsppireDialogActionSplit");
1746       g_assert (a);
1747       g_signal_connect_swapped (ti, "clicked",
1748                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1749       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1750       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-split-file");
1751       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Split the active dataset"));
1752     }
1753
1754     {
1755       GtkToolItem *ti = gtk_tool_button_new (NULL, "Weight");
1756       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1757                                                "PsppireDialogActionWeight");
1758       g_assert (a);
1759       g_signal_connect_swapped (ti, "clicked",
1760                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1761       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1762       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-weight-cases");
1763       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Weight cases by variable"));
1764     }
1765
1766     {
1767       de->ti_value_labels_button = gtk_toggle_tool_button_new ();
1768       gtk_tool_button_set_label (GTK_TOOL_BUTTON (de->ti_value_labels_button),
1769                                  "Value Labels");
1770       g_signal_connect (de->ti_value_labels_button, "toggled",
1771                         G_CALLBACK (on_labels_button_toggle), de);
1772       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_value_labels_button, idx++);
1773       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_value_labels_button), "view-value-labels");
1774       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_value_labels_button), _("Show/hide value labels"));
1775     }
1776   }
1777
1778
1779   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1780   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1781
1782   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_file_menu (de), 0);
1783   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_edit_menu (de), 1);
1784   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_windows_menu (GTK_WINDOW (de)));
1785   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1786
1787   g_signal_connect (de->data_editor, "switch-page",
1788                     G_CALLBACK (on_switch_page), de);
1789
1790   gtk_widget_show (GTK_WIDGET (de->data_editor));
1791   gtk_widget_show_all (box);
1792
1793   ll_push_head (&all_data_windows, &de->ll);
1794 }
1795
1796
1797 static void
1798 psppire_data_window_dispose (GObject *object)
1799 {
1800   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1801
1802   if (dw->builder != NULL)
1803     {
1804       g_object_unref (dw->builder);
1805       dw->builder = NULL;
1806     }
1807
1808   if (dw->dict)
1809     {
1810       g_signal_handlers_disconnect_by_func (dw->dict,
1811                                             G_CALLBACK (enable_save), dw);
1812       g_signal_handlers_disconnect_by_func (dw->dict,
1813                                             G_CALLBACK (on_weight_change), dw);
1814       g_signal_handlers_disconnect_by_func (dw->dict,
1815                                             G_CALLBACK (on_filter_change), dw);
1816       g_signal_handlers_disconnect_by_func (dw->dict,
1817                                             G_CALLBACK (on_split_change), dw);
1818
1819       g_object_unref (dw->dict);
1820       dw->dict = NULL;
1821     }
1822
1823   if (dw->data_store)
1824     {
1825       g_object_unref (dw->data_store);
1826       dw->data_store = NULL;
1827     }
1828
1829   if (dw->ll.next != NULL)
1830     {
1831       ll_remove (&dw->ll);
1832       dw->ll.next = NULL;
1833     }
1834
1835   if (G_OBJECT_CLASS (parent_class)->dispose)
1836     G_OBJECT_CLASS (parent_class)->dispose (object);
1837 }
1838
1839 static void
1840 psppire_data_window_finalize (GObject *object)
1841 {
1842   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1843
1844   if (dw->dataset)
1845     {
1846       struct dataset *dataset = dw->dataset;
1847       struct session *session = dataset_session (dataset);
1848
1849       dw->dataset = NULL;
1850
1851       dataset_set_callbacks (dataset, NULL, NULL);
1852       session_set_active_dataset (session, NULL);
1853       dataset_destroy (dataset);
1854     }
1855
1856   if (G_OBJECT_CLASS (parent_class)->finalize)
1857     G_OBJECT_CLASS (parent_class)->finalize (object);
1858 }
1859
1860 static void
1861 psppire_data_window_set_property (GObject         *object,
1862                                   guint            prop_id,
1863                                   const GValue    *value,
1864                                   GParamSpec      *pspec)
1865 {
1866   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1867
1868   switch (prop_id)
1869     {
1870     case PROP_DATASET:
1871       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1872       break;
1873     default:
1874       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1875       break;
1876     };
1877 }
1878
1879 static void
1880 psppire_data_window_get_property (GObject         *object,
1881                                   guint            prop_id,
1882                                   GValue          *value,
1883                                   GParamSpec      *pspec)
1884 {
1885   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1886
1887   switch (prop_id)
1888     {
1889     case PROP_DATASET:
1890       g_value_set_pointer (value, window->dataset);
1891       break;
1892     default:
1893       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1894       break;
1895     };
1896 }
1897
1898
1899
1900 GtkWidget*
1901 psppire_data_window_new (struct dataset *ds)
1902 {
1903   GtkWidget *dw;
1904
1905   if (the_session == NULL)
1906     the_session = session_create (NULL);
1907
1908   if (ds == NULL)
1909     {
1910       char *dataset_name = session_generate_dataset_name (the_session);
1911       ds = dataset_create (the_session, dataset_name);
1912       free (dataset_name);
1913     }
1914   assert (dataset_session (ds) == the_session);
1915
1916   dw = GTK_WIDGET (
1917                    g_object_new (
1918                                  psppire_data_window_get_type (),
1919                                  "description", _("Data Editor"),
1920                                  "dataset", ds,
1921                                  NULL));
1922
1923   if (dataset_name (ds) != NULL)
1924     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1925
1926
1927   GApplication *app = g_application_get_default ();
1928   gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (dw));
1929
1930   return dw;
1931 }
1932
1933
1934
1935 bool
1936 psppire_data_window_is_empty (PsppireDataWindow *dw)
1937 {
1938   return psppire_dict_get_var_cnt (dw->dict) == 0;
1939 }
1940
1941
1942 static void
1943 psppire_data_window_iface_init (PsppireWindowIface *iface)
1944 {
1945   iface->save = save_file;
1946   iface->pick_filename = data_pick_filename;
1947   iface->load = load_file;
1948 }
1949
1950 \f
1951
1952
1953 PsppireDataWindow *
1954 psppire_default_data_window (void)
1955 {
1956   if (ll_is_empty (&all_data_windows))
1957     create_data_window ();
1958   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1959 }
1960
1961
1962
1963 void
1964 psppire_data_window_set_default (PsppireDataWindow *pdw)
1965 {
1966   ll_remove (&pdw->ll);
1967   ll_push_head (&all_data_windows, &pdw->ll);
1968 }
1969
1970 void
1971 psppire_data_window_undefault (PsppireDataWindow *pdw)
1972 {
1973   ll_remove (&pdw->ll);
1974   ll_push_tail (&all_data_windows, &pdw->ll);
1975 }
1976
1977
1978
1979 PsppireDataWindow *
1980 psppire_data_window_for_dataset (struct dataset *ds)
1981 {
1982   PsppireDataWindow *pdw;
1983
1984   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1985     if (pdw->dataset == ds)
1986       return pdw;
1987
1988   return NULL;
1989 }
1990
1991 PsppireDataWindow *
1992 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1993 {
1994   PsppireDataWindow *pdw;
1995
1996   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1997     if (pdw->data_store == data_store)
1998       return pdw;
1999
2000   return NULL;
2001 }
2002
2003 GtkWindow *
2004 create_data_window (void)
2005 {
2006   GtkWidget *w = psppire_data_window_new (NULL);
2007
2008   gtk_widget_show (w);
2009
2010   return GTK_WINDOW (w);
2011 }
2012
2013 GtkWindow *
2014 open_data_window (PsppireWindow *victim, const char *file_name,
2015                   const char *encoding, gpointer hint)
2016 {
2017   GtkWidget *window;
2018
2019   if (PSPPIRE_IS_DATA_WINDOW (victim)
2020       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
2021     {
2022       window = GTK_WIDGET (victim);
2023       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
2024     }
2025   else
2026     window = psppire_data_window_new (NULL);
2027
2028   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
2029   gtk_widget_show_all (window);
2030   return GTK_WINDOW (window);
2031 }