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