Merge remote branch 'origin/sourceview'
[pspp] / 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/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/variable-info-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_pattern (filter, "*.sav");
510   gtk_file_filter_add_pattern (filter, "*.SAV");
511   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
512
513   filter = gtk_file_filter_new ();
514   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
515   gtk_file_filter_add_pattern (filter, "*.por");
516   gtk_file_filter_add_pattern (filter, "*.POR");
517   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
518
519   filter = gtk_file_filter_new ();
520   gtk_file_filter_set_name (filter, _("All Files"));
521   gtk_file_filter_add_pattern (filter, "*");
522   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
523
524   {
525     GtkWidget *button_por;
526     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
527     button_sys =
528       gtk_radio_button_new_with_label (NULL, _("System File"));
529
530     button_por =
531       gtk_radio_button_new_with_label
532       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
533        _("Portable File"));
534
535     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
536     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
537
538     gtk_widget_show_all (vbox);
539
540     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
541   }
542
543   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
544                                                   TRUE);
545
546   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
547     {
548     case GTK_RESPONSE_ACCEPT:
549       {
550         GString *filename =
551           g_string_new
552           (
553            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
554            );
555
556         de->save_as_portable =
557           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
558
559         if ( ! name_has_suffix (filename->str))
560           {
561             if ( de->save_as_portable)
562               g_string_append (filename, ".por");
563             else
564               g_string_append (filename, ".sav");
565           }
566
567         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
568
569         g_string_free (filename, TRUE);
570       }
571       break;
572     default:
573       break;
574     }
575
576   gtk_widget_destroy (dialog);
577 }
578
579 static bool
580 confirm_delete_dataset (PsppireDataWindow *de,
581                         const char *old_dataset,
582                         const char *new_dataset,
583                         const char *existing_dataset)
584 {
585   GtkWidget *dialog;
586   int result;
587
588   dialog = gtk_message_dialog_new (
589     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
590     _("Delete Existing Dataset?"));
591
592   gtk_message_dialog_format_secondary_text (
593     GTK_MESSAGE_DIALOG (dialog),
594     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
595       "dataset named \"%s\".  Are you sure that you want to do this?"),
596     old_dataset, new_dataset, existing_dataset);
597
598   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
599                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
600                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
601                           NULL);
602
603   g_object_set (dialog, "icon-name", "psppicon", NULL);
604
605   result = gtk_dialog_run (GTK_DIALOG (dialog));
606
607   gtk_widget_destroy (dialog);
608
609   return result == GTK_RESPONSE_OK;
610 }
611
612 static void
613 on_rename_dataset (PsppireDataWindow *de)
614 {
615   struct dataset *ds = de->dataset;
616   struct session *session = dataset_session (ds);
617   const char *old_name = dataset_name (ds);
618   struct dataset *existing_dataset;
619   char *new_name;
620   char *prompt;
621
622   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
623                       old_name);
624   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
625                                old_name);
626   free (prompt);
627
628   if (new_name == NULL)
629     return;
630
631   existing_dataset = session_lookup_dataset (session, new_name);
632   if (existing_dataset == NULL || existing_dataset == ds
633       || confirm_delete_dataset (de, old_name, new_name,
634                                  dataset_name (existing_dataset)))
635     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
636                                                         new_name)));
637
638   free (new_name);
639 }
640
641 static void
642 on_edit_paste (PsppireDataWindow  *de)
643 {
644   psppire_data_editor_clip_paste (de->data_editor);
645 }
646
647 static void
648 on_edit_copy (PsppireDataWindow  *de)
649 {
650   psppire_data_editor_clip_copy (de->data_editor);
651 }
652
653
654
655 static void
656 on_edit_cut (PsppireDataWindow  *de)
657 {
658   psppire_data_editor_clip_cut (de->data_editor);
659 }
660
661
662 static void
663 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
664 {
665   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
666
667   if ( gtk_toggle_action_get_active (action))
668     gtk_widget_show (statusbar);
669   else
670     gtk_widget_hide (statusbar);
671 }
672
673
674 static void
675 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
676 {
677   const gboolean grid_visible = gtk_toggle_action_get_active (action);
678
679   psppire_data_editor_show_grid (de->data_editor, grid_visible);
680 }
681
682 static void
683 data_view_activate (PsppireDataWindow  *de)
684 {
685   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
686 }
687
688
689 static void
690 variable_view_activate (PsppireDataWindow  *de)
691 {
692   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
693 }
694
695
696 static void
697 fonts_activate (PsppireDataWindow  *de)
698 {
699   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
700   PangoFontDescription *current_font;
701   gchar *font_name;
702   GtkWidget *dialog =
703     gtk_font_selection_dialog_new (_("Font Selection"));
704
705
706   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
707   font_name = pango_font_description_to_string (current_font);
708
709   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
710
711   g_free (font_name);
712
713   gtk_window_set_transient_for (GTK_WINDOW (dialog),
714                                 GTK_WINDOW (toplevel));
715
716   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
717     {
718       const gchar *font = gtk_font_selection_dialog_get_font_name
719         (GTK_FONT_SELECTION_DIALOG (dialog));
720
721       PangoFontDescription* font_desc =
722         pango_font_description_from_string (font);
723
724       psppire_data_editor_set_font (de->data_editor, font_desc);
725     }
726
727   gtk_widget_hide (dialog);
728 }
729
730
731
732 /* Callback for the value labels action */
733 static void
734 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
735 {
736   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
737 }
738
739 static void
740 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
741 {
742   psppire_data_editor_split_window (de->data_editor,
743                                     gtk_toggle_action_get_active (ta));
744 }
745
746
747 static void
748 file_quit (PsppireDataWindow *de)
749 {
750   /* FIXME: Need to be more intelligent here.
751      Give the user the opportunity to save any unsaved data.
752   */
753   psppire_quit ();
754 }
755
756
757 static void
758 on_recent_data_select (GtkMenuShell *menushell,
759                        PsppireWindow *window)
760 {
761   gchar *file;
762
763   gchar *uri =
764     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
765
766   file = g_filename_from_uri (uri, NULL, NULL);
767
768   g_free (uri);
769
770   open_data_window (window, file);
771
772   g_free (file);
773 }
774
775 static char *
776 charset_from_mime_type (const char *mime_type)
777 {
778   const char *charset;
779   struct string s;
780   const char *p;
781
782   if (mime_type == NULL)
783     return NULL;
784
785   charset = c_strcasestr (mime_type, "charset=");
786   if (charset == NULL)
787     return NULL;
788
789   ds_init_empty (&s);
790   p = charset + 8;
791   if (*p == '"')
792     {
793       /* Parse a "quoted-string" as defined by RFC 822. */
794       for (p++; *p != '\0' && *p != '"'; p++)
795         {
796           if (*p != '\\')
797             ds_put_byte (&s, *p);
798           else if (*++p != '\0')
799             ds_put_byte (&s, *p);
800         }
801     }
802   else
803     {
804       /* Parse a "token" as defined by RFC 2045. */
805       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
806         ds_put_byte (&s, *p++);
807     }
808   if (!ds_is_empty (&s))
809     return ds_steal_cstr (&s);
810
811   ds_destroy (&s);
812   return NULL;
813 }
814
815 static void
816 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
817 {
818   GtkRecentInfo *item;
819   char *encoding;
820   GtkWidget *se;
821   gchar *file;
822
823   /* Get the file name and its encoding. */
824   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
825   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
826   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
827   gtk_recent_info_unref (item);
828
829   se = psppire_syntax_window_new (encoding);
830
831   free (encoding);
832
833   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
834     gtk_widget_show (se);
835   else
836     gtk_widget_destroy (se);
837
838   g_free (file);
839 }
840
841
842
843 static void
844 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
845 {
846   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
847
848   gtk_action_set_visible (de->delete_cases, case_num != -1);
849 }
850
851
852 static void
853 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
854 {
855   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
856
857   gtk_action_set_visible (de->delete_variables, var != -1);
858 }
859
860 /* Callback for when the datasheet/varsheet is selected */
861 static void
862 on_switch_sheet (GtkNotebook *notebook,
863                  GtkNotebookPage *page,
864                  guint page_num,
865                  gpointer user_data)
866 {
867   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
868
869   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
870
871   GtkWidget *view_data =
872     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
873
874   GtkWidget *view_variables =
875     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
876
877   switch (page_num)
878     {
879     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
880       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
881                                       TRUE);
882       gtk_action_set_sensitive (de->insert_variable, TRUE);
883       gtk_action_set_sensitive (de->insert_case, FALSE);
884       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
885       break;
886     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
887       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
888       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
889       gtk_action_set_sensitive (de->insert_case, TRUE);
890       break;
891     default:
892       g_assert_not_reached ();
893       break;
894     }
895
896 #if 0
897   update_paste_menuitem (de, page_num);
898 #endif
899 }
900
901
902
903 static void
904 set_unsaved (gpointer w)
905 {
906   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
907 }
908
909
910 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
911    Returns a pointer to the action
912 */
913 static GtkAction *
914 connect_action (PsppireDataWindow *dw, const char *action_name, 
915                                     GCallback handler)
916 {
917   GtkAction *action = get_action_assert (dw->builder, action_name);
918  
919   g_signal_connect_swapped (action, "activate", handler, dw);
920
921   return action;
922 }
923
924 /* Initializes as much of a PsppireDataWindow as we can and must before the
925    dataset has been set.
926
927    In particular, the 'menu' member is required in case the "filename" property
928    is set before the "dataset" property: otherwise PsppireWindow will try to
929    modify the menu as part of the "filename" property_set() function and end up
930    with a Gtk-CRITICAL since 'menu' is NULL.  */
931 static void
932 psppire_data_window_init (PsppireDataWindow *de)
933 {
934   GtkUIManager *uim;
935
936   de->builder = builder_new ("data-editor.ui");
937
938   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
939
940   PSPPIRE_WINDOW (de)->menu =
941     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
942 }
943
944 static void
945 psppire_data_window_finish_init (PsppireDataWindow *de,
946                                  struct dataset *ds)
947 {
948   static const struct dataset_callbacks cbs =
949     {
950       set_unsaved,                    /* changed */
951       transformation_change_callback, /* transformations_changed */
952     };
953
954   PsppireDict *dict;
955
956   GtkWidget *menubar;
957   GtkWidget *hb ;
958   GtkWidget *sb ;
959
960   GtkWidget *box = gtk_vbox_new (FALSE, 0);
961
962   de->dataset = ds;
963   dict = psppire_dict_new_from_dict (dataset_dict (ds));
964   de->var_store = psppire_var_store_new (dict);
965   de->data_store = psppire_data_store_new (dict);
966   psppire_data_store_set_reader (de->data_store, NULL);
967
968   menubar = get_widget_assert (de->builder, "menubar");
969   hb = get_widget_assert (de->builder, "handlebox1");
970   sb = get_widget_assert (de->builder, "status-bar");
971
972   de->data_editor =
973     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
974                                                   de->data_store));
975
976   g_signal_connect_swapped (de->data_store, "case-changed",
977                             G_CALLBACK (set_unsaved), de);
978
979   g_signal_connect_swapped (de->data_store, "case-inserted",
980                             G_CALLBACK (set_unsaved), de);
981
982   g_signal_connect_swapped (de->data_store, "cases-deleted",
983                             G_CALLBACK (set_unsaved), de);
984
985   dataset_set_callbacks (de->dataset, &cbs, de);
986
987   connect_help (de->builder);
988
989   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
990   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
991   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
992   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
993
994   gtk_container_add (GTK_CONTAINER (de), box);
995
996   set_cut_copy_menuitem_sensitivity (de, FALSE);
997
998   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
999                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
1000
1001
1002   set_paste_menuitem_sensitivity (de, FALSE);
1003
1004   g_signal_connect_swapped (de->data_editor, "data-available-changed",
1005                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1006
1007   g_signal_connect (dict, "weight-changed",
1008                     G_CALLBACK (on_weight_change),
1009                     de);
1010
1011   g_signal_connect (dict, "filter-changed",
1012                     G_CALLBACK (on_filter_change),
1013                     de);
1014
1015   g_signal_connect (dict, "split-changed",
1016                     G_CALLBACK (on_split_change),
1017                     de);
1018
1019
1020   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1021
1022   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1023
1024   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1025
1026   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1027
1028   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1029  
1030   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1031
1032   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1033
1034   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1035
1036   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1037
1038   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1039
1040   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1041
1042   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1043
1044   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1045
1046   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1047
1048   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1049
1050   {
1051     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1052
1053     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1054
1055     gtk_action_set_visible (de->delete_cases, FALSE);
1056   }
1057
1058
1059   {
1060     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1061
1062     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1063
1064     gtk_action_set_visible (de->delete_variables, FALSE);
1065   }
1066
1067
1068   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1069
1070   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1071  
1072   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1073
1074   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1075
1076   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1077
1078   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1079
1080   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1081
1082   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1083
1084   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1085
1086
1087   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1088  
1089   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1090
1091   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1092
1093   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1094
1095   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1096
1097   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1098  
1099   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1100
1101   connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1102  
1103   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1104  
1105   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1106
1107   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1108  
1109   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1110  
1111   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1112  
1113   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1114  
1115   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1116  
1117   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1118  
1119   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1120
1121   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1122  
1123   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1124
1125   connect_action (de, "k-means", G_CALLBACK (k_means_dialog));
1126
1127   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1128   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1129   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1130   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1131   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1132   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1133  
1134
1135   {
1136     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1137
1138     GtkWidget *recent_data =
1139       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1140
1141     GtkWidget *recent_files =
1142       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1143
1144
1145     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1146       gtk_recent_manager_get_default ());
1147
1148     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1149       gtk_recent_manager_get_default ());
1150
1151     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1152     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1153
1154     {
1155       GtkRecentFilter *filter = gtk_recent_filter_new ();
1156
1157       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1158       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1159
1160       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1161
1162       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1163     }
1164
1165     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1166
1167
1168     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1169
1170     {
1171       GtkRecentFilter *filter = gtk_recent_filter_new ();
1172
1173       gtk_recent_filter_add_pattern (filter, "*.sps");
1174       gtk_recent_filter_add_pattern (filter, "*.SPS");
1175
1176       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1177
1178       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1179     }
1180
1181     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1182
1183     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1184
1185   }
1186
1187   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1188
1189
1190   g_signal_connect (de->data_editor,
1191                     "cases-selected",
1192                     G_CALLBACK (enable_delete_cases),
1193                     de);
1194
1195   g_signal_connect (de->data_editor,
1196                     "variables-selected",
1197                     G_CALLBACK (enable_delete_variables),
1198                     de);
1199
1200
1201   g_signal_connect (de->data_editor,
1202                     "switch-page",
1203                     G_CALLBACK (on_switch_sheet), de);
1204
1205   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1206   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1207
1208   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1209
1210   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1211
1212   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1213
1214   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1215
1216   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1217
1218   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1219
1220   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1221
1222   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1223
1224   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1225
1226   {
1227     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1228
1229     merge_help_menu (uim);
1230   }
1231
1232   {
1233     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1234                                                                 "datasheet-cases-popup");
1235
1236     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1237                                                                   "varsheet-variable-popup");
1238
1239     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1240                                                                    "datasheet-variable-popup");
1241
1242     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1243                               G_CALLBACK (psppire_data_editor_sort_ascending),
1244                               de->data_editor);
1245
1246     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1247                               G_CALLBACK (psppire_data_editor_sort_descending),
1248                               de->data_editor);
1249
1250     g_object_set (de->data_editor,
1251                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1252                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1253                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1254                   NULL);
1255   }
1256
1257   gtk_widget_show (GTK_WIDGET (de->data_editor));
1258   gtk_widget_show (box);
1259
1260   ll_push_head (&all_data_windows, &de->ll);
1261 }
1262
1263 static void
1264 psppire_data_window_dispose (GObject *object)
1265 {
1266   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1267
1268   if (dw->builder != NULL)
1269     {
1270       g_object_unref (dw->builder);
1271       dw->builder = NULL;
1272     }
1273
1274   if (dw->var_store)
1275     {
1276       g_object_unref (dw->var_store);
1277       dw->var_store = NULL;
1278     }
1279
1280   if (dw->data_store)
1281     {
1282       g_object_unref (dw->data_store);
1283       dw->data_store = NULL;
1284     }
1285
1286   if (dw->ll.next != NULL)
1287     {
1288       ll_remove (&dw->ll);
1289       dw->ll.next = NULL;
1290     }
1291
1292   if (G_OBJECT_CLASS (parent_class)->dispose)
1293     G_OBJECT_CLASS (parent_class)->dispose (object);
1294 }
1295
1296 static void
1297 psppire_data_window_set_property (GObject         *object,
1298                                   guint            prop_id,
1299                                   const GValue    *value,
1300                                   GParamSpec      *pspec)
1301 {
1302   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1303
1304   switch (prop_id)
1305     {
1306     case PROP_DATASET:
1307       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1308       break;
1309     default:
1310       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1311       break;
1312     };
1313 }
1314
1315 static void
1316 psppire_data_window_get_property (GObject         *object,
1317                                   guint            prop_id,
1318                                   GValue          *value,
1319                                   GParamSpec      *pspec)
1320 {
1321   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1322
1323   switch (prop_id)
1324     {
1325     case PROP_DATASET:
1326       g_value_set_pointer (value, window->dataset);
1327       break;
1328     default:
1329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1330       break;
1331     };
1332 }
1333
1334 GtkWidget*
1335 psppire_data_window_new (struct dataset *ds)
1336 {
1337   GtkWidget *dw;
1338
1339   if (the_session == NULL)
1340     the_session = session_create ();
1341
1342   if (ds == NULL)
1343     {
1344       static int n_datasets;
1345       char *dataset_name;
1346
1347       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1348       ds = dataset_create (the_session, dataset_name);
1349       free (dataset_name);
1350     }
1351   assert (dataset_session (ds) == the_session);
1352
1353   dw = GTK_WIDGET (
1354     g_object_new (
1355       psppire_data_window_get_type (),
1356       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1357       "description", _("Data Editor"),
1358       "dataset", ds,
1359       NULL));
1360
1361   if (dataset_name (ds) != NULL)
1362     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1363
1364   return dw;
1365 }
1366
1367 bool
1368 psppire_data_window_is_empty (PsppireDataWindow *dw)
1369 {
1370   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1371 }
1372
1373 static void
1374 psppire_data_window_iface_init (PsppireWindowIface *iface)
1375 {
1376   iface->save = save_file;
1377   iface->pick_filename = data_pick_filename;
1378   iface->load = load_file;
1379 }
1380 \f
1381 PsppireDataWindow *
1382 psppire_default_data_window (void)
1383 {
1384   if (ll_is_empty (&all_data_windows))
1385     create_data_window ();
1386   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1387 }
1388
1389 void
1390 psppire_data_window_set_default (PsppireDataWindow *pdw)
1391 {
1392   ll_remove (&pdw->ll);
1393   ll_push_head (&all_data_windows, &pdw->ll);
1394 }
1395
1396 void
1397 psppire_data_window_undefault (PsppireDataWindow *pdw)
1398 {
1399   ll_remove (&pdw->ll);
1400   ll_push_tail (&all_data_windows, &pdw->ll);
1401 }
1402
1403 PsppireDataWindow *
1404 psppire_data_window_for_dataset (struct dataset *ds)
1405 {
1406   PsppireDataWindow *pdw;
1407
1408   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1409     if (pdw->dataset == ds)
1410       return pdw;
1411
1412   return NULL;
1413 }
1414
1415 void
1416 create_data_window (void)
1417 {
1418   gtk_widget_show (psppire_data_window_new (NULL));
1419 }
1420
1421 void
1422 open_data_window (PsppireWindow *victim, const char *file_name)
1423 {
1424   GtkWidget *window;
1425
1426   if (PSPPIRE_IS_DATA_WINDOW (victim)
1427       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1428     window = GTK_WIDGET (victim);
1429   else
1430     window = psppire_data_window_new (NULL);
1431
1432   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1433   gtk_widget_show (window);
1434 }