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