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