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