remove case-inserted signal
[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   g_signal_connect_swapped (de->data_store, "cases-deleted",
1495                             G_CALLBACK (set_unsaved), de);
1496
1497   dataset_set_callbacks (de->dataset, &cbs, de);
1498
1499   connect_help (de->builder);
1500
1501   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1502   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1503   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1504   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1505
1506   gtk_container_add (GTK_CONTAINER (de), box);
1507
1508   g_signal_connect (de->dict, "weight-changed",
1509                     G_CALLBACK (on_weight_change),
1510                     de);
1511
1512   g_signal_connect (de->dict, "filter-changed",
1513                     G_CALLBACK (on_filter_change),
1514                     de);
1515
1516   g_signal_connect (de->dict, "split-changed",
1517                     G_CALLBACK (on_split_change),
1518                     de);
1519
1520   g_signal_connect_swapped (de->dict, "changed",
1521                             G_CALLBACK (enable_save), de);
1522   g_signal_connect_swapped (de->dict, "variable-inserted",
1523                             G_CALLBACK (enable_save), de);
1524   g_signal_connect_swapped (de->dict, "variable-deleted",
1525                             G_CALLBACK (enable_save), de);
1526   enable_save (de);
1527
1528   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SORT,  de);
1529   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SPLIT,  de);
1530   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FLIP,  de);
1531   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AGGREGATE,  de);
1532   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_WEIGHT,  de);
1533   
1534   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMPUTE,  de);
1535   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COUNT,  de);
1536   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_AUTORECODE,  de);
1537   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RANK,  de);
1538   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SELECT,  de);
1539   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_SAME,  de);
1540   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RECODE_DIFFERENT,  de);
1541
1542     
1543   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_DESCRIPTIVES,  de);
1544   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FREQUENCIES,  de);
1545   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_EXAMINE,  de);
1546   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CROSSTABS,  de);
1547
1548   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_INDEP_SAMPS,  de);
1549   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_PAIRED,  de);
1550
1551   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_MEANS,  de);
1552   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TT1S,  de);
1553
1554   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ONEWAY, de);
1555   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_UNIVARIATE, de);
1556   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_KMEANS, de);
1557   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_FACTOR, de);
1558   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CORRELATION, de);
1559   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RELIABILITY, de);
1560   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_REGRESSION, de);
1561   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_LOGISTIC, de);
1562   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_ROC, de);
1563   
1564   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_COMMENTS, de);
1565   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_VAR_INFO, de);
1566
1567   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, de);
1568   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, de);
1569   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, de);
1570
1571   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_CHISQUARE, de);
1572   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_BINOMIAL, de);
1573   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_RUNS, de);
1574   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_1SKS, de);
1575   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_TWO_SAMPLE, de);
1576   connect_dialog_action (PSPPIRE_TYPE_DIALOG_ACTION_K_RELATED, de);
1577
1578   {
1579     GSimpleAction *file_import_action = g_simple_action_new ("file-import", NULL);
1580     g_signal_connect_swapped (file_import_action, "activate", G_CALLBACK (file_import), de);
1581     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (file_import_action));
1582   }
1583   
1584   {
1585     GSimpleAction *save = g_simple_action_new ("save", NULL);
1586     g_signal_connect_swapped (save, "activate", G_CALLBACK (psppire_window_save), de);
1587     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save));
1588   }
1589
1590   {
1591     GSimpleAction *open = g_simple_action_new ("open", NULL);
1592     g_signal_connect_swapped (open, "activate", G_CALLBACK (psppire_window_open), de);
1593     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (open));
1594   }
1595
1596   {
1597     GSimpleAction *save_as = g_simple_action_new ("save-as", NULL);
1598     g_signal_connect_swapped (save_as, "activate", G_CALLBACK (psppire_window_save_as), de);
1599     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (save_as));
1600   }
1601
1602   {
1603     GSimpleAction *rename_dataset_act = g_simple_action_new ("rename-dataset", NULL);
1604     g_signal_connect_swapped (rename_dataset_act, "activate",
1605                               G_CALLBACK (on_rename_dataset), de);
1606     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (rename_dataset_act));
1607   }
1608
1609   {
1610     GSimpleAction *info_working = g_simple_action_new ("info-working", NULL);
1611     g_signal_connect_swapped (info_working, "activate", G_CALLBACK (display_dict), de);
1612     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_working));
1613   }
1614   {
1615     GSimpleAction *info_external = g_simple_action_new ("info-external", NULL);
1616     g_signal_connect_swapped (info_external, "activate", G_CALLBACK (sysfile_info), de);
1617     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (info_external));
1618   }
1619
1620   {
1621     GSimpleAction *act_statusbar = g_simple_action_new_stateful ("statusbar", NULL, g_variant_new_boolean (TRUE));
1622     g_signal_connect (act_statusbar, "activate", G_CALLBACK (status_bar_activate), de);
1623     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_statusbar));
1624   }
1625
1626   {
1627     GSimpleAction *act_gridlines = g_simple_action_new_stateful ("gridlines", NULL, g_variant_new_boolean (TRUE));
1628     g_signal_connect (act_gridlines, "activate", G_CALLBACK (grid_lines_activate), de);
1629     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_gridlines));
1630   }
1631
1632   
1633   {
1634     GSimpleAction *act_view_data = g_simple_action_new_stateful ("view_dv", G_VARIANT_TYPE_STRING,
1635                                                                  g_variant_new_string ("DATA"));
1636     g_signal_connect (act_view_data, "activate", G_CALLBACK (activate_change_view), de);
1637     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_view_data));
1638   }
1639
1640   {
1641     GSimpleAction *act_fonts = g_simple_action_new ("fonts", NULL);
1642     g_signal_connect_swapped (act_fonts, "activate", G_CALLBACK (fonts_activate), de);
1643     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_fonts));
1644   }
1645
1646   {
1647     GSimpleAction *act_value_labels =
1648       g_simple_action_new_stateful ("value_labels", NULL,
1649                                     g_variant_new_boolean (FALSE));
1650     g_signal_connect (act_value_labels, "activate", G_CALLBACK (value_labels_activate), de);
1651     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_value_labels));
1652   }
1653
1654   {
1655     GSimpleAction *act_transform_pending = g_simple_action_new ("transform-pending", NULL);
1656     g_signal_connect_swapped (act_transform_pending, "activate", G_CALLBACK (execute), de);
1657     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_transform_pending));
1658   }
1659
1660   {
1661     GSimpleAction *act_jump_to_variable = g_simple_action_new ("jump-to-variable", NULL);
1662     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_variable));
1663   }
1664
1665   {
1666     GSimpleAction *act_insert_variable = g_simple_action_new ("insert-variable", NULL);
1667     g_signal_connect_swapped (act_insert_variable, "activate", G_CALLBACK (insert_variable), de);
1668     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_variable));
1669   }
1670
1671   {
1672     GSimpleAction *act_jump_to_case = g_simple_action_new ("jump-to-case", NULL);
1673     g_signal_connect_swapped (act_jump_to_case, "activate", G_CALLBACK (goto_case), de);
1674     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_jump_to_case));
1675   }
1676
1677   {
1678     GSimpleAction *act_insert_case = g_simple_action_new ("insert-case", NULL);
1679     g_signal_connect_swapped (act_insert_case, "activate", G_CALLBACK (insert_case_at_row), de);
1680     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (act_insert_case));
1681   }
1682
1683   {
1684     GSimpleAction *find = g_simple_action_new ("find", NULL);
1685     g_signal_connect_swapped (find, "activate", G_CALLBACK (find_dialog), de);
1686     g_action_map_add_action (G_ACTION_MAP (de), G_ACTION (find));
1687   }
1688   
1689   {
1690     int idx = 0;
1691     {
1692       GtkToolItem *ti = gtk_tool_button_new (NULL, "Open");
1693       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_open), de);
1694       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1695       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-open-data");
1696     }
1697
1698     {
1699       GtkToolItem *ti = gtk_tool_button_new (NULL, "Save");
1700       g_signal_connect_swapped (ti, "clicked", G_CALLBACK (psppire_window_save), de);
1701       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1702       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "file-save-data");
1703     }
1704
1705     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1706
1707     {
1708       de->ti_jump_to_variable = gtk_tool_button_new (NULL, "Goto Var");
1709
1710       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "PsppireDialogActionVarInfo");
1711       g_assert (a);
1712       g_signal_connect_swapped (de->ti_jump_to_variable, "clicked",
1713                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1714
1715       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_variable, idx++);
1716       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_variable), "edit-go-to-variable");
1717       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_variable), _("Jump to variable"));
1718     }
1719
1720     {
1721       de->ti_jump_to_case = gtk_tool_button_new (NULL, "Jump to Case");
1722       
1723       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "jump-to-case");
1724       g_assert (a);
1725       g_signal_connect_swapped (de->ti_jump_to_case, "clicked",
1726                                 G_CALLBACK (g_action_activate_null), a);
1727       
1728       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_jump_to_case, idx++);
1729       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_jump_to_case), "edit-go-to-case");
1730       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_jump_to_case), _("Jump to a case in the data sheet"));
1731     }
1732
1733     {
1734       de->ti_find = gtk_tool_button_new (NULL, "Find");
1735
1736       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "find");
1737       g_assert (a);
1738       g_signal_connect_swapped (de->ti_find, "clicked",
1739                                 G_CALLBACK (g_action_activate_null), a);
1740
1741       
1742       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_find, idx++);
1743       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_find), "edit-find");
1744       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_find), _("Search for values in the data"));
1745     }
1746
1747     {
1748       de->ti_insert_case = gtk_tool_button_new (NULL, "Create Case");
1749       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-case");
1750       g_assert (a);
1751       g_signal_connect_swapped (de->ti_insert_case, "clicked",
1752                                 G_CALLBACK (g_action_activate_null), a);
1753
1754       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_case, idx++);
1755       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_case), "edit-insert-case");
1756       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_case), _("Create a new case at the current position"));
1757     }
1758
1759     {
1760       de->ti_insert_variable = gtk_tool_button_new (NULL, "Create Variable");
1761       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),  "insert-variable");
1762       g_assert (a);
1763       g_signal_connect_swapped (de->ti_insert_variable, "clicked",
1764                                 G_CALLBACK (g_action_activate_null), a);
1765
1766       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_insert_variable, idx++);
1767       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_insert_variable), "edit-insert-variable");
1768       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_insert_variable), _("Create a new variable at the current position"));
1769     }
1770
1771     gtk_toolbar_insert (GTK_TOOLBAR (hb), gtk_separator_tool_item_new (), idx++);
1772
1773     {
1774       GtkToolItem *ti = gtk_tool_button_new (NULL, "Split");
1775       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1776                                                "PsppireDialogActionSplit");
1777       g_assert (a);
1778       g_signal_connect_swapped (ti, "clicked",
1779                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1780       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1781       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-split-file");
1782       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Split the active dataset"));
1783     }
1784
1785     {
1786       GtkToolItem *ti = gtk_tool_button_new (NULL, "Weight");
1787       GAction *a = g_action_map_lookup_action (G_ACTION_MAP (de),
1788                                                "PsppireDialogActionWeight");
1789       g_assert (a);
1790       g_signal_connect_swapped (ti, "clicked",
1791                                 G_CALLBACK (psppire_dialog_action_activate_null), a);
1792       gtk_toolbar_insert (GTK_TOOLBAR (hb), ti, idx++);
1793       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (ti), "data-weight-cases");
1794       gtk_widget_set_tooltip_text (GTK_WIDGET (ti), _("Weight cases by variable"));
1795     }
1796
1797     {
1798       de->ti_value_labels_button = gtk_toggle_tool_button_new ();
1799       gtk_tool_button_set_label (GTK_TOOL_BUTTON (de->ti_value_labels_button),
1800                                  "Value Labels");
1801       g_signal_connect (de->ti_value_labels_button, "toggled",
1802                         G_CALLBACK (on_labels_button_toggle), de);
1803       gtk_toolbar_insert (GTK_TOOLBAR (hb), de->ti_value_labels_button, idx++);
1804       gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (de->ti_value_labels_button), "view-value-labels");
1805       gtk_widget_set_tooltip_text (GTK_WIDGET (de->ti_value_labels_button), _("Show/hide value labels"));
1806     }
1807   }
1808
1809
1810   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1811   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1812
1813   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_file_menu (de), 0);
1814   gtk_menu_shell_insert (GTK_MENU_SHELL (menubar),  create_edit_menu (de), 1);
1815   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_windows_menu (GTK_WINDOW (de)));
1816   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1817
1818   g_signal_connect (de->data_editor, "switch-page",
1819                     G_CALLBACK (on_switch_page), de);
1820
1821   gtk_widget_show (GTK_WIDGET (de->data_editor));
1822   gtk_widget_show_all (box);
1823
1824   ll_push_head (&all_data_windows, &de->ll);
1825 }
1826
1827
1828 static void
1829 psppire_data_window_dispose (GObject *object)
1830 {
1831   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1832
1833   if (dw->builder != NULL)
1834     {
1835       g_object_unref (dw->builder);
1836       dw->builder = NULL;
1837     }
1838
1839   if (dw->dict)
1840     {
1841       g_signal_handlers_disconnect_by_func (dw->dict,
1842                                             G_CALLBACK (enable_save), dw);
1843       g_signal_handlers_disconnect_by_func (dw->dict,
1844                                             G_CALLBACK (on_weight_change), dw);
1845       g_signal_handlers_disconnect_by_func (dw->dict,
1846                                             G_CALLBACK (on_filter_change), dw);
1847       g_signal_handlers_disconnect_by_func (dw->dict,
1848                                             G_CALLBACK (on_split_change), dw);
1849
1850       g_object_unref (dw->dict);
1851       dw->dict = NULL;
1852     }
1853
1854   if (dw->data_store)
1855     {
1856       g_object_unref (dw->data_store);
1857       dw->data_store = NULL;
1858     }
1859
1860   if (dw->ll.next != NULL)
1861     {
1862       ll_remove (&dw->ll);
1863       dw->ll.next = NULL;
1864     }
1865
1866   if (G_OBJECT_CLASS (parent_class)->dispose)
1867     G_OBJECT_CLASS (parent_class)->dispose (object);
1868 }
1869
1870 static void
1871 psppire_data_window_finalize (GObject *object)
1872 {
1873   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1874
1875   if (dw->dataset)
1876     {
1877       struct dataset *dataset = dw->dataset;
1878       struct session *session = dataset_session (dataset);
1879
1880       dw->dataset = NULL;
1881
1882       dataset_set_callbacks (dataset, NULL, NULL);
1883       session_set_active_dataset (session, NULL);
1884       dataset_destroy (dataset);
1885     }
1886
1887   if (G_OBJECT_CLASS (parent_class)->finalize)
1888     G_OBJECT_CLASS (parent_class)->finalize (object);
1889 }
1890
1891 static void
1892 psppire_data_window_set_property (GObject         *object,
1893                                   guint            prop_id,
1894                                   const GValue    *value,
1895                                   GParamSpec      *pspec)
1896 {
1897   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1898
1899   switch (prop_id)
1900     {
1901     case PROP_DATASET:
1902       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1903       break;
1904     default:
1905       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1906       break;
1907     };
1908 }
1909
1910 static void
1911 psppire_data_window_get_property (GObject         *object,
1912                                   guint            prop_id,
1913                                   GValue          *value,
1914                                   GParamSpec      *pspec)
1915 {
1916   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1917
1918   switch (prop_id)
1919     {
1920     case PROP_DATASET:
1921       g_value_set_pointer (value, window->dataset);
1922       break;
1923     default:
1924       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1925       break;
1926     };
1927 }
1928
1929
1930
1931 GtkWidget*
1932 psppire_data_window_new (struct dataset *ds)
1933 {
1934   GtkWidget *dw;
1935
1936   if (the_session == NULL)
1937     the_session = session_create (NULL);
1938
1939   if (ds == NULL)
1940     {
1941       char *dataset_name = session_generate_dataset_name (the_session);
1942       ds = dataset_create (the_session, dataset_name);
1943       free (dataset_name);
1944     }
1945   assert (dataset_session (ds) == the_session);
1946
1947   dw = GTK_WIDGET (
1948                    g_object_new (
1949                                  psppire_data_window_get_type (),
1950                                  "description", _("Data Editor"),
1951                                  "dataset", ds,
1952                                  NULL));
1953
1954   if (dataset_name (ds) != NULL)
1955     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1956
1957
1958   GApplication *app = g_application_get_default ();
1959   gtk_application_add_window (GTK_APPLICATION (app), GTK_WINDOW (dw));
1960   
1961   return dw;
1962 }
1963
1964
1965
1966 bool
1967 psppire_data_window_is_empty (PsppireDataWindow *dw)
1968 {
1969   return psppire_dict_get_var_cnt (dw->dict) == 0;
1970 }
1971
1972
1973 static void
1974 psppire_data_window_iface_init (PsppireWindowIface *iface)
1975 {
1976   iface->save = save_file;
1977   iface->pick_filename = data_pick_filename;
1978   iface->load = load_file;
1979 }
1980
1981 \f
1982
1983
1984 PsppireDataWindow *
1985 psppire_default_data_window (void)
1986 {
1987   if (ll_is_empty (&all_data_windows))
1988     create_data_window ();
1989   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1990 }
1991
1992
1993
1994 void
1995 psppire_data_window_set_default (PsppireDataWindow *pdw)
1996 {
1997   ll_remove (&pdw->ll);
1998   ll_push_head (&all_data_windows, &pdw->ll);
1999 }
2000
2001 void
2002 psppire_data_window_undefault (PsppireDataWindow *pdw)
2003 {
2004   ll_remove (&pdw->ll);
2005   ll_push_tail (&all_data_windows, &pdw->ll);
2006 }
2007
2008
2009
2010 PsppireDataWindow *
2011 psppire_data_window_for_dataset (struct dataset *ds)
2012 {
2013   PsppireDataWindow *pdw;
2014
2015   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
2016     if (pdw->dataset == ds)
2017       return pdw;
2018
2019   return NULL;
2020 }
2021
2022 GtkWindow *
2023 create_data_window (void)
2024 {
2025   GtkWidget *w = psppire_data_window_new (NULL);
2026
2027   gtk_widget_show (w);
2028   
2029   return GTK_WINDOW (w);
2030 }
2031
2032 GtkWindow *
2033 open_data_window (PsppireWindow *victim, const char *file_name,
2034                   const char *encoding, gpointer hint)
2035 {
2036   GtkWidget *window;
2037
2038   if (PSPPIRE_IS_DATA_WINDOW (victim)
2039       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
2040     {
2041       window = GTK_WIDGET (victim);
2042       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
2043     }
2044   else
2045     window = psppire_data_window_new (NULL);
2046
2047   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
2048   gtk_widget_show_all (window);
2049   return GTK_WINDOW (window);
2050 }