psppire-data-window: Destroy dataset from finalize.
[pspp] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <gtk/gtk.h>
20 #include <stdlib.h>
21
22 #include "data/dataset.h"
23 #include "data/session.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "libpspp/str.h"
27 #include "ui/gui/aggregate-dialog.h"
28 #include "ui/gui/autorecode-dialog.h"
29 #include "ui/gui/binomial-dialog.h"
30 #include "ui/gui/builder-wrapper.h"
31 #include "ui/gui/chi-square-dialog.h"
32 #include "ui/gui/comments-dialog.h"
33 #include "ui/gui/compute-dialog.h"
34 #include "ui/gui/count-dialog.h"
35 #include "ui/gui/crosstabs-dialog.h"
36 #include "ui/gui/entry-dialog.h"
37 #include "ui/gui/executor.h"
38 #include "ui/gui/find-dialog.h"
39 #include "ui/gui/frequencies-dialog.h"
40 #include "ui/gui/goto-case-dialog.h"
41 #include "ui/gui/help-menu.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/helper.h"
44 #include "ui/gui/k-related-dialog.h"
45 #include "ui/gui/npar-two-sample-related.h"
46 #include "ui/gui/oneway-anova-dialog.h"
47 #include "ui/gui/psppire-data-window.h"
48 #include "ui/gui/psppire-syntax-window.h"
49 #include "ui/gui/psppire-window.h"
50 #include "ui/gui/psppire.h"
51 #include "ui/gui/runs-dialog.h"
52 #include "ui/gui/ks-one-sample-dialog.h"
53 #include "ui/gui/recode-dialog.h"
54 #include "ui/gui/select-cases-dialog.h"
55 #include "ui/gui/split-file-dialog.h"
56 #include "ui/gui/t-test-one-sample.h"
57 #include "ui/gui/t-test-paired-samples.h"
58 #include "ui/gui/text-data-import-dialog.h"
59 #include "ui/gui/transpose-dialog.h"
60 #include "ui/gui/univariate-dialog.h"
61 #include "ui/gui/weight-cases-dialog.h"
62 #include "ui/syntax-gen.h"
63
64 #include "gl/c-strcase.h"
65 #include "gl/c-strcasestr.h"
66 #include "gl/xvasprintf.h"
67
68 #include <gettext.h>
69 #define _(msgid) gettext (msgid)
70 #define N_(msgid) msgid
71
72 struct session *the_session;
73 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
74
75 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
76 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
77
78
79 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
80
81 static void psppire_data_window_dispose (GObject *object);
82 static void psppire_data_window_finalize (GObject *object);
83 static void psppire_data_window_set_property (GObject         *object,
84                                               guint            prop_id,
85                                               const GValue    *value,
86                                               GParamSpec      *pspec);
87 static void psppire_data_window_get_property (GObject         *object,
88                                               guint            prop_id,
89                                               GValue          *value,
90                                               GParamSpec      *pspec);
91
92 GType
93 psppire_data_window_get_type (void)
94 {
95   static GType psppire_data_window_type = 0;
96
97   if (!psppire_data_window_type)
98     {
99       static const GTypeInfo psppire_data_window_info =
100         {
101           sizeof (PsppireDataWindowClass),
102           NULL,
103           NULL,
104           (GClassInitFunc)psppire_data_window_class_init,
105           (GClassFinalizeFunc) NULL,
106           NULL,
107           sizeof (PsppireDataWindow),
108           0,
109           (GInstanceInitFunc) psppire_data_window_init,
110         };
111
112       static const GInterfaceInfo window_interface_info =
113         {
114           (GInterfaceInitFunc) psppire_data_window_iface_init,
115           NULL,
116           NULL
117         };
118
119       psppire_data_window_type =
120         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
121                                 &psppire_data_window_info, 0);
122
123
124       g_type_add_interface_static (psppire_data_window_type,
125                                    PSPPIRE_TYPE_WINDOW_MODEL,
126                                    &window_interface_info);
127     }
128
129   return psppire_data_window_type;
130 }
131
132 static GObjectClass *parent_class ;
133
134 enum {
135     PROP_DATASET = 1
136 };
137
138 static void
139 psppire_data_window_class_init (PsppireDataWindowClass *class)
140 {
141   GObjectClass *object_class = G_OBJECT_CLASS (class);
142
143   parent_class = g_type_class_peek_parent (class);
144
145   object_class->dispose = psppire_data_window_dispose;
146   object_class->finalize = psppire_data_window_finalize;
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   g_object_unref (dict);
957   de->data_store = psppire_data_store_new (dict);
958   psppire_data_store_set_reader (de->data_store, NULL);
959
960   menubar = get_widget_assert (de->builder, "menubar");
961   hb = get_widget_assert (de->builder, "handlebox1");
962   sb = get_widget_assert (de->builder, "status-bar");
963
964   de->data_editor =
965     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
966                                                   de->data_store));
967
968   g_signal_connect_swapped (de->data_store, "case-changed",
969                             G_CALLBACK (set_unsaved), de);
970
971   g_signal_connect_swapped (de->data_store, "case-inserted",
972                             G_CALLBACK (set_unsaved), de);
973
974   g_signal_connect_swapped (de->data_store, "cases-deleted",
975                             G_CALLBACK (set_unsaved), de);
976
977   dataset_set_callbacks (de->dataset, &cbs, de);
978
979   connect_help (de->builder);
980
981   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
982   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
983   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
984   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
985
986   gtk_container_add (GTK_CONTAINER (de), box);
987
988   set_cut_copy_menuitem_sensitivity (de, FALSE);
989
990   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
991                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
992
993
994   set_paste_menuitem_sensitivity (de, FALSE);
995
996   g_signal_connect_swapped (de->data_editor, "data-available-changed",
997                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
998
999   g_signal_connect (dict, "weight-changed",
1000                     G_CALLBACK (on_weight_change),
1001                     de);
1002
1003   g_signal_connect (dict, "filter-changed",
1004                     G_CALLBACK (on_filter_change),
1005                     de);
1006
1007   g_signal_connect (dict, "split-changed",
1008                     G_CALLBACK (on_split_change),
1009                     de);
1010
1011
1012   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1013
1014   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1015
1016   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1017
1018   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1019
1020   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1021  
1022   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1023
1024   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1025
1026   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1027
1028   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1029
1030   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1031
1032   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1033
1034   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1035
1036   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1037
1038   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1039
1040   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1041
1042   {
1043     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1044
1045     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1046
1047     gtk_action_set_visible (de->delete_cases, FALSE);
1048   }
1049
1050
1051   {
1052     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1053
1054     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1055
1056     gtk_action_set_visible (de->delete_variables, FALSE);
1057   }
1058
1059
1060   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1061   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1062   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1063   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1064   connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1065   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1066   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1067   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1068   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_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, "univariate", G_CALLBACK (univariate_dialog));
1078   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1079   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1080   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1081   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1082   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1083   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1084
1085   {
1086     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1087
1088     GtkWidget *recent_data =
1089       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1090
1091     GtkWidget *recent_files =
1092       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1093
1094
1095     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1096       gtk_recent_manager_get_default ());
1097
1098     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1099       gtk_recent_manager_get_default ());
1100
1101     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1102     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1103
1104     {
1105       GtkRecentFilter *filter = gtk_recent_filter_new ();
1106
1107       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1108       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1109
1110       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1111
1112       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1113     }
1114
1115     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1116
1117
1118     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1119
1120     {
1121       GtkRecentFilter *filter = gtk_recent_filter_new ();
1122
1123       gtk_recent_filter_add_pattern (filter, "*.sps");
1124       gtk_recent_filter_add_pattern (filter, "*.SPS");
1125
1126       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1127
1128       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1129     }
1130
1131     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1132
1133     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1134
1135   }
1136
1137   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1138
1139
1140   g_signal_connect (de->data_editor,
1141                     "cases-selected",
1142                     G_CALLBACK (enable_delete_cases),
1143                     de);
1144
1145   g_signal_connect (de->data_editor,
1146                     "variables-selected",
1147                     G_CALLBACK (enable_delete_variables),
1148                     de);
1149
1150
1151   g_signal_connect (de->data_editor,
1152                     "switch-page",
1153                     G_CALLBACK (on_switch_sheet), de);
1154
1155   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1156   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1157
1158   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1159
1160   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1161
1162   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1163
1164   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1165
1166   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1167
1168   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1169
1170   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1171
1172   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1173
1174   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1175
1176   {
1177     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1178
1179     merge_help_menu (uim);
1180   }
1181
1182   {
1183     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1184                                                                 "datasheet-cases-popup");
1185
1186     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1187                                                                   "varsheet-variable-popup");
1188
1189     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1190                                                                    "datasheet-variable-popup");
1191
1192     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1193                               G_CALLBACK (psppire_data_editor_sort_ascending),
1194                               de->data_editor);
1195
1196     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1197                               G_CALLBACK (psppire_data_editor_sort_descending),
1198                               de->data_editor);
1199
1200     g_object_set (de->data_editor,
1201                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1202                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1203                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1204                   NULL);
1205   }
1206
1207   gtk_widget_show (GTK_WIDGET (de->data_editor));
1208   gtk_widget_show (box);
1209
1210   ll_push_head (&all_data_windows, &de->ll);
1211 }
1212
1213 static void
1214 psppire_data_window_dispose (GObject *object)
1215 {
1216   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1217
1218   if (dw->builder != NULL)
1219     {
1220       g_object_unref (dw->builder);
1221       dw->builder = NULL;
1222     }
1223
1224   if (dw->var_store)
1225     {
1226       g_object_unref (dw->var_store);
1227       dw->var_store = NULL;
1228     }
1229
1230   if (dw->data_store)
1231     {
1232       g_object_unref (dw->data_store);
1233       dw->data_store = NULL;
1234     }
1235
1236   if (dw->ll.next != NULL)
1237     {
1238       ll_remove (&dw->ll);
1239       dw->ll.next = NULL;
1240     }
1241
1242   if (G_OBJECT_CLASS (parent_class)->dispose)
1243     G_OBJECT_CLASS (parent_class)->dispose (object);
1244 }
1245
1246 static void
1247 psppire_data_window_finalize (GObject *object)
1248 {
1249   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1250
1251   if (dw->dataset)
1252     {
1253       struct dataset *dataset = dw->dataset;
1254       struct session *session = dataset_session (dataset);
1255
1256       dw->dataset = NULL;
1257
1258       dataset_set_callbacks (dataset, NULL, NULL);
1259       session_set_active_dataset (session, NULL);
1260       dataset_destroy (dataset);
1261     }
1262
1263   if (G_OBJECT_CLASS (parent_class)->finalize)
1264     G_OBJECT_CLASS (parent_class)->finalize (object);
1265 }
1266
1267 static void
1268 psppire_data_window_set_property (GObject         *object,
1269                                   guint            prop_id,
1270                                   const GValue    *value,
1271                                   GParamSpec      *pspec)
1272 {
1273   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1274
1275   switch (prop_id)
1276     {
1277     case PROP_DATASET:
1278       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1279       break;
1280     default:
1281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1282       break;
1283     };
1284 }
1285
1286 static void
1287 psppire_data_window_get_property (GObject         *object,
1288                                   guint            prop_id,
1289                                   GValue          *value,
1290                                   GParamSpec      *pspec)
1291 {
1292   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1293
1294   switch (prop_id)
1295     {
1296     case PROP_DATASET:
1297       g_value_set_pointer (value, window->dataset);
1298       break;
1299     default:
1300       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1301       break;
1302     };
1303 }
1304
1305 GtkWidget*
1306 psppire_data_window_new (struct dataset *ds)
1307 {
1308   GtkWidget *dw;
1309
1310   if (the_session == NULL)
1311     the_session = session_create ();
1312
1313   if (ds == NULL)
1314     {
1315       char *dataset_name = session_generate_dataset_name (the_session);
1316       ds = dataset_create (the_session, dataset_name);
1317       free (dataset_name);
1318     }
1319   assert (dataset_session (ds) == the_session);
1320
1321   dw = GTK_WIDGET (
1322     g_object_new (
1323       psppire_data_window_get_type (),
1324       "description", _("Data Editor"),
1325       "dataset", ds,
1326       NULL));
1327
1328   if (dataset_name (ds) != NULL)
1329     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1330
1331   return dw;
1332 }
1333
1334 bool
1335 psppire_data_window_is_empty (PsppireDataWindow *dw)
1336 {
1337   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1338 }
1339
1340 static void
1341 psppire_data_window_iface_init (PsppireWindowIface *iface)
1342 {
1343   iface->save = save_file;
1344   iface->pick_filename = data_pick_filename;
1345   iface->load = load_file;
1346 }
1347 \f
1348 PsppireDataWindow *
1349 psppire_default_data_window (void)
1350 {
1351   if (ll_is_empty (&all_data_windows))
1352     create_data_window ();
1353   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1354 }
1355
1356 void
1357 psppire_data_window_set_default (PsppireDataWindow *pdw)
1358 {
1359   ll_remove (&pdw->ll);
1360   ll_push_head (&all_data_windows, &pdw->ll);
1361 }
1362
1363 void
1364 psppire_data_window_undefault (PsppireDataWindow *pdw)
1365 {
1366   ll_remove (&pdw->ll);
1367   ll_push_tail (&all_data_windows, &pdw->ll);
1368 }
1369
1370 PsppireDataWindow *
1371 psppire_data_window_for_dataset (struct dataset *ds)
1372 {
1373   PsppireDataWindow *pdw;
1374
1375   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1376     if (pdw->dataset == ds)
1377       return pdw;
1378
1379   return NULL;
1380 }
1381
1382 void
1383 create_data_window (void)
1384 {
1385   gtk_widget_show (psppire_data_window_new (NULL));
1386 }
1387
1388 void
1389 open_data_window (PsppireWindow *victim, const char *file_name)
1390 {
1391   GtkWidget *window;
1392
1393   if (PSPPIRE_IS_DATA_WINDOW (victim)
1394       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1395     window = GTK_WIDGET (victim);
1396   else
1397     window = psppire_data_window_new (NULL);
1398
1399   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1400   gtk_widget_show (window);
1401 }