Merge remote branch 'savannah/master' into sourceview
[pspp] / 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
834 static void
835 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
836 {
837   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
838
839   gtk_action_set_visible (de->delete_cases, case_num != -1);
840 }
841
842
843 static void
844 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
845 {
846   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
847
848   gtk_action_set_visible (de->delete_variables, var != -1);
849 }
850
851 /* Callback for when the datasheet/varsheet is selected */
852 static void
853 on_switch_sheet (GtkNotebook *notebook,
854                  GtkNotebookPage *page,
855                  guint page_num,
856                  gpointer user_data)
857 {
858   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
859
860   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
861
862   GtkWidget *view_data =
863     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
864
865   GtkWidget *view_variables =
866     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
867
868   switch (page_num)
869     {
870     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
871       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
872                                       TRUE);
873       gtk_action_set_sensitive (de->insert_variable, TRUE);
874       gtk_action_set_sensitive (de->insert_case, FALSE);
875       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
876       break;
877     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
878       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
879       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
880       gtk_action_set_sensitive (de->insert_case, TRUE);
881       break;
882     default:
883       g_assert_not_reached ();
884       break;
885     }
886
887 #if 0
888   update_paste_menuitem (de, page_num);
889 #endif
890 }
891
892
893
894 static void
895 set_unsaved (gpointer w)
896 {
897   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
898 }
899
900
901 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
902    Returns a pointer to the action
903 */
904 static GtkAction *
905 connect_action (PsppireDataWindow *dw, const char *action_name, 
906                                     GCallback handler)
907 {
908   GtkAction *action = get_action_assert (dw->builder, action_name);
909  
910   g_signal_connect_swapped (action, "activate", handler, dw);
911
912   return action;
913 }
914
915 /* Initializes as much of a PsppireDataWindow as we can and must before the
916    dataset has been set.
917
918    In particular, the 'menu' member is required in case the "filename" property
919    is set before the "dataset" property: otherwise PsppireWindow will try to
920    modify the menu as part of the "filename" property_set() function and end up
921    with a Gtk-CRITICAL since 'menu' is NULL.  */
922 static void
923 psppire_data_window_init (PsppireDataWindow *de)
924 {
925   GtkUIManager *uim;
926
927   de->builder = builder_new ("data-editor.ui");
928
929   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
930
931   PSPPIRE_WINDOW (de)->menu =
932     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
933 }
934
935 static void
936 psppire_data_window_finish_init (PsppireDataWindow *de,
937                                  struct dataset *ds)
938 {
939   static const struct dataset_callbacks cbs =
940     {
941       set_unsaved,                    /* changed */
942       transformation_change_callback, /* transformations_changed */
943     };
944
945   PsppireDict *dict;
946
947   GtkWidget *menubar;
948   GtkWidget *hb ;
949   GtkWidget *sb ;
950
951   GtkWidget *box = gtk_vbox_new (FALSE, 0);
952
953   de->dataset = ds;
954   dict = psppire_dict_new_from_dict (dataset_dict (ds));
955   de->var_store = psppire_var_store_new (dict);
956   de->data_store = psppire_data_store_new (dict);
957   psppire_data_store_set_reader (de->data_store, NULL);
958
959   menubar = get_widget_assert (de->builder, "menubar");
960   hb = get_widget_assert (de->builder, "handlebox1");
961   sb = get_widget_assert (de->builder, "status-bar");
962
963   de->data_editor =
964     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
965                                                   de->data_store));
966
967   g_signal_connect_swapped (de->data_store, "case-changed",
968                             G_CALLBACK (set_unsaved), de);
969
970   g_signal_connect_swapped (de->data_store, "case-inserted",
971                             G_CALLBACK (set_unsaved), de);
972
973   g_signal_connect_swapped (de->data_store, "cases-deleted",
974                             G_CALLBACK (set_unsaved), de);
975
976   dataset_set_callbacks (de->dataset, &cbs, de);
977
978   connect_help (de->builder);
979
980   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
981   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
982   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
983   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
984
985   gtk_container_add (GTK_CONTAINER (de), box);
986
987   set_cut_copy_menuitem_sensitivity (de, FALSE);
988
989   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
990                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
991
992
993   set_paste_menuitem_sensitivity (de, FALSE);
994
995   g_signal_connect_swapped (de->data_editor, "data-available-changed",
996                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
997
998   g_signal_connect (dict, "weight-changed",
999                     G_CALLBACK (on_weight_change),
1000                     de);
1001
1002   g_signal_connect (dict, "filter-changed",
1003                     G_CALLBACK (on_filter_change),
1004                     de);
1005
1006   g_signal_connect (dict, "split-changed",
1007                     G_CALLBACK (on_split_change),
1008                     de);
1009
1010
1011   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1012
1013   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1014
1015   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1016
1017   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1018
1019   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1020  
1021   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1022
1023   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1024
1025   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1026
1027   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1028
1029   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1030
1031   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1032
1033   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1034
1035   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1036
1037   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1038
1039   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1040
1041   {
1042     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1043
1044     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1045
1046     gtk_action_set_visible (de->delete_cases, FALSE);
1047   }
1048
1049
1050   {
1051     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1052
1053     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1054
1055     gtk_action_set_visible (de->delete_variables, FALSE);
1056   }
1057
1058
1059   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1060
1061   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1062  
1063   connect_action (de, "data_sort-cases", G_CALLBACK (sort_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, "edit_find", G_CALLBACK (find_dialog));
1070
1071   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1072
1073   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1074
1075
1076   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1077  
1078   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1079
1080   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1081
1082   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1083
1084   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1085
1086   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1087  
1088   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1089  
1090   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1091  
1092   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1093
1094   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1095  
1096   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1097  
1098   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1099  
1100   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1101  
1102   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1103  
1104   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1105  
1106   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1107
1108   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1109  
1110   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1111
1112   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1113
1114   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1115
1116   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1117  
1118
1119   {
1120     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1121
1122     GtkWidget *recent_data =
1123       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1124
1125     GtkWidget *recent_files =
1126       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1127
1128
1129     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1130       gtk_recent_manager_get_default ());
1131
1132     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1133       gtk_recent_manager_get_default ());
1134
1135     {
1136       GtkRecentFilter *filter = gtk_recent_filter_new ();
1137
1138       gtk_recent_filter_add_pattern (filter, "*.sav");
1139       gtk_recent_filter_add_pattern (filter, "*.SAV");
1140       gtk_recent_filter_add_pattern (filter, "*.por");
1141       gtk_recent_filter_add_pattern (filter, "*.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 }