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