Added COUNT dialog box. Addresses one part of bug #29869
[pspp-builds.git] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011  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/binomial-dialog.h"
29 #include "ui/gui/chi-square-dialog.h"
30 #include "ui/gui/comments-dialog.h"
31 #include "ui/gui/compute-dialog.h"
32 #include "ui/gui/correlation-dialog.h"
33 #include "ui/gui/count-dialog.h"
34 #include "ui/gui/crosstabs-dialog.h"
35 #include "ui/gui/descriptives-dialog.h"
36 #include "ui/gui/entry-dialog.h"
37 #include "ui/gui/examine-dialog.h"
38 #include "ui/gui/executor.h"
39 #include "ui/gui/factor-dialog.h"
40 #include "ui/gui/find-dialog.h"
41 #include "ui/gui/frequencies-dialog.h"
42 #include "ui/gui/goto-case-dialog.h"
43 #include "ui/gui/help-menu.h"
44 #include "ui/gui/helper.h"
45 #include "ui/gui/k-related-dialog.h"
46 #include "ui/gui/oneway-anova-dialog.h"
47 #include "ui/gui/psppire-data-window.h"
48 #include "ui/gui/psppire-syntax-window.h"
49 #include "ui/gui/psppire-window.h"
50 #include "ui/gui/psppire.h"
51 #include "ui/gui/rank-dialog.h"
52 #include "ui/gui/recode-dialog.h"
53 #include "ui/gui/regression-dialog.h"
54 #include "ui/gui/reliability-dialog.h"
55 #include "ui/gui/roc-dialog.h"
56 #include "ui/gui/select-cases-dialog.h"
57 #include "ui/gui/sort-cases-dialog.h"
58 #include "ui/gui/split-file-dialog.h"
59 #include "ui/gui/t-test-independent-samples-dialog.h"
60 #include "ui/gui/t-test-one-sample.h"
61 #include "ui/gui/t-test-paired-samples.h"
62 #include "ui/gui/text-data-import-dialog.h"
63 #include "ui/gui/transpose-dialog.h"
64 #include "ui/gui/variable-info-dialog.h"
65 #include "ui/gui/weight-cases-dialog.h"
66 #include "ui/syntax-gen.h"
67
68 #include "gl/c-strcase.h"
69 #include "gl/c-strcasestr.h"
70 #include "gl/xvasprintf.h"
71
72 #include <gettext.h>
73 #define _(msgid) gettext (msgid)
74 #define N_(msgid) msgid
75
76 struct session *the_session;
77 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
78
79 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
80 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
81
82
83 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
84
85 static void psppire_data_window_dispose (GObject *object);
86 static void psppire_data_window_set_property (GObject         *object,
87                                               guint            prop_id,
88                                               const GValue    *value,
89                                               GParamSpec      *pspec);
90 static void psppire_data_window_get_property (GObject         *object,
91                                               guint            prop_id,
92                                               GValue          *value,
93                                               GParamSpec      *pspec);
94
95 GType
96 psppire_data_window_get_type (void)
97 {
98   static GType psppire_data_window_type = 0;
99
100   if (!psppire_data_window_type)
101     {
102       static const GTypeInfo psppire_data_window_info =
103         {
104           sizeof (PsppireDataWindowClass),
105           NULL,
106           NULL,
107           (GClassInitFunc)psppire_data_window_class_init,
108           (GClassFinalizeFunc) NULL,
109           NULL,
110           sizeof (PsppireDataWindow),
111           0,
112           (GInstanceInitFunc) psppire_data_window_init,
113         };
114
115       static const GInterfaceInfo window_interface_info =
116         {
117           (GInterfaceInitFunc) psppire_data_window_iface_init,
118           NULL,
119           NULL
120         };
121
122       psppire_data_window_type =
123         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
124                                 &psppire_data_window_info, 0);
125
126
127       g_type_add_interface_static (psppire_data_window_type,
128                                    PSPPIRE_TYPE_WINDOW_MODEL,
129                                    &window_interface_info);
130     }
131
132   return psppire_data_window_type;
133 }
134
135 static GObjectClass *parent_class ;
136
137 enum {
138     PROP_DATASET = 1
139 };
140
141 static void
142 psppire_data_window_class_init (PsppireDataWindowClass *class)
143 {
144   GObjectClass *object_class = G_OBJECT_CLASS (class);
145
146   parent_class = g_type_class_peek_parent (class);
147
148   object_class->dispose = psppire_data_window_dispose;
149   object_class->set_property = psppire_data_window_set_property;
150   object_class->get_property = psppire_data_window_get_property;
151
152   g_object_class_install_property (
153     object_class, PROP_DATASET,
154     g_param_spec_pointer ("dataset", "Dataset",
155                           "'struct datset *' represented by the window",
156                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
157 }
158 \f
159 static void
160 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
161 {
162   GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
163
164   gtk_action_set_sensitive (edit_paste, x);
165 }
166
167 static void
168 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
169 {
170   GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
171   GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
172
173   gtk_action_set_sensitive (edit_copy, x);
174   gtk_action_set_sensitive (edit_cut, x);
175 }
176
177 /* Run the EXECUTE command. */
178 static void
179 execute (PsppireDataWindow *dw)
180 {
181   execute_const_syntax_string (dw, "EXECUTE.");
182 }
183
184 static void
185 transformation_change_callback (bool transformations_pending,
186                                 gpointer data)
187 {
188   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
189
190   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
191
192   GtkWidget *menuitem =
193     gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
194
195   GtkWidget *status_label  =
196     get_widget_assert (de->builder, "case-counter-area");
197
198   gtk_widget_set_sensitive (menuitem, transformations_pending);
199
200
201   if ( transformations_pending)
202     gtk_label_set_text (GTK_LABEL (status_label),
203                         _("Transformations Pending"));
204   else
205     gtk_label_set_text (GTK_LABEL (status_label), "");
206 }
207
208 /* Callback for when the dictionary changes its filter variable */
209 static void
210 on_filter_change (GObject *o, gint filter_index, gpointer data)
211 {
212   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
213
214   GtkWidget *filter_status_area =
215     get_widget_assert (de->builder, "filter-use-status-area");
216
217   if ( filter_index == -1 )
218     {
219       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
220     }
221   else
222     {
223       PsppireVarStore *vs = NULL;
224       PsppireDict *dict = NULL;
225       struct variable *var ;
226       gchar *text ;
227
228       g_object_get (de->data_editor, "var-store", &vs, NULL);
229       g_object_get (vs, "dictionary", &dict, NULL);
230
231       var = psppire_dict_get_variable (dict, filter_index);
232
233       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
234
235       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
236
237       g_free (text);
238     }
239 }
240
241 /* Callback for when the dictionary changes its split variables */
242 static void
243 on_split_change (PsppireDict *dict, gpointer data)
244 {
245   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
246
247   size_t n_split_vars = dict_get_split_cnt (dict->dict);
248
249   GtkWidget *split_status_area =
250     get_widget_assert (de->builder, "split-file-status-area");
251
252   if ( n_split_vars == 0 )
253     {
254       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
255     }
256   else
257     {
258       gint i;
259       GString *text;
260       const struct variable *const * split_vars =
261         dict_get_split_vars (dict->dict);
262
263       text = g_string_new (_("Split by "));
264
265       for (i = 0 ; i < n_split_vars - 1; ++i )
266         {
267           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
268         }
269       g_string_append (text, var_get_name (split_vars[i]));
270
271       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
272
273       g_string_free (text, TRUE);
274     }
275 }
276
277
278
279
280 /* Callback for when the dictionary changes its weights */
281 static void
282 on_weight_change (GObject *o, gint weight_index, gpointer data)
283 {
284   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
285
286   GtkWidget *weight_status_area =
287     get_widget_assert (de->builder, "weight-status-area");
288
289   if ( weight_index == -1 )
290     {
291       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
292     }
293   else
294     {
295       struct variable *var ;
296       PsppireVarStore *vs = NULL;
297       PsppireDict *dict = NULL;
298       gchar *text;
299
300       g_object_get (de->data_editor, "var-store", &vs, NULL);
301       g_object_get (vs, "dictionary", &dict, NULL);
302
303       var = psppire_dict_get_variable (dict, weight_index);
304
305       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
306
307       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
308
309       g_free (text);
310     }
311 }
312
313 #if 0
314 static void
315 dump_rm (GtkRecentManager *rm)
316 {
317   GList *items = gtk_recent_manager_get_items (rm);
318
319   GList *i;
320
321   g_print ("Recent Items:\n");
322   for (i = items; i; i = i->next)
323     {
324       GtkRecentInfo *ri = i->data;
325
326       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
327                gtk_recent_info_get_short_name (ri),
328                gtk_recent_info_get_mime_type (ri),
329                gtk_recent_info_get_description (ri),
330                gtk_recent_info_get_uri (ri)
331                );
332
333
334       gtk_recent_info_unref (ri);
335     }
336
337   g_list_free (items);
338 }
339 #endif
340
341 static gboolean
342 name_has_por_suffix (const gchar *name)
343 {
344   size_t length = strlen (name);
345   return length > 4 && !c_strcasecmp (&name[length - 4], ".por");
346 }
347
348 static gboolean
349 name_has_sav_suffix (const gchar *name)
350 {
351   size_t length = strlen (name);
352   return length > 4 && !c_strcasecmp (&name[length - 4], ".sav");
353 }
354
355 /* Returns true if NAME has a suffix which might denote a PSPP file */
356 static gboolean
357 name_has_suffix (const gchar *name)
358 {
359   return name_has_por_suffix (name) || name_has_sav_suffix (name);
360 }
361
362 static gboolean
363 load_file (PsppireWindow *de, const gchar *file_name)
364 {
365   struct string filename;
366   gchar *utf8_file_name;
367   const char *mime_type;
368   gchar *syntax;
369   bool ok;
370
371   ds_init_empty (&filename);
372
373   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
374
375   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
376
377   g_free (utf8_file_name);
378
379   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
380   ds_destroy (&filename);
381
382   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
383                        lex_reader_for_string (syntax));
384   g_free (syntax);
385
386   mime_type = (name_has_por_suffix (file_name)
387                ? "application/x-spss-por"
388                : "application/x-spss-sav");
389   add_most_recent (ds_cstr (&filename), 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   GtkWidget *button_sys;
491   GtkWidget *dialog =
492     gtk_file_chooser_dialog_new (_("Save"),
493                                  GTK_WINDOW (de),
494                                  GTK_FILE_CHOOSER_ACTION_SAVE,
495                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
496                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
497                                  NULL);
498
499   GtkFileFilter *filter = gtk_file_filter_new ();
500   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
501   gtk_file_filter_add_pattern (filter, "*.sav");
502   gtk_file_filter_add_pattern (filter, "*.SAV");
503   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
504
505   filter = gtk_file_filter_new ();
506   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
507   gtk_file_filter_add_pattern (filter, "*.por");
508   gtk_file_filter_add_pattern (filter, "*.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 delete 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", "psppicon", 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 static void
835 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
836 {
837   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
838
839   gtk_action_set_visible (de->delete_cases, case_num != -1);
840 }
841
842
843 static void
844 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
845 {
846   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
847
848   gtk_action_set_visible (de->delete_variables, var != -1);
849 }
850
851 /* Callback for when the datasheet/varsheet is selected */
852 static void
853 on_switch_sheet (GtkNotebook *notebook,
854                  GtkNotebookPage *page,
855                  guint page_num,
856                  gpointer user_data)
857 {
858   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
859
860   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
861
862   GtkWidget *view_data =
863     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
864
865   GtkWidget *view_variables =
866     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
867
868   switch (page_num)
869     {
870     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
871       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
872                                       TRUE);
873       gtk_action_set_sensitive (de->insert_variable, TRUE);
874       gtk_action_set_sensitive (de->insert_case, FALSE);
875       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
876       break;
877     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
878       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
879       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
880       gtk_action_set_sensitive (de->insert_case, TRUE);
881       break;
882     default:
883       g_assert_not_reached ();
884       break;
885     }
886
887 #if 0
888   update_paste_menuitem (de, page_num);
889 #endif
890 }
891
892
893
894 static void
895 set_unsaved (gpointer w)
896 {
897   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
898 }
899
900
901 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
902    Returns a pointer to the action
903 */
904 static GtkAction *
905 connect_action (PsppireDataWindow *dw, const char *action_name, 
906                                     GCallback handler)
907 {
908   GtkAction *action = get_action_assert (dw->builder, action_name);
909  
910   g_signal_connect_swapped (action, "activate", handler, dw);
911
912   return action;
913 }
914
915 /* Initializes as much of a PsppireDataWindow as we can and must before the
916    dataset has been set.
917
918    In particular, the 'menu' member is required in case the "filename" property
919    is set before the "dataset" property: otherwise PsppireWindow will try to
920    modify the menu as part of the "filename" property_set() function and end up
921    with a Gtk-CRITICAL since 'menu' is NULL.  */
922 static void
923 psppire_data_window_init (PsppireDataWindow *de)
924 {
925   GtkUIManager *uim;
926
927   de->builder = builder_new ("data-editor.ui");
928
929   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
930
931   PSPPIRE_WINDOW (de)->menu =
932     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
933 }
934
935 static void
936 psppire_data_window_finish_init (PsppireDataWindow *de,
937                                  struct dataset *ds)
938 {
939   static const struct dataset_callbacks cbs =
940     {
941       set_unsaved,                    /* changed */
942       transformation_change_callback, /* transformations_changed */
943     };
944
945   PsppireDict *dict;
946
947   GtkWidget *menubar;
948   GtkWidget *hb ;
949   GtkWidget *sb ;
950
951   GtkWidget *box = gtk_vbox_new (FALSE, 0);
952
953   de->dataset = ds;
954   dict = psppire_dict_new_from_dict (dataset_dict (ds));
955   de->var_store = psppire_var_store_new (dict);
956   de->data_store = psppire_data_store_new (dict);
957   psppire_data_store_set_reader (de->data_store, NULL);
958
959   menubar = get_widget_assert (de->builder, "menubar");
960   hb = get_widget_assert (de->builder, "handlebox1");
961   sb = get_widget_assert (de->builder, "status-bar");
962
963   de->data_editor =
964     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
965                                                   de->data_store));
966
967   g_signal_connect_swapped (de->data_store, "case-changed",
968                             G_CALLBACK (set_unsaved), de);
969
970   g_signal_connect_swapped (de->data_store, "case-inserted",
971                             G_CALLBACK (set_unsaved), de);
972
973   g_signal_connect_swapped (de->data_store, "cases-deleted",
974                             G_CALLBACK (set_unsaved), de);
975
976   dataset_set_callbacks (de->dataset, &cbs, de);
977
978   connect_help (de->builder);
979
980   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
981   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
982   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
983   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
984
985   gtk_container_add (GTK_CONTAINER (de), box);
986
987   set_cut_copy_menuitem_sensitivity (de, FALSE);
988
989   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
990                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
991
992
993   set_paste_menuitem_sensitivity (de, FALSE);
994
995   g_signal_connect_swapped (de->data_editor, "data-available-changed",
996                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
997
998   g_signal_connect (dict, "weight-changed",
999                     G_CALLBACK (on_weight_change),
1000                     de);
1001
1002   g_signal_connect (dict, "filter-changed",
1003                     G_CALLBACK (on_filter_change),
1004                     de);
1005
1006   g_signal_connect (dict, "split-changed",
1007                     G_CALLBACK (on_split_change),
1008                     de);
1009
1010
1011   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1012
1013   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1014
1015   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1016
1017   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1018
1019   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1020  
1021   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1022
1023   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1024
1025   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1026
1027   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1028
1029   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1030
1031   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1032
1033   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1034
1035   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1036
1037   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1038
1039   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1040
1041   {
1042     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1043
1044     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1045
1046     gtk_action_set_visible (de->delete_cases, FALSE);
1047   }
1048
1049
1050   {
1051     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1052
1053     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1054
1055     gtk_action_set_visible (de->delete_variables, FALSE);
1056   }
1057
1058
1059   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1060
1061   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1062  
1063   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1064
1065   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1066
1067   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1068
1069   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1070
1071   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1072
1073   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1074
1075
1076   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1077  
1078   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1079
1080   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1081
1082   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1083
1084   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1085
1086   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1087  
1088   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1089
1090   connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1091  
1092   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1093  
1094   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1095
1096   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1097  
1098   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1099  
1100   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1101  
1102   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1103  
1104   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1105  
1106   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1107  
1108   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1109
1110   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1111  
1112   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1113
1114   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1115
1116   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1117
1118   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1119  
1120
1121   {
1122     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1123
1124     GtkWidget *recent_data =
1125       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1126
1127     GtkWidget *recent_files =
1128       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1129
1130
1131     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1132       gtk_recent_manager_get_default ());
1133
1134     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1135       gtk_recent_manager_get_default ());
1136
1137     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1138     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1139
1140     {
1141       GtkRecentFilter *filter = gtk_recent_filter_new ();
1142
1143       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1144       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1145
1146       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1147
1148       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1149     }
1150
1151     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1152
1153
1154     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1155
1156     {
1157       GtkRecentFilter *filter = gtk_recent_filter_new ();
1158
1159       gtk_recent_filter_add_pattern (filter, "*.sps");
1160       gtk_recent_filter_add_pattern (filter, "*.SPS");
1161
1162       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1163
1164       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1165     }
1166
1167     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1168
1169     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1170
1171   }
1172
1173   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1174
1175
1176   g_signal_connect (de->data_editor,
1177                     "cases-selected",
1178                     G_CALLBACK (enable_delete_cases),
1179                     de);
1180
1181   g_signal_connect (de->data_editor,
1182                     "variables-selected",
1183                     G_CALLBACK (enable_delete_variables),
1184                     de);
1185
1186
1187   g_signal_connect (de->data_editor,
1188                     "switch-page",
1189                     G_CALLBACK (on_switch_sheet), de);
1190
1191   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1192   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1193
1194   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1195
1196   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1197
1198   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1199
1200   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1201
1202   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1203
1204   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1205
1206   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1207
1208   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1209
1210   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1211
1212   {
1213     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1214
1215     merge_help_menu (uim);
1216   }
1217
1218   {
1219     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1220                                                                 "datasheet-cases-popup");
1221
1222     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1223                                                                   "varsheet-variable-popup");
1224
1225     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1226                                                                    "datasheet-variable-popup");
1227
1228     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1229                               G_CALLBACK (psppire_data_editor_sort_ascending),
1230                               de->data_editor);
1231
1232     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1233                               G_CALLBACK (psppire_data_editor_sort_descending),
1234                               de->data_editor);
1235
1236     g_object_set (de->data_editor,
1237                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1238                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1239                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1240                   NULL);
1241   }
1242
1243   gtk_widget_show (GTK_WIDGET (de->data_editor));
1244   gtk_widget_show (box);
1245
1246   ll_push_head (&all_data_windows, &de->ll);
1247 }
1248
1249 static void
1250 psppire_data_window_dispose (GObject *object)
1251 {
1252   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1253
1254   if (dw->builder != NULL)
1255     {
1256       g_object_unref (dw->builder);
1257       dw->builder = NULL;
1258     }
1259
1260   if (dw->var_store)
1261     {
1262       g_object_unref (dw->var_store);
1263       dw->var_store = NULL;
1264     }
1265
1266   if (dw->data_store)
1267     {
1268       g_object_unref (dw->data_store);
1269       dw->data_store = NULL;
1270     }
1271
1272   if (dw->ll.next != NULL)
1273     {
1274       ll_remove (&dw->ll);
1275       dw->ll.next = NULL;
1276     }
1277
1278   if (G_OBJECT_CLASS (parent_class)->dispose)
1279     G_OBJECT_CLASS (parent_class)->dispose (object);
1280 }
1281
1282 static void
1283 psppire_data_window_set_property (GObject         *object,
1284                                   guint            prop_id,
1285                                   const GValue    *value,
1286                                   GParamSpec      *pspec)
1287 {
1288   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1289
1290   switch (prop_id)
1291     {
1292     case PROP_DATASET:
1293       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1294       break;
1295     default:
1296       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1297       break;
1298     };
1299 }
1300
1301 static void
1302 psppire_data_window_get_property (GObject         *object,
1303                                   guint            prop_id,
1304                                   GValue          *value,
1305                                   GParamSpec      *pspec)
1306 {
1307   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1308
1309   switch (prop_id)
1310     {
1311     case PROP_DATASET:
1312       g_value_set_pointer (value, window->dataset);
1313       break;
1314     default:
1315       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1316       break;
1317     };
1318 }
1319
1320 GtkWidget*
1321 psppire_data_window_new (struct dataset *ds)
1322 {
1323   GtkWidget *dw;
1324
1325   if (the_session == NULL)
1326     the_session = session_create ();
1327
1328   if (ds == NULL)
1329     {
1330       static int n_datasets;
1331       char *dataset_name;
1332
1333       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1334       ds = dataset_create (the_session, dataset_name);
1335       free (dataset_name);
1336     }
1337   assert (dataset_session (ds) == the_session);
1338
1339   dw = GTK_WIDGET (
1340     g_object_new (
1341       psppire_data_window_get_type (),
1342       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1343       "description", _("Data Editor"),
1344       "dataset", ds,
1345       NULL));
1346
1347   if (dataset_name (ds) != NULL)
1348     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1349
1350   return dw;
1351 }
1352
1353 bool
1354 psppire_data_window_is_empty (PsppireDataWindow *dw)
1355 {
1356   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1357 }
1358
1359 static void
1360 psppire_data_window_iface_init (PsppireWindowIface *iface)
1361 {
1362   iface->save = save_file;
1363   iface->pick_filename = data_pick_filename;
1364   iface->load = load_file;
1365 }
1366 \f
1367 PsppireDataWindow *
1368 psppire_default_data_window (void)
1369 {
1370   if (ll_is_empty (&all_data_windows))
1371     create_data_window ();
1372   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1373 }
1374
1375 void
1376 psppire_data_window_set_default (PsppireDataWindow *pdw)
1377 {
1378   ll_remove (&pdw->ll);
1379   ll_push_head (&all_data_windows, &pdw->ll);
1380 }
1381
1382 void
1383 psppire_data_window_undefault (PsppireDataWindow *pdw)
1384 {
1385   ll_remove (&pdw->ll);
1386   ll_push_tail (&all_data_windows, &pdw->ll);
1387 }
1388
1389 PsppireDataWindow *
1390 psppire_data_window_for_dataset (struct dataset *ds)
1391 {
1392   PsppireDataWindow *pdw;
1393
1394   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1395     if (pdw->dataset == ds)
1396       return pdw;
1397
1398   return NULL;
1399 }
1400
1401 void
1402 create_data_window (void)
1403 {
1404   gtk_widget_show (psppire_data_window_new (NULL));
1405 }
1406
1407 void
1408 open_data_window (PsppireWindow *victim, const char *file_name)
1409 {
1410   GtkWidget *window;
1411
1412   if (PSPPIRE_IS_DATA_WINDOW (victim)
1413       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1414     window = GTK_WIDGET (victim);
1415   else
1416     window = psppire_data_window_new (NULL);
1417
1418   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1419   gtk_widget_show (window);
1420 }