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