Filter file choosers by mimetype instead of file name
[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/ks-one-sample-dialog.h"
57 #include "ui/gui/recode-dialog.h"
58 #include "ui/gui/regression-dialog.h"
59 #include "ui/gui/reliability-dialog.h"
60 #include "ui/gui/roc-dialog.h"
61 #include "ui/gui/select-cases-dialog.h"
62 #include "ui/gui/sort-cases-dialog.h"
63 #include "ui/gui/split-file-dialog.h"
64 #include "ui/gui/t-test-independent-samples-dialog.h"
65 #include "ui/gui/t-test-one-sample.h"
66 #include "ui/gui/t-test-paired-samples.h"
67 #include "ui/gui/text-data-import-dialog.h"
68 #include "ui/gui/transpose-dialog.h"
69 #include "ui/gui/variable-info-dialog.h"
70 #include "ui/gui/weight-cases-dialog.h"
71 #include "ui/syntax-gen.h"
72
73 #include "gl/c-strcase.h"
74 #include "gl/c-strcasestr.h"
75 #include "gl/xvasprintf.h"
76
77 #include <gettext.h>
78 #define _(msgid) gettext (msgid)
79 #define N_(msgid) msgid
80
81 struct session *the_session;
82 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
83
84 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
85 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
86
87
88 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
89
90 static void psppire_data_window_dispose (GObject *object);
91 static void psppire_data_window_set_property (GObject         *object,
92                                               guint            prop_id,
93                                               const GValue    *value,
94                                               GParamSpec      *pspec);
95 static void psppire_data_window_get_property (GObject         *object,
96                                               guint            prop_id,
97                                               GValue          *value,
98                                               GParamSpec      *pspec);
99
100 GType
101 psppire_data_window_get_type (void)
102 {
103   static GType psppire_data_window_type = 0;
104
105   if (!psppire_data_window_type)
106     {
107       static const GTypeInfo psppire_data_window_info =
108         {
109           sizeof (PsppireDataWindowClass),
110           NULL,
111           NULL,
112           (GClassInitFunc)psppire_data_window_class_init,
113           (GClassFinalizeFunc) NULL,
114           NULL,
115           sizeof (PsppireDataWindow),
116           0,
117           (GInstanceInitFunc) psppire_data_window_init,
118         };
119
120       static const GInterfaceInfo window_interface_info =
121         {
122           (GInterfaceInitFunc) psppire_data_window_iface_init,
123           NULL,
124           NULL
125         };
126
127       psppire_data_window_type =
128         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
129                                 &psppire_data_window_info, 0);
130
131
132       g_type_add_interface_static (psppire_data_window_type,
133                                    PSPPIRE_TYPE_WINDOW_MODEL,
134                                    &window_interface_info);
135     }
136
137   return psppire_data_window_type;
138 }
139
140 static GObjectClass *parent_class ;
141
142 enum {
143     PROP_DATASET = 1
144 };
145
146 static void
147 psppire_data_window_class_init (PsppireDataWindowClass *class)
148 {
149   GObjectClass *object_class = G_OBJECT_CLASS (class);
150
151   parent_class = g_type_class_peek_parent (class);
152
153   object_class->dispose = psppire_data_window_dispose;
154   object_class->set_property = psppire_data_window_set_property;
155   object_class->get_property = psppire_data_window_get_property;
156
157   g_object_class_install_property (
158     object_class, PROP_DATASET,
159     g_param_spec_pointer ("dataset", "Dataset",
160                           "'struct datset *' represented by the window",
161                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
162 }
163 \f
164 static void
165 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
166 {
167   GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
168
169   gtk_action_set_sensitive (edit_paste, x);
170 }
171
172 static void
173 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
174 {
175   GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
176   GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
177
178   gtk_action_set_sensitive (edit_copy, x);
179   gtk_action_set_sensitive (edit_cut, x);
180 }
181
182 /* Run the EXECUTE command. */
183 static void
184 execute (PsppireDataWindow *dw)
185 {
186   execute_const_syntax_string (dw, "EXECUTE.");
187 }
188
189 static void
190 transformation_change_callback (bool transformations_pending,
191                                 gpointer data)
192 {
193   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
194
195   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
196
197   GtkWidget *menuitem =
198     gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
199
200   GtkWidget *status_label  =
201     get_widget_assert (de->builder, "case-counter-area");
202
203   gtk_widget_set_sensitive (menuitem, transformations_pending);
204
205
206   if ( transformations_pending)
207     gtk_label_set_text (GTK_LABEL (status_label),
208                         _("Transformations Pending"));
209   else
210     gtk_label_set_text (GTK_LABEL (status_label), "");
211 }
212
213 /* Callback for when the dictionary changes its filter variable */
214 static void
215 on_filter_change (GObject *o, gint filter_index, gpointer data)
216 {
217   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
218
219   GtkWidget *filter_status_area =
220     get_widget_assert (de->builder, "filter-use-status-area");
221
222   if ( filter_index == -1 )
223     {
224       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
225     }
226   else
227     {
228       PsppireVarStore *vs = NULL;
229       PsppireDict *dict = NULL;
230       struct variable *var ;
231       gchar *text ;
232
233       g_object_get (de->data_editor, "var-store", &vs, NULL);
234       g_object_get (vs, "dictionary", &dict, NULL);
235
236       var = psppire_dict_get_variable (dict, filter_index);
237
238       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
239
240       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
241
242       g_free (text);
243     }
244 }
245
246 /* Callback for when the dictionary changes its split variables */
247 static void
248 on_split_change (PsppireDict *dict, gpointer data)
249 {
250   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
251
252   size_t n_split_vars = dict_get_split_cnt (dict->dict);
253
254   GtkWidget *split_status_area =
255     get_widget_assert (de->builder, "split-file-status-area");
256
257   if ( n_split_vars == 0 )
258     {
259       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
260     }
261   else
262     {
263       gint i;
264       GString *text;
265       const struct variable *const * split_vars =
266         dict_get_split_vars (dict->dict);
267
268       text = g_string_new (_("Split by "));
269
270       for (i = 0 ; i < n_split_vars - 1; ++i )
271         {
272           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
273         }
274       g_string_append (text, var_get_name (split_vars[i]));
275
276       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
277
278       g_string_free (text, TRUE);
279     }
280 }
281
282
283
284
285 /* Callback for when the dictionary changes its weights */
286 static void
287 on_weight_change (GObject *o, gint weight_index, gpointer data)
288 {
289   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
290
291   GtkWidget *weight_status_area =
292     get_widget_assert (de->builder, "weight-status-area");
293
294   if ( weight_index == -1 )
295     {
296       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
297     }
298   else
299     {
300       struct variable *var ;
301       PsppireVarStore *vs = NULL;
302       PsppireDict *dict = NULL;
303       gchar *text;
304
305       g_object_get (de->data_editor, "var-store", &vs, NULL);
306       g_object_get (vs, "dictionary", &dict, NULL);
307
308       var = psppire_dict_get_variable (dict, weight_index);
309
310       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
311
312       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
313
314       g_free (text);
315     }
316 }
317
318 #if 0
319 static void
320 dump_rm (GtkRecentManager *rm)
321 {
322   GList *items = gtk_recent_manager_get_items (rm);
323
324   GList *i;
325
326   g_print ("Recent Items:\n");
327   for (i = items; i; i = i->next)
328     {
329       GtkRecentInfo *ri = i->data;
330
331       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
332                gtk_recent_info_get_short_name (ri),
333                gtk_recent_info_get_mime_type (ri),
334                gtk_recent_info_get_description (ri),
335                gtk_recent_info_get_uri (ri)
336                );
337
338
339       gtk_recent_info_unref (ri);
340     }
341
342   g_list_free (items);
343 }
344 #endif
345
346 static gboolean
347 name_has_por_suffix (const gchar *name)
348 {
349   size_t length = strlen (name);
350   return length > 4 && !c_strcasecmp (&name[length - 4], ".por");
351 }
352
353 static gboolean
354 name_has_sav_suffix (const gchar *name)
355 {
356   size_t length = strlen (name);
357   return length > 4 && !c_strcasecmp (&name[length - 4], ".sav");
358 }
359
360 /* Returns true if NAME has a suffix which might denote a PSPP file */
361 static gboolean
362 name_has_suffix (const gchar *name)
363 {
364   return name_has_por_suffix (name) || name_has_sav_suffix (name);
365 }
366
367 static gboolean
368 load_file (PsppireWindow *de, const gchar *file_name)
369 {
370   struct string filename;
371   gchar *utf8_file_name;
372   const char *mime_type;
373   gchar *syntax;
374   bool ok;
375
376   ds_init_empty (&filename);
377
378   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
379
380   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
381
382   g_free (utf8_file_name);
383
384   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
385   ds_destroy (&filename);
386
387   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
388                        lex_reader_for_string (syntax));
389   g_free (syntax);
390
391   mime_type = (name_has_por_suffix (file_name)
392                ? "application/x-spss-por"
393                : "application/x-spss-sav");
394
395   add_most_recent (file_name, mime_type);
396
397   return ok;
398 }
399
400 /* Save DE to file */
401 static void
402 save_file (PsppireWindow *w)
403 {
404   const gchar *file_name = NULL;
405   gchar *utf8_file_name = NULL;
406   GString *fnx;
407   struct string filename ;
408   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
409   gchar *syntax;
410
411   file_name = psppire_window_get_filename (w);
412
413   fnx = g_string_new (file_name);
414
415   if ( ! name_has_suffix (fnx->str))
416     {
417       if ( de->save_as_portable)
418         g_string_append (fnx, ".por");
419       else
420         g_string_append (fnx, ".sav");
421     }
422
423   ds_init_empty (&filename);
424
425   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
426
427   g_string_free (fnx, TRUE);
428
429   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
430   g_free (utf8_file_name);
431
432   syntax = g_strdup_printf ("%s OUTFILE=%s.",
433                             de->save_as_portable ? "EXPORT" : "SAVE",
434                             ds_cstr (&filename));
435
436   ds_destroy (&filename);
437
438   g_free (execute_syntax_string (de, syntax));
439 }
440
441
442 static void
443 insert_case (PsppireDataWindow *dw)
444 {
445   psppire_data_editor_insert_case (dw->data_editor);
446 }
447
448 static void
449 on_insert_variable (PsppireDataWindow *dw)
450 {
451   psppire_data_editor_insert_variable (dw->data_editor);
452 }
453
454
455 static void
456 display_dict (PsppireDataWindow *de)
457 {
458   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
459 }
460
461 static void
462 sysfile_info (PsppireDataWindow *de)
463 {
464   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
465
466   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
467     {
468       struct string filename;
469       gchar *file_name =
470         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
471
472       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
473                                                   NULL);
474
475       gchar *syntax;
476
477       ds_init_empty (&filename);
478
479       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
480
481       g_free (utf8_file_name);
482
483       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
484       g_free (execute_syntax_string (de, syntax));
485     }
486
487   gtk_widget_destroy (dialog);
488 }
489
490
491 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
492 static void
493 data_pick_filename (PsppireWindow *window)
494 {
495   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
496   GtkFileFilter *filter = gtk_file_filter_new ();
497   GtkWidget *button_sys;
498   GtkWidget *dialog =
499     gtk_file_chooser_dialog_new (_("Save"),
500                                  GTK_WINDOW (de),
501                                  GTK_FILE_CHOOSER_ACTION_SAVE,
502                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
503                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
504                                  NULL);
505
506   g_object_set (dialog, "local-only", FALSE, NULL);
507
508   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
509   gtk_file_filter_add_mime_type (filter, "application/x-spss-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_mime_type (filter, "application/x-spss-por");
515   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516
517   filter = gtk_file_filter_new ();
518   gtk_file_filter_set_name (filter, _("All Files"));
519   gtk_file_filter_add_pattern (filter, "*");
520   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
521
522   {
523     GtkWidget *button_por;
524     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
525     button_sys =
526       gtk_radio_button_new_with_label (NULL, _("System File"));
527
528     button_por =
529       gtk_radio_button_new_with_label
530       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
531        _("Portable File"));
532
533     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
534     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
535
536     gtk_widget_show_all (vbox);
537
538     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
539   }
540
541   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
542                                                   TRUE);
543
544   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
545     {
546     case GTK_RESPONSE_ACCEPT:
547       {
548         GString *filename =
549           g_string_new
550           (
551            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
552            );
553
554         de->save_as_portable =
555           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
556
557         if ( ! name_has_suffix (filename->str))
558           {
559             if ( de->save_as_portable)
560               g_string_append (filename, ".por");
561             else
562               g_string_append (filename, ".sav");
563           }
564
565         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
566
567         g_string_free (filename, TRUE);
568       }
569       break;
570     default:
571       break;
572     }
573
574   gtk_widget_destroy (dialog);
575 }
576
577 static bool
578 confirm_delete_dataset (PsppireDataWindow *de,
579                         const char *old_dataset,
580                         const char *new_dataset,
581                         const char *existing_dataset)
582 {
583   GtkWidget *dialog;
584   int result;
585
586   dialog = gtk_message_dialog_new (
587     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
588     _("Delete Existing Dataset?"));
589
590   gtk_message_dialog_format_secondary_text (
591     GTK_MESSAGE_DIALOG (dialog),
592     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
593       "dataset named \"%s\".  Are you sure that you want to do this?"),
594     old_dataset, new_dataset, existing_dataset);
595
596   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
597                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
598                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
599                           NULL);
600
601   g_object_set (dialog, "icon-name", "pspp", NULL);
602
603   result = gtk_dialog_run (GTK_DIALOG (dialog));
604
605   gtk_widget_destroy (dialog);
606
607   return result == GTK_RESPONSE_OK;
608 }
609
610 static void
611 on_rename_dataset (PsppireDataWindow *de)
612 {
613   struct dataset *ds = de->dataset;
614   struct session *session = dataset_session (ds);
615   const char *old_name = dataset_name (ds);
616   struct dataset *existing_dataset;
617   char *new_name;
618   char *prompt;
619
620   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
621                       old_name);
622   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
623                                old_name);
624   free (prompt);
625
626   if (new_name == NULL)
627     return;
628
629   existing_dataset = session_lookup_dataset (session, new_name);
630   if (existing_dataset == NULL || existing_dataset == ds
631       || confirm_delete_dataset (de, old_name, new_name,
632                                  dataset_name (existing_dataset)))
633     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
634                                                         new_name)));
635
636   free (new_name);
637 }
638
639 static void
640 on_edit_paste (PsppireDataWindow  *de)
641 {
642   psppire_data_editor_clip_paste (de->data_editor);
643 }
644
645 static void
646 on_edit_copy (PsppireDataWindow  *de)
647 {
648   psppire_data_editor_clip_copy (de->data_editor);
649 }
650
651
652
653 static void
654 on_edit_cut (PsppireDataWindow  *de)
655 {
656   psppire_data_editor_clip_cut (de->data_editor);
657 }
658
659
660 static void
661 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
662 {
663   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
664
665   if ( gtk_toggle_action_get_active (action))
666     gtk_widget_show (statusbar);
667   else
668     gtk_widget_hide (statusbar);
669 }
670
671
672 static void
673 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
674 {
675   const gboolean grid_visible = gtk_toggle_action_get_active (action);
676
677   psppire_data_editor_show_grid (de->data_editor, grid_visible);
678 }
679
680 static void
681 data_view_activate (PsppireDataWindow  *de)
682 {
683   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
684 }
685
686
687 static void
688 variable_view_activate (PsppireDataWindow  *de)
689 {
690   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
691 }
692
693
694 static void
695 fonts_activate (PsppireDataWindow  *de)
696 {
697   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
698   PangoFontDescription *current_font;
699   gchar *font_name;
700   GtkWidget *dialog =
701     gtk_font_selection_dialog_new (_("Font Selection"));
702
703
704   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
705   font_name = pango_font_description_to_string (current_font);
706
707   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
708
709   g_free (font_name);
710
711   gtk_window_set_transient_for (GTK_WINDOW (dialog),
712                                 GTK_WINDOW (toplevel));
713
714   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
715     {
716       const gchar *font = gtk_font_selection_dialog_get_font_name
717         (GTK_FONT_SELECTION_DIALOG (dialog));
718
719       PangoFontDescription* font_desc =
720         pango_font_description_from_string (font);
721
722       psppire_data_editor_set_font (de->data_editor, font_desc);
723     }
724
725   gtk_widget_hide (dialog);
726 }
727
728
729
730 /* Callback for the value labels action */
731 static void
732 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
733 {
734   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
735 }
736
737 static void
738 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
739 {
740   psppire_data_editor_split_window (de->data_editor,
741                                     gtk_toggle_action_get_active (ta));
742 }
743
744
745 static void
746 file_quit (PsppireDataWindow *de)
747 {
748   /* FIXME: Need to be more intelligent here.
749      Give the user the opportunity to save any unsaved data.
750   */
751   psppire_quit ();
752 }
753
754
755 static void
756 on_recent_data_select (GtkMenuShell *menushell,
757                        PsppireWindow *window)
758 {
759   gchar *file;
760
761   gchar *uri =
762     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
763
764   file = g_filename_from_uri (uri, NULL, NULL);
765
766   g_free (uri);
767
768   open_data_window (window, file);
769
770   g_free (file);
771 }
772
773 static char *
774 charset_from_mime_type (const char *mime_type)
775 {
776   const char *charset;
777   struct string s;
778   const char *p;
779
780   if (mime_type == NULL)
781     return NULL;
782
783   charset = c_strcasestr (mime_type, "charset=");
784   if (charset == NULL)
785     return NULL;
786
787   ds_init_empty (&s);
788   p = charset + 8;
789   if (*p == '"')
790     {
791       /* Parse a "quoted-string" as defined by RFC 822. */
792       for (p++; *p != '\0' && *p != '"'; p++)
793         {
794           if (*p != '\\')
795             ds_put_byte (&s, *p);
796           else if (*++p != '\0')
797             ds_put_byte (&s, *p);
798         }
799     }
800   else
801     {
802       /* Parse a "token" as defined by RFC 2045. */
803       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
804         ds_put_byte (&s, *p++);
805     }
806   if (!ds_is_empty (&s))
807     return ds_steal_cstr (&s);
808
809   ds_destroy (&s);
810   return NULL;
811 }
812
813 static void
814 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
815 {
816   GtkRecentInfo *item;
817   char *encoding;
818   GtkWidget *se;
819   gchar *file;
820
821   /* Get the file name and its encoding. */
822   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
823   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
824   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
825   gtk_recent_info_unref (item);
826
827   se = psppire_syntax_window_new (encoding);
828
829   free (encoding);
830
831   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
832     gtk_widget_show (se);
833   else
834     gtk_widget_destroy (se);
835
836   g_free (file);
837 }
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, "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 }