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