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