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