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