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