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