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