e695310e0e17cfe2e6086035108deab43c5779ff
[pspp-builds.git] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011  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/aggregate-dialog.h"
28 #include "ui/gui/autorecode-dialog.h"
29 #include "ui/gui/binomial-dialog.h"
30 #include "ui/gui/chi-square-dialog.h"
31 #include "ui/gui/comments-dialog.h"
32 #include "ui/gui/compute-dialog.h"
33 #include "ui/gui/correlation-dialog.h"
34 #include "ui/gui/count-dialog.h"
35 #include "ui/gui/crosstabs-dialog.h"
36 #include "ui/gui/descriptives-dialog.h"
37 #include "ui/gui/entry-dialog.h"
38 #include "ui/gui/examine-dialog.h"
39 #include "ui/gui/executor.h"
40 #include "ui/gui/factor-dialog.h"
41 #include "ui/gui/find-dialog.h"
42 #include "ui/gui/frequencies-dialog.h"
43 #include "ui/gui/goto-case-dialog.h"
44 #include "ui/gui/help-menu.h"
45 #include "ui/gui/helper.h"
46 #include "ui/gui/k-means-dialog.h"
47 #include "ui/gui/k-related-dialog.h"
48 #include "ui/gui/npar-two-sample-related.h"
49 #include "ui/gui/oneway-anova-dialog.h"
50 #include "ui/gui/psppire-data-window.h"
51 #include "ui/gui/psppire-syntax-window.h"
52 #include "ui/gui/psppire-window.h"
53 #include "ui/gui/psppire.h"
54 #include "ui/gui/rank-dialog.h"
55 #include "ui/gui/runs-dialog.h"
56 #include "ui/gui/recode-dialog.h"
57 #include "ui/gui/regression-dialog.h"
58 #include "ui/gui/reliability-dialog.h"
59 #include "ui/gui/roc-dialog.h"
60 #include "ui/gui/select-cases-dialog.h"
61 #include "ui/gui/sort-cases-dialog.h"
62 #include "ui/gui/split-file-dialog.h"
63 #include "ui/gui/t-test-independent-samples-dialog.h"
64 #include "ui/gui/t-test-one-sample.h"
65 #include "ui/gui/t-test-paired-samples.h"
66 #include "ui/gui/text-data-import-dialog.h"
67 #include "ui/gui/transpose-dialog.h"
68 #include "ui/gui/variable-info-dialog.h"
69 #include "ui/gui/weight-cases-dialog.h"
70 #include "ui/syntax-gen.h"
71
72 #include "gl/c-strcase.h"
73 #include "gl/c-strcasestr.h"
74 #include "gl/xvasprintf.h"
75
76 #include <gettext.h>
77 #define _(msgid) gettext (msgid)
78 #define N_(msgid) msgid
79
80 struct session *the_session;
81 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
82
83 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
84 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
85
86
87 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
88
89 static void psppire_data_window_dispose (GObject *object);
90 static void psppire_data_window_set_property (GObject         *object,
91                                               guint            prop_id,
92                                               const GValue    *value,
93                                               GParamSpec      *pspec);
94 static void psppire_data_window_get_property (GObject         *object,
95                                               guint            prop_id,
96                                               GValue          *value,
97                                               GParamSpec      *pspec);
98
99 GType
100 psppire_data_window_get_type (void)
101 {
102   static GType psppire_data_window_type = 0;
103
104   if (!psppire_data_window_type)
105     {
106       static const GTypeInfo psppire_data_window_info =
107         {
108           sizeof (PsppireDataWindowClass),
109           NULL,
110           NULL,
111           (GClassInitFunc)psppire_data_window_class_init,
112           (GClassFinalizeFunc) NULL,
113           NULL,
114           sizeof (PsppireDataWindow),
115           0,
116           (GInstanceInitFunc) psppire_data_window_init,
117         };
118
119       static const GInterfaceInfo window_interface_info =
120         {
121           (GInterfaceInitFunc) psppire_data_window_iface_init,
122           NULL,
123           NULL
124         };
125
126       psppire_data_window_type =
127         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
128                                 &psppire_data_window_info, 0);
129
130
131       g_type_add_interface_static (psppire_data_window_type,
132                                    PSPPIRE_TYPE_WINDOW_MODEL,
133                                    &window_interface_info);
134     }
135
136   return psppire_data_window_type;
137 }
138
139 static GObjectClass *parent_class ;
140
141 enum {
142     PROP_DATASET = 1
143 };
144
145 static void
146 psppire_data_window_class_init (PsppireDataWindowClass *class)
147 {
148   GObjectClass *object_class = G_OBJECT_CLASS (class);
149
150   parent_class = g_type_class_peek_parent (class);
151
152   object_class->dispose = psppire_data_window_dispose;
153   object_class->set_property = psppire_data_window_set_property;
154   object_class->get_property = psppire_data_window_get_property;
155
156   g_object_class_install_property (
157     object_class, PROP_DATASET,
158     g_param_spec_pointer ("dataset", "Dataset",
159                           "'struct datset *' represented by the window",
160                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
161 }
162 \f
163 static void
164 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
165 {
166   GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
167
168   gtk_action_set_sensitive (edit_paste, x);
169 }
170
171 static void
172 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
173 {
174   GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
175   GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
176
177   gtk_action_set_sensitive (edit_copy, x);
178   gtk_action_set_sensitive (edit_cut, x);
179 }
180
181 /* Run the EXECUTE command. */
182 static void
183 execute (PsppireDataWindow *dw)
184 {
185   execute_const_syntax_string (dw, "EXECUTE.");
186 }
187
188 static void
189 transformation_change_callback (bool transformations_pending,
190                                 gpointer data)
191 {
192   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
193
194   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
195
196   GtkWidget *menuitem =
197     gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
198
199   GtkWidget *status_label  =
200     get_widget_assert (de->builder, "case-counter-area");
201
202   gtk_widget_set_sensitive (menuitem, transformations_pending);
203
204
205   if ( transformations_pending)
206     gtk_label_set_text (GTK_LABEL (status_label),
207                         _("Transformations Pending"));
208   else
209     gtk_label_set_text (GTK_LABEL (status_label), "");
210 }
211
212 /* Callback for when the dictionary changes its filter variable */
213 static void
214 on_filter_change (GObject *o, gint filter_index, gpointer data)
215 {
216   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
217
218   GtkWidget *filter_status_area =
219     get_widget_assert (de->builder, "filter-use-status-area");
220
221   if ( filter_index == -1 )
222     {
223       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
224     }
225   else
226     {
227       PsppireVarStore *vs = NULL;
228       PsppireDict *dict = NULL;
229       struct variable *var ;
230       gchar *text ;
231
232       g_object_get (de->data_editor, "var-store", &vs, NULL);
233       g_object_get (vs, "dictionary", &dict, NULL);
234
235       var = psppire_dict_get_variable (dict, filter_index);
236
237       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
238
239       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
240
241       g_free (text);
242     }
243 }
244
245 /* Callback for when the dictionary changes its split variables */
246 static void
247 on_split_change (PsppireDict *dict, gpointer data)
248 {
249   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
250
251   size_t n_split_vars = dict_get_split_cnt (dict->dict);
252
253   GtkWidget *split_status_area =
254     get_widget_assert (de->builder, "split-file-status-area");
255
256   if ( n_split_vars == 0 )
257     {
258       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
259     }
260   else
261     {
262       gint i;
263       GString *text;
264       const struct variable *const * split_vars =
265         dict_get_split_vars (dict->dict);
266
267       text = g_string_new (_("Split by "));
268
269       for (i = 0 ; i < n_split_vars - 1; ++i )
270         {
271           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
272         }
273       g_string_append (text, var_get_name (split_vars[i]));
274
275       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
276
277       g_string_free (text, TRUE);
278     }
279 }
280
281
282
283
284 /* Callback for when the dictionary changes its weights */
285 static void
286 on_weight_change (GObject *o, gint weight_index, gpointer data)
287 {
288   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
289
290   GtkWidget *weight_status_area =
291     get_widget_assert (de->builder, "weight-status-area");
292
293   if ( weight_index == -1 )
294     {
295       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
296     }
297   else
298     {
299       struct variable *var ;
300       PsppireVarStore *vs = NULL;
301       PsppireDict *dict = NULL;
302       gchar *text;
303
304       g_object_get (de->data_editor, "var-store", &vs, NULL);
305       g_object_get (vs, "dictionary", &dict, NULL);
306
307       var = psppire_dict_get_variable (dict, weight_index);
308
309       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
310
311       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
312
313       g_free (text);
314     }
315 }
316
317 #if 0
318 static void
319 dump_rm (GtkRecentManager *rm)
320 {
321   GList *items = gtk_recent_manager_get_items (rm);
322
323   GList *i;
324
325   g_print ("Recent Items:\n");
326   for (i = items; i; i = i->next)
327     {
328       GtkRecentInfo *ri = i->data;
329
330       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
331                gtk_recent_info_get_short_name (ri),
332                gtk_recent_info_get_mime_type (ri),
333                gtk_recent_info_get_description (ri),
334                gtk_recent_info_get_uri (ri)
335                );
336
337
338       gtk_recent_info_unref (ri);
339     }
340
341   g_list_free (items);
342 }
343 #endif
344
345 static gboolean
346 name_has_por_suffix (const gchar *name)
347 {
348   size_t length = strlen (name);
349   return length > 4 && !c_strcasecmp (&name[length - 4], ".por");
350 }
351
352 static gboolean
353 name_has_sav_suffix (const gchar *name)
354 {
355   size_t length = strlen (name);
356   return length > 4 && !c_strcasecmp (&name[length - 4], ".sav");
357 }
358
359 /* Returns true if NAME has a suffix which might denote a PSPP file */
360 static gboolean
361 name_has_suffix (const gchar *name)
362 {
363   return name_has_por_suffix (name) || name_has_sav_suffix (name);
364 }
365
366 static gboolean
367 load_file (PsppireWindow *de, const gchar *file_name)
368 {
369   struct string filename;
370   gchar *utf8_file_name;
371   const char *mime_type;
372   gchar *syntax;
373   bool ok;
374
375   ds_init_empty (&filename);
376
377   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
378
379   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
380
381   g_free (utf8_file_name);
382
383   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
384   ds_destroy (&filename);
385
386   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
387                        lex_reader_for_string (syntax));
388   g_free (syntax);
389
390   mime_type = (name_has_por_suffix (file_name)
391                ? "application/x-spss-por"
392                : "application/x-spss-sav");
393
394   add_most_recent (file_name, mime_type);
395
396   return ok;
397 }
398
399 /* Save DE to file */
400 static void
401 save_file (PsppireWindow *w)
402 {
403   const gchar *file_name = NULL;
404   gchar *utf8_file_name = NULL;
405   GString *fnx;
406   struct string filename ;
407   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
408   gchar *syntax;
409
410   file_name = psppire_window_get_filename (w);
411
412   fnx = g_string_new (file_name);
413
414   if ( ! name_has_suffix (fnx->str))
415     {
416       if ( de->save_as_portable)
417         g_string_append (fnx, ".por");
418       else
419         g_string_append (fnx, ".sav");
420     }
421
422   ds_init_empty (&filename);
423
424   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
425
426   g_string_free (fnx, TRUE);
427
428   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
429   g_free (utf8_file_name);
430
431   syntax = g_strdup_printf ("%s OUTFILE=%s.",
432                             de->save_as_portable ? "EXPORT" : "SAVE",
433                             ds_cstr (&filename));
434
435   ds_destroy (&filename);
436
437   g_free (execute_syntax_string (de, syntax));
438 }
439
440
441 static void
442 insert_case (PsppireDataWindow *dw)
443 {
444   psppire_data_editor_insert_case (dw->data_editor);
445 }
446
447 static void
448 on_insert_variable (PsppireDataWindow *dw)
449 {
450   psppire_data_editor_insert_variable (dw->data_editor);
451 }
452
453
454 static void
455 display_dict (PsppireDataWindow *de)
456 {
457   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
458 }
459
460 static void
461 sysfile_info (PsppireDataWindow *de)
462 {
463   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
464
465   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
466     {
467       struct string filename;
468       gchar *file_name =
469         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
470
471       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
472                                                   NULL);
473
474       gchar *syntax;
475
476       ds_init_empty (&filename);
477
478       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
479
480       g_free (utf8_file_name);
481
482       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
483       g_free (execute_syntax_string (de, syntax));
484     }
485
486   gtk_widget_destroy (dialog);
487 }
488
489
490 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
491 static void
492 data_pick_filename (PsppireWindow *window)
493 {
494   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
495   GtkWidget *button_sys;
496   GtkWidget *dialog =
497     gtk_file_chooser_dialog_new (_("Save"),
498                                  GTK_WINDOW (de),
499                                  GTK_FILE_CHOOSER_ACTION_SAVE,
500                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
501                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
502                                  NULL);
503
504   GtkFileFilter *filter = gtk_file_filter_new ();
505   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
506   gtk_file_filter_add_pattern (filter, "*.sav");
507   gtk_file_filter_add_pattern (filter, "*.SAV");
508   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
509
510   filter = gtk_file_filter_new ();
511   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
512   gtk_file_filter_add_pattern (filter, "*.por");
513   gtk_file_filter_add_pattern (filter, "*.POR");
514   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
515
516   filter = gtk_file_filter_new ();
517   gtk_file_filter_set_name (filter, _("All Files"));
518   gtk_file_filter_add_pattern (filter, "*");
519   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
520
521   {
522     GtkWidget *button_por;
523     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
524     button_sys =
525       gtk_radio_button_new_with_label (NULL, _("System File"));
526
527     button_por =
528       gtk_radio_button_new_with_label
529       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
530        _("Portable File"));
531
532     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
533     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
534
535     gtk_widget_show_all (vbox);
536
537     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
538   }
539
540   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
541                                                   TRUE);
542
543   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
544     {
545     case GTK_RESPONSE_ACCEPT:
546       {
547         GString *filename =
548           g_string_new
549           (
550            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
551            );
552
553         de->save_as_portable =
554           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
555
556         if ( ! name_has_suffix (filename->str))
557           {
558             if ( de->save_as_portable)
559               g_string_append (filename, ".por");
560             else
561               g_string_append (filename, ".sav");
562           }
563
564         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
565
566         g_string_free (filename, TRUE);
567       }
568       break;
569     default:
570       break;
571     }
572
573   gtk_widget_destroy (dialog);
574 }
575
576 static bool
577 confirm_delete_dataset (PsppireDataWindow *de,
578                         const char *old_dataset,
579                         const char *new_dataset,
580                         const char *existing_dataset)
581 {
582   GtkWidget *dialog;
583   int result;
584
585   dialog = gtk_message_dialog_new (
586     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
587     _("Delete Existing Dataset?"));
588
589   gtk_message_dialog_format_secondary_text (
590     GTK_MESSAGE_DIALOG (dialog),
591     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
592       "dataset named \"%s\".  Are you sure that you want to do this?"),
593     old_dataset, new_dataset, existing_dataset);
594
595   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
596                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
597                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
598                           NULL);
599
600   g_object_set (dialog, "icon-name", "psppicon", NULL);
601
602   result = gtk_dialog_run (GTK_DIALOG (dialog));
603
604   gtk_widget_destroy (dialog);
605
606   return result == GTK_RESPONSE_OK;
607 }
608
609 static void
610 on_rename_dataset (PsppireDataWindow *de)
611 {
612   struct dataset *ds = de->dataset;
613   struct session *session = dataset_session (ds);
614   const char *old_name = dataset_name (ds);
615   struct dataset *existing_dataset;
616   char *new_name;
617   char *prompt;
618
619   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
620                       old_name);
621   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
622                                old_name);
623   free (prompt);
624
625   if (new_name == NULL)
626     return;
627
628   existing_dataset = session_lookup_dataset (session, new_name);
629   if (existing_dataset == NULL || existing_dataset == ds
630       || confirm_delete_dataset (de, old_name, new_name,
631                                  dataset_name (existing_dataset)))
632     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
633                                                         new_name)));
634
635   free (new_name);
636 }
637
638 static void
639 on_edit_paste (PsppireDataWindow  *de)
640 {
641   psppire_data_editor_clip_paste (de->data_editor);
642 }
643
644 static void
645 on_edit_copy (PsppireDataWindow  *de)
646 {
647   psppire_data_editor_clip_copy (de->data_editor);
648 }
649
650
651
652 static void
653 on_edit_cut (PsppireDataWindow  *de)
654 {
655   psppire_data_editor_clip_cut (de->data_editor);
656 }
657
658
659 static void
660 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
661 {
662   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
663
664   if ( gtk_toggle_action_get_active (action))
665     gtk_widget_show (statusbar);
666   else
667     gtk_widget_hide (statusbar);
668 }
669
670
671 static void
672 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
673 {
674   const gboolean grid_visible = gtk_toggle_action_get_active (action);
675
676   psppire_data_editor_show_grid (de->data_editor, grid_visible);
677 }
678
679 static void
680 data_view_activate (PsppireDataWindow  *de)
681 {
682   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
683 }
684
685
686 static void
687 variable_view_activate (PsppireDataWindow  *de)
688 {
689   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
690 }
691
692
693 static void
694 fonts_activate (PsppireDataWindow  *de)
695 {
696   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
697   PangoFontDescription *current_font;
698   gchar *font_name;
699   GtkWidget *dialog =
700     gtk_font_selection_dialog_new (_("Font Selection"));
701
702
703   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
704   font_name = pango_font_description_to_string (current_font);
705
706   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
707
708   g_free (font_name);
709
710   gtk_window_set_transient_for (GTK_WINDOW (dialog),
711                                 GTK_WINDOW (toplevel));
712
713   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
714     {
715       const gchar *font = gtk_font_selection_dialog_get_font_name
716         (GTK_FONT_SELECTION_DIALOG (dialog));
717
718       PangoFontDescription* font_desc =
719         pango_font_description_from_string (font);
720
721       psppire_data_editor_set_font (de->data_editor, font_desc);
722     }
723
724   gtk_widget_hide (dialog);
725 }
726
727
728
729 /* Callback for the value labels action */
730 static void
731 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
732 {
733   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
734 }
735
736 static void
737 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
738 {
739   psppire_data_editor_split_window (de->data_editor,
740                                     gtk_toggle_action_get_active (ta));
741 }
742
743
744 static void
745 file_quit (PsppireDataWindow *de)
746 {
747   /* FIXME: Need to be more intelligent here.
748      Give the user the opportunity to save any unsaved data.
749   */
750   psppire_quit ();
751 }
752
753
754 static void
755 on_recent_data_select (GtkMenuShell *menushell,
756                        PsppireWindow *window)
757 {
758   gchar *file;
759
760   gchar *uri =
761     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
762
763   file = g_filename_from_uri (uri, NULL, NULL);
764
765   g_free (uri);
766
767   open_data_window (window, file);
768
769   g_free (file);
770 }
771
772 static char *
773 charset_from_mime_type (const char *mime_type)
774 {
775   const char *charset;
776   struct string s;
777   const char *p;
778
779   if (mime_type == NULL)
780     return NULL;
781
782   charset = c_strcasestr (mime_type, "charset=");
783   if (charset == NULL)
784     return NULL;
785
786   ds_init_empty (&s);
787   p = charset + 8;
788   if (*p == '"')
789     {
790       /* Parse a "quoted-string" as defined by RFC 822. */
791       for (p++; *p != '\0' && *p != '"'; p++)
792         {
793           if (*p != '\\')
794             ds_put_byte (&s, *p);
795           else if (*++p != '\0')
796             ds_put_byte (&s, *p);
797         }
798     }
799   else
800     {
801       /* Parse a "token" as defined by RFC 2045. */
802       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
803         ds_put_byte (&s, *p++);
804     }
805   if (!ds_is_empty (&s))
806     return ds_steal_cstr (&s);
807
808   ds_destroy (&s);
809   return NULL;
810 }
811
812 static void
813 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
814 {
815   GtkRecentInfo *item;
816   char *encoding;
817   GtkWidget *se;
818   gchar *file;
819
820   /* Get the file name and its encoding. */
821   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
822   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
823   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
824   gtk_recent_info_unref (item);
825
826   se = psppire_syntax_window_new (encoding);
827
828   free (encoding);
829
830   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
831     gtk_widget_show (se);
832   else
833     gtk_widget_destroy (se);
834
835   g_free (file);
836 }
837
838
839 static void
840 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
841 {
842   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
843
844   gtk_action_set_visible (de->delete_cases, case_num != -1);
845 }
846
847
848 static void
849 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
850 {
851   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
852
853   gtk_action_set_visible (de->delete_variables, var != -1);
854 }
855
856 /* Callback for when the datasheet/varsheet is selected */
857 static void
858 on_switch_sheet (GtkNotebook *notebook,
859                  GtkNotebookPage *page,
860                  guint page_num,
861                  gpointer user_data)
862 {
863   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
864
865   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
866
867   GtkWidget *view_data =
868     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
869
870   GtkWidget *view_variables =
871     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
872
873   switch (page_num)
874     {
875     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
876       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
877                                       TRUE);
878       gtk_action_set_sensitive (de->insert_variable, TRUE);
879       gtk_action_set_sensitive (de->insert_case, FALSE);
880       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
881       break;
882     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
883       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
884       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
885       gtk_action_set_sensitive (de->insert_case, TRUE);
886       break;
887     default:
888       g_assert_not_reached ();
889       break;
890     }
891
892 #if 0
893   update_paste_menuitem (de, page_num);
894 #endif
895 }
896
897
898
899 static void
900 set_unsaved (gpointer w)
901 {
902   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
903 }
904
905
906 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
907    Returns a pointer to the action
908 */
909 static GtkAction *
910 connect_action (PsppireDataWindow *dw, const char *action_name, 
911                                     GCallback handler)
912 {
913   GtkAction *action = get_action_assert (dw->builder, action_name);
914  
915   g_signal_connect_swapped (action, "activate", handler, dw);
916
917   return action;
918 }
919
920 /* Initializes as much of a PsppireDataWindow as we can and must before the
921    dataset has been set.
922
923    In particular, the 'menu' member is required in case the "filename" property
924    is set before the "dataset" property: otherwise PsppireWindow will try to
925    modify the menu as part of the "filename" property_set() function and end up
926    with a Gtk-CRITICAL since 'menu' is NULL.  */
927 static void
928 psppire_data_window_init (PsppireDataWindow *de)
929 {
930   GtkUIManager *uim;
931
932   de->builder = builder_new ("data-editor.ui");
933
934   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
935
936   PSPPIRE_WINDOW (de)->menu =
937     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
938 }
939
940 static void
941 psppire_data_window_finish_init (PsppireDataWindow *de,
942                                  struct dataset *ds)
943 {
944   static const struct dataset_callbacks cbs =
945     {
946       set_unsaved,                    /* changed */
947       transformation_change_callback, /* transformations_changed */
948     };
949
950   PsppireDict *dict;
951
952   GtkWidget *menubar;
953   GtkWidget *hb ;
954   GtkWidget *sb ;
955
956   GtkWidget *box = gtk_vbox_new (FALSE, 0);
957
958   de->dataset = ds;
959   dict = psppire_dict_new_from_dict (dataset_dict (ds));
960   de->var_store = psppire_var_store_new (dict);
961   de->data_store = psppire_data_store_new (dict);
962   psppire_data_store_set_reader (de->data_store, NULL);
963
964   menubar = get_widget_assert (de->builder, "menubar");
965   hb = get_widget_assert (de->builder, "handlebox1");
966   sb = get_widget_assert (de->builder, "status-bar");
967
968   de->data_editor =
969     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
970                                                   de->data_store));
971
972   g_signal_connect_swapped (de->data_store, "case-changed",
973                             G_CALLBACK (set_unsaved), de);
974
975   g_signal_connect_swapped (de->data_store, "case-inserted",
976                             G_CALLBACK (set_unsaved), de);
977
978   g_signal_connect_swapped (de->data_store, "cases-deleted",
979                             G_CALLBACK (set_unsaved), de);
980
981   dataset_set_callbacks (de->dataset, &cbs, de);
982
983   connect_help (de->builder);
984
985   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
986   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
987   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
988   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
989
990   gtk_container_add (GTK_CONTAINER (de), box);
991
992   set_cut_copy_menuitem_sensitivity (de, FALSE);
993
994   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
995                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
996
997
998   set_paste_menuitem_sensitivity (de, FALSE);
999
1000   g_signal_connect_swapped (de->data_editor, "data-available-changed",
1001                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1002
1003   g_signal_connect (dict, "weight-changed",
1004                     G_CALLBACK (on_weight_change),
1005                     de);
1006
1007   g_signal_connect (dict, "filter-changed",
1008                     G_CALLBACK (on_filter_change),
1009                     de);
1010
1011   g_signal_connect (dict, "split-changed",
1012                     G_CALLBACK (on_split_change),
1013                     de);
1014
1015
1016   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1017
1018   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1019
1020   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1021
1022   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1023
1024   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1025  
1026   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1027
1028   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1029
1030   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1031
1032   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1033
1034   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1035
1036   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1037
1038   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1039
1040   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1041
1042   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1043
1044   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1045
1046   {
1047     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1048
1049     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1050
1051     gtk_action_set_visible (de->delete_cases, FALSE);
1052   }
1053
1054
1055   {
1056     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1057
1058     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1059
1060     gtk_action_set_visible (de->delete_variables, FALSE);
1061   }
1062
1063
1064   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1065
1066   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1067  
1068   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1069
1070   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1071
1072   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1073
1074   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1075
1076   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1077
1078   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1079
1080   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1081
1082
1083   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1084  
1085   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1086
1087   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1088
1089   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1090
1091   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1092
1093   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1094  
1095   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1096
1097   connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1098  
1099   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1100  
1101   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1102
1103   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1104  
1105   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1106  
1107   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1108  
1109   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1110  
1111   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1112  
1113   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1114  
1115   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1116
1117   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1118  
1119   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1120
1121   connect_action (de, "k-means", G_CALLBACK (k_means_dialog));
1122
1123   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1124   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1125   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1126   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1127   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1128  
1129
1130   {
1131     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1132
1133     GtkWidget *recent_data =
1134       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1135
1136     GtkWidget *recent_files =
1137       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1138
1139
1140     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1141       gtk_recent_manager_get_default ());
1142
1143     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1144       gtk_recent_manager_get_default ());
1145
1146     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1147     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1148
1149     {
1150       GtkRecentFilter *filter = gtk_recent_filter_new ();
1151
1152       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1153       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1154
1155       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1156
1157       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1158     }
1159
1160     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1161
1162
1163     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1164
1165     {
1166       GtkRecentFilter *filter = gtk_recent_filter_new ();
1167
1168       gtk_recent_filter_add_pattern (filter, "*.sps");
1169       gtk_recent_filter_add_pattern (filter, "*.SPS");
1170
1171       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1172
1173       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1174     }
1175
1176     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1177
1178     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1179
1180   }
1181
1182   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1183
1184
1185   g_signal_connect (de->data_editor,
1186                     "cases-selected",
1187                     G_CALLBACK (enable_delete_cases),
1188                     de);
1189
1190   g_signal_connect (de->data_editor,
1191                     "variables-selected",
1192                     G_CALLBACK (enable_delete_variables),
1193                     de);
1194
1195
1196   g_signal_connect (de->data_editor,
1197                     "switch-page",
1198                     G_CALLBACK (on_switch_sheet), de);
1199
1200   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1201   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1202
1203   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1204
1205   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1206
1207   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1208
1209   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1210
1211   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1212
1213   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1214
1215   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1216
1217   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1218
1219   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1220
1221   {
1222     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1223
1224     merge_help_menu (uim);
1225   }
1226
1227   {
1228     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1229                                                                 "datasheet-cases-popup");
1230
1231     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1232                                                                   "varsheet-variable-popup");
1233
1234     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1235                                                                    "datasheet-variable-popup");
1236
1237     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1238                               G_CALLBACK (psppire_data_editor_sort_ascending),
1239                               de->data_editor);
1240
1241     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1242                               G_CALLBACK (psppire_data_editor_sort_descending),
1243                               de->data_editor);
1244
1245     g_object_set (de->data_editor,
1246                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1247                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1248                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1249                   NULL);
1250   }
1251
1252   gtk_widget_show (GTK_WIDGET (de->data_editor));
1253   gtk_widget_show (box);
1254
1255   ll_push_head (&all_data_windows, &de->ll);
1256 }
1257
1258 static void
1259 psppire_data_window_dispose (GObject *object)
1260 {
1261   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1262
1263   if (dw->builder != NULL)
1264     {
1265       g_object_unref (dw->builder);
1266       dw->builder = NULL;
1267     }
1268
1269   if (dw->var_store)
1270     {
1271       g_object_unref (dw->var_store);
1272       dw->var_store = NULL;
1273     }
1274
1275   if (dw->data_store)
1276     {
1277       g_object_unref (dw->data_store);
1278       dw->data_store = NULL;
1279     }
1280
1281   if (dw->ll.next != NULL)
1282     {
1283       ll_remove (&dw->ll);
1284       dw->ll.next = NULL;
1285     }
1286
1287   if (G_OBJECT_CLASS (parent_class)->dispose)
1288     G_OBJECT_CLASS (parent_class)->dispose (object);
1289 }
1290
1291 static void
1292 psppire_data_window_set_property (GObject         *object,
1293                                   guint            prop_id,
1294                                   const GValue    *value,
1295                                   GParamSpec      *pspec)
1296 {
1297   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1298
1299   switch (prop_id)
1300     {
1301     case PROP_DATASET:
1302       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1303       break;
1304     default:
1305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1306       break;
1307     };
1308 }
1309
1310 static void
1311 psppire_data_window_get_property (GObject         *object,
1312                                   guint            prop_id,
1313                                   GValue          *value,
1314                                   GParamSpec      *pspec)
1315 {
1316   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1317
1318   switch (prop_id)
1319     {
1320     case PROP_DATASET:
1321       g_value_set_pointer (value, window->dataset);
1322       break;
1323     default:
1324       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1325       break;
1326     };
1327 }
1328
1329 GtkWidget*
1330 psppire_data_window_new (struct dataset *ds)
1331 {
1332   GtkWidget *dw;
1333
1334   if (the_session == NULL)
1335     the_session = session_create ();
1336
1337   if (ds == NULL)
1338     {
1339       static int n_datasets;
1340       char *dataset_name;
1341
1342       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1343       ds = dataset_create (the_session, dataset_name);
1344       free (dataset_name);
1345     }
1346   assert (dataset_session (ds) == the_session);
1347
1348   dw = GTK_WIDGET (
1349     g_object_new (
1350       psppire_data_window_get_type (),
1351       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1352       "description", _("Data Editor"),
1353       "dataset", ds,
1354       NULL));
1355
1356   if (dataset_name (ds) != NULL)
1357     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1358
1359   return dw;
1360 }
1361
1362 bool
1363 psppire_data_window_is_empty (PsppireDataWindow *dw)
1364 {
1365   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1366 }
1367
1368 static void
1369 psppire_data_window_iface_init (PsppireWindowIface *iface)
1370 {
1371   iface->save = save_file;
1372   iface->pick_filename = data_pick_filename;
1373   iface->load = load_file;
1374 }
1375 \f
1376 PsppireDataWindow *
1377 psppire_default_data_window (void)
1378 {
1379   if (ll_is_empty (&all_data_windows))
1380     create_data_window ();
1381   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1382 }
1383
1384 void
1385 psppire_data_window_set_default (PsppireDataWindow *pdw)
1386 {
1387   ll_remove (&pdw->ll);
1388   ll_push_head (&all_data_windows, &pdw->ll);
1389 }
1390
1391 void
1392 psppire_data_window_undefault (PsppireDataWindow *pdw)
1393 {
1394   ll_remove (&pdw->ll);
1395   ll_push_tail (&all_data_windows, &pdw->ll);
1396 }
1397
1398 PsppireDataWindow *
1399 psppire_data_window_for_dataset (struct dataset *ds)
1400 {
1401   PsppireDataWindow *pdw;
1402
1403   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1404     if (pdw->dataset == ds)
1405       return pdw;
1406
1407   return NULL;
1408 }
1409
1410 void
1411 create_data_window (void)
1412 {
1413   gtk_widget_show (psppire_data_window_new (NULL));
1414 }
1415
1416 void
1417 open_data_window (PsppireWindow *victim, const char *file_name)
1418 {
1419   GtkWidget *window;
1420
1421   if (PSPPIRE_IS_DATA_WINDOW (victim)
1422       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1423     window = GTK_WIDGET (victim);
1424   else
1425     window = psppire_data_window_new (NULL);
1426
1427   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1428   gtk_widget_show (window);
1429 }