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