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