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