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