Frequencies dialog: Convert to new system
[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/builder-wrapper.h"
30 #include "ui/gui/chi-square-dialog.h"
31 #include "ui/gui/comments-dialog.h"
32 #include "ui/gui/compute-dialog.h"
33 #include "ui/gui/count-dialog.h"
34 #include "ui/gui/crosstabs-dialog.h"
35 #include "ui/gui/entry-dialog.h"
36 #include "ui/gui/executor.h"
37 #include "ui/gui/find-dialog.h"
38 #include "ui/gui/goto-case-dialog.h"
39 #include "ui/gui/help-menu.h"
40 #include "ui/gui/helper.h"
41 #include "ui/gui/helper.h"
42 #include "ui/gui/k-related-dialog.h"
43 #include "ui/gui/npar-two-sample-related.h"
44 #include "ui/gui/oneway-anova-dialog.h"
45 #include "ui/gui/psppire-data-window.h"
46 #include "ui/gui/psppire-syntax-window.h"
47 #include "ui/gui/psppire-window.h"
48 #include "ui/gui/psppire.h"
49 #include "ui/gui/runs-dialog.h"
50 #include "ui/gui/ks-one-sample-dialog.h"
51 #include "ui/gui/recode-dialog.h"
52 #include "ui/gui/select-cases-dialog.h"
53 #include "ui/gui/split-file-dialog.h"
54 #include "ui/gui/t-test-one-sample.h"
55 #include "ui/gui/t-test-paired-samples.h"
56 #include "ui/gui/text-data-import-dialog.h"
57 #include "ui/gui/transpose-dialog.h"
58 #include "ui/gui/univariate-dialog.h"
59 #include "ui/gui/weight-cases-dialog.h"
60 #include "ui/syntax-gen.h"
61
62 #include "gl/c-strcase.h"
63 #include "gl/c-strcasestr.h"
64 #include "gl/xvasprintf.h"
65
66 #include <gettext.h>
67 #define _(msgid) gettext (msgid)
68 #define N_(msgid) msgid
69
70 struct session *the_session;
71 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
72
73 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
74 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
75
76
77 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
78
79 static void psppire_data_window_dispose (GObject *object);
80 static void psppire_data_window_finalize (GObject *object);
81 static void psppire_data_window_set_property (GObject         *object,
82                                               guint            prop_id,
83                                               const GValue    *value,
84                                               GParamSpec      *pspec);
85 static void psppire_data_window_get_property (GObject         *object,
86                                               guint            prop_id,
87                                               GValue          *value,
88                                               GParamSpec      *pspec);
89
90 GType
91 psppire_data_window_get_type (void)
92 {
93   static GType psppire_data_window_type = 0;
94
95   if (!psppire_data_window_type)
96     {
97       static const GTypeInfo psppire_data_window_info =
98         {
99           sizeof (PsppireDataWindowClass),
100           NULL,
101           NULL,
102           (GClassInitFunc)psppire_data_window_class_init,
103           (GClassFinalizeFunc) NULL,
104           NULL,
105           sizeof (PsppireDataWindow),
106           0,
107           (GInstanceInitFunc) psppire_data_window_init,
108         };
109
110       static const GInterfaceInfo window_interface_info =
111         {
112           (GInterfaceInitFunc) psppire_data_window_iface_init,
113           NULL,
114           NULL
115         };
116
117       psppire_data_window_type =
118         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
119                                 &psppire_data_window_info, 0);
120
121
122       g_type_add_interface_static (psppire_data_window_type,
123                                    PSPPIRE_TYPE_WINDOW_MODEL,
124                                    &window_interface_info);
125     }
126
127   return psppire_data_window_type;
128 }
129
130 static GObjectClass *parent_class ;
131
132 enum {
133     PROP_DATASET = 1
134 };
135
136 static void
137 psppire_data_window_class_init (PsppireDataWindowClass *class)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (class);
140
141   parent_class = g_type_class_peek_parent (class);
142
143   object_class->dispose = psppire_data_window_dispose;
144   object_class->finalize = psppire_data_window_finalize;
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, "crosstabs", G_CALLBACK (crosstabs_dialog));
1074   connect_action (de, "univariate", G_CALLBACK (univariate_dialog));
1075   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1076   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1077   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1078   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1079   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1080
1081   {
1082     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1083
1084     GtkWidget *recent_data =
1085       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1086
1087     GtkWidget *recent_files =
1088       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1089
1090
1091     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1092       gtk_recent_manager_get_default ());
1093
1094     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1095       gtk_recent_manager_get_default ());
1096
1097     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1098     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1099
1100     {
1101       GtkRecentFilter *filter = gtk_recent_filter_new ();
1102
1103       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1104       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1105
1106       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1107
1108       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1109     }
1110
1111     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1112
1113
1114     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1115
1116     {
1117       GtkRecentFilter *filter = gtk_recent_filter_new ();
1118
1119       gtk_recent_filter_add_pattern (filter, "*.sps");
1120       gtk_recent_filter_add_pattern (filter, "*.SPS");
1121
1122       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1123
1124       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1125     }
1126
1127     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1128
1129     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1130
1131   }
1132
1133   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1134
1135
1136   g_signal_connect (de->data_editor,
1137                     "cases-selected",
1138                     G_CALLBACK (enable_delete_cases),
1139                     de);
1140
1141   g_signal_connect (de->data_editor,
1142                     "variables-selected",
1143                     G_CALLBACK (enable_delete_variables),
1144                     de);
1145
1146
1147   g_signal_connect (de->data_editor,
1148                     "switch-page",
1149                     G_CALLBACK (on_switch_sheet), de);
1150
1151   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1152   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1153
1154   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1155
1156   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1157
1158   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1159
1160   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1161
1162   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1163
1164   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1165
1166   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1167
1168   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1169
1170   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1171
1172   {
1173     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1174
1175     merge_help_menu (uim);
1176   }
1177
1178   {
1179     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1180                                                                 "datasheet-cases-popup");
1181
1182     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1183                                                                   "varsheet-variable-popup");
1184
1185     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1186                                                                    "datasheet-variable-popup");
1187
1188     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1189                               G_CALLBACK (psppire_data_editor_sort_ascending),
1190                               de->data_editor);
1191
1192     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1193                               G_CALLBACK (psppire_data_editor_sort_descending),
1194                               de->data_editor);
1195
1196     g_object_set (de->data_editor,
1197                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1198                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1199                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1200                   NULL);
1201   }
1202
1203   gtk_widget_show (GTK_WIDGET (de->data_editor));
1204   gtk_widget_show (box);
1205
1206   ll_push_head (&all_data_windows, &de->ll);
1207 }
1208
1209 static void
1210 psppire_data_window_dispose (GObject *object)
1211 {
1212   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1213
1214   if (dw->builder != NULL)
1215     {
1216       g_object_unref (dw->builder);
1217       dw->builder = NULL;
1218     }
1219
1220   if (dw->var_store)
1221     {
1222       g_object_unref (dw->var_store);
1223       dw->var_store = NULL;
1224     }
1225
1226   if (dw->data_store)
1227     {
1228       g_object_unref (dw->data_store);
1229       dw->data_store = NULL;
1230     }
1231
1232   if (dw->ll.next != NULL)
1233     {
1234       ll_remove (&dw->ll);
1235       dw->ll.next = NULL;
1236     }
1237
1238   if (G_OBJECT_CLASS (parent_class)->dispose)
1239     G_OBJECT_CLASS (parent_class)->dispose (object);
1240 }
1241
1242 static void
1243 psppire_data_window_finalize (GObject *object)
1244 {
1245   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1246
1247   if (dw->dataset)
1248     {
1249       struct dataset *dataset = dw->dataset;
1250       struct session *session = dataset_session (dataset);
1251
1252       dw->dataset = NULL;
1253
1254       dataset_set_callbacks (dataset, NULL, NULL);
1255       session_set_active_dataset (session, NULL);
1256       dataset_destroy (dataset);
1257     }
1258
1259   if (G_OBJECT_CLASS (parent_class)->finalize)
1260     G_OBJECT_CLASS (parent_class)->finalize (object);
1261 }
1262
1263 static void
1264 psppire_data_window_set_property (GObject         *object,
1265                                   guint            prop_id,
1266                                   const GValue    *value,
1267                                   GParamSpec      *pspec)
1268 {
1269   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1270
1271   switch (prop_id)
1272     {
1273     case PROP_DATASET:
1274       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1275       break;
1276     default:
1277       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1278       break;
1279     };
1280 }
1281
1282 static void
1283 psppire_data_window_get_property (GObject         *object,
1284                                   guint            prop_id,
1285                                   GValue          *value,
1286                                   GParamSpec      *pspec)
1287 {
1288   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1289
1290   switch (prop_id)
1291     {
1292     case PROP_DATASET:
1293       g_value_set_pointer (value, window->dataset);
1294       break;
1295     default:
1296       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1297       break;
1298     };
1299 }
1300
1301 GtkWidget*
1302 psppire_data_window_new (struct dataset *ds)
1303 {
1304   GtkWidget *dw;
1305
1306   if (the_session == NULL)
1307     the_session = session_create ();
1308
1309   if (ds == NULL)
1310     {
1311       char *dataset_name = session_generate_dataset_name (the_session);
1312       ds = dataset_create (the_session, dataset_name);
1313       free (dataset_name);
1314     }
1315   assert (dataset_session (ds) == the_session);
1316
1317   dw = GTK_WIDGET (
1318     g_object_new (
1319       psppire_data_window_get_type (),
1320       "description", _("Data Editor"),
1321       "dataset", ds,
1322       NULL));
1323
1324   if (dataset_name (ds) != NULL)
1325     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1326
1327   return dw;
1328 }
1329
1330 bool
1331 psppire_data_window_is_empty (PsppireDataWindow *dw)
1332 {
1333   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1334 }
1335
1336 static void
1337 psppire_data_window_iface_init (PsppireWindowIface *iface)
1338 {
1339   iface->save = save_file;
1340   iface->pick_filename = data_pick_filename;
1341   iface->load = load_file;
1342 }
1343 \f
1344 PsppireDataWindow *
1345 psppire_default_data_window (void)
1346 {
1347   if (ll_is_empty (&all_data_windows))
1348     create_data_window ();
1349   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1350 }
1351
1352 void
1353 psppire_data_window_set_default (PsppireDataWindow *pdw)
1354 {
1355   ll_remove (&pdw->ll);
1356   ll_push_head (&all_data_windows, &pdw->ll);
1357 }
1358
1359 void
1360 psppire_data_window_undefault (PsppireDataWindow *pdw)
1361 {
1362   ll_remove (&pdw->ll);
1363   ll_push_tail (&all_data_windows, &pdw->ll);
1364 }
1365
1366 PsppireDataWindow *
1367 psppire_data_window_for_dataset (struct dataset *ds)
1368 {
1369   PsppireDataWindow *pdw;
1370
1371   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1372     if (pdw->dataset == ds)
1373       return pdw;
1374
1375   return NULL;
1376 }
1377
1378 void
1379 create_data_window (void)
1380 {
1381   gtk_widget_show (psppire_data_window_new (NULL));
1382 }
1383
1384 void
1385 open_data_window (PsppireWindow *victim, const char *file_name)
1386 {
1387   GtkWidget *window;
1388
1389   if (PSPPIRE_IS_DATA_WINDOW (victim)
1390       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1391     window = GTK_WIDGET (victim);
1392   else
1393     window = psppire_data_window_new (NULL);
1394
1395   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1396   gtk_widget_show (window);
1397 }