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