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