GtkTable -> GtkGrid
[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                                  _("Cancel"), GTK_RESPONSE_CANCEL,
494                                  _("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_box_new (GTK_ORIENTATION_HORIZONTAL, 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                           _("Cancel"), GTK_RESPONSE_CANCEL,
622                           _("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   GtkWidget *dialog =  gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
702   GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
703   PangoFontDescription *current_font = style->font_desc;
704
705   gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
706
707   gtk_window_set_transient_for (GTK_WINDOW (dialog),
708                                 GTK_WINDOW (toplevel));
709
710   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
711     {
712       PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
713
714       psppire_data_editor_set_font (de->data_editor, font_desc);
715     }
716
717   gtk_widget_hide (dialog);
718 }
719
720
721
722 /* Callback for the value labels action */
723 static void
724 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
725 {
726   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
727 }
728
729 static void
730 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
731 {
732   psppire_data_editor_split_window (de->data_editor,
733                                     gtk_toggle_action_get_active (ta));
734 }
735
736
737 static void
738 file_quit (PsppireDataWindow *de)
739 {
740   /* FIXME: Need to be more intelligent here.
741      Give the user the opportunity to save any unsaved data.
742   */
743   psppire_quit ();
744 }
745
746 static void
747 on_recent_data_select (GtkMenuShell *menushell,
748                        PsppireWindow *window)
749 {
750   gchar *file;
751
752   gchar *uri =
753     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
754
755   file = g_filename_from_uri (uri, NULL, NULL);
756
757   g_free (uri);
758
759   open_data_window (window, file, NULL, NULL);
760
761   g_free (file);
762 }
763
764 static char *
765 charset_from_mime_type (const char *mime_type)
766 {
767   const char *charset;
768   struct string s;
769   const char *p;
770
771   if (mime_type == NULL)
772     return NULL;
773
774   charset = c_strcasestr (mime_type, "charset=");
775   if (charset == NULL)
776     return NULL;
777
778   ds_init_empty (&s);
779   p = charset + 8;
780   if (*p == '"')
781     {
782       /* Parse a "quoted-string" as defined by RFC 822. */
783       for (p++; *p != '\0' && *p != '"'; p++)
784         {
785           if (*p != '\\')
786             ds_put_byte (&s, *p);
787           else if (*++p != '\0')
788             ds_put_byte (&s, *p);
789         }
790     }
791   else
792     {
793       /* Parse a "token" as defined by RFC 2045. */
794       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
795         ds_put_byte (&s, *p++);
796     }
797   if (!ds_is_empty (&s))
798     return ds_steal_cstr (&s);
799
800   ds_destroy (&s);
801   return NULL;
802 }
803
804 static void
805 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
806 {
807   GtkRecentInfo *item;
808   char *encoding;
809   GtkWidget *se;
810   gchar *file;
811
812   /* Get the file name and its encoding. */
813   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
814   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
815   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
816   gtk_recent_info_unref (item);
817
818   se = psppire_syntax_window_new (encoding);
819
820   free (encoding);
821
822   if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) ) 
823     gtk_widget_show (se);
824   else
825     gtk_widget_destroy (se);
826
827   g_free (file);
828 }
829
830 static void
831 set_unsaved (gpointer w)
832 {
833   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
834 }
835
836 static void
837 on_switch_page (PsppireDataEditor *de, gpointer p,
838                 gint pagenum, PsppireDataWindow *dw)
839 {
840   GtkWidget *page_menu_item;
841   gboolean is_ds;
842   const char *path;
843
844   is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
845   path = (is_ds
846           ? "/ui/menubar/view/view_data"
847           : "/ui/menubar/view/view_variables");
848   page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
849   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
850 }
851
852 static void
853 on_ui_manager_changed (PsppireDataEditor *de,
854                        GParamSpec *pspec UNUSED,
855                        PsppireDataWindow *dw)
856 {
857   GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
858   if (uim == dw->uim)
859     return;
860
861   if (dw->uim)
862     {
863       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
864       g_object_unref (dw->uim);
865       dw->uim = NULL;
866     }
867
868   dw->uim = uim;
869   if (dw->uim)
870     {
871       g_object_ref (dw->uim);
872       dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
873     }
874 }
875
876 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
877    Returns a pointer to the action
878 */
879 static GtkAction *
880 connect_action (PsppireDataWindow *dw, const char *action_name, 
881                                     GCallback handler)
882 {
883   GtkAction *action = get_action_assert (dw->builder, action_name);
884
885   g_signal_connect_swapped (action, "activate", handler, dw);
886
887   return action;
888 }
889
890 /* Only a data file with at least one variable can be saved. */
891 static void
892 enable_save (PsppireDataWindow *dw)
893 {
894   gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
895
896   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
897                             enable);
898   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
899                             enable);
900 }
901
902 /* Initializes as much of a PsppireDataWindow as we can and must before the
903    dataset has been set.
904
905    In particular, the 'menu' member is required in case the "filename" property
906    is set before the "dataset" property: otherwise PsppireWindow will try to
907    modify the menu as part of the "filename" property_set() function and end up
908    with a Gtk-CRITICAL since 'menu' is NULL.  */
909 static void
910 psppire_data_window_init (PsppireDataWindow *de)
911 {
912   GtkWidget *w ;
913   de->builder = builder_new ("data-editor.ui");
914
915   de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
916
917   w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
918
919   PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
920
921   de->uim = NULL;
922   de->merge_id = 0;
923 }
924
925 static void
926 psppire_data_window_finish_init (PsppireDataWindow *de,
927                                  struct dataset *ds)
928 {
929   static const struct dataset_callbacks cbs =
930     {
931       set_unsaved,                    /* changed */
932       transformation_change_callback, /* transformations_changed */
933     };
934
935   GtkWidget *menubar;
936   GtkWidget *hb ;
937   GtkWidget *sb ;
938
939   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
940
941   de->dataset = ds;
942   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
943   de->data_store = psppire_data_store_new (de->dict);
944   psppire_data_store_set_reader (de->data_store, NULL);
945
946   menubar = get_widget_assert (de->builder, "menubar");
947   hb = get_widget_assert (de->builder, "handlebox1");
948   sb = get_widget_assert (de->builder, "status-bar");
949
950   de->uim = NULL;
951   de->merge_id = 0;
952
953   de->data_editor =
954     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
955   g_signal_connect (de->data_editor, "switch-page",
956                     G_CALLBACK (on_switch_page), de);
957
958   g_signal_connect_swapped (de->data_store, "case-changed",
959                             G_CALLBACK (set_unsaved), de);
960
961   g_signal_connect_swapped (de->data_store, "case-inserted",
962                             G_CALLBACK (set_unsaved), de);
963
964   g_signal_connect_swapped (de->data_store, "cases-deleted",
965                             G_CALLBACK (set_unsaved), de);
966
967   dataset_set_callbacks (de->dataset, &cbs, de);
968
969   connect_help (de->builder);
970
971   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
972   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
973   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
974   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
975
976   gtk_container_add (GTK_CONTAINER (de), box);
977
978   g_signal_connect (de->dict, "weight-changed",
979                     G_CALLBACK (on_weight_change),
980                     de);
981
982   g_signal_connect (de->dict, "filter-changed",
983                     G_CALLBACK (on_filter_change),
984                     de);
985
986   g_signal_connect (de->dict, "split-changed",
987                     G_CALLBACK (on_split_change),
988                     de);
989
990   g_signal_connect_swapped (de->dict, "backend-changed",
991                             G_CALLBACK (enable_save), de);
992   g_signal_connect_swapped (de->dict, "variable-inserted",
993                             G_CALLBACK (enable_save), de);
994   g_signal_connect_swapped (de->dict, "variable-deleted",
995                             G_CALLBACK (enable_save), de);
996   enable_save (de);
997
998   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
999   connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
1000   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1001   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1002   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1003   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1004   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1005   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1006
1007   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1008
1009   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1010   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1011   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_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