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