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