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