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