Converted Sort dialog to a PsppireDialogAction object
[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, 2012  Free Software Foundation
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <gtk/gtk.h>
20 #include <stdlib.h>
21
22 #include "data/dataset.h"
23 #include "data/session.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "libpspp/str.h"
27 #include "ui/gui/aggregate-dialog.h"
28 #include "ui/gui/autorecode-dialog.h"
29 #include "ui/gui/binomial-dialog.h"
30 #include "ui/gui/builder-wrapper.h"
31 #include "ui/gui/chi-square-dialog.h"
32 #include "ui/gui/comments-dialog.h"
33 #include "ui/gui/compute-dialog.h"
34 #include "ui/gui/count-dialog.h"
35 #include "ui/gui/crosstabs-dialog.h"
36 #include "ui/gui/entry-dialog.h"
37 #include "ui/gui/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/helper.h"
46 #include "ui/gui/k-related-dialog.h"
47 #include "ui/gui/npar-two-sample-related.h"
48 #include "ui/gui/oneway-anova-dialog.h"
49 #include "ui/gui/psppire-data-window.h"
50 #include "ui/gui/psppire-syntax-window.h"
51 #include "ui/gui/psppire-window.h"
52 #include "ui/gui/psppire.h"
53 #include "ui/gui/rank-dialog.h"
54 #include "ui/gui/runs-dialog.h"
55 #include "ui/gui/ks-one-sample-dialog.h"
56 #include "ui/gui/recode-dialog.h"
57 #include "ui/gui/regression-dialog.h"
58 #include "ui/gui/select-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/univariate-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
391   add_most_recent (file_name, mime_type);
392
393   return ok;
394 }
395
396 /* Save DE to file */
397 static void
398 save_file (PsppireWindow *w)
399 {
400   const gchar *file_name = NULL;
401   gchar *utf8_file_name = NULL;
402   GString *fnx;
403   struct string filename ;
404   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
405   gchar *syntax;
406
407   file_name = psppire_window_get_filename (w);
408
409   fnx = g_string_new (file_name);
410
411   if ( ! name_has_suffix (fnx->str))
412     {
413       if ( de->save_as_portable)
414         g_string_append (fnx, ".por");
415       else
416         g_string_append (fnx, ".sav");
417     }
418
419   ds_init_empty (&filename);
420
421   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
422
423   g_string_free (fnx, TRUE);
424
425   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
426   g_free (utf8_file_name);
427
428   syntax = g_strdup_printf ("%s OUTFILE=%s.",
429                             de->save_as_portable ? "EXPORT" : "SAVE",
430                             ds_cstr (&filename));
431
432   ds_destroy (&filename);
433
434   g_free (execute_syntax_string (de, syntax));
435 }
436
437
438 static void
439 insert_case (PsppireDataWindow *dw)
440 {
441   psppire_data_editor_insert_case (dw->data_editor);
442 }
443
444 static void
445 on_insert_variable (PsppireDataWindow *dw)
446 {
447   psppire_data_editor_insert_variable (dw->data_editor);
448 }
449
450
451 static void
452 display_dict (PsppireDataWindow *de)
453 {
454   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
455 }
456
457 static void
458 sysfile_info (PsppireDataWindow *de)
459 {
460   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
461
462   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
463     {
464       struct string filename;
465       gchar *file_name =
466         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
467
468       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
469                                                   NULL);
470
471       gchar *syntax;
472
473       ds_init_empty (&filename);
474
475       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
476
477       g_free (utf8_file_name);
478
479       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
480       g_free (execute_syntax_string (de, syntax));
481     }
482
483   gtk_widget_destroy (dialog);
484 }
485
486
487 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
488 static void
489 data_pick_filename (PsppireWindow *window)
490 {
491   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
492   GtkFileFilter *filter = gtk_file_filter_new ();
493   GtkWidget *button_sys;
494   GtkWidget *dialog =
495     gtk_file_chooser_dialog_new (_("Save"),
496                                  GTK_WINDOW (de),
497                                  GTK_FILE_CHOOSER_ACTION_SAVE,
498                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
499                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
500                                  NULL);
501
502   g_object_set (dialog, "local-only", FALSE, NULL);
503
504   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
505   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
506   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
507
508   filter = gtk_file_filter_new ();
509   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
510   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
511   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
512
513   filter = gtk_file_filter_new ();
514   gtk_file_filter_set_name (filter, _("All Files"));
515   gtk_file_filter_add_pattern (filter, "*");
516   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
517
518   {
519     GtkWidget *button_por;
520     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
521     button_sys =
522       gtk_radio_button_new_with_label (NULL, _("System File"));
523
524     button_por =
525       gtk_radio_button_new_with_label
526       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
527        _("Portable File"));
528
529     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
530     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
531
532     gtk_widget_show_all (vbox);
533
534     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
535   }
536
537   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
538                                                   TRUE);
539
540   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
541     {
542     case GTK_RESPONSE_ACCEPT:
543       {
544         GString *filename =
545           g_string_new
546           (
547            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
548            );
549
550         de->save_as_portable =
551           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
552
553         if ( ! name_has_suffix (filename->str))
554           {
555             if ( de->save_as_portable)
556               g_string_append (filename, ".por");
557             else
558               g_string_append (filename, ".sav");
559           }
560
561         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
562
563         g_string_free (filename, TRUE);
564       }
565       break;
566     default:
567       break;
568     }
569
570   gtk_widget_destroy (dialog);
571 }
572
573 static bool
574 confirm_delete_dataset (PsppireDataWindow *de,
575                         const char *old_dataset,
576                         const char *new_dataset,
577                         const char *existing_dataset)
578 {
579   GtkWidget *dialog;
580   int result;
581
582   dialog = gtk_message_dialog_new (
583     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
584     _("Delete Existing Dataset?"));
585
586   gtk_message_dialog_format_secondary_text (
587     GTK_MESSAGE_DIALOG (dialog),
588     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
589       "dataset named \"%s\".  Are you sure that you want to do this?"),
590     old_dataset, new_dataset, existing_dataset);
591
592   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
593                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
594                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
595                           NULL);
596
597   g_object_set (dialog, "icon-name", "pspp", NULL);
598
599   result = gtk_dialog_run (GTK_DIALOG (dialog));
600
601   gtk_widget_destroy (dialog);
602
603   return result == GTK_RESPONSE_OK;
604 }
605
606 static void
607 on_rename_dataset (PsppireDataWindow *de)
608 {
609   struct dataset *ds = de->dataset;
610   struct session *session = dataset_session (ds);
611   const char *old_name = dataset_name (ds);
612   struct dataset *existing_dataset;
613   char *new_name;
614   char *prompt;
615
616   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
617                       old_name);
618   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
619                                old_name);
620   free (prompt);
621
622   if (new_name == NULL)
623     return;
624
625   existing_dataset = session_lookup_dataset (session, new_name);
626   if (existing_dataset == NULL || existing_dataset == ds
627       || confirm_delete_dataset (de, old_name, new_name,
628                                  dataset_name (existing_dataset)))
629     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
630                                                         new_name)));
631
632   free (new_name);
633 }
634
635 static void
636 on_edit_paste (PsppireDataWindow  *de)
637 {
638   psppire_data_editor_clip_paste (de->data_editor);
639 }
640
641 static void
642 on_edit_copy (PsppireDataWindow  *de)
643 {
644   psppire_data_editor_clip_copy (de->data_editor);
645 }
646
647
648
649 static void
650 on_edit_cut (PsppireDataWindow  *de)
651 {
652   psppire_data_editor_clip_cut (de->data_editor);
653 }
654
655
656 static void
657 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
658 {
659   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
660
661   if ( gtk_toggle_action_get_active (action))
662     gtk_widget_show (statusbar);
663   else
664     gtk_widget_hide (statusbar);
665 }
666
667
668 static void
669 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
670 {
671   const gboolean grid_visible = gtk_toggle_action_get_active (action);
672
673   psppire_data_editor_show_grid (de->data_editor, grid_visible);
674 }
675
676 static void
677 data_view_activate (PsppireDataWindow  *de)
678 {
679   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
680 }
681
682
683 static void
684 variable_view_activate (PsppireDataWindow  *de)
685 {
686   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
687 }
688
689
690 static void
691 fonts_activate (PsppireDataWindow  *de)
692 {
693   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
694   PangoFontDescription *current_font;
695   gchar *font_name;
696   GtkWidget *dialog =
697     gtk_font_selection_dialog_new (_("Font Selection"));
698
699
700   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
701   font_name = pango_font_description_to_string (current_font);
702
703   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
704
705   g_free (font_name);
706
707   gtk_window_set_transient_for (GTK_WINDOW (dialog),
708                                 GTK_WINDOW (toplevel));
709
710   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
711     {
712       const gchar *font = gtk_font_selection_dialog_get_font_name
713         (GTK_FONT_SELECTION_DIALOG (dialog));
714
715       PangoFontDescription* font_desc =
716         pango_font_description_from_string (font);
717
718       psppire_data_editor_set_font (de->data_editor, font_desc);
719     }
720
721   gtk_widget_hide (dialog);
722 }
723
724
725
726 /* Callback for the value labels action */
727 static void
728 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
729 {
730   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
731 }
732
733 static void
734 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
735 {
736   psppire_data_editor_split_window (de->data_editor,
737                                     gtk_toggle_action_get_active (ta));
738 }
739
740
741 static void
742 file_quit (PsppireDataWindow *de)
743 {
744   /* FIXME: Need to be more intelligent here.
745      Give the user the opportunity to save any unsaved data.
746   */
747   psppire_quit ();
748 }
749
750
751 static void
752 on_recent_data_select (GtkMenuShell *menushell,
753                        PsppireWindow *window)
754 {
755   gchar *file;
756
757   gchar *uri =
758     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
759
760   file = g_filename_from_uri (uri, NULL, NULL);
761
762   g_free (uri);
763
764   open_data_window (window, file);
765
766   g_free (file);
767 }
768
769 static char *
770 charset_from_mime_type (const char *mime_type)
771 {
772   const char *charset;
773   struct string s;
774   const char *p;
775
776   if (mime_type == NULL)
777     return NULL;
778
779   charset = c_strcasestr (mime_type, "charset=");
780   if (charset == NULL)
781     return NULL;
782
783   ds_init_empty (&s);
784   p = charset + 8;
785   if (*p == '"')
786     {
787       /* Parse a "quoted-string" as defined by RFC 822. */
788       for (p++; *p != '\0' && *p != '"'; p++)
789         {
790           if (*p != '\\')
791             ds_put_byte (&s, *p);
792           else if (*++p != '\0')
793             ds_put_byte (&s, *p);
794         }
795     }
796   else
797     {
798       /* Parse a "token" as defined by RFC 2045. */
799       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
800         ds_put_byte (&s, *p++);
801     }
802   if (!ds_is_empty (&s))
803     return ds_steal_cstr (&s);
804
805   ds_destroy (&s);
806   return NULL;
807 }
808
809 static void
810 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
811 {
812   GtkRecentInfo *item;
813   char *encoding;
814   GtkWidget *se;
815   gchar *file;
816
817   /* Get the file name and its encoding. */
818   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
819   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
820   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
821   gtk_recent_info_unref (item);
822
823   se = psppire_syntax_window_new (encoding);
824
825   free (encoding);
826
827   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
828     gtk_widget_show (se);
829   else
830     gtk_widget_destroy (se);
831
832   g_free (file);
833 }
834
835
836
837 static void
838 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
839 {
840   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
841
842   gtk_action_set_visible (de->delete_cases, case_num != -1);
843 }
844
845
846 static void
847 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
848 {
849   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
850
851   gtk_action_set_visible (de->delete_variables, var != -1);
852 }
853
854 /* Callback for when the datasheet/varsheet is selected */
855 static void
856 on_switch_sheet (GtkNotebook *notebook,
857                  GtkNotebookPage *page,
858                  guint page_num,
859                  gpointer user_data)
860 {
861   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
862
863   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
864
865   GtkWidget *view_data =
866     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
867
868   GtkWidget *view_variables =
869     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
870
871   switch (page_num)
872     {
873     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
874       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
875                                       TRUE);
876       gtk_action_set_sensitive (de->insert_variable, TRUE);
877       gtk_action_set_sensitive (de->insert_case, FALSE);
878       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
879       break;
880     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
881       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
882       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
883       gtk_action_set_sensitive (de->insert_case, TRUE);
884       break;
885     default:
886       g_assert_not_reached ();
887       break;
888     }
889
890 #if 0
891   update_paste_menuitem (de, page_num);
892 #endif
893 }
894
895
896
897 static void
898 set_unsaved (gpointer w)
899 {
900   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
901 }
902
903
904 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
905    Returns a pointer to the action
906 */
907 static GtkAction *
908 connect_action (PsppireDataWindow *dw, const char *action_name, 
909                                     GCallback handler)
910 {
911   GtkAction *action = get_action_assert (dw->builder, action_name);
912  
913   g_signal_connect_swapped (action, "activate", handler, dw);
914
915   return action;
916 }
917
918 /* Initializes as much of a PsppireDataWindow as we can and must before the
919    dataset has been set.
920
921    In particular, the 'menu' member is required in case the "filename" property
922    is set before the "dataset" property: otherwise PsppireWindow will try to
923    modify the menu as part of the "filename" property_set() function and end up
924    with a Gtk-CRITICAL since 'menu' is NULL.  */
925 static void
926 psppire_data_window_init (PsppireDataWindow *de)
927 {
928   GtkUIManager *uim;
929
930   de->builder = builder_new ("data-editor.ui");
931
932   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
933
934   PSPPIRE_WINDOW (de)->menu =
935     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
936 }
937
938 static void
939 psppire_data_window_finish_init (PsppireDataWindow *de,
940                                  struct dataset *ds)
941 {
942   static const struct dataset_callbacks cbs =
943     {
944       set_unsaved,                    /* changed */
945       transformation_change_callback, /* transformations_changed */
946     };
947
948   PsppireDict *dict;
949
950   GtkWidget *menubar;
951   GtkWidget *hb ;
952   GtkWidget *sb ;
953
954   GtkWidget *box = gtk_vbox_new (FALSE, 0);
955
956   de->dataset = ds;
957   dict = psppire_dict_new_from_dict (dataset_dict (ds));
958   de->var_store = psppire_var_store_new (dict);
959   de->data_store = psppire_data_store_new (dict);
960   psppire_data_store_set_reader (de->data_store, NULL);
961
962   menubar = get_widget_assert (de->builder, "menubar");
963   hb = get_widget_assert (de->builder, "handlebox1");
964   sb = get_widget_assert (de->builder, "status-bar");
965
966   de->data_editor =
967     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
968                                                   de->data_store));
969
970   g_signal_connect_swapped (de->data_store, "case-changed",
971                             G_CALLBACK (set_unsaved), de);
972
973   g_signal_connect_swapped (de->data_store, "case-inserted",
974                             G_CALLBACK (set_unsaved), de);
975
976   g_signal_connect_swapped (de->data_store, "cases-deleted",
977                             G_CALLBACK (set_unsaved), de);
978
979   dataset_set_callbacks (de->dataset, &cbs, de);
980
981   connect_help (de->builder);
982
983   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
984   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
985   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
986   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
987
988   gtk_container_add (GTK_CONTAINER (de), box);
989
990   set_cut_copy_menuitem_sensitivity (de, FALSE);
991
992   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
993                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
994
995
996   set_paste_menuitem_sensitivity (de, FALSE);
997
998   g_signal_connect_swapped (de->data_editor, "data-available-changed",
999                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1000
1001   g_signal_connect (dict, "weight-changed",
1002                     G_CALLBACK (on_weight_change),
1003                     de);
1004
1005   g_signal_connect (dict, "filter-changed",
1006                     G_CALLBACK (on_filter_change),
1007                     de);
1008
1009   g_signal_connect (dict, "split-changed",
1010                     G_CALLBACK (on_split_change),
1011                     de);
1012
1013
1014   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1015
1016   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1017
1018   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1019
1020   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1021
1022   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1023  
1024   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1025
1026   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1027
1028   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1029
1030   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1031
1032   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1033
1034   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1035
1036   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1037
1038   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1039
1040   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1041
1042   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1043
1044   {
1045     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1046
1047     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1048
1049     gtk_action_set_visible (de->delete_cases, FALSE);
1050   }
1051
1052
1053   {
1054     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1055
1056     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1057
1058     gtk_action_set_visible (de->delete_variables, FALSE);
1059   }
1060
1061
1062   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1063
1064   connect_action (de, "data_select-cases", G_CALLBACK (select_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, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1071
1072   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1073
1074   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1075
1076   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_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_frequencies", G_CALLBACK (frequencies_dialog));
1097  
1098   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1099  
1100   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1101  
1102   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1103  
1104   connect_action (de, "univariate", G_CALLBACK (univariate_dialog));
1105
1106   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1107
1108   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1109   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1110   connect_action (de, "runs", G_CALLBACK (runs_dialog));
1111   connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1112   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1113   connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1114  
1115
1116   {
1117     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1118
1119     GtkWidget *recent_data =
1120       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1121
1122     GtkWidget *recent_files =
1123       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1124
1125
1126     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1127       gtk_recent_manager_get_default ());
1128
1129     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1130       gtk_recent_manager_get_default ());
1131
1132     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1133     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1134
1135     {
1136       GtkRecentFilter *filter = gtk_recent_filter_new ();
1137
1138       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1139       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1140
1141       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1142
1143       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1144     }
1145
1146     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1147
1148
1149     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1150
1151     {
1152       GtkRecentFilter *filter = gtk_recent_filter_new ();
1153
1154       gtk_recent_filter_add_pattern (filter, "*.sps");
1155       gtk_recent_filter_add_pattern (filter, "*.SPS");
1156
1157       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1158
1159       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1160     }
1161
1162     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1163
1164     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1165
1166   }
1167
1168   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1169
1170
1171   g_signal_connect (de->data_editor,
1172                     "cases-selected",
1173                     G_CALLBACK (enable_delete_cases),
1174                     de);
1175
1176   g_signal_connect (de->data_editor,
1177                     "variables-selected",
1178                     G_CALLBACK (enable_delete_variables),
1179                     de);
1180
1181
1182   g_signal_connect (de->data_editor,
1183                     "switch-page",
1184                     G_CALLBACK (on_switch_sheet), de);
1185
1186   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1187   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1188
1189   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1190
1191   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1192
1193   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1194
1195   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1196
1197   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1198
1199   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1200
1201   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1202
1203   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1204
1205   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1206
1207   {
1208     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1209
1210     merge_help_menu (uim);
1211   }
1212
1213   {
1214     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1215                                                                 "datasheet-cases-popup");
1216
1217     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1218                                                                   "varsheet-variable-popup");
1219
1220     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1221                                                                    "datasheet-variable-popup");
1222
1223     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1224                               G_CALLBACK (psppire_data_editor_sort_ascending),
1225                               de->data_editor);
1226
1227     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1228                               G_CALLBACK (psppire_data_editor_sort_descending),
1229                               de->data_editor);
1230
1231     g_object_set (de->data_editor,
1232                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1233                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1234                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1235                   NULL);
1236   }
1237
1238   gtk_widget_show (GTK_WIDGET (de->data_editor));
1239   gtk_widget_show (box);
1240
1241   ll_push_head (&all_data_windows, &de->ll);
1242 }
1243
1244 static void
1245 psppire_data_window_dispose (GObject *object)
1246 {
1247   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1248
1249   if (dw->builder != NULL)
1250     {
1251       g_object_unref (dw->builder);
1252       dw->builder = NULL;
1253     }
1254
1255   if (dw->var_store)
1256     {
1257       g_object_unref (dw->var_store);
1258       dw->var_store = NULL;
1259     }
1260
1261   if (dw->data_store)
1262     {
1263       g_object_unref (dw->data_store);
1264       dw->data_store = NULL;
1265     }
1266
1267   if (dw->ll.next != NULL)
1268     {
1269       ll_remove (&dw->ll);
1270       dw->ll.next = NULL;
1271     }
1272
1273   if (G_OBJECT_CLASS (parent_class)->dispose)
1274     G_OBJECT_CLASS (parent_class)->dispose (object);
1275 }
1276
1277 static void
1278 psppire_data_window_set_property (GObject         *object,
1279                                   guint            prop_id,
1280                                   const GValue    *value,
1281                                   GParamSpec      *pspec)
1282 {
1283   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1284
1285   switch (prop_id)
1286     {
1287     case PROP_DATASET:
1288       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1289       break;
1290     default:
1291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1292       break;
1293     };
1294 }
1295
1296 static void
1297 psppire_data_window_get_property (GObject         *object,
1298                                   guint            prop_id,
1299                                   GValue          *value,
1300                                   GParamSpec      *pspec)
1301 {
1302   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1303
1304   switch (prop_id)
1305     {
1306     case PROP_DATASET:
1307       g_value_set_pointer (value, window->dataset);
1308       break;
1309     default:
1310       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1311       break;
1312     };
1313 }
1314
1315 GtkWidget*
1316 psppire_data_window_new (struct dataset *ds)
1317 {
1318   GtkWidget *dw;
1319
1320   if (the_session == NULL)
1321     the_session = session_create ();
1322
1323   if (ds == NULL)
1324     {
1325       static int n_datasets;
1326       char *dataset_name;
1327
1328       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1329       ds = dataset_create (the_session, dataset_name);
1330       free (dataset_name);
1331     }
1332   assert (dataset_session (ds) == the_session);
1333
1334   dw = GTK_WIDGET (
1335     g_object_new (
1336       psppire_data_window_get_type (),
1337       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1338       "description", _("Data Editor"),
1339       "dataset", ds,
1340       NULL));
1341
1342   if (dataset_name (ds) != NULL)
1343     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1344
1345   return dw;
1346 }
1347
1348 bool
1349 psppire_data_window_is_empty (PsppireDataWindow *dw)
1350 {
1351   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1352 }
1353
1354 static void
1355 psppire_data_window_iface_init (PsppireWindowIface *iface)
1356 {
1357   iface->save = save_file;
1358   iface->pick_filename = data_pick_filename;
1359   iface->load = load_file;
1360 }
1361 \f
1362 PsppireDataWindow *
1363 psppire_default_data_window (void)
1364 {
1365   if (ll_is_empty (&all_data_windows))
1366     create_data_window ();
1367   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1368 }
1369
1370 void
1371 psppire_data_window_set_default (PsppireDataWindow *pdw)
1372 {
1373   ll_remove (&pdw->ll);
1374   ll_push_head (&all_data_windows, &pdw->ll);
1375 }
1376
1377 void
1378 psppire_data_window_undefault (PsppireDataWindow *pdw)
1379 {
1380   ll_remove (&pdw->ll);
1381   ll_push_tail (&all_data_windows, &pdw->ll);
1382 }
1383
1384 PsppireDataWindow *
1385 psppire_data_window_for_dataset (struct dataset *ds)
1386 {
1387   PsppireDataWindow *pdw;
1388
1389   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1390     if (pdw->dataset == ds)
1391       return pdw;
1392
1393   return NULL;
1394 }
1395
1396 void
1397 create_data_window (void)
1398 {
1399   gtk_widget_show (psppire_data_window_new (NULL));
1400 }
1401
1402 void
1403 open_data_window (PsppireWindow *victim, const char *file_name)
1404 {
1405   GtkWidget *window;
1406
1407   if (PSPPIRE_IS_DATA_WINDOW (victim)
1408       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1409     window = GTK_WIDGET (victim);
1410   else
1411     window = psppire_data_window_new (NULL);
1412
1413   psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1414   gtk_widget_show (window);
1415 }