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