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