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