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