c68f65e129c3a2311d4a7636d34ddc11e5e443f5
[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, "UTF-8"));
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   /* Set the appropriate ui_manager according to the selected page.
844      This is necessary, because the menus for the variable view and
845      the data view are different (slightly). */
846
847   gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
848   const char *path = (is_ds
849           ? "/ui/menubar/view/view_data"
850           : "/ui/menubar/view/view_variables");
851
852   GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
853   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
854 }
855
856 static void
857 on_ui_manager_changed (PsppireDataEditor *de,
858                        GParamSpec *pspec UNUSED,
859                        PsppireDataWindow *dw)
860 {
861   GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
862   if (uim == dw->uim)
863     return;
864
865   if (dw->uim)
866     {
867       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
868       g_object_unref (dw->uim);
869       dw->uim = NULL;
870     }
871
872   dw->uim = uim;
873   if (dw->uim)
874     {
875       g_object_ref (dw->uim);
876       dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
877     }
878 }
879
880 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
881    Returns a pointer to the action
882 */
883 static GtkAction *
884 connect_action (PsppireDataWindow *dw, const char *action_name, 
885                                     GCallback handler)
886 {
887   GtkAction *action = get_action_assert (dw->builder, action_name);
888
889   g_signal_connect_swapped (action, "activate", handler, dw);
890
891   return action;
892 }
893
894 /* Only a data file with at least one variable can be saved. */
895 static void
896 enable_save (PsppireDataWindow *dw)
897 {
898   gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
899
900   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
901                             enable);
902   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
903                             enable);
904 }
905
906 /* Initializes as much of a PsppireDataWindow as we can and must before the
907    dataset has been set.
908
909    In particular, the 'menu' member is required in case the "filename" property
910    is set before the "dataset" property: otherwise PsppireWindow will try to
911    modify the menu as part of the "filename" property_set() function and end up
912    with a Gtk-CRITICAL since 'menu' is NULL.  */
913 static void
914 psppire_data_window_init (PsppireDataWindow *de)
915 {
916   GtkWidget *w ;
917   de->builder = builder_new ("data-editor.ui");
918
919   de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
920
921   w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
922
923   PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
924
925   de->uim = NULL;
926   de->merge_id = 0;
927 }
928
929 static void
930 psppire_data_window_finish_init (PsppireDataWindow *de,
931                                  struct dataset *ds)
932 {
933   static const struct dataset_callbacks cbs =
934     {
935       set_unsaved,                    /* changed */
936       transformation_change_callback, /* transformations_changed */
937     };
938
939   GtkWidget *menubar;
940   GtkWidget *hb ;
941   GtkWidget *sb ;
942
943   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
944
945   de->dataset = ds;
946   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
947   de->data_store = psppire_data_store_new (de->dict);
948   psppire_data_store_set_reader (de->data_store, NULL);
949
950   menubar = get_widget_assert (de->builder, "menubar");
951   hb = get_widget_assert (de->builder, "toolbar");
952   sb = get_widget_assert (de->builder, "status-bar");
953
954   de->uim = NULL;
955   de->merge_id = 0;
956
957   de->data_editor =
958     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
959   g_signal_connect (de->data_editor, "switch-page",
960                     G_CALLBACK (on_switch_page), de);
961
962   g_signal_connect_swapped (de->data_store, "case-changed",
963                             G_CALLBACK (set_unsaved), de);
964
965   g_signal_connect_swapped (de->data_store, "case-inserted",
966                             G_CALLBACK (set_unsaved), de);
967
968   g_signal_connect_swapped (de->data_store, "cases-deleted",
969                             G_CALLBACK (set_unsaved), de);
970
971   dataset_set_callbacks (de->dataset, &cbs, de);
972
973   connect_help (de->builder);
974
975   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
976   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
977   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
978   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
979
980   gtk_container_add (GTK_CONTAINER (de), box);
981
982   g_signal_connect (de->dict, "weight-changed",
983                     G_CALLBACK (on_weight_change),
984                     de);
985
986   g_signal_connect (de->dict, "filter-changed",
987                     G_CALLBACK (on_filter_change),
988                     de);
989
990   g_signal_connect (de->dict, "split-changed",
991                     G_CALLBACK (on_split_change),
992                     de);
993
994   g_signal_connect_swapped (de->dict, "backend-changed",
995                             G_CALLBACK (enable_save), de);
996   g_signal_connect_swapped (de->dict, "variable-inserted",
997                             G_CALLBACK (enable_save), de);
998   g_signal_connect_swapped (de->dict, "variable-deleted",
999                             G_CALLBACK (enable_save), de);
1000   enable_save (de);
1001
1002   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1003   connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
1004   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1005   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1006   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1007   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1008   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1009   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1010
1011   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1012
1013   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1014   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1015   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1016   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1017   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1018   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1019   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1020   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1021
1022   {
1023     GtkWidget *recent_data =
1024       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1025
1026     GtkWidget *recent_files =
1027       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1028
1029
1030     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1031       gtk_recent_manager_get_default ());
1032
1033     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1034       gtk_recent_manager_get_default ());
1035
1036     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1037     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1038
1039     {
1040       GtkRecentFilter *filter = gtk_recent_filter_new ();
1041
1042       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1043       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1044
1045       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1046
1047       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1048     }
1049
1050     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1051
1052
1053     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1054
1055     {
1056       GtkRecentFilter *filter = gtk_recent_filter_new ();
1057
1058       gtk_recent_filter_add_pattern (filter, "*.sps");
1059       gtk_recent_filter_add_pattern (filter, "*.SPS");
1060
1061       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1062
1063       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1064     }
1065
1066     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1067
1068     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1069
1070   }
1071
1072   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1073
1074
1075   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1076   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1077
1078   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1079
1080   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1081
1082   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1083
1084   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1085
1086   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1087
1088   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1089
1090   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1091
1092   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1093
1094   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1095
1096   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1097   
1098   g_signal_connect (de->data_editor, "notify::ui-manager",
1099                     G_CALLBACK (on_ui_manager_changed), de);
1100   on_ui_manager_changed (de->data_editor, NULL, de);
1101
1102   gtk_widget_show (GTK_WIDGET (de->data_editor));
1103   gtk_widget_show (box);
1104
1105   ll_push_head (&all_data_windows, &de->ll);
1106 }
1107
1108 static void
1109 psppire_data_window_dispose (GObject *object)
1110 {
1111   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1112
1113   if (dw->uim)
1114     {
1115       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1116       g_object_unref (dw->uim);
1117       dw->uim = NULL;
1118     }
1119
1120   if (dw->builder != NULL)
1121     {
1122       g_object_unref (dw->builder);
1123       dw->builder = NULL;
1124     }
1125
1126   if (dw->dict)
1127     {
1128       g_signal_handlers_disconnect_by_func (dw->dict,
1129                                             G_CALLBACK (enable_save), dw);
1130       g_signal_handlers_disconnect_by_func (dw->dict,
1131                                             G_CALLBACK (on_weight_change), dw);
1132       g_signal_handlers_disconnect_by_func (dw->dict,
1133                                             G_CALLBACK (on_filter_change), dw);
1134       g_signal_handlers_disconnect_by_func (dw->dict,
1135                                             G_CALLBACK (on_split_change), dw);
1136
1137       g_object_unref (dw->dict);
1138       dw->dict = NULL;
1139     }
1140
1141   if (dw->data_store)
1142     {
1143       g_object_unref (dw->data_store);
1144       dw->data_store = NULL;
1145     }
1146
1147   if (dw->ll.next != NULL)
1148     {
1149       ll_remove (&dw->ll);
1150       dw->ll.next = NULL;
1151     }
1152
1153   if (G_OBJECT_CLASS (parent_class)->dispose)
1154     G_OBJECT_CLASS (parent_class)->dispose (object);
1155 }
1156
1157 static void
1158 psppire_data_window_finalize (GObject *object)
1159 {
1160   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1161
1162   if (dw->dataset)
1163     {
1164       struct dataset *dataset = dw->dataset;
1165       struct session *session = dataset_session (dataset);
1166
1167       dw->dataset = NULL;
1168
1169       dataset_set_callbacks (dataset, NULL, NULL);
1170       session_set_active_dataset (session, NULL);
1171       dataset_destroy (dataset);
1172     }
1173
1174   if (G_OBJECT_CLASS (parent_class)->finalize)
1175     G_OBJECT_CLASS (parent_class)->finalize (object);
1176 }
1177
1178 static void
1179 psppire_data_window_set_property (GObject         *object,
1180                                   guint            prop_id,
1181                                   const GValue    *value,
1182                                   GParamSpec      *pspec)
1183 {
1184   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1185
1186   switch (prop_id)
1187     {
1188     case PROP_DATASET:
1189       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1190       break;
1191     default:
1192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1193       break;
1194     };
1195 }
1196
1197 static void
1198 psppire_data_window_get_property (GObject         *object,
1199                                   guint            prop_id,
1200                                   GValue          *value,
1201                                   GParamSpec      *pspec)
1202 {
1203   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1204
1205   switch (prop_id)
1206     {
1207     case PROP_DATASET:
1208       g_value_set_pointer (value, window->dataset);
1209       break;
1210     default:
1211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1212       break;
1213     };
1214 }
1215
1216 static guint
1217 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1218 {
1219   gchar *ui_string;
1220   guint merge_id;
1221   GList *list;
1222
1223   ui_string = gtk_ui_manager_get_ui (uim);
1224   merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1225                                                 -1, NULL);
1226   g_free (ui_string);
1227
1228   g_return_val_if_fail (merge_id != 0, 0);
1229
1230   list = gtk_ui_manager_get_action_groups (uim);
1231   for (; list != NULL; list = list->next)
1232     {
1233       GtkActionGroup *action_group = list->data;
1234       GList *actions = gtk_action_group_list_actions (action_group);
1235       GList *action;
1236
1237       for (action = actions; action != NULL; action = action->next)
1238         {
1239           GtkAction *a = action->data;
1240
1241           if (PSPPIRE_IS_DIALOG_ACTION (a))
1242             g_object_set (a, "manager", pdw->ui_manager, NULL);
1243         }
1244
1245       gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1246     }
1247
1248   gtk_window_add_accel_group (GTK_WINDOW (pdw),
1249                               gtk_ui_manager_get_accel_group (uim));
1250
1251   return merge_id;
1252 }
1253
1254 static void
1255 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1256                                GtkUIManager *uim, guint merge_id)
1257 {
1258   GList *list;
1259
1260   g_return_if_fail (merge_id != 0);
1261
1262   gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1263
1264   list = gtk_ui_manager_get_action_groups (uim);
1265   for (; list != NULL; list = list->next)
1266     {
1267       GtkActionGroup *action_group = list->data;
1268       gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1269     }
1270
1271   gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1272                                  gtk_ui_manager_get_accel_group (uim));
1273 }
1274
1275 GtkWidget*
1276 psppire_data_window_new (struct dataset *ds)
1277 {
1278   GtkWidget *dw;
1279
1280   if (the_session == NULL)
1281     the_session = session_create (NULL);
1282
1283   if (ds == NULL)
1284     {
1285       char *dataset_name = session_generate_dataset_name (the_session);
1286       ds = dataset_create (the_session, dataset_name);
1287       free (dataset_name);
1288     }
1289   assert (dataset_session (ds) == the_session);
1290
1291   dw = GTK_WIDGET (
1292     g_object_new (
1293       psppire_data_window_get_type (),
1294       "description", _("Data Editor"),
1295       "dataset", ds,
1296       NULL));
1297
1298   if (dataset_name (ds) != NULL)
1299     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1300
1301   return dw;
1302 }
1303
1304 bool
1305 psppire_data_window_is_empty (PsppireDataWindow *dw)
1306 {
1307   return psppire_dict_get_var_cnt (dw->dict) == 0;
1308 }
1309
1310 static void
1311 psppire_data_window_iface_init (PsppireWindowIface *iface)
1312 {
1313   iface->save = save_file;
1314   iface->pick_filename = data_pick_filename;
1315   iface->load = load_file;
1316 }
1317 \f
1318 PsppireDataWindow *
1319 psppire_default_data_window (void)
1320 {
1321   if (ll_is_empty (&all_data_windows))
1322     create_data_window ();
1323   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1324 }
1325
1326 void
1327 psppire_data_window_set_default (PsppireDataWindow *pdw)
1328 {
1329   ll_remove (&pdw->ll);
1330   ll_push_head (&all_data_windows, &pdw->ll);
1331 }
1332
1333 void
1334 psppire_data_window_undefault (PsppireDataWindow *pdw)
1335 {
1336   ll_remove (&pdw->ll);
1337   ll_push_tail (&all_data_windows, &pdw->ll);
1338 }
1339
1340 PsppireDataWindow *
1341 psppire_data_window_for_dataset (struct dataset *ds)
1342 {
1343   PsppireDataWindow *pdw;
1344
1345   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1346     if (pdw->dataset == ds)
1347       return pdw;
1348
1349   return NULL;
1350 }
1351
1352 PsppireDataWindow *
1353 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1354 {
1355   PsppireDataWindow *pdw;
1356
1357   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1358     if (pdw->data_store == data_store)
1359       return pdw;
1360
1361   return NULL;
1362 }
1363
1364 void
1365 create_data_window (void)
1366 {
1367   gtk_widget_show (psppire_data_window_new (NULL));
1368 }
1369
1370 void
1371 open_data_window (PsppireWindow *victim, const char *file_name,
1372                   const char *encoding, gpointer hint)
1373 {
1374   GtkWidget *window;
1375
1376   if (PSPPIRE_IS_DATA_WINDOW (victim)
1377       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1378     {
1379       window = GTK_WIDGET (victim);
1380       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1381     }
1382   else
1383     window = psppire_data_window_new (NULL);
1384
1385   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1386   gtk_widget_show_all (window);
1387 }
1388