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