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