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