gui: Change View|Data and View|Variables to radio button menu items.
[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
156 extern GtkRecentManager *the_recent_mgr;
157
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
341 static gboolean
342 load_file (PsppireWindow *de, const gchar *file_name)
343 {
344   gchar *utf8_file_name;
345   struct string filename;
346   gchar *syntax;
347   bool ok;
348
349   ds_init_empty (&filename);
350
351   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
352
353   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
354
355   g_free (utf8_file_name);
356
357   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
358   ds_destroy (&filename);
359
360   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
361                        lex_reader_for_string (syntax));
362   g_free (syntax);
363   return ok;
364 }
365
366 /* Returns true if NAME has a suffix which might denote a PSPP file */
367 static gboolean
368 name_has_suffix (const gchar *name)
369 {
370   if ( g_str_has_suffix (name, ".sav"))
371     return TRUE;
372   if ( g_str_has_suffix (name, ".SAV"))
373     return TRUE;
374   if ( g_str_has_suffix (name, ".por"))
375     return TRUE;
376   if ( g_str_has_suffix (name, ".POR"))
377     return TRUE;
378
379   return FALSE;
380 }
381
382
383 /* Save DE to file */
384 static void
385 save_file (PsppireWindow *w)
386 {
387   const gchar *file_name = NULL;
388   gchar *utf8_file_name = NULL;
389   GString *fnx;
390   struct string filename ;
391   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
392   gchar *syntax;
393
394   file_name = psppire_window_get_filename (w);
395
396   fnx = g_string_new (file_name);
397
398   if ( ! name_has_suffix (fnx->str))
399     {
400       if ( de->save_as_portable)
401         g_string_append (fnx, ".por");
402       else
403         g_string_append (fnx, ".sav");
404     }
405
406   ds_init_empty (&filename);
407
408   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
409
410   g_string_free (fnx, TRUE);
411
412   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
413   g_free (utf8_file_name);
414
415   syntax = g_strdup_printf ("%s OUTFILE=%s.",
416                             de->save_as_portable ? "EXPORT" : "SAVE",
417                             ds_cstr (&filename));
418
419   ds_destroy (&filename);
420
421   g_free (execute_syntax_string (de, syntax));
422 }
423
424
425 static void
426 insert_case (PsppireDataWindow *dw)
427 {
428   psppire_data_editor_insert_case (dw->data_editor);
429 }
430
431 static void
432 on_insert_variable (PsppireDataWindow *dw)
433 {
434   psppire_data_editor_insert_variable (dw->data_editor);
435 }
436
437
438 static void
439 display_dict (PsppireDataWindow *de)
440 {
441   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
442 }
443
444 static void
445 sysfile_info (PsppireDataWindow *de)
446 {
447   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
448
449   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
450     {
451       struct string filename;
452       gchar *file_name =
453         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
454
455       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
456                                                   NULL);
457
458       gchar *syntax;
459
460       ds_init_empty (&filename);
461
462       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
463
464       g_free (utf8_file_name);
465
466       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
467       g_free (execute_syntax_string (de, syntax));
468     }
469
470   gtk_widget_destroy (dialog);
471 }
472
473
474 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
475 static void
476 data_pick_filename (PsppireWindow *window)
477 {
478   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
479   GtkWidget *button_sys;
480   GtkWidget *dialog =
481     gtk_file_chooser_dialog_new (_("Save"),
482                                  GTK_WINDOW (de),
483                                  GTK_FILE_CHOOSER_ACTION_SAVE,
484                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
485                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
486                                  NULL);
487
488   GtkFileFilter *filter = gtk_file_filter_new ();
489   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
490   gtk_file_filter_add_pattern (filter, "*.sav");
491   gtk_file_filter_add_pattern (filter, "*.SAV");
492   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
493
494   filter = gtk_file_filter_new ();
495   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
496   gtk_file_filter_add_pattern (filter, "*.por");
497   gtk_file_filter_add_pattern (filter, "*.POR");
498   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
499
500   filter = gtk_file_filter_new ();
501   gtk_file_filter_set_name (filter, _("All Files"));
502   gtk_file_filter_add_pattern (filter, "*");
503   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
504
505   {
506     GtkWidget *button_por;
507     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
508     button_sys =
509       gtk_radio_button_new_with_label (NULL, _("System File"));
510
511     button_por =
512       gtk_radio_button_new_with_label
513       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
514        _("Portable File"));
515
516     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
517     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
518
519     gtk_widget_show_all (vbox);
520
521     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
522   }
523
524   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
525                                                   TRUE);
526
527   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
528     {
529     case GTK_RESPONSE_ACCEPT:
530       {
531         GString *filename =
532           g_string_new
533           (
534            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
535            );
536
537         de->save_as_portable =
538           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
539
540         if ( ! name_has_suffix (filename->str))
541           {
542             if ( de->save_as_portable)
543               g_string_append (filename, ".por");
544             else
545               g_string_append (filename, ".sav");
546           }
547
548         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
549
550         g_string_free (filename, TRUE);
551       }
552       break;
553     default:
554       break;
555     }
556
557   gtk_widget_destroy (dialog);
558 }
559
560 static bool
561 confirm_delete_dataset (PsppireDataWindow *de,
562                         const char *old_dataset,
563                         const char *new_dataset,
564                         const char *existing_dataset)
565 {
566   GtkWidget *dialog;
567   int result;
568
569   dialog = gtk_message_dialog_new (
570     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
571     _("Delete Existing Dataset?"));
572
573   gtk_message_dialog_format_secondary_text (
574     GTK_MESSAGE_DIALOG (dialog),
575     _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
576       "dataset named \"%s\".  Are you sure that you want to do this?"),
577     old_dataset, new_dataset, existing_dataset);
578
579   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
580                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
581                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
582                           NULL);
583
584   g_object_set (dialog, "icon-name", "psppicon", NULL);
585
586   result = gtk_dialog_run (GTK_DIALOG (dialog));
587
588   gtk_widget_destroy (dialog);
589
590   return result == GTK_RESPONSE_OK;
591 }
592
593 static void
594 on_rename_dataset (PsppireDataWindow *de)
595 {
596   struct dataset *ds = de->dataset;
597   struct session *session = dataset_session (ds);
598   const char *old_name = dataset_name (ds);
599   struct dataset *existing_dataset;
600   char *new_name;
601   char *prompt;
602
603   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
604                       old_name);
605   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
606                                old_name);
607   free (prompt);
608
609   if (new_name == NULL)
610     return;
611
612   existing_dataset = session_lookup_dataset (session, new_name);
613   if (existing_dataset == NULL || existing_dataset == ds
614       || confirm_delete_dataset (de, old_name, new_name,
615                                  dataset_name (existing_dataset)))
616     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
617                                                         new_name)));
618
619   free (new_name);
620 }
621
622 static void
623 on_edit_paste (PsppireDataWindow  *de)
624 {
625   psppire_data_editor_clip_paste (de->data_editor);
626 }
627
628 static void
629 on_edit_copy (PsppireDataWindow  *de)
630 {
631   psppire_data_editor_clip_copy (de->data_editor);
632 }
633
634
635
636 static void
637 on_edit_cut (PsppireDataWindow  *de)
638 {
639   psppire_data_editor_clip_cut (de->data_editor);
640 }
641
642
643 static void
644 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
645 {
646   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
647
648   if ( gtk_toggle_action_get_active (action))
649     gtk_widget_show (statusbar);
650   else
651     gtk_widget_hide (statusbar);
652 }
653
654
655 static void
656 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
657 {
658   const gboolean grid_visible = gtk_toggle_action_get_active (action);
659
660   psppire_data_editor_show_grid (de->data_editor, grid_visible);
661 }
662
663 static void
664 data_view_activate (PsppireDataWindow  *de)
665 {
666   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
667 }
668
669
670 static void
671 variable_view_activate (PsppireDataWindow  *de)
672 {
673   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
674 }
675
676
677 static void
678 fonts_activate (PsppireDataWindow  *de)
679 {
680   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
681   PangoFontDescription *current_font;
682   gchar *font_name;
683   GtkWidget *dialog =
684     gtk_font_selection_dialog_new (_("Font Selection"));
685
686
687   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
688   font_name = pango_font_description_to_string (current_font);
689
690   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
691
692   g_free (font_name);
693
694   gtk_window_set_transient_for (GTK_WINDOW (dialog),
695                                 GTK_WINDOW (toplevel));
696
697   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
698     {
699       const gchar *font = gtk_font_selection_dialog_get_font_name
700         (GTK_FONT_SELECTION_DIALOG (dialog));
701
702       PangoFontDescription* font_desc =
703         pango_font_description_from_string (font);
704
705       psppire_data_editor_set_font (de->data_editor, font_desc);
706     }
707
708   gtk_widget_hide (dialog);
709 }
710
711
712
713 /* Callback for the value labels action */
714 static void
715 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
716 {
717   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
718 }
719
720 static void
721 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
722 {
723   psppire_data_editor_split_window (de->data_editor,
724                                     gtk_toggle_action_get_active (ta));
725 }
726
727
728 static void
729 file_quit (PsppireDataWindow *de)
730 {
731   /* FIXME: Need to be more intelligent here.
732      Give the user the opportunity to save any unsaved data.
733   */
734   psppire_quit ();
735 }
736
737
738 static void
739 on_recent_data_select (GtkMenuShell *menushell,
740                        PsppireWindow *window)
741 {
742   gchar *file;
743
744   gchar *uri =
745     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
746
747   file = g_filename_from_uri (uri, NULL, NULL);
748
749   g_free (uri);
750
751   psppire_window_load (window, file);
752
753   g_free (file);
754 }
755
756 static void
757 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
758 {
759   gchar *file;
760
761   GtkWidget *se ;
762
763   gchar *uri =
764     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
765
766   file = g_filename_from_uri (uri, NULL, NULL);
767
768   g_free (uri);
769
770   se = psppire_syntax_window_new ();
771
772   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
773     gtk_widget_show (se);
774   else
775     gtk_widget_destroy (se);
776
777   g_free (file);
778 }
779
780
781 static void
782 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
783 {
784   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
785
786   gtk_action_set_visible (de->delete_cases, case_num != -1);
787 }
788
789
790 static void
791 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
792 {
793   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
794
795   gtk_action_set_visible (de->delete_variables, var != -1);
796 }
797
798 /* Callback for when the datasheet/varsheet is selected */
799 static void
800 on_switch_sheet (GtkNotebook *notebook,
801                  GtkNotebookPage *page,
802                  guint page_num,
803                  gpointer user_data)
804 {
805   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
806
807   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
808
809   GtkWidget *view_data =
810     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
811
812   GtkWidget *view_variables =
813     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
814
815   switch (page_num)
816     {
817     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
818       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
819                                       TRUE);
820       gtk_action_set_sensitive (de->insert_variable, TRUE);
821       gtk_action_set_sensitive (de->insert_case, FALSE);
822       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
823       break;
824     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
825       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
826       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
827       gtk_action_set_sensitive (de->insert_case, TRUE);
828       break;
829     default:
830       g_assert_not_reached ();
831       break;
832     }
833
834 #if 0
835   update_paste_menuitem (de, page_num);
836 #endif
837 }
838
839
840
841 static void
842 set_unsaved (gpointer w)
843 {
844   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
845 }
846
847
848 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
849    Returns a pointer to the action
850 */
851 static GtkAction *
852 connect_action (PsppireDataWindow *dw, const char *action_name, 
853                                     GCallback handler)
854 {
855   GtkAction *action = get_action_assert (dw->builder, action_name);
856  
857   g_signal_connect_swapped (action, "activate", handler, dw);
858
859   return action;
860 }
861
862 /* Initializes as much of a PsppireDataWindow as we can and must before the
863    dataset has been set.
864
865    In particular, the 'menu' member is required in case the "filename" property
866    is set before the "dataset" property: otherwise PsppireWindow will try to
867    modify the menu as part of the "filename" property_set() function and end up
868    with a Gtk-CRITICAL since 'menu' is NULL.  */
869 static void
870 psppire_data_window_init (PsppireDataWindow *de)
871 {
872   GtkUIManager *uim;
873
874   de->builder = builder_new ("data-editor.ui");
875
876   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
877
878   PSPPIRE_WINDOW (de)->menu =
879     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
880 }
881
882 static void
883 psppire_data_window_finish_init (PsppireDataWindow *de,
884                                  struct dataset *ds)
885 {
886   static const struct dataset_callbacks cbs =
887     {
888       set_unsaved,                    /* changed */
889       transformation_change_callback, /* transformations_changed */
890     };
891
892   PsppireDict *dict;
893
894   GtkWidget *menubar;
895   GtkWidget *hb ;
896   GtkWidget *sb ;
897
898   GtkWidget *box = gtk_vbox_new (FALSE, 0);
899
900   de->dataset = ds;
901   dict = psppire_dict_new_from_dict (dataset_dict (ds));
902   de->var_store = psppire_var_store_new (dict);
903   de->data_store = psppire_data_store_new (dict);
904   psppire_data_store_set_reader (de->data_store, NULL);
905
906   menubar = get_widget_assert (de->builder, "menubar");
907   hb = get_widget_assert (de->builder, "handlebox1");
908   sb = get_widget_assert (de->builder, "status-bar");
909
910   de->data_editor =
911     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
912                                                   de->data_store));
913
914   g_signal_connect_swapped (de->data_store, "case-changed",
915                             G_CALLBACK (set_unsaved), de);
916
917   g_signal_connect_swapped (de->data_store, "case-inserted",
918                             G_CALLBACK (set_unsaved), de);
919
920   g_signal_connect_swapped (de->data_store, "cases-deleted",
921                             G_CALLBACK (set_unsaved), de);
922
923   dataset_set_callbacks (de->dataset, &cbs, de);
924
925   connect_help (de->builder);
926
927   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
928   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
929   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
930   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
931
932   gtk_container_add (GTK_CONTAINER (de), box);
933
934   set_cut_copy_menuitem_sensitivity (de, FALSE);
935
936   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
937                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
938
939
940   set_paste_menuitem_sensitivity (de, FALSE);
941
942   g_signal_connect_swapped (de->data_editor, "data-available-changed",
943                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
944
945   g_signal_connect (dict, "weight-changed",
946                     G_CALLBACK (on_weight_change),
947                     de);
948
949   g_signal_connect (dict, "filter-changed",
950                     G_CALLBACK (on_filter_change),
951                     de);
952
953   g_signal_connect (dict, "split-changed",
954                     G_CALLBACK (on_split_change),
955                     de);
956
957
958   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
959
960   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
961
962   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
963
964   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
965
966   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
967  
968   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
969
970   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
971
972   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
973
974   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
975
976   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
977
978   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
979
980   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
981
982   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
983
984   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
985
986   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
987
988   {
989     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
990
991     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
992
993     gtk_action_set_visible (de->delete_cases, FALSE);
994   }
995
996
997   {
998     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
999
1000     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1001
1002     gtk_action_set_visible (de->delete_variables, FALSE);
1003   }
1004
1005
1006   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1007
1008   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1009  
1010   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1011
1012   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1013
1014   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1015
1016   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1017
1018   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1019
1020   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1021
1022
1023   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1024  
1025   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1026
1027   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1028
1029   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1030
1031   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1032
1033   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1034  
1035   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1036  
1037   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1038  
1039   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1040
1041   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1042  
1043   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1044  
1045   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1046  
1047   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1048  
1049   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1050  
1051   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1052  
1053   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1054
1055   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1056  
1057   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1058
1059   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1060
1061   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1062
1063   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1064  
1065
1066   {
1067     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1068
1069     GtkWidget *recent_data =
1070       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1071
1072     GtkWidget *recent_files =
1073       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1074
1075
1076     GtkWidget *menu_data =
1077       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1078
1079     GtkWidget *menu_files =
1080       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1081
1082     {
1083       GtkRecentFilter *filter = gtk_recent_filter_new ();
1084
1085       gtk_recent_filter_add_pattern (filter, "*.sav");
1086       gtk_recent_filter_add_pattern (filter, "*.SAV");
1087       gtk_recent_filter_add_pattern (filter, "*.por");
1088       gtk_recent_filter_add_pattern (filter, "*.POR");
1089
1090       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1091
1092       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1093     }
1094
1095     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1096
1097
1098     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1099
1100     {
1101       GtkRecentFilter *filter = gtk_recent_filter_new ();
1102
1103       gtk_recent_filter_add_pattern (filter, "*.sps");
1104       gtk_recent_filter_add_pattern (filter, "*.SPS");
1105
1106       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1107
1108       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1109     }
1110
1111     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1112
1113     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1114
1115   }
1116
1117   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1118
1119
1120   g_signal_connect (de->data_editor,
1121                     "cases-selected",
1122                     G_CALLBACK (enable_delete_cases),
1123                     de);
1124
1125   g_signal_connect (de->data_editor,
1126                     "variables-selected",
1127                     G_CALLBACK (enable_delete_variables),
1128                     de);
1129
1130
1131   g_signal_connect (de->data_editor,
1132                     "switch-page",
1133                     G_CALLBACK (on_switch_sheet), de);
1134
1135   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1136   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1137
1138   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1139
1140   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1141
1142   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1143
1144   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1145
1146   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1147
1148   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1149
1150   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1151
1152   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1153
1154   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1155
1156   {
1157     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1158
1159     merge_help_menu (uim);
1160   }
1161
1162   {
1163     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1164                                                                 "datasheet-cases-popup");
1165
1166     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1167                                                                   "varsheet-variable-popup");
1168
1169     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1170                                                                    "datasheet-variable-popup");
1171
1172     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1173                               G_CALLBACK (psppire_data_editor_sort_ascending),
1174                               de->data_editor);
1175
1176     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1177                               G_CALLBACK (psppire_data_editor_sort_descending),
1178                               de->data_editor);
1179
1180     g_object_set (de->data_editor,
1181                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1182                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1183                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1184                   NULL);
1185   }
1186
1187   gtk_widget_show (GTK_WIDGET (de->data_editor));
1188   gtk_widget_show (box);
1189
1190   ll_push_head (&all_data_windows, &de->ll);
1191 }
1192
1193 static void
1194 psppire_data_window_dispose (GObject *object)
1195 {
1196   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1197
1198   if (dw->builder != NULL)
1199     {
1200       g_object_unref (dw->builder);
1201       dw->builder = NULL;
1202     }
1203
1204   if (dw->var_store)
1205     {
1206       g_object_unref (dw->var_store);
1207       dw->var_store = NULL;
1208     }
1209
1210   if (dw->data_store)
1211     {
1212       g_object_unref (dw->data_store);
1213       dw->data_store = NULL;
1214     }
1215
1216   if (dw->ll.next != NULL)
1217     {
1218       ll_remove (&dw->ll);
1219       dw->ll.next = NULL;
1220     }
1221
1222   if (G_OBJECT_CLASS (parent_class)->dispose)
1223     G_OBJECT_CLASS (parent_class)->dispose (object);
1224 }
1225
1226 static void
1227 psppire_data_window_set_property (GObject         *object,
1228                                   guint            prop_id,
1229                                   const GValue    *value,
1230                                   GParamSpec      *pspec)
1231 {
1232   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1233
1234   switch (prop_id)
1235     {
1236     case PROP_DATASET:
1237       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1238       break;
1239     default:
1240       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1241       break;
1242     };
1243 }
1244
1245 static void
1246 psppire_data_window_get_property (GObject         *object,
1247                                   guint            prop_id,
1248                                   GValue          *value,
1249                                   GParamSpec      *pspec)
1250 {
1251   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1252
1253   switch (prop_id)
1254     {
1255     case PROP_DATASET:
1256       g_value_set_pointer (value, window->dataset);
1257       break;
1258     default:
1259       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1260       break;
1261     };
1262 }
1263
1264 GtkWidget*
1265 psppire_data_window_new (struct dataset *ds)
1266 {
1267   GtkWidget *dw;
1268
1269   if (the_session == NULL)
1270     the_session = session_create ();
1271
1272   if (ds == NULL)
1273     {
1274       static int n_datasets;
1275       char *dataset_name;
1276
1277       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1278       ds = dataset_create (the_session, dataset_name);
1279       free (dataset_name);
1280     }
1281   assert (dataset_session (ds) == the_session);
1282
1283   dw = GTK_WIDGET (
1284     g_object_new (
1285       psppire_data_window_get_type (),
1286       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1287       "description", _("Data Editor"),
1288       "dataset", ds,
1289       NULL));
1290
1291   if (dataset_name (ds) != NULL)
1292     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1293
1294   return dw;
1295 }
1296
1297 bool
1298 psppire_data_window_is_empty (PsppireDataWindow *dw)
1299 {
1300   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1301 }
1302
1303 static void
1304 psppire_data_window_iface_init (PsppireWindowIface *iface)
1305 {
1306   iface->save = save_file;
1307   iface->pick_filename = data_pick_filename;
1308   iface->load = load_file;
1309 }
1310 \f
1311 PsppireDataWindow *
1312 psppire_default_data_window (void)
1313 {
1314   if (ll_is_empty (&all_data_windows))
1315     create_data_window ();
1316   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1317 }
1318
1319 void
1320 psppire_data_window_set_default (PsppireDataWindow *pdw)
1321 {
1322   ll_remove (&pdw->ll);
1323   ll_push_head (&all_data_windows, &pdw->ll);
1324 }
1325
1326 void
1327 psppire_data_window_undefault (PsppireDataWindow *pdw)
1328 {
1329   ll_remove (&pdw->ll);
1330   ll_push_tail (&all_data_windows, &pdw->ll);
1331 }
1332
1333 PsppireDataWindow *
1334 psppire_data_window_for_dataset (struct dataset *ds)
1335 {
1336   PsppireDataWindow *pdw;
1337
1338   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1339     if (pdw->dataset == ds)
1340       return pdw;
1341
1342   return NULL;
1343 }
1344
1345 void
1346 create_data_window (void)
1347 {
1348   gtk_widget_show (psppire_data_window_new (NULL));
1349 }