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