Converted the correlations dialog to a PsppireDialogAction object
[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, 2012  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/builder-wrapper.h"
31 #include "ui/gui/chi-square-dialog.h"
32 #include "ui/gui/comments-dialog.h"
33 #include "ui/gui/compute-dialog.h"
34 #include "ui/gui/count-dialog.h"
35 #include "ui/gui/crosstabs-dialog.h"
36 #include "ui/gui/entry-dialog.h"
37 #include "ui/gui/examine-dialog.h"
38 #include "ui/gui/executor.h"
39 #include "ui/gui/factor-dialog.h"
40 #include "ui/gui/find-dialog.h"
41 #include "ui/gui/frequencies-dialog.h"
42 #include "ui/gui/goto-case-dialog.h"
43 #include "ui/gui/help-menu.h"
44 #include "ui/gui/helper.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/ks-one-sample-dialog.h"
57 #include "ui/gui/recode-dialog.h"
58 #include "ui/gui/regression-dialog.h"
59 #include "ui/gui/reliability-dialog.h"
60 #include "ui/gui/roc-dialog.h"
61 #include "ui/gui/select-cases-dialog.h"
62 #include "ui/gui/sort-cases-dialog.h"
63 #include "ui/gui/split-file-dialog.h"
64 #include "ui/gui/t-test-independent-samples-dialog.h"
65 #include "ui/gui/t-test-one-sample.h"
66 #include "ui/gui/t-test-paired-samples.h"
67 #include "ui/gui/text-data-import-dialog.h"
68 #include "ui/gui/transpose-dialog.h"
69 #include "ui/gui/univariate-dialog.h"
70 #include "ui/gui/weight-cases-dialog.h"
71 #include "ui/syntax-gen.h"
72
73 #include "gl/c-strcase.h"
74 #include "gl/c-strcasestr.h"
75 #include "gl/xvasprintf.h"
76
77 #include <gettext.h>
78 #define _(msgid) gettext (msgid)
79 #define N_(msgid) msgid
80
81 struct session *the_session;
82 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
83
84 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
85 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
86
87
88 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
89
90 static void psppire_data_window_dispose (GObject *object);
91 static void psppire_data_window_set_property (GObject         *object,
92                                               guint            prop_id,
93                                               const GValue    *value,
94                                               GParamSpec      *pspec);
95 static void psppire_data_window_get_property (GObject         *object,
96                                               guint            prop_id,
97                                               GValue          *value,
98                                               GParamSpec      *pspec);
99
100 GType
101 psppire_data_window_get_type (void)
102 {
103   static GType psppire_data_window_type = 0;
104
105   if (!psppire_data_window_type)
106     {
107       static const GTypeInfo psppire_data_window_info =
108         {
109           sizeof (PsppireDataWindowClass),
110           NULL,
111           NULL,
112           (GClassInitFunc)psppire_data_window_class_init,
113           (GClassFinalizeFunc) NULL,
114           NULL,
115           sizeof (PsppireDataWindow),
116           0,
117           (GInstanceInitFunc) psppire_data_window_init,
118         };
119
120       static const GInterfaceInfo window_interface_info =
121         {
122           (GInterfaceInitFunc) psppire_data_window_iface_init,
123           NULL,
124           NULL
125         };
126
127       psppire_data_window_type =
128         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
129                                 &psppire_data_window_info, 0);
130
131
132       g_type_add_interface_static (psppire_data_window_type,
133                                    PSPPIRE_TYPE_WINDOW_MODEL,
134                                    &window_interface_info);
135     }
136
137   return psppire_data_window_type;
138 }
139
140 static GObjectClass *parent_class ;
141
142 enum {
143     PROP_DATASET = 1
144 };
145
146 static void
147 psppire_data_window_class_init (PsppireDataWindowClass *class)
148 {
149   GObjectClass *object_class = G_OBJECT_CLASS (class);
150
151   parent_class = g_type_class_peek_parent (class);
152
153   object_class->dispose = psppire_data_window_dispose;
154   object_class->set_property = psppire_data_window_set_property;
155   object_class->get_property = psppire_data_window_get_property;
156
157   g_object_class_install_property (
158     object_class, PROP_DATASET,
159     g_param_spec_pointer ("dataset", "Dataset",
160                           "'struct datset *' represented by the window",
161                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
162 }
163 \f
164 static void
165 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
166 {
167   GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
168
169   gtk_action_set_sensitive (edit_paste, x);
170 }
171
172 static void
173 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
174 {
175   GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
176   GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
177
178   gtk_action_set_sensitive (edit_copy, x);
179   gtk_action_set_sensitive (edit_cut, x);
180 }
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   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
196
197   GtkWidget *menuitem =
198     gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
199
200   GtkWidget *status_label  =
201     get_widget_assert (de->builder, "case-counter-area");
202
203   gtk_widget_set_sensitive (menuitem, 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       PsppireVarStore *vs = NULL;
229       PsppireDict *dict = NULL;
230       struct variable *var ;
231       gchar *text ;
232
233       g_object_get (de->data_editor, "var-store", &vs, NULL);
234       g_object_get (vs, "dictionary", &dict, NULL);
235
236       var = psppire_dict_get_variable (dict, filter_index);
237
238       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
239
240       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
241
242       g_free (text);
243     }
244 }
245
246 /* Callback for when the dictionary changes its split variables */
247 static void
248 on_split_change (PsppireDict *dict, gpointer data)
249 {
250   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
251
252   size_t n_split_vars = dict_get_split_cnt (dict->dict);
253
254   GtkWidget *split_status_area =
255     get_widget_assert (de->builder, "split-file-status-area");
256
257   if ( n_split_vars == 0 )
258     {
259       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
260     }
261   else
262     {
263       gint i;
264       GString *text;
265       const struct variable *const * split_vars =
266         dict_get_split_vars (dict->dict);
267
268       text = g_string_new (_("Split by "));
269
270       for (i = 0 ; i < n_split_vars - 1; ++i )
271         {
272           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
273         }
274       g_string_append (text, var_get_name (split_vars[i]));
275
276       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
277
278       g_string_free (text, TRUE);
279     }
280 }
281
282
283
284
285 /* Callback for when the dictionary changes its weights */
286 static void
287 on_weight_change (GObject *o, gint weight_index, gpointer data)
288 {
289   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
290
291   GtkWidget *weight_status_area =
292     get_widget_assert (de->builder, "weight-status-area");
293
294   if ( weight_index == -1 )
295     {
296       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
297     }
298   else
299     {
300       struct variable *var ;
301       PsppireVarStore *vs = NULL;
302       PsppireDict *dict = NULL;
303       gchar *text;
304
305       g_object_get (de->data_editor, "var-store", &vs, NULL);
306       g_object_get (vs, "dictionary", &dict, NULL);
307
308       var = psppire_dict_get_variable (dict, weight_index);
309
310       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
311
312       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
313
314       g_free (text);
315     }
316 }
317
318 #if 0
319 static void
320 dump_rm (GtkRecentManager *rm)
321 {
322   GList *items = gtk_recent_manager_get_items (rm);
323
324   GList *i;
325
326   g_print ("Recent Items:\n");
327   for (i = items; i; i = i->next)
328     {
329       GtkRecentInfo *ri = i->data;
330
331       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
332                gtk_recent_info_get_short_name (ri),
333                gtk_recent_info_get_mime_type (ri),
334                gtk_recent_info_get_description (ri),
335                gtk_recent_info_get_uri (ri)
336                );
337
338
339       gtk_recent_info_unref (ri);
340     }
341
342   g_list_free (items);
343 }
344 #endif
345
346 static gboolean
347 name_has_por_suffix (const gchar *name)
348 {
349   size_t length = strlen (name);
350   return length > 4 && !c_strcasecmp (&name[length - 4], ".por");
351 }
352
353 static gboolean
354 name_has_sav_suffix (const gchar *name)
355 {
356   size_t length = strlen (name);
357   return length > 4 && !c_strcasecmp (&name[length - 4], ".sav");
358 }
359
360 /* Returns true if NAME has a suffix which might denote a PSPP file */
361 static gboolean
362 name_has_suffix (const gchar *name)
363 {
364   return name_has_por_suffix (name) || name_has_sav_suffix (name);
365 }
366
367 static gboolean
368 load_file (PsppireWindow *de, const gchar *file_name)
369 {
370   struct string filename;
371   gchar *utf8_file_name;
372   const char *mime_type;
373   gchar *syntax;
374   bool ok;
375
376   ds_init_empty (&filename);
377
378   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
379
380   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
381
382   g_free (utf8_file_name);
383
384   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
385   ds_destroy (&filename);
386
387   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
388                        lex_reader_for_string (syntax));
389   g_free (syntax);
390
391   mime_type = (name_has_por_suffix (file_name)
392                ? "application/x-spss-por"
393                : "application/x-spss-sav");
394
395   add_most_recent (file_name, mime_type);
396
397   return ok;
398 }
399
400 /* Save DE to file */
401 static void
402 save_file (PsppireWindow *w)
403 {
404   const gchar *file_name = NULL;
405   gchar *utf8_file_name = NULL;
406   GString *fnx;
407   struct string filename ;
408   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
409   gchar *syntax;
410
411   file_name = psppire_window_get_filename (w);
412
413   fnx = g_string_new (file_name);
414
415   if ( ! name_has_suffix (fnx->str))
416     {
417       if ( de->save_as_portable)
418         g_string_append (fnx, ".por");
419       else
420         g_string_append (fnx, ".sav");
421     }
422
423   ds_init_empty (&filename);
424
425   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
426
427   g_string_free (fnx, TRUE);
428
429   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
430   g_free (utf8_file_name);
431
432   syntax = g_strdup_printf ("%s OUTFILE=%s.",
433                             de->save_as_portable ? "EXPORT" : "SAVE",
434                             ds_cstr (&filename));
435
436   ds_destroy (&filename);
437
438   g_free (execute_syntax_string (de, syntax));
439 }
440
441
442 static void
443 insert_case (PsppireDataWindow *dw)
444 {
445   psppire_data_editor_insert_case (dw->data_editor);
446 }
447
448 static void
449 on_insert_variable (PsppireDataWindow *dw)
450 {
451   psppire_data_editor_insert_variable (dw->data_editor);
452 }
453
454
455 static void
456 display_dict (PsppireDataWindow *de)
457 {
458   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
459 }
460
461 static void
462 sysfile_info (PsppireDataWindow *de)
463 {
464   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
465
466   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
467     {
468       struct string filename;
469       gchar *file_name =
470         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
471
472       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
473                                                   NULL);
474
475       gchar *syntax;
476
477       ds_init_empty (&filename);
478
479       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
480
481       g_free (utf8_file_name);
482
483       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
484       g_free (execute_syntax_string (de, syntax));
485     }
486
487   gtk_widget_destroy (dialog);
488 }
489
490
491 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
492 static void
493 data_pick_filename (PsppireWindow *window)
494 {
495   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
496   GtkFileFilter *filter = gtk_file_filter_new ();
497   GtkWidget *button_sys;
498   GtkWidget *dialog =
499     gtk_file_chooser_dialog_new (_("Save"),
500                                  GTK_WINDOW (de),
501                                  GTK_FILE_CHOOSER_ACTION_SAVE,
502                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
503                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
504                                  NULL);
505
506   g_object_set (dialog, "local-only", FALSE, NULL);
507
508   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
509   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
510   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
511
512   filter = gtk_file_filter_new ();
513   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
514   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
515   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516
517   filter = gtk_file_filter_new ();
518   gtk_file_filter_set_name (filter, _("All Files"));
519   gtk_file_filter_add_pattern (filter, "*");
520   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
521
522   {
523     GtkWidget *button_por;
524     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
525     button_sys =
526       gtk_radio_button_new_with_label (NULL, _("System File"));
527
528     button_por =
529       gtk_radio_button_new_with_label
530       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
531        _("Portable File"));
532
533     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
534     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
535
536     gtk_widget_show_all (vbox);
537
538     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
539   }
540
541   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
542                                                   TRUE);
543
544   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
545     {
546     case GTK_RESPONSE_ACCEPT:
547       {
548         GString *filename =
549           g_string_new
550           (
551            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
552            );
553
554         de->save_as_portable =
555           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
556
557         if ( ! name_has_suffix (filename->str))
558           {
559             if ( de->save_as_portable)
560               g_string_append (filename, ".por");
561             else
562               g_string_append (filename, ".sav");
563           }
564
565         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
566
567         g_string_free (filename, TRUE);
568       }
569       break;
570     default:
571       break;
572     }
573
574   gtk_widget_destroy (dialog);
575 }
576
577 static bool
578 confirm_delete_dataset (PsppireDataWindow *de,
579                         const char *old_dataset,
580                         const char *new_dataset,
581                         const char *existing_dataset)
582 {
583   GtkWidget *dialog;
584   int result;
585
586   dialog = gtk_message_dialog_new (
587     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
588     _("Delete Existing Dataset?"));
589
590   gtk_message_dialog_format_secondary_text (
591     GTK_MESSAGE_DIALOG (dialog),
592     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
593       "dataset named \"%s\".  Are you sure that you want to do this?"),
594     old_dataset, new_dataset, existing_dataset);
595
596   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
597                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
598                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
599                           NULL);
600
601   g_object_set (dialog, "icon-name", "pspp", NULL);
602
603   result = gtk_dialog_run (GTK_DIALOG (dialog));
604
605   gtk_widget_destroy (dialog);
606
607   return result == GTK_RESPONSE_OK;
608 }
609
610 static void
611 on_rename_dataset (PsppireDataWindow *de)
612 {
613   struct dataset *ds = de->dataset;
614   struct session *session = dataset_session (ds);
615   const char *old_name = dataset_name (ds);
616   struct dataset *existing_dataset;
617   char *new_name;
618   char *prompt;
619
620   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
621                       old_name);
622   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
623                                old_name);
624   free (prompt);
625
626   if (new_name == NULL)
627     return;
628
629   existing_dataset = session_lookup_dataset (session, new_name);
630   if (existing_dataset == NULL || existing_dataset == ds
631       || confirm_delete_dataset (de, old_name, new_name,
632                                  dataset_name (existing_dataset)))
633     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
634                                                         new_name)));
635
636   free (new_name);
637 }
638
639 static void
640 on_edit_paste (PsppireDataWindow  *de)
641 {
642   psppire_data_editor_clip_paste (de->data_editor);
643 }
644
645 static void
646 on_edit_copy (PsppireDataWindow  *de)
647 {
648   psppire_data_editor_clip_copy (de->data_editor);
649 }
650
651
652
653 static void
654 on_edit_cut (PsppireDataWindow  *de)
655 {
656   psppire_data_editor_clip_cut (de->data_editor);
657 }
658
659
660 static void
661 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
662 {
663   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
664
665   if ( gtk_toggle_action_get_active (action))
666     gtk_widget_show (statusbar);
667   else
668     gtk_widget_hide (statusbar);
669 }
670
671
672 static void
673 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
674 {
675   const gboolean grid_visible = gtk_toggle_action_get_active (action);
676
677   psppire_data_editor_show_grid (de->data_editor, grid_visible);
678 }
679
680 static void
681 data_view_activate (PsppireDataWindow  *de)
682 {
683   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
684 }
685
686
687 static void
688 variable_view_activate (PsppireDataWindow  *de)
689 {
690   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
691 }
692
693
694 static void
695 fonts_activate (PsppireDataWindow  *de)
696 {
697   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
698   PangoFontDescription *current_font;
699   gchar *font_name;
700   GtkWidget *dialog =
701     gtk_font_selection_dialog_new (_("Font Selection"));
702
703
704   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
705   font_name = pango_font_description_to_string (current_font);
706
707   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
708
709   g_free (font_name);
710
711   gtk_window_set_transient_for (GTK_WINDOW (dialog),
712                                 GTK_WINDOW (toplevel));
713
714   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
715     {
716       const gchar *font = gtk_font_selection_dialog_get_font_name
717         (GTK_FONT_SELECTION_DIALOG (dialog));
718
719       PangoFontDescription* font_desc =
720         pango_font_description_from_string (font);
721
722       psppire_data_editor_set_font (de->data_editor, font_desc);
723     }
724
725   gtk_widget_hide (dialog);
726 }
727
728
729
730 /* Callback for the value labels action */
731 static void
732 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
733 {
734   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
735 }
736
737 static void
738 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
739 {
740   psppire_data_editor_split_window (de->data_editor,
741                                     gtk_toggle_action_get_active (ta));
742 }
743
744
745 static void
746 file_quit (PsppireDataWindow *de)
747 {
748   /* FIXME: Need to be more intelligent here.
749      Give the user the opportunity to save any unsaved data.
750   */
751   psppire_quit ();
752 }
753
754
755 static void
756 on_recent_data_select (GtkMenuShell *menushell,
757                        PsppireWindow *window)
758 {
759   gchar *file;
760
761   gchar *uri =
762     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
763
764   file = g_filename_from_uri (uri, NULL, NULL);
765
766   g_free (uri);
767
768   open_data_window (window, file);
769
770   g_free (file);
771 }
772
773 static char *
774 charset_from_mime_type (const char *mime_type)
775 {
776   const char *charset;
777   struct string s;
778   const char *p;
779
780   if (mime_type == NULL)
781     return NULL;
782
783   charset = c_strcasestr (mime_type, "charset=");
784   if (charset == NULL)
785     return NULL;
786
787   ds_init_empty (&s);
788   p = charset + 8;
789   if (*p == '"')
790     {
791       /* Parse a "quoted-string" as defined by RFC 822. */
792       for (p++; *p != '\0' && *p != '"'; p++)
793         {
794           if (*p != '\\')
795             ds_put_byte (&s, *p);
796           else if (*++p != '\0')
797             ds_put_byte (&s, *p);
798         }
799     }
800   else
801     {
802       /* Parse a "token" as defined by RFC 2045. */
803       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
804         ds_put_byte (&s, *p++);
805     }
806   if (!ds_is_empty (&s))
807     return ds_steal_cstr (&s);
808
809   ds_destroy (&s);
810   return NULL;
811 }
812
813 static void
814 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
815 {
816   GtkRecentInfo *item;
817   char *encoding;
818   GtkWidget *se;
819   gchar *file;
820
821   /* Get the file name and its encoding. */
822   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
823   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
824   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
825   gtk_recent_info_unref (item);
826
827   se = psppire_syntax_window_new (encoding);
828
829   free (encoding);
830
831   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
832     gtk_widget_show (se);
833   else
834     gtk_widget_destroy (se);
835
836   g_free (file);
837 }
838
839
840
841 static void
842 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
843 {
844   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
845
846   gtk_action_set_visible (de->delete_cases, case_num != -1);
847 }
848
849
850 static void
851 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
852 {
853   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
854
855   gtk_action_set_visible (de->delete_variables, var != -1);
856 }
857
858 /* Callback for when the datasheet/varsheet is selected */
859 static void
860 on_switch_sheet (GtkNotebook *notebook,
861                  GtkNotebookPage *page,
862                  guint page_num,
863                  gpointer user_data)
864 {
865   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
866
867   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
868
869   GtkWidget *view_data =
870     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
871
872   GtkWidget *view_variables =
873     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
874
875   switch (page_num)
876     {
877     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
878       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
879                                       TRUE);
880       gtk_action_set_sensitive (de->insert_variable, TRUE);
881       gtk_action_set_sensitive (de->insert_case, FALSE);
882       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
883       break;
884     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
885       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
886       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
887       gtk_action_set_sensitive (de->insert_case, TRUE);
888       break;
889     default:
890       g_assert_not_reached ();
891       break;
892     }
893
894 #if 0
895   update_paste_menuitem (de, page_num);
896 #endif
897 }
898
899
900
901 static void
902 set_unsaved (gpointer w)
903 {
904   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
905 }
906
907
908 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
909    Returns a pointer to the action
910 */
911 static GtkAction *
912 connect_action (PsppireDataWindow *dw, const char *action_name, 
913                                     GCallback handler)
914 {
915   GtkAction *action = get_action_assert (dw->builder, action_name);
916  
917   g_signal_connect_swapped (action, "activate", handler, dw);
918
919   return action;
920 }
921
922 /* Initializes as much of a PsppireDataWindow as we can and must before the
923    dataset has been set.
924
925    In particular, the 'menu' member is required in case the "filename" property
926    is set before the "dataset" property: otherwise PsppireWindow will try to
927    modify the menu as part of the "filename" property_set() function and end up
928    with a Gtk-CRITICAL since 'menu' is NULL.  */
929 static void
930 psppire_data_window_init (PsppireDataWindow *de)
931 {
932   GtkUIManager *uim;
933
934   de->builder = builder_new ("data-editor.ui");
935
936   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
937
938   PSPPIRE_WINDOW (de)->menu =
939     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
940 }
941
942 static void
943 psppire_data_window_finish_init (PsppireDataWindow *de,
944                                  struct dataset *ds)
945 {
946   static const struct dataset_callbacks cbs =
947     {
948       set_unsaved,                    /* changed */
949       transformation_change_callback, /* transformations_changed */
950     };
951
952   PsppireDict *dict;
953
954   GtkWidget *menubar;
955   GtkWidget *hb ;
956   GtkWidget *sb ;
957
958   GtkWidget *box = gtk_vbox_new (FALSE, 0);
959
960   de->dataset = ds;
961   dict = psppire_dict_new_from_dict (dataset_dict (ds));
962   de->var_store = psppire_var_store_new (dict);
963   de->data_store = psppire_data_store_new (dict);
964   psppire_data_store_set_reader (de->data_store, NULL);
965
966   menubar = get_widget_assert (de->builder, "menubar");
967   hb = get_widget_assert (de->builder, "handlebox1");
968   sb = get_widget_assert (de->builder, "status-bar");
969
970   de->data_editor =
971     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
972                                                   de->data_store));
973
974   g_signal_connect_swapped (de->data_store, "case-changed",
975                             G_CALLBACK (set_unsaved), de);
976
977   g_signal_connect_swapped (de->data_store, "case-inserted",
978                             G_CALLBACK (set_unsaved), de);
979
980   g_signal_connect_swapped (de->data_store, "cases-deleted",
981                             G_CALLBACK (set_unsaved), de);
982
983   dataset_set_callbacks (de->dataset, &cbs, de);
984
985   connect_help (de->builder);
986
987   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
988   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
989   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
990   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
991
992   gtk_container_add (GTK_CONTAINER (de), box);
993
994   set_cut_copy_menuitem_sensitivity (de, FALSE);
995
996   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
997                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
998
999
1000   set_paste_menuitem_sensitivity (de, FALSE);
1001
1002   g_signal_connect_swapped (de->data_editor, "data-available-changed",
1003                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1004
1005   g_signal_connect (dict, "weight-changed",
1006                     G_CALLBACK (on_weight_change),
1007                     de);
1008
1009   g_signal_connect (dict, "filter-changed",
1010                     G_CALLBACK (on_filter_change),
1011                     de);
1012
1013   g_signal_connect (dict, "split-changed",
1014                     G_CALLBACK (on_split_change),
1015                     de);
1016
1017
1018   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1019
1020   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1021
1022   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1023
1024   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1025
1026   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1027  
1028   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1029
1030   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1031
1032   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1033
1034   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1035
1036   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1037
1038   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1039
1040   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1041
1042   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1043
1044   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1045
1046   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1047
1048   {
1049     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1050
1051     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1052
1053     gtk_action_set_visible (de->delete_cases, FALSE);
1054   }
1055
1056
1057   {
1058     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1059
1060     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1061
1062     gtk_action_set_visible (de->delete_variables, FALSE);
1063   }
1064
1065
1066   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1067
1068   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1069  
1070   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1071
1072   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1073
1074   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1075
1076   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1077
1078   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1079
1080   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1081
1082   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1083
1084   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1085
1086   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1087
1088   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1089
1090   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1091
1092   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1093  
1094   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1095
1096   connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1097  
1098   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1099  
1100   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1101
1102   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1103  
1104   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1105  
1106   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1107  
1108   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1109  
1110   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1111  
1112   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1113
1114   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1115
1116   connect_action (de, "univariate", G_CALLBACK (univariate_dialog));
1117
1118   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1119
1120   connect_action (de, "k-means", G_CALLBACK (k_means_dialog));
1121
1122   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1123   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1124   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1125   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_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 }