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