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