Added an implementation for the One Sample Kolmogorov-Smirnov Test
[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 static void
843 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
844 {
845   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
846
847   gtk_action_set_visible (de->delete_cases, case_num != -1);
848 }
849
850
851 static void
852 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
853 {
854   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
855
856   gtk_action_set_visible (de->delete_variables, var != -1);
857 }
858
859 /* Callback for when the datasheet/varsheet is selected */
860 static void
861 on_switch_sheet (GtkNotebook *notebook,
862                  GtkNotebookPage *page,
863                  guint page_num,
864                  gpointer user_data)
865 {
866   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
867
868   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
869
870   GtkWidget *view_data =
871     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
872
873   GtkWidget *view_variables =
874     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
875
876   switch (page_num)
877     {
878     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
879       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
880                                       TRUE);
881       gtk_action_set_sensitive (de->insert_variable, TRUE);
882       gtk_action_set_sensitive (de->insert_case, FALSE);
883       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
884       break;
885     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
886       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
887       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
888       gtk_action_set_sensitive (de->insert_case, TRUE);
889       break;
890     default:
891       g_assert_not_reached ();
892       break;
893     }
894
895 #if 0
896   update_paste_menuitem (de, page_num);
897 #endif
898 }
899
900
901
902 static void
903 set_unsaved (gpointer w)
904 {
905   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
906 }
907
908
909 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
910    Returns a pointer to the action
911 */
912 static GtkAction *
913 connect_action (PsppireDataWindow *dw, const char *action_name, 
914                                     GCallback handler)
915 {
916   GtkAction *action = get_action_assert (dw->builder, action_name);
917  
918   g_signal_connect_swapped (action, "activate", handler, dw);
919
920   return action;
921 }
922
923 /* Initializes as much of a PsppireDataWindow as we can and must before the
924    dataset has been set.
925
926    In particular, the 'menu' member is required in case the "filename" property
927    is set before the "dataset" property: otherwise PsppireWindow will try to
928    modify the menu as part of the "filename" property_set() function and end up
929    with a Gtk-CRITICAL since 'menu' is NULL.  */
930 static void
931 psppire_data_window_init (PsppireDataWindow *de)
932 {
933   GtkUIManager *uim;
934
935   de->builder = builder_new ("data-editor.ui");
936
937   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
938
939   PSPPIRE_WINDOW (de)->menu =
940     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
941 }
942
943 static void
944 psppire_data_window_finish_init (PsppireDataWindow *de,
945                                  struct dataset *ds)
946 {
947   static const struct dataset_callbacks cbs =
948     {
949       set_unsaved,                    /* changed */
950       transformation_change_callback, /* transformations_changed */
951     };
952
953   PsppireDict *dict;
954
955   GtkWidget *menubar;
956   GtkWidget *hb ;
957   GtkWidget *sb ;
958
959   GtkWidget *box = gtk_vbox_new (FALSE, 0);
960
961   de->dataset = ds;
962   dict = psppire_dict_new_from_dict (dataset_dict (ds));
963   de->var_store = psppire_var_store_new (dict);
964   de->data_store = psppire_data_store_new (dict);
965   psppire_data_store_set_reader (de->data_store, NULL);
966
967   menubar = get_widget_assert (de->builder, "menubar");
968   hb = get_widget_assert (de->builder, "handlebox1");
969   sb = get_widget_assert (de->builder, "status-bar");
970
971   de->data_editor =
972     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
973                                                   de->data_store));
974
975   g_signal_connect_swapped (de->data_store, "case-changed",
976                             G_CALLBACK (set_unsaved), de);
977
978   g_signal_connect_swapped (de->data_store, "case-inserted",
979                             G_CALLBACK (set_unsaved), de);
980
981   g_signal_connect_swapped (de->data_store, "cases-deleted",
982                             G_CALLBACK (set_unsaved), de);
983
984   dataset_set_callbacks (de->dataset, &cbs, de);
985
986   connect_help (de->builder);
987
988   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
989   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
990   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
991   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
992
993   gtk_container_add (GTK_CONTAINER (de), box);
994
995   set_cut_copy_menuitem_sensitivity (de, FALSE);
996
997   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
998                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
999
1000
1001   set_paste_menuitem_sensitivity (de, FALSE);
1002
1003   g_signal_connect_swapped (de->data_editor, "data-available-changed",
1004                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1005
1006   g_signal_connect (dict, "weight-changed",
1007                     G_CALLBACK (on_weight_change),
1008                     de);
1009
1010   g_signal_connect (dict, "filter-changed",
1011                     G_CALLBACK (on_filter_change),
1012                     de);
1013
1014   g_signal_connect (dict, "split-changed",
1015                     G_CALLBACK (on_split_change),
1016                     de);
1017
1018
1019   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1020
1021   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1022
1023   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1024
1025   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1026
1027   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1028  
1029   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1030
1031   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1032
1033   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1034
1035   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1036
1037   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1038
1039   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1040
1041   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1042
1043   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1044
1045   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1046
1047   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1048
1049   {
1050     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1051
1052     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1053
1054     gtk_action_set_visible (de->delete_cases, FALSE);
1055   }
1056
1057
1058   {
1059     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1060
1061     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1062
1063     gtk_action_set_visible (de->delete_variables, FALSE);
1064   }
1065
1066
1067   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1068
1069   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1070  
1071   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1072
1073   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1074
1075   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1076
1077   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1078
1079   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1080
1081   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1082
1083   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1084
1085
1086   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1087  
1088   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1089
1090   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1091
1092   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1093
1094   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1095
1096   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1097  
1098   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1099
1100   connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1101  
1102   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1103  
1104   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1105
1106   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1107  
1108   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1109  
1110   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1111  
1112   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1113  
1114   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1115  
1116   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1117  
1118   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1119
1120   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1121  
1122   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1123
1124   connect_action (de, "k-means", G_CALLBACK (k_means_dialog));
1125
1126   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1127   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1128   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1129   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1130   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1131   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1132  
1133
1134   {
1135     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1136
1137     GtkWidget *recent_data =
1138       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1139
1140     GtkWidget *recent_files =
1141       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1142
1143
1144     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1145       gtk_recent_manager_get_default ());
1146
1147     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1148       gtk_recent_manager_get_default ());
1149
1150     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1151     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1152
1153     {
1154       GtkRecentFilter *filter = gtk_recent_filter_new ();
1155
1156       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1157       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1158
1159       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1160
1161       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1162     }
1163
1164     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1165
1166
1167     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1168
1169     {
1170       GtkRecentFilter *filter = gtk_recent_filter_new ();
1171
1172       gtk_recent_filter_add_pattern (filter, "*.sps");
1173       gtk_recent_filter_add_pattern (filter, "*.SPS");
1174
1175       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1176
1177       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1178     }
1179
1180     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1181
1182     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1183
1184   }
1185
1186   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1187
1188
1189   g_signal_connect (de->data_editor,
1190                     "cases-selected",
1191                     G_CALLBACK (enable_delete_cases),
1192                     de);
1193
1194   g_signal_connect (de->data_editor,
1195                     "variables-selected",
1196                     G_CALLBACK (enable_delete_variables),
1197                     de);
1198
1199
1200   g_signal_connect (de->data_editor,
1201                     "switch-page",
1202                     G_CALLBACK (on_switch_sheet), de);
1203
1204   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1205   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1206
1207   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1208
1209   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1210
1211   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1212
1213   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1214
1215   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1216
1217   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1218
1219   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1220
1221   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1222
1223   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1224
1225   {
1226     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1227
1228     merge_help_menu (uim);
1229   }
1230
1231   {
1232     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1233                                                                 "datasheet-cases-popup");
1234
1235     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1236                                                                   "varsheet-variable-popup");
1237
1238     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1239                                                                    "datasheet-variable-popup");
1240
1241     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1242                               G_CALLBACK (psppire_data_editor_sort_ascending),
1243                               de->data_editor);
1244
1245     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1246                               G_CALLBACK (psppire_data_editor_sort_descending),
1247                               de->data_editor);
1248
1249     g_object_set (de->data_editor,
1250                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1251                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1252                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1253                   NULL);
1254   }
1255
1256   gtk_widget_show (GTK_WIDGET (de->data_editor));
1257   gtk_widget_show (box);
1258
1259   ll_push_head (&all_data_windows, &de->ll);
1260 }
1261
1262 static void
1263 psppire_data_window_dispose (GObject *object)
1264 {
1265   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1266
1267   if (dw->builder != NULL)
1268     {
1269       g_object_unref (dw->builder);
1270       dw->builder = NULL;
1271     }
1272
1273   if (dw->var_store)
1274     {
1275       g_object_unref (dw->var_store);
1276       dw->var_store = NULL;
1277     }
1278
1279   if (dw->data_store)
1280     {
1281       g_object_unref (dw->data_store);
1282       dw->data_store = NULL;
1283     }
1284
1285   if (dw->ll.next != NULL)
1286     {
1287       ll_remove (&dw->ll);
1288       dw->ll.next = NULL;
1289     }
1290
1291   if (G_OBJECT_CLASS (parent_class)->dispose)
1292     G_OBJECT_CLASS (parent_class)->dispose (object);
1293 }
1294
1295 static void
1296 psppire_data_window_set_property (GObject         *object,
1297                                   guint            prop_id,
1298                                   const GValue    *value,
1299                                   GParamSpec      *pspec)
1300 {
1301   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1302
1303   switch (prop_id)
1304     {
1305     case PROP_DATASET:
1306       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1307       break;
1308     default:
1309       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1310       break;
1311     };
1312 }
1313
1314 static void
1315 psppire_data_window_get_property (GObject         *object,
1316                                   guint            prop_id,
1317                                   GValue          *value,
1318                                   GParamSpec      *pspec)
1319 {
1320   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1321
1322   switch (prop_id)
1323     {
1324     case PROP_DATASET:
1325       g_value_set_pointer (value, window->dataset);
1326       break;
1327     default:
1328       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1329       break;
1330     };
1331 }
1332
1333 GtkWidget*
1334 psppire_data_window_new (struct dataset *ds)
1335 {
1336   GtkWidget *dw;
1337
1338   if (the_session == NULL)
1339     the_session = session_create ();
1340
1341   if (ds == NULL)
1342     {
1343       static int n_datasets;
1344       char *dataset_name;
1345
1346       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1347       ds = dataset_create (the_session, dataset_name);
1348       free (dataset_name);
1349     }
1350   assert (dataset_session (ds) == the_session);
1351
1352   dw = GTK_WIDGET (
1353     g_object_new (
1354       psppire_data_window_get_type (),
1355       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1356       "description", _("Data Editor"),
1357       "dataset", ds,
1358       NULL));
1359
1360   if (dataset_name (ds) != NULL)
1361     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1362
1363   return dw;
1364 }
1365
1366 bool
1367 psppire_data_window_is_empty (PsppireDataWindow *dw)
1368 {
1369   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1370 }
1371
1372 static void
1373 psppire_data_window_iface_init (PsppireWindowIface *iface)
1374 {
1375   iface->save = save_file;
1376   iface->pick_filename = data_pick_filename;
1377   iface->load = load_file;
1378 }
1379 \f
1380 PsppireDataWindow *
1381 psppire_default_data_window (void)
1382 {
1383   if (ll_is_empty (&all_data_windows))
1384     create_data_window ();
1385   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1386 }
1387
1388 void
1389 psppire_data_window_set_default (PsppireDataWindow *pdw)
1390 {
1391   ll_remove (&pdw->ll);
1392   ll_push_head (&all_data_windows, &pdw->ll);
1393 }
1394
1395 void
1396 psppire_data_window_undefault (PsppireDataWindow *pdw)
1397 {
1398   ll_remove (&pdw->ll);
1399   ll_push_tail (&all_data_windows, &pdw->ll);
1400 }
1401
1402 PsppireDataWindow *
1403 psppire_data_window_for_dataset (struct dataset *ds)
1404 {
1405   PsppireDataWindow *pdw;
1406
1407   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1408     if (pdw->dataset == ds)
1409       return pdw;
1410
1411   return NULL;
1412 }
1413
1414 void
1415 create_data_window (void)
1416 {
1417   gtk_widget_show (psppire_data_window_new (NULL));
1418 }
1419
1420 void
1421 open_data_window (PsppireWindow *victim, const char *file_name)
1422 {
1423   GtkWidget *window;
1424
1425   if (PSPPIRE_IS_DATA_WINDOW (victim)
1426       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1427     window = GTK_WIDGET (victim);
1428   else
1429     window = psppire_data_window_new (NULL);
1430
1431   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1432   gtk_widget_show (window);
1433 }