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