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