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