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