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