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