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