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