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