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