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