Enable the Edit Copy functionality
[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 #if SHEET_MERGE
1067   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1068   if (p == 0)
1069     {
1070       PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1071       psppire_data_sheet_edit_cut (ds);
1072     }
1073 #endif
1074 }
1075
1076
1077
1078 static void
1079 on_copy (PsppireDataWindow *dw)
1080 {
1081   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1082   if (p == 0)
1083     {
1084       GtkClipboard *clip =
1085         gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (dw)),
1086                                    GDK_SELECTION_CLIPBOARD);
1087
1088       jmd_sheet_set_clip (JMD_SHEET (dw->data_editor->data_sheet), clip);
1089     }
1090 }
1091
1092 static void
1093 on_paste (PsppireDataWindow *dw)
1094 {
1095 #if SHEET_MERGE
1096   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1097   if (p == 0)
1098     {
1099       PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1100       psppire_data_sheet_edit_paste (ds);
1101     }
1102 #endif
1103 }
1104
1105
1106 static void
1107 on_clear_cases (PsppireDataWindow *dw)
1108 {
1109 #if SHEET_MERGE
1110   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1111   if (p == 0)
1112     {
1113       PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1114       psppire_data_sheet_edit_clear_cases (ds);
1115     }
1116 #endif
1117 }
1118
1119 static void
1120 on_clear_variables (PsppireDataWindow *dw)
1121 {
1122 #if SHEET_MERGE
1123   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1124   if (p == 0)
1125     {
1126       PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1127       psppire_data_sheet_edit_clear_variables (ds);
1128     }
1129   else
1130     {
1131       psppire_var_sheet_clear_variables (PSPPIRE_VAR_SHEET (dw->data_editor->var_sheet));
1132     }
1133 #endif
1134 }
1135
1136
1137
1138 static void
1139 insert_variable (PsppireDataWindow *dw)
1140 {
1141 #if SHEET_MERGE
1142   int p = gtk_notebook_get_current_page (GTK_NOTEBOOK (dw->data_editor));
1143   if (p == 0)
1144     {
1145       PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1146       psppire_data_sheet_insert_variable (ds);
1147     }
1148   else
1149     {
1150       psppire_var_sheet_insert_variable (PSPPIRE_VAR_SHEET (dw->data_editor->var_sheet));
1151     }
1152 #endif
1153 }
1154
1155
1156
1157 static void
1158 insert_case_at_row (PsppireDataWindow *dw)
1159 {
1160 #if SHEET_MERGE
1161   PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1162
1163   psppire_data_sheet_insert_case (ds);
1164 #endif
1165 }
1166
1167
1168
1169 static void
1170 goto_case (PsppireDataWindow *dw)
1171 {
1172 #if SHEET_MERGE
1173   PsppireDataSheet *ds = psppire_data_editor_get_active_data_sheet (dw->data_editor);
1174
1175   goto_case_dialog (ds);
1176 #endif
1177 }
1178
1179
1180 static GtkWidget *
1181 create_file_menu (PsppireDataWindow *dw)
1182 {
1183   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_File"));
1184   GtkWidget *menu = gtk_menu_new ();
1185
1186   {
1187     GtkWidget *new = gtk_menu_item_new_with_mnemonic (_("_New"));
1188     gtk_menu_attach (GTK_MENU (menu), new,        0, 1, 0, 1);
1189
1190     GtkWidget *new_menu = gtk_menu_new ();
1191
1192     g_object_set (new, "submenu", new_menu, NULL);
1193         
1194     GtkWidget *syntax  = gtk_menu_item_new_with_mnemonic (_("_Syntax"));
1195     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-syntax", syntax, 0);
1196     
1197     GtkWidget *data = gtk_menu_item_new_with_mnemonic (_("_Data"));
1198     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()), "new-data", data, 0);
1199
1200     gtk_menu_attach (GTK_MENU (new_menu), syntax,    0, 1, 0, 1);
1201     gtk_menu_attach (GTK_MENU (new_menu), data,      0, 1, 1, 2);
1202   }
1203   
1204   GtkWidget *open = gtk_menu_item_new_with_mnemonic (_("_Open"));
1205   connect_action_to_menuitem (G_ACTION_MAP (dw), "open", open, "<Ctrl>O");
1206   
1207   GtkWidget *import = gtk_menu_item_new_with_mnemonic (_("_Import Data..."));
1208   connect_action_to_menuitem (G_ACTION_MAP (dw), "file-import", import, 0);
1209   
1210   gtk_menu_attach (GTK_MENU (menu), open,       0, 1, 1, 2);
1211   gtk_menu_attach (GTK_MENU (menu), import,     0, 1, 2, 3);
1212
1213   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 3, 4);
1214
1215   GtkWidget *save = gtk_menu_item_new_with_mnemonic (_("_Save..."));
1216   connect_action_to_menuitem (G_ACTION_MAP (dw), "save", save, "<Ctrl>S");
1217   
1218   GtkWidget *save_as = gtk_menu_item_new_with_mnemonic (_("Save _As..."));
1219   connect_action_to_menuitem (G_ACTION_MAP (dw), "save-as", save_as, "<Shift><Ctrl>S");
1220   
1221   GtkWidget *rename_dataset = gtk_menu_item_new_with_mnemonic (_("_Rename Dataset..."));
1222   connect_action_to_menuitem (G_ACTION_MAP (dw), "rename-dataset", rename_dataset, 0);
1223
1224   
1225   gtk_menu_attach (GTK_MENU (menu), save,        0, 1, 4, 5);
1226   gtk_menu_attach (GTK_MENU (menu), save_as,     0, 1, 5, 6);
1227   gtk_menu_attach (GTK_MENU (menu), rename_dataset,     0, 1, 6, 7);
1228
1229   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 7, 8);
1230
1231   {
1232     GtkWidget *display_data = gtk_menu_item_new_with_mnemonic (_("_Display Data File Information"));
1233     gtk_menu_attach (GTK_MENU (menu), display_data,     0, 1, 8, 9);
1234
1235     GtkWidget *dd_menu = gtk_menu_new ();
1236
1237     g_object_set (display_data, "submenu", dd_menu, NULL);
1238     
1239     GtkWidget *working_file  = gtk_menu_item_new_with_mnemonic (_("Working File"));
1240     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-working", working_file, 0);
1241     GtkWidget *external_file = gtk_menu_item_new_with_mnemonic (_("_External File..."));
1242     connect_action_to_menuitem (G_ACTION_MAP (dw), "info-external", external_file, 0);
1243
1244     gtk_menu_attach (GTK_MENU (dd_menu), working_file,    0, 1, 0, 1);
1245     gtk_menu_attach (GTK_MENU (dd_menu), external_file,   0, 1, 1, 2);
1246   }
1247   
1248   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 9, 10);
1249
1250   {
1251     GtkWidget *mi_data = gtk_menu_item_new_with_mnemonic (_("_Recently Used Data"));
1252     GtkWidget *mi_files = gtk_menu_item_new_with_mnemonic (_("Recently Used _Files"));
1253
1254     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1255       gtk_recent_manager_get_default ());
1256
1257     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1258       gtk_recent_manager_get_default ());
1259
1260     gtk_menu_attach (GTK_MENU (menu), mi_data,       0, 1, 10, 11);
1261     gtk_menu_attach (GTK_MENU (menu), mi_files,      0, 1, 11, 12);
1262     
1263     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1264     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1265
1266     g_object_set (mi_data, "submenu",  menu_data, NULL);
1267     g_object_set (mi_files, "submenu", menu_files, NULL);
1268     
1269     {
1270       GtkRecentFilter *filter = gtk_recent_filter_new ();
1271
1272       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1273       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1274
1275       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1276
1277       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1278     }
1279
1280     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), dw);
1281
1282     {
1283       GtkRecentFilter *filter = gtk_recent_filter_new ();
1284
1285       gtk_recent_filter_add_pattern (filter, "*.sps");
1286       gtk_recent_filter_add_pattern (filter, "*.SPS");
1287
1288       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1289
1290       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1291     }
1292
1293     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), dw);
1294   }
1295
1296   gtk_menu_attach (GTK_MENU (menu), gtk_separator_menu_item_new (), 0, 1, 12, 13);
1297
1298   {
1299     GtkWidget *quit = gtk_menu_item_new_with_mnemonic (_("_Quit"));
1300     gtk_menu_attach (GTK_MENU (menu), quit,     0, 1, 13, 14);
1301
1302     connect_action_to_menuitem (G_ACTION_MAP (g_application_get_default ()),
1303                                 "quit", quit, "<Ctrl>Q");
1304   }
1305   
1306   g_object_set (menuitem, "submenu", menu, NULL);
1307   gtk_widget_show_all (menuitem);
1308   
1309   return menuitem;
1310 }
1311
1312
1313 static GtkWidget *
1314 create_edit_menu (PsppireDataWindow *dw)
1315 {
1316   int i = 0;
1317   GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (_("_Edit"));
1318
1319   GtkWidget *menu = gtk_menu_new ();
1320
1321   dw->mi_insert_var = gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
1322   dw->mi_insert_case = gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
1323   GtkWidget *go_to_variable = gtk_menu_item_new_with_mnemonic (_("_Go To Variable..."));
1324   dw->mi_go_to_case = gtk_menu_item_new_with_mnemonic (_("_Go To Case..."));
1325
1326   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_var,        0, 1, i, i + 1); ++i;
1327   gtk_menu_attach (GTK_MENU (menu), dw->mi_insert_case,     0, 1, i, i + 1); ++i;
1328
1329   g_signal_connect_swapped (dw->mi_insert_case, "activate", G_CALLBACK (insert_case_at_row), dw);
1330   g_signal_connect_swapped (dw->mi_go_to_case, "activate", G_CALLBACK (goto_case), dw);
1331   g_signal_connect_swapped (dw->mi_insert_var, "activate", G_CALLBACK (insert_variable), dw);
1332
1333   GAction *a = g_action_map_lookup_action (G_ACTION_MAP (dw),  "PsppireDialogActionVarInfo");
1334   g_assert (a);
1335   g_signal_connect_swapped (go_to_variable, "activate", G_CALLBACK (psppire_dialog_action_activate_null), a);
1336   
1337   gtk_menu_attach (GTK_MENU (menu), go_to_variable,         0, 1, i, i + 1); ++i;
1338   gtk_menu_attach (GTK_MENU (menu), dw->mi_go_to_case,      0, 1, i, i + 1); ++i;
1339
1340   {
1341     GtkAccelGroup *ag = gtk_accel_group_new ();
1342     
1343     dw->mi_edit_separator = gtk_separator_menu_item_new ();
1344     gtk_menu_attach (GTK_MENU (menu), dw->mi_edit_separator, 0, 1, i, i + 1); ++i;
1345
1346     dw->mi_cut = gtk_menu_item_new_with_mnemonic (_("Cu_t"));
1347     gtk_menu_attach (GTK_MENU (menu), dw->mi_cut,     0, 1, i, i + 1); ++i;
1348     g_signal_connect_swapped (dw->mi_cut, "activate", G_CALLBACK (on_cut), dw);
1349
1350     gtk_window_add_accel_group (GTK_WINDOW (dw), ag);
1351     gtk_widget_add_accelerator (dw->mi_cut, "activate", ag,
1352                                 'X', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1353     
1354     dw->mi_copy = gtk_menu_item_new_with_mnemonic (_("_Copy"));
1355     gtk_menu_attach (GTK_MENU (menu), dw->mi_copy,     0, 1, i, i + 1); ++i;
1356     g_signal_connect_swapped (dw->mi_copy, "activate", G_CALLBACK (on_copy), dw);
1357     gtk_widget_add_accelerator (dw->mi_copy, "activate", ag,
1358                                 'C', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1359         
1360     dw->mi_paste = gtk_menu_item_new_with_mnemonic (_("_Paste"));
1361     gtk_menu_attach (GTK_MENU (menu), dw->mi_paste,     0, 1, i, i + 1); ++i;
1362     g_signal_connect_swapped (dw->mi_paste, "activate", G_CALLBACK (on_paste), dw);
1363     gtk_widget_add_accelerator (dw->mi_paste, "activate", ag,
1364                                 'V', GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
1365
1366     dw->mi_clear_variables = gtk_menu_item_new_with_mnemonic (_("Clear _Variables"));
1367     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_variables,     0, 1, i, i + 1); ++i;
1368     g_signal_connect_swapped (dw->mi_clear_variables, "activate", G_CALLBACK (on_clear_variables), dw);
1369     
1370     dw->mi_clear_cases = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
1371     gtk_menu_attach (GTK_MENU (menu), dw->mi_clear_cases,     0, 1, i, i + 1); ++i;
1372     g_signal_connect_swapped (dw->mi_clear_cases, "activate", G_CALLBACK (on_clear_cases), dw);
1373   }
1374   
1375   {
1376     dw->mi_find_separator = gtk_separator_menu_item_new ();
1377     gtk_menu_attach (GTK_MENU (menu), dw->mi_find_separator, 0, 1, i, i + 1); ++i;
1378   
1379     dw->mi_find = gtk_menu_item_new_with_mnemonic (_("_Find..."));
1380     g_signal_connect_swapped (dw->mi_find, "activate", G_CALLBACK (find_dialog), dw);
1381     gtk_menu_attach (GTK_MENU (menu), dw->mi_find,      0, 1,  i, i + 1); ++i;
1382   }
1383   
1384   g_object_set (menuitem, "submenu", menu, NULL);
1385   
1386   gtk_widget_show_all (menuitem);
1387   
1388   return menuitem;
1389 }
1390
1391 static void
1392 psppire_data_window_finish_init (PsppireDataWindow *de,
1393                                  struct dataset *ds)
1394 {
1395   static const struct dataset_callbacks cbs =
1396     {
1397       set_unsaved,                    /* changed */
1398       transformation_change_callback, /* transformations_changed */
1399     };
1400
1401   GtkWidget *menubar;
1402   GtkWidget *hb ;
1403   GtkWidget *sb ;
1404
1405   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1406
1407   de->dataset = ds;
1408   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
1409   de->data_store = psppire_data_store_new (de->dict);
1410   psppire_data_store_set_reader (de->data_store, NULL);
1411
1412   GObject *menu = get_object_assert (de->builder, "data-editor-menu", G_TYPE_MENU);
1413   menubar = gtk_menu_bar_new_from_model (G_MENU_MODEL (menu));
1414   gtk_widget_show (menubar);
1415
1416   hb = gtk_toolbar_new ();
1417   sb = get_widget_assert (de->builder, "status-bar");
1418
1419   de->data_editor =
1420     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
1421   
1422   g_signal_connect (de, "realize",
1423                     G_CALLBACK (set_data_page), de);
1424
1425   g_signal_connect_swapped (de->data_store, "case-changed",
1426                             G_CALLBACK (set_unsaved), de);
1427
1428   g_signal_connect_swapped (de->data_store, "case-inserted",
1429                             G_CALLBACK (set_unsaved), de);
1430
1431   g_signal_connect_swapped (de->data_store, "cases-deleted",
1432                             G_CALLBACK (set_unsaved), de);
1433
1434   dataset_set_callbacks (de->dataset, &cbs, de);
1435
1436   connect_help (de->builder);
1437
1438   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1439   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1440   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1441   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1442
1443   gtk_container_add (GTK_CONTAINER (de), box);
1444
1445   g_signal_connect (de->dict, "weight-changed",
1446                     G_CALLBACK (on_weight_change),
1447                     de);
1448
1449   g_signal_connect (de->dict, "filter-changed",
1450                     G_CALLBACK (on_filter_change),
1451                     de);
1452
1453   g_signal_connect (de->dict, "split-changed",
1454                     G_CALLBACK (on_split_change),
1455                     de);
1456
1457   g_signal_connect_swapped (de->dict, "changed",
1458                             G_CALLBACK (enable_save), de);
1459   g_signal_connect_swapped (de->dict, "variable-inserted",
1460                             G_CALLBACK (enable_save), de);
1461   g_signal_connect_swapped (de->dict, "variable-deleted",
1462                             G_CALLBACK (enable_save), de);
1463   enable_save (de);
1464
1465   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SORT,  de);
1466   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SPLIT,  de);
1467   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FLIP,  de);
1468   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AGGREGATE,  de);
1469   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_WEIGHT,  de);
1470   
1471   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMPUTE,  de);
1472   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COUNT,  de);
1473   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AUTORECODE,  de);
1474   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RANK,  de);
1475   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SELECT,  de);
1476   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME,  de);
1477   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT,  de);
1478
1479     
1480   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_DESCRIPTIVES,  de);
1481   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FREQUENCIES,  de);
1482   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_EXAMINE,  de);
1483   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CROSSTABS,  de);
1484
1485   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_INDEP_SAMPS,  de);
1486   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_PAIRED,  de);
1487
1488   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_MEANS,  de);
1489   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TT1S,  de);
1490
1491   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ONEWAY, de);
1492   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_UNIVARIATE, de);
1493   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_KMEANS, de);
1494   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FACTOR, de);
1495   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CORRELATION, de);
1496   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RELIABILITY, de);
1497   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_REGRESSION, de);
1498   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_LOGISTIC, de);
1499   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ROC, de);
1500   
1501   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMMENTS, de);
1502   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_VAR_INFO, de);
1503
1504   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, de);
1505   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, de);
1506   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, de);
1507
1508   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CHISQUARE, de);
1509   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BINOMIAL, de);
1510   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RUNS, de);
1511   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_1SKS, de);
1512   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TWO_SAMPLE, de);
1513   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_RELATED, de);
1514
1515   {
1516     GSimpleAction *file_import_action = g_simple_action_new ("file-import", NULL);
1517     g_signal_connect_swapped (file_import_action, "activate", G_CALLBACK (file_import), de);
1518     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (file_import_action));
1519   }
1520   
1521   {
1522     GSimpleAction *save = g_simple_action_new ("save", NULL);
1523     g_signal_connect_swapped (save, "activate", G_CALLBACK (psppire_window_save), de);
1524     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save));
1525   }
1526
1527   {
1528     GSimpleAction *open = g_simple_action_new ("open", NULL);
1529     g_signal_connect_swapped (open, "activate", G_CALLBACK (psppire_window_open), de);
1530     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (open));
1531   }
1532
1533   {
1534     GSimpleAction *save_as = g_simple_action_new ("save-as", NULL);
1535     g_signal_connect_swapped (save_as, "activate", G_CALLBACK (psppire_window_save_as), de);
1536     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save_as));
1537   }
1538
1539   {
1540     GSimpleAction *rename_dataset_act = g_simple_action_new ("rename-dataset", NULL);
1541     g_signal_connect_swapped (rename_dataset_act, "activate",
1542                               G_CALLBACK (on_rename_dataset), de);
1543     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (rename_dataset_act));
1544   }
1545
1546   {
1547     GSimpleAction *info_working = g_simple_action_new ("info-working", NULL);
1548     g_signal_connect_swapped (info_working, "activate", G_CALLBACK (display_dict), de);
1549     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_working));
1550   }
1551   {
1552     GSimpleAction *info_external = g_simple_action_new ("info-external", NULL);
1553     g_signal_connect_swapped (info_external, "activate", G_CALLBACK (sysfile_info), de);
1554     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_external));
1555   }
1556
1557   {
1558     GSimpleAction *act_statusbar = g_simple_action_new_stateful ("statusbar", NULL, g_variant_new_boolean (TRUE));
1559     g_signal_connect (act_statusbar, "activate", G_CALLBACK (status_bar_activate), de);
1560     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_statusbar));
1561   }
1562
1563   {
1564     GSimpleAction *act_gridlines = g_simple_action_new_stateful ("gridlines", NULL, g_variant_new_boolean (TRUE));
1565     g_signal_connect (act_gridlines, "activate", G_CALLBACK (grid_lines_activate), de);
1566     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_gridlines));
1567   }
1568
1569   
1570   {
1571     GSimpleAction *act_view_data = g_simple_action_new_stateful ("view_dv", G_VARIANT_TYPE_STRING,
1572                                                                  g_variant_new_string ("DATA"));
1573     g_signal_connect (act_view_data, "activate", G_CALLBACK (activate_change_view), de);
1574     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_view_data));
1575   }
1576
1577   {
1578     GSimpleAction *act_fonts = g_simple_action_new ("fonts", NULL);
1579     g_signal_connect_swapped (act_fonts, "activate", G_CALLBACK (fonts_activate), de);
1580     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_fonts));
1581   }
1582
1583   {
1584     GSimpleAction *act_value_labels =
1585       g_simple_action_new_stateful ("value_labels", NULL,
1586                                     g_variant_new_boolean (FALSE));
1587     g_signal_connect (act_value_labels, "activate", G_CALLBACK (value_labels_activate), de);
1588     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_value_labels));
1589   }
1590
1591   {
1592     GSimpleAction *act_transform_pending = g_simple_action_new ("transform-pending", NULL);
1593     g_signal_connect_swapped (act_transform_pending, "activate", G_CALLBACK (execute), de);
1594     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_transform_pending));
1595   }
1596
1597   {
1598     GSimpleAction *act_jump_to_variable = g_simple_action_new ("jump-to-variable", NULL);
1599     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_variable));
1600   }
1601
1602   {
1603     GSimpleAction *act_insert_variable = g_simple_action_new ("insert-variable", NULL);
1604     g_signal_connect_swapped (act_insert_variable, "activate", G_CALLBACK (insert_variable), de);
1605     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_variable));
1606   }
1607
1608   {
1609     GSimpleAction *act_jump_to_case = g_simple_action_new ("jump-to-case", NULL);
1610     g_signal_connect_swapped (act_jump_to_case, "activate", G_CALLBACK (goto_case), de);
1611     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_case));
1612   }
1613
1614   {
1615     GSimpleAction *act_insert_case = g_simple_action_new ("insert-case", NULL);
1616     g_signal_connect_swapped (act_insert_case, "activate", G_CALLBACK (insert_case_at_row), de);
1617     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_case));
1618   }
1619
1620   {
1621     GSimpleAction *find = g_simple_action_new ("find", NULL);
1622     g_signal_connect_swapped (find, "activate", G_CALLBACK (find_dialog), de);
1623     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (find));
1624   }
1625   
1626   {
1627     int idx = 0;
1628     {
1629       GtkToolItem *ti = gtk_tool_button_new (NULL, "Open");
1630       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_open), de);
1631       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1632       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-open-data");
1633     }
1634
1635     {
1636       GtkToolItem *ti = gtk_tool_button_new (NULL, "Save");
1637       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_save), de);
1638       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1639       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-save-data");
1640     }
1641
1642     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1643
1644     {
1645       de->ti_jump_to_variable = gtk_tool_button_new (NULL, "Goto Var");
1646
1647       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "PsppireDialogActionVarInfo");
1648       g_assert (a);
1649       g_signal_connect_swapped (de->ti_jump_to_variable, "clicked",
1650                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1651
1652       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_variable, idx++);
1653       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_variable), "edit-go-to-variable");
1654       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_variable), _("Jump to variable"));
1655     }
1656
1657     {
1658       de->ti_jump_to_case = gtk_tool_button_new (NULL, "Jump to Case");
1659       
1660       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "jump-to-case");
1661       g_assert (a);
1662       g_signal_connect_swapped (de->ti_jump_to_case, "clicked",
1663                                 G_CALLBACK (g_action_activate_null), a);
1664       
1665       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_case, idx++);
1666       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_case), "edit-go-to-case");
1667       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_case), _("Jump to a case in the data sheet"));
1668     }
1669
1670     {
1671       de->ti_find = gtk_tool_button_new (NULL, "Find");
1672
1673       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "find");
1674       g_assert (a);
1675       g_signal_connect_swapped (de->ti_find, "clicked",
1676                                 G_CALLBACK (g_action_activate_null), a);
1677
1678       
1679       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_find, idx++);
1680       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_find), "edit-find");
1681       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_find), _("Search for values in the data"));
1682     }
1683
1684     {
1685       de->ti_insert_case = gtk_tool_button_new (NULL, "Create Case");
1686       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-case");
1687       g_assert (a);
1688       g_signal_connect_swapped (de->ti_insert_case, "clicked",
1689                                 G_CALLBACK (g_action_activate_null), a);
1690
1691       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_case, idx++);
1692       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_case), "edit-insert-case");
1693       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_case), _("Create a new case at the current position"));
1694     }
1695
1696     {
1697       de->ti_insert_variable = gtk_tool_button_new (NULL, "Create Variable");
1698       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-variable");
1699       g_assert (a);
1700       g_signal_connect_swapped (de->ti_insert_variable, "clicked",
1701                                 G_CALLBACK (g_action_activate_null), a);
1702
1703       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_variable, idx++);
1704       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_variable), "edit-insert-variable");
1705       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_variable), _("Create a new variable at the current position"));
1706     }
1707
1708     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1709
1710     {
1711       GtkToolItem *ti = gtk_tool_button_new (NULL, "Split");
1712       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1713                                                "PsppireDialogActionSplit");
1714       g_assert (a);
1715       g_signal_connect_swapped (ti, "clicked",
1716                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1717       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1718       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-split-file");
1719       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Split the active dataset"));
1720     }
1721
1722     {
1723       GtkToolItem *ti = gtk_tool_button_new (NULL, "Weight");
1724       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1725                                                "PsppireDialogActionWeight");
1726       g_assert (a);
1727       g_signal_connect_swapped (ti, "clicked",
1728                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1729       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1730       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-weight-cases");
1731       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Weight cases by variable"));
1732     }
1733
1734     {
1735       de->ti_value_labels_button = gtk_toggle_tool_button_new ();
1736       gtk_tool_button_set_label (GTK_TOOL_BUTTON (de->ti_value_labels_button),
1737                                  "Value Labels");
1738       g_signal_connect (de->ti_value_labels_button, "toggled",
1739                         G_CALLBACK (on_labels_button_toggle), de);
1740       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_value_labels_button, idx++);
1741       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_value_labels_button), "view-value-labels");
1742       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_value_labels_button), _("Show/hide value labels"));
1743     }
1744   }
1745
1746
1747   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1748   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1749
1750   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_file_menu (de), 0);
1751   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_edit_menu (de), 1);
1752   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_windows_menu (GTK_WINDOW (de)));
1753   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1754
1755   g_signal_connect (de->data_editor, "switch-page",
1756                     G_CALLBACK (on_switch_page), de);
1757
1758   gtk_widget_show (GTK_WIDGET (de->data_editor));
1759   gtk_widget_show_all (box);
1760
1761   ll_push_head (&all_data_windows, &de->ll);
1762 }
1763
1764
1765 static void
1766 psppire_data_window_dispose (GObject *object)
1767 {
1768   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1769
1770   if (dw->builder != NULL)
1771     {
1772       g_object_unref (dw->builder);
1773       dw->builder = NULL;
1774     }
1775
1776   if (dw->dict)
1777     {
1778       g_signal_handlers_disconnect_by_func (dw->dict,
1779                                             G_CALLBACK (enable_save), dw);
1780       g_signal_handlers_disconnect_by_func (dw->dict,
1781                                             G_CALLBACK (on_weight_change), dw);
1782       g_signal_handlers_disconnect_by_func (dw->dict,
1783                                             G_CALLBACK (on_filter_change), dw);
1784       g_signal_handlers_disconnect_by_func (dw->dict,
1785                                             G_CALLBACK (on_split_change), dw);
1786
1787       g_object_unref (dw->dict);
1788       dw->dict = NULL;
1789     }
1790
1791   if (dw->data_store)
1792     {
1793       g_object_unref (dw->data_store);
1794       dw->data_store = NULL;
1795     }
1796
1797   if (dw->ll.next != NULL)
1798     {
1799       ll_remove (&dw->ll);
1800       dw->ll.next = NULL;
1801     }
1802
1803   if (G_OBJECT_CLASS (parent_class)->dispose)
1804     G_OBJECT_CLASS (parent_class)->dispose (object);
1805 }
1806
1807 static void
1808 psppire_data_window_finalize (GObject *object)
1809 {
1810   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1811
1812   if (dw->dataset)
1813     {
1814       struct dataset *dataset = dw->dataset;
1815       struct session *session = dataset_session (dataset);
1816
1817       dw->dataset = NULL;
1818
1819       dataset_set_callbacks (dataset, NULL, NULL);
1820       session_set_active_dataset (session, NULL);
1821       dataset_destroy (dataset);
1822     }
1823
1824   if (G_OBJECT_CLASS (parent_class)->finalize)
1825     G_OBJECT_CLASS (parent_class)->finalize (object);
1826 }
1827
1828 static void
1829 psppire_data_window_set_property (GObject         *object,
1830                                   guint            prop_id,
1831                                   const GValue    *value,
1832                                   GParamSpec      *pspec)
1833 {
1834   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1835
1836   switch (prop_id)
1837     {
1838     case PROP_DATASET:
1839       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1840       break;
1841     default:
1842       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1843       break;
1844     };
1845 }
1846
1847 static void
1848 psppire_data_window_get_property (GObject         *object,
1849                                   guint            prop_id,
1850                                   GValue          *value,
1851                                   GParamSpec      *pspec)
1852 {
1853   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1854
1855   switch (prop_id)
1856     {
1857     case PROP_DATASET:
1858       g_value_set_pointer (value, window->dataset);
1859       break;
1860     default:
1861       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1862       break;
1863     };
1864 }
1865
1866
1867
1868 GtkWidget*
1869 psppire_data_window_new (struct dataset *ds)
1870 {
1871   GtkWidget *dw;
1872
1873   if (the_session == NULL)
1874     the_session = session_create (NULL);
1875
1876   if (ds == NULL)
1877     {
1878       char *dataset_name = session_generate_dataset_name (the_session);
1879       ds = dataset_create (the_session, dataset_name);
1880       free (dataset_name);
1881     }
1882   assert (dataset_session (ds) == the_session);
1883
1884   dw = GTK_WIDGET (
1885                    g_object_new (
1886                                  psppire_data_window_get_type (),
1887                                  "description", _("Data Editor"),
1888                                  "dataset", ds,
1889                                  NULL));
1890
1891   if (dataset_name (ds) != NULL)
1892     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1893
1894
1895   GApplication *app = g_application_get_default ();
1896   gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (dw));
1897   
1898   return dw;
1899 }
1900
1901
1902
1903 bool
1904 psppire_data_window_is_empty (PsppireDataWindow *dw)
1905 {
1906   return psppire_dict_get_var_cnt (dw->dict) == 0;
1907 }
1908
1909
1910 static void
1911 psppire_data_window_iface_init (PsppireWindowIface *iface)
1912 {
1913   iface->save = save_file;
1914   iface->pick_filename = data_pick_filename;
1915   iface->load = load_file;
1916 }
1917
1918 \f
1919
1920
1921 PsppireDataWindow *
1922 psppire_default_data_window (void)
1923 {
1924   if (ll_is_empty (&all_data_windows))
1925     create_data_window ();
1926   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1927 }
1928
1929
1930
1931 void
1932 psppire_data_window_set_default (PsppireDataWindow *pdw)
1933 {
1934   ll_remove (&pdw->ll);
1935   ll_push_head (&all_data_windows, &pdw->ll);
1936 }
1937
1938 void
1939 psppire_data_window_undefault (PsppireDataWindow *pdw)
1940 {
1941   ll_remove (&pdw->ll);
1942   ll_push_tail (&all_data_windows, &pdw->ll);
1943 }
1944
1945
1946
1947 PsppireDataWindow *
1948 psppire_data_window_for_dataset (struct dataset *ds)
1949 {
1950   PsppireDataWindow *pdw;
1951
1952   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1953     if (pdw->dataset == ds)
1954       return pdw;
1955
1956   return NULL;
1957 }
1958
1959 #if SHEET_MERGE
1960
1961 PsppireDataWindow *
1962 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1963 {
1964   PsppireDataWindow *pdw;
1965
1966   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1967     if (pdw->data_store == data_store)
1968       return pdw;
1969
1970   return NULL;
1971 }
1972
1973 #endif
1974
1975 GtkWindow *
1976 create_data_window (void)
1977 {
1978   GtkWidget *w = psppire_data_window_new (NULL);
1979
1980   gtk_widget_show (w);
1981   
1982   return GTK_WINDOW (w);
1983 }
1984
1985
1986
1987 void
1988 open_data_window (PsppireWindow *victim, const char *file_name,
1989                   const char *encoding, gpointer hint)
1990 {
1991   GtkWidget *window;
1992
1993   if (PSPPIRE_IS_DATA_WINDOW (victim)
1994       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1995     {
1996       window = GTK_WIDGET (victim);
1997       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1998     }
1999   else
2000     window = psppire_data_window_new (NULL);
2001
2002   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
2003   gtk_widget_show_all (window);
2004 }