Merge 'master' into 'gtk3'.
[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       ds_init_empty (&filename);
345       
346       utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
347     
348       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
349       
350       g_free (utf8_file_name);
351       
352       syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
353       ds_destroy (&filename);
354
355     }
356   else
357     {
358       syntax = syn;
359     }
360
361   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
362                        lex_reader_for_string (syntax));
363   g_free (syntax);
364
365   if (ok && syn == NULL)
366     {
367       if (name_has_por_suffix (file_name))
368         mime_type = "application/x-spss-por";
369       else if (name_has_sav_suffix (file_name))
370         mime_type = "application/x-spss-sav";
371       
372       add_most_recent (file_name, mime_type);
373     }
374
375   return ok;
376 }
377
378 static const char *
379 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
380 {
381   if (format == PSPPIRE_DATA_WINDOW_SAV)
382     return ".sav";
383   else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
384     return ".zsav";
385   else
386     return ".por";
387 }
388
389 /* Save DE to file */
390 static void
391 save_file (PsppireWindow *w)
392 {
393   const gchar *file_name = NULL;
394   gchar *utf8_file_name = NULL;
395   GString *fnx;
396   struct string filename ;
397   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
398   gchar *syntax;
399
400   file_name = psppire_window_get_filename (w);
401
402   fnx = g_string_new (file_name);
403
404   if ( ! name_has_suffix (fnx->str))
405     g_string_append (fnx, psppire_data_window_format_to_string (de->format));
406
407   ds_init_empty (&filename);
408
409   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
410
411   g_string_free (fnx, TRUE);
412
413   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
414   g_free (utf8_file_name);
415
416   if (de->format == PSPPIRE_DATA_WINDOW_SAV)
417     syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
418   else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
419     syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
420                               ds_cstr (&filename));
421   else
422     syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
423
424   ds_destroy (&filename);
425
426   g_free (execute_syntax_string (de, syntax));
427 }
428
429
430 static void
431 display_dict (PsppireDataWindow *de)
432 {
433   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
434 }
435
436 static void
437 sysfile_info (PsppireDataWindow *de)
438 {
439   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
440
441   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
442     {
443       struct string filename;
444       gchar *file_name =
445         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
446
447       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
448                                                   NULL);
449
450       gchar *syntax;
451
452       ds_init_empty (&filename);
453
454       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
455
456       g_free (utf8_file_name);
457
458       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
459       g_free (execute_syntax_string (de, syntax));
460     }
461
462   gtk_widget_destroy (dialog);
463 }
464
465
466 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
467 static void
468 data_pick_filename (PsppireWindow *window)
469 {
470   GtkListStore *list_store;
471   GtkWidget *combo_box;
472
473   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
474   GtkFileFilter *filter;
475   GtkWidget *dialog =
476     gtk_file_chooser_dialog_new (_("Save"),
477                                  GTK_WINDOW (de),
478                                  GTK_FILE_CHOOSER_ACTION_SAVE,
479                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
480                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
481                                  NULL);
482
483   g_object_set (dialog, "local-only", FALSE, NULL);
484
485   filter = gtk_file_filter_new ();
486   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
487   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
488   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
489
490   filter = gtk_file_filter_new ();
491   gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
492   gtk_file_filter_add_pattern (filter, "*.zsav");
493   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
494
495   filter = gtk_file_filter_new ();
496   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
497   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
498   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
499
500   filter = gtk_file_filter_new ();
501   gtk_file_filter_set_name (filter, _("All Files"));
502   gtk_file_filter_add_pattern (filter, "*");
503   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
504   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
505
506   {
507     GtkCellRenderer *cell;
508     GtkWidget *label;
509     GtkTreeIter iter;
510     GtkWidget *hbox;
511
512     list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
513     combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
514
515     gtk_list_store_append (list_store, &iter);
516     gtk_list_store_set (list_store, &iter,
517                         0, PSPPIRE_DATA_WINDOW_SAV,
518                         1, _("System File"),
519                         -1);
520     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
521
522     gtk_list_store_append (list_store, &iter);
523     gtk_list_store_set (list_store, &iter,
524                         0, PSPPIRE_DATA_WINDOW_ZSAV,
525                         1, _("Compressed System File"),
526                         -1);
527
528     gtk_list_store_append (list_store, &iter);
529     gtk_list_store_set (list_store, &iter,
530                         0, PSPPIRE_DATA_WINDOW_POR,
531                         1, _("Portable File"),
532                         -1);
533
534     label = gtk_label_new (_("Format:"));
535
536     cell = gtk_cell_renderer_text_new ();
537     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
538     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
539                                    "text", 1);
540
541     hbox = gtk_hbox_new (FALSE, 0);
542     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
543     gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
544     gtk_widget_show_all (hbox);
545
546     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
547   }
548
549   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
550                                                   TRUE);
551
552   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
553     {
554     case GTK_RESPONSE_ACCEPT:
555       {
556         GString *filename =
557           g_string_new
558           (
559            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
560            );
561
562         GtkTreeIter iter;
563         int format;
564
565         gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
566         gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
567                             0, &format,
568                             -1);
569         de->format = format;
570
571         if ( ! name_has_suffix (filename->str))
572           g_string_append (filename,
573                            psppire_data_window_format_to_string (format));
574
575         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
576
577         g_string_free (filename, TRUE);
578       }
579       break;
580     default:
581       break;
582     }
583
584   gtk_widget_destroy (dialog);
585 }
586
587 static bool
588 confirm_delete_dataset (PsppireDataWindow *de,
589                         const char *old_dataset,
590                         const char *new_dataset,
591                         const char *existing_dataset)
592 {
593   GtkWidget *dialog;
594   int result;
595
596   dialog = gtk_message_dialog_new (
597     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
598     _("Delete Existing Dataset?"));
599
600   gtk_message_dialog_format_secondary_text (
601     GTK_MESSAGE_DIALOG (dialog),
602     _("Renaming \"%s\" to \"%s\" will destroy the existing "
603       "dataset named \"%s\".  Are you sure that you want to do this?"),
604     old_dataset, new_dataset, existing_dataset);
605
606   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
607                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
608                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
609                           NULL);
610
611   g_object_set (dialog, "icon-name", "pspp", NULL);
612
613   result = gtk_dialog_run (GTK_DIALOG (dialog));
614
615   gtk_widget_destroy (dialog);
616
617   return result == GTK_RESPONSE_OK;
618 }
619
620 static void
621 on_rename_dataset (PsppireDataWindow *de)
622 {
623   struct dataset *ds = de->dataset;
624   struct session *session = dataset_session (ds);
625   const char *old_name = dataset_name (ds);
626   struct dataset *existing_dataset;
627   char *new_name;
628   char *prompt;
629
630   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
631                       old_name);
632   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
633                                old_name);
634   free (prompt);
635
636   if (new_name == NULL)
637     return;
638
639   existing_dataset = session_lookup_dataset (session, new_name);
640   if (existing_dataset == NULL || existing_dataset == ds
641       || confirm_delete_dataset (de, old_name, new_name,
642                                  dataset_name (existing_dataset)))
643     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
644                                                         new_name)));
645
646   free (new_name);
647 }
648
649 static void
650 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
651 {
652   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
653
654   if ( gtk_toggle_action_get_active (action))
655     gtk_widget_show (statusbar);
656   else
657     gtk_widget_hide (statusbar);
658 }
659
660
661 static void
662 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
663 {
664   const gboolean grid_visible = gtk_toggle_action_get_active (action);
665
666   psppire_data_editor_show_grid (de->data_editor, grid_visible);
667 }
668
669 static void
670 data_view_activate (PsppireDataWindow  *de)
671 {
672   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
673 }
674
675
676 static void
677 variable_view_activate (PsppireDataWindow  *de)
678 {
679   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
680 }
681
682
683 static void
684 fonts_activate (PsppireDataWindow  *de)
685 {
686   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
687   GtkWidget *dialog =  gtk_font_selection_dialog_new (_("Font Selection"));
688   GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
689   PangoFontDescription *current_font = style->font_desc;
690   gchar *font_name = pango_font_description_to_string (current_font);
691
692   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
693
694   g_free (font_name);
695
696   gtk_window_set_transient_for (GTK_WINDOW (dialog),
697                                 GTK_WINDOW (toplevel));
698
699   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
700     {
701       const gchar *font = gtk_font_selection_dialog_get_font_name
702         (GTK_FONT_SELECTION_DIALOG (dialog));
703
704       PangoFontDescription* font_desc =
705         pango_font_description_from_string (font);
706
707       psppire_data_editor_set_font (de->data_editor, font_desc);
708     }
709
710   gtk_widget_hide (dialog);
711 }
712
713
714
715 /* Callback for the value labels action */
716 static void
717 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
718 {
719   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
720 }
721
722 static void
723 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
724 {
725   psppire_data_editor_split_window (de->data_editor,
726                                     gtk_toggle_action_get_active (ta));
727 }
728
729
730 static void
731 file_quit (PsppireDataWindow *de)
732 {
733   /* FIXME: Need to be more intelligent here.
734      Give the user the opportunity to save any unsaved data.
735   */
736   psppire_quit ();
737 }
738
739 static void
740 on_recent_data_select (GtkMenuShell *menushell,
741                        PsppireWindow *window)
742 {
743   gchar *file;
744
745   gchar *uri =
746     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
747
748   file = g_filename_from_uri (uri, NULL, NULL);
749
750   g_free (uri);
751
752   open_data_window (window, file, NULL);
753
754   g_free (file);
755 }
756
757 static char *
758 charset_from_mime_type (const char *mime_type)
759 {
760   const char *charset;
761   struct string s;
762   const char *p;
763
764   if (mime_type == NULL)
765     return NULL;
766
767   charset = c_strcasestr (mime_type, "charset=");
768   if (charset == NULL)
769     return NULL;
770
771   ds_init_empty (&s);
772   p = charset + 8;
773   if (*p == '"')
774     {
775       /* Parse a "quoted-string" as defined by RFC 822. */
776       for (p++; *p != '\0' && *p != '"'; p++)
777         {
778           if (*p != '\\')
779             ds_put_byte (&s, *p);
780           else if (*++p != '\0')
781             ds_put_byte (&s, *p);
782         }
783     }
784   else
785     {
786       /* Parse a "token" as defined by RFC 2045. */
787       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
788         ds_put_byte (&s, *p++);
789     }
790   if (!ds_is_empty (&s))
791     return ds_steal_cstr (&s);
792
793   ds_destroy (&s);
794   return NULL;
795 }
796
797 static void
798 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
799 {
800   GtkRecentInfo *item;
801   char *encoding;
802   GtkWidget *se;
803   gchar *file;
804
805   /* Get the file name and its encoding. */
806   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
807   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
808   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
809   gtk_recent_info_unref (item);
810
811   se = psppire_syntax_window_new (encoding);
812
813   free (encoding);
814
815   if ( psppire_window_load (PSPPIRE_WINDOW (se), file, NULL) ) 
816     gtk_widget_show (se);
817   else
818     gtk_widget_destroy (se);
819
820   g_free (file);
821 }
822
823 static void
824 set_unsaved (gpointer w)
825 {
826   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
827 }
828
829 static void
830 on_switch_page (PsppireDataEditor *de, gpointer p,
831                 gint pagenum, PsppireDataWindow *dw)
832 {
833   GtkWidget *page_menu_item;
834   gboolean is_ds;
835   const char *path;
836
837   is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
838   path = (is_ds
839           ? "/ui/menubar/view/view_data"
840           : "/ui/menubar/view/view_variables");
841   page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
842   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
843 }
844
845 static void
846 on_ui_manager_changed (PsppireDataEditor *de,
847                        GParamSpec *pspec UNUSED,
848                        PsppireDataWindow *dw)
849 {
850   GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
851   if (uim == dw->uim)
852     return;
853
854   if (dw->uim)
855     {
856       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
857       g_object_unref (dw->uim);
858       dw->uim = NULL;
859     }
860
861   dw->uim = uim;
862   if (dw->uim)
863     {
864       g_object_ref (dw->uim);
865       dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
866     }
867 }
868
869 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
870    Returns a pointer to the action
871 */
872 static GtkAction *
873 connect_action (PsppireDataWindow *dw, const char *action_name, 
874                                     GCallback handler)
875 {
876   GtkAction *action = get_action_assert (dw->builder, action_name);
877
878   g_signal_connect_swapped (action, "activate", handler, dw);
879
880   return action;
881 }
882
883 /* Only a data file with at least one variable can be saved. */
884 static void
885 enable_save (PsppireDataWindow *dw)
886 {
887   gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
888
889   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
890                             enable);
891   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
892                             enable);
893 }
894
895 /* Initializes as much of a PsppireDataWindow as we can and must before the
896    dataset has been set.
897
898    In particular, the 'menu' member is required in case the "filename" property
899    is set before the "dataset" property: otherwise PsppireWindow will try to
900    modify the menu as part of the "filename" property_set() function and end up
901    with a Gtk-CRITICAL since 'menu' is NULL.  */
902 static void
903 psppire_data_window_init (PsppireDataWindow *de)
904 {
905   GtkWidget *w ;
906   de->builder = builder_new ("data-editor.ui");
907
908   de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
909
910   w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
911
912   PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
913
914   de->uim = NULL;
915   de->merge_id = 0;
916 }
917
918 static void
919 psppire_data_window_finish_init (PsppireDataWindow *de,
920                                  struct dataset *ds)
921 {
922   static const struct dataset_callbacks cbs =
923     {
924       set_unsaved,                    /* changed */
925       transformation_change_callback, /* transformations_changed */
926     };
927
928   GtkWidget *menubar;
929   GtkWidget *hb ;
930   GtkWidget *sb ;
931
932   GtkWidget *box = gtk_vbox_new (FALSE, 0);
933
934   de->dataset = ds;
935   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
936   de->data_store = psppire_data_store_new (de->dict);
937   psppire_data_store_set_reader (de->data_store, NULL);
938
939   menubar = get_widget_assert (de->builder, "menubar");
940   hb = get_widget_assert (de->builder, "handlebox1");
941   sb = get_widget_assert (de->builder, "status-bar");
942
943   de->uim = NULL;
944   de->merge_id = 0;
945
946   de->data_editor =
947     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
948   g_signal_connect (de->data_editor, "switch-page",
949                     G_CALLBACK (on_switch_page), de);
950
951   g_signal_connect_swapped (de->data_store, "case-changed",
952                             G_CALLBACK (set_unsaved), de);
953
954   g_signal_connect_swapped (de->data_store, "case-inserted",
955                             G_CALLBACK (set_unsaved), de);
956
957   g_signal_connect_swapped (de->data_store, "cases-deleted",
958                             G_CALLBACK (set_unsaved), de);
959
960   dataset_set_callbacks (de->dataset, &cbs, de);
961
962   connect_help (de->builder);
963
964   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
965   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
966   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
967   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
968
969   gtk_container_add (GTK_CONTAINER (de), box);
970
971   g_signal_connect (de->dict, "weight-changed",
972                     G_CALLBACK (on_weight_change),
973                     de);
974
975   g_signal_connect (de->dict, "filter-changed",
976                     G_CALLBACK (on_filter_change),
977                     de);
978
979   g_signal_connect (de->dict, "split-changed",
980                     G_CALLBACK (on_split_change),
981                     de);
982
983   g_signal_connect_swapped (de->dict, "backend-changed",
984                             G_CALLBACK (enable_save), de);
985   g_signal_connect_swapped (de->dict, "variable-inserted",
986                             G_CALLBACK (enable_save), de);
987   g_signal_connect_swapped (de->dict, "variable-deleted",
988                             G_CALLBACK (enable_save), de);
989   enable_save (de);
990
991   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
992   connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
993   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
994   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
995   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
996   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
997   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
998   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
999
1000   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1001
1002   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1003   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1004   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1005   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1006   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1007   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1008   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1009   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1010
1011   {
1012     GtkWidget *recent_data =
1013       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1014
1015     GtkWidget *recent_files =
1016       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1017
1018
1019     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1020       gtk_recent_manager_get_default ());
1021
1022     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1023       gtk_recent_manager_get_default ());
1024
1025     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1026     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1027
1028     {
1029       GtkRecentFilter *filter = gtk_recent_filter_new ();
1030
1031       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1032       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1033
1034       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1035
1036       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1037     }
1038
1039     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1040
1041
1042     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1043
1044     {
1045       GtkRecentFilter *filter = gtk_recent_filter_new ();
1046
1047       gtk_recent_filter_add_pattern (filter, "*.sps");
1048       gtk_recent_filter_add_pattern (filter, "*.SPS");
1049
1050       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1051
1052       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1053     }
1054
1055     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1056
1057     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1058
1059   }
1060
1061   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1062
1063
1064   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1065   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1066
1067   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1068
1069   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1070
1071   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1072
1073   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1074
1075   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1076
1077   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1078
1079   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1080
1081   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1082
1083   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1084
1085   merge_help_menu (de->ui_manager);
1086
1087   g_signal_connect (de->data_editor, "notify::ui-manager",
1088                     G_CALLBACK (on_ui_manager_changed), de);
1089   on_ui_manager_changed (de->data_editor, NULL, de);
1090
1091   gtk_widget_show (GTK_WIDGET (de->data_editor));
1092   gtk_widget_show (box);
1093
1094   ll_push_head (&all_data_windows, &de->ll);
1095 }
1096
1097 static void
1098 psppire_data_window_dispose (GObject *object)
1099 {
1100   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1101
1102   if (dw->uim)
1103     {
1104       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1105       g_object_unref (dw->uim);
1106       dw->uim = NULL;
1107     }
1108
1109   if (dw->builder != NULL)
1110     {
1111       g_object_unref (dw->builder);
1112       dw->builder = NULL;
1113     }
1114
1115   if (dw->dict)
1116     {
1117       g_signal_handlers_disconnect_by_func (dw->dict,
1118                                             G_CALLBACK (enable_save), dw);
1119       g_signal_handlers_disconnect_by_func (dw->dict,
1120                                             G_CALLBACK (on_weight_change), dw);
1121       g_signal_handlers_disconnect_by_func (dw->dict,
1122                                             G_CALLBACK (on_filter_change), dw);
1123       g_signal_handlers_disconnect_by_func (dw->dict,
1124                                             G_CALLBACK (on_split_change), dw);
1125
1126       g_object_unref (dw->dict);
1127       dw->dict = NULL;
1128     }
1129
1130   if (dw->data_store)
1131     {
1132       g_object_unref (dw->data_store);
1133       dw->data_store = NULL;
1134     }
1135
1136   if (dw->ll.next != NULL)
1137     {
1138       ll_remove (&dw->ll);
1139       dw->ll.next = NULL;
1140     }
1141
1142   if (G_OBJECT_CLASS (parent_class)->dispose)
1143     G_OBJECT_CLASS (parent_class)->dispose (object);
1144 }
1145
1146 static void
1147 psppire_data_window_finalize (GObject *object)
1148 {
1149   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1150
1151   if (dw->dataset)
1152     {
1153       struct dataset *dataset = dw->dataset;
1154       struct session *session = dataset_session (dataset);
1155
1156       dw->dataset = NULL;
1157
1158       dataset_set_callbacks (dataset, NULL, NULL);
1159       session_set_active_dataset (session, NULL);
1160       dataset_destroy (dataset);
1161     }
1162
1163   if (G_OBJECT_CLASS (parent_class)->finalize)
1164     G_OBJECT_CLASS (parent_class)->finalize (object);
1165 }
1166
1167 static void
1168 psppire_data_window_set_property (GObject         *object,
1169                                   guint            prop_id,
1170                                   const GValue    *value,
1171                                   GParamSpec      *pspec)
1172 {
1173   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1174
1175   switch (prop_id)
1176     {
1177     case PROP_DATASET:
1178       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1179       break;
1180     default:
1181       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1182       break;
1183     };
1184 }
1185
1186 static void
1187 psppire_data_window_get_property (GObject         *object,
1188                                   guint            prop_id,
1189                                   GValue          *value,
1190                                   GParamSpec      *pspec)
1191 {
1192   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1193
1194   switch (prop_id)
1195     {
1196     case PROP_DATASET:
1197       g_value_set_pointer (value, window->dataset);
1198       break;
1199     default:
1200       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1201       break;
1202     };
1203 }
1204
1205 static guint
1206 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1207 {
1208   gchar *ui_string;
1209   guint merge_id;
1210   GList *list;
1211
1212   ui_string = gtk_ui_manager_get_ui (uim);
1213   merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1214                                                 -1, NULL);
1215   g_free (ui_string);
1216
1217   g_return_val_if_fail (merge_id != 0, 0);
1218
1219   list = gtk_ui_manager_get_action_groups (uim);
1220   for (; list != NULL; list = list->next)
1221     {
1222       GtkActionGroup *action_group = list->data;
1223       GList *actions = gtk_action_group_list_actions (action_group);
1224       GList *action;
1225
1226       for (action = actions; action != NULL; action = action->next)
1227         {
1228           GtkAction *a = action->data;
1229
1230           if (PSPPIRE_IS_DIALOG_ACTION (a))
1231             g_object_set (a, "manager", pdw->ui_manager, NULL);
1232         }
1233
1234       gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1235     }
1236
1237   gtk_window_add_accel_group (GTK_WINDOW (pdw),
1238                               gtk_ui_manager_get_accel_group (uim));
1239
1240   return merge_id;
1241 }
1242
1243 static void
1244 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1245                                GtkUIManager *uim, guint merge_id)
1246 {
1247   GList *list;
1248
1249   g_return_if_fail (merge_id != 0);
1250
1251   gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1252
1253   list = gtk_ui_manager_get_action_groups (uim);
1254   for (; list != NULL; list = list->next)
1255     {
1256       GtkActionGroup *action_group = list->data;
1257       gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1258     }
1259
1260   gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1261                                  gtk_ui_manager_get_accel_group (uim));
1262 }
1263
1264 GtkWidget*
1265 psppire_data_window_new (struct dataset *ds)
1266 {
1267   GtkWidget *dw;
1268
1269   if (the_session == NULL)
1270     the_session = session_create (NULL);
1271
1272   if (ds == NULL)
1273     {
1274       char *dataset_name = session_generate_dataset_name (the_session);
1275       ds = dataset_create (the_session, dataset_name);
1276       free (dataset_name);
1277     }
1278   assert (dataset_session (ds) == the_session);
1279
1280   dw = GTK_WIDGET (
1281     g_object_new (
1282       psppire_data_window_get_type (),
1283       "description", _("Data Editor"),
1284       "dataset", ds,
1285       NULL));
1286
1287   if (dataset_name (ds) != NULL)
1288     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1289
1290   return dw;
1291 }
1292
1293 bool
1294 psppire_data_window_is_empty (PsppireDataWindow *dw)
1295 {
1296   return psppire_dict_get_var_cnt (dw->dict) == 0;
1297 }
1298
1299 static void
1300 psppire_data_window_iface_init (PsppireWindowIface *iface)
1301 {
1302   iface->save = save_file;
1303   iface->pick_filename = data_pick_filename;
1304   iface->load = load_file;
1305 }
1306 \f
1307 PsppireDataWindow *
1308 psppire_default_data_window (void)
1309 {
1310   if (ll_is_empty (&all_data_windows))
1311     create_data_window ();
1312   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1313 }
1314
1315 void
1316 psppire_data_window_set_default (PsppireDataWindow *pdw)
1317 {
1318   ll_remove (&pdw->ll);
1319   ll_push_head (&all_data_windows, &pdw->ll);
1320 }
1321
1322 void
1323 psppire_data_window_undefault (PsppireDataWindow *pdw)
1324 {
1325   ll_remove (&pdw->ll);
1326   ll_push_tail (&all_data_windows, &pdw->ll);
1327 }
1328
1329 PsppireDataWindow *
1330 psppire_data_window_for_dataset (struct dataset *ds)
1331 {
1332   PsppireDataWindow *pdw;
1333
1334   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1335     if (pdw->dataset == ds)
1336       return pdw;
1337
1338   return NULL;
1339 }
1340
1341 PsppireDataWindow *
1342 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1343 {
1344   PsppireDataWindow *pdw;
1345
1346   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1347     if (pdw->data_store == data_store)
1348       return pdw;
1349
1350   return NULL;
1351 }
1352
1353 void
1354 create_data_window (void)
1355 {
1356   gtk_widget_show (psppire_data_window_new (NULL));
1357 }
1358
1359 void
1360 open_data_window (PsppireWindow *victim, const char *file_name, gpointer hint)
1361 {
1362   GtkWidget *window;
1363
1364   if (PSPPIRE_IS_DATA_WINDOW (victim)
1365       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1366     {
1367       window = GTK_WIDGET (victim);
1368       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1369     }
1370   else
1371     window = psppire_data_window_new (NULL);
1372
1373   psppire_window_load (PSPPIRE_WINDOW (window), file_name, hint);
1374   gtk_widget_show_all (window);
1375 }
1376