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