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