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