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