ef06d83a7ca46b46c88071555dac96546b916b62
[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_widget_hide (view_variables);
819       gtk_widget_show (view_data);
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_widget_show (view_variables);
826       gtk_widget_hide (view_data);
827       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
828       gtk_action_set_sensitive (de->insert_case, TRUE);
829       break;
830     default:
831       g_assert_not_reached ();
832       break;
833     }
834
835 #if 0
836   update_paste_menuitem (de, page_num);
837 #endif
838 }
839
840
841
842 static void
843 set_unsaved (gpointer w)
844 {
845   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
846 }
847
848
849 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
850    Returns a pointer to the action
851 */
852 static GtkAction *
853 connect_action (PsppireDataWindow *dw, const char *action_name, 
854                                     GCallback handler)
855 {
856   GtkAction *action = get_action_assert (dw->builder, action_name);
857  
858   g_signal_connect_swapped (action, "activate", handler, dw);
859
860   return action;
861 }
862
863 /* Initializes as much of a PsppireDataWindow as we can and must before the
864    dataset has been set.
865
866    In particular, the 'menu' member is required in case the "filename" property
867    is set before the "dataset" property: otherwise PsppireWindow will try to
868    modify the menu as part of the "filename" property_set() function and end up
869    with a Gtk-CRITICAL since 'menu' is NULL.  */
870 static void
871 psppire_data_window_init (PsppireDataWindow *de)
872 {
873   GtkUIManager *uim;
874
875   de->builder = builder_new ("data-editor.ui");
876
877   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
878
879   PSPPIRE_WINDOW (de)->menu =
880     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
881 }
882
883 static void
884 psppire_data_window_finish_init (PsppireDataWindow *de,
885                                  struct dataset *ds)
886 {
887   static const struct dataset_callbacks cbs =
888     {
889       set_unsaved,                    /* changed */
890       transformation_change_callback, /* transformations_changed */
891     };
892
893   PsppireDict *dict;
894
895   GtkWidget *menubar;
896   GtkWidget *hb ;
897   GtkWidget *sb ;
898
899   GtkWidget *box = gtk_vbox_new (FALSE, 0);
900
901   de->dataset = ds;
902   dict = psppire_dict_new_from_dict (dataset_dict (ds));
903   de->var_store = psppire_var_store_new (dict);
904   de->data_store = psppire_data_store_new (dict);
905   psppire_data_store_set_reader (de->data_store, NULL);
906
907   menubar = get_widget_assert (de->builder, "menubar");
908   hb = get_widget_assert (de->builder, "handlebox1");
909   sb = get_widget_assert (de->builder, "status-bar");
910
911   de->data_editor =
912     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
913                                                   de->data_store));
914
915   g_signal_connect_swapped (de->data_store, "case-changed",
916                             G_CALLBACK (set_unsaved), de);
917
918   g_signal_connect_swapped (de->data_store, "case-inserted",
919                             G_CALLBACK (set_unsaved), de);
920
921   g_signal_connect_swapped (de->data_store, "cases-deleted",
922                             G_CALLBACK (set_unsaved), de);
923
924   dataset_set_callbacks (de->dataset, &cbs, de);
925
926   connect_help (de->builder);
927
928   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
929   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
930   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
931   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
932
933   gtk_container_add (GTK_CONTAINER (de), box);
934
935   set_cut_copy_menuitem_sensitivity (de, FALSE);
936
937   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
938                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
939
940
941   set_paste_menuitem_sensitivity (de, FALSE);
942
943   g_signal_connect_swapped (de->data_editor, "data-available-changed",
944                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
945
946   g_signal_connect (dict, "weight-changed",
947                     G_CALLBACK (on_weight_change),
948                     de);
949
950   g_signal_connect (dict, "filter-changed",
951                     G_CALLBACK (on_filter_change),
952                     de);
953
954   g_signal_connect (dict, "split-changed",
955                     G_CALLBACK (on_split_change),
956                     de);
957
958
959   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
960
961   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
962
963   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
964
965   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
966
967   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
968  
969   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
970
971   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
972
973   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
974
975   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
976
977   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
978
979   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
980
981   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
982
983   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
984
985   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
986
987   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
988
989   {
990     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
991
992     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
993
994     gtk_action_set_visible (de->delete_cases, FALSE);
995   }
996
997
998   {
999     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1000
1001     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1002
1003     gtk_action_set_visible (de->delete_variables, FALSE);
1004   }
1005
1006
1007   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1008
1009   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1010  
1011   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1012
1013   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1014
1015   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1016
1017   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1018
1019   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1020
1021   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1022
1023
1024   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1025  
1026   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1027
1028   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1029
1030   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1031
1032   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1033
1034   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1035  
1036   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1037  
1038   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1039  
1040   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1041
1042   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1043  
1044   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1045  
1046   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1047  
1048   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1049  
1050   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1051  
1052   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1053  
1054   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1055
1056   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1057  
1058   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1059
1060   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1061
1062   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1063
1064   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1065  
1066
1067   {
1068     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1069
1070     GtkWidget *recent_data =
1071       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1072
1073     GtkWidget *recent_files =
1074       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1075
1076
1077     GtkWidget *menu_data =
1078       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1079
1080     GtkWidget *menu_files =
1081       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1082
1083     {
1084       GtkRecentFilter *filter = gtk_recent_filter_new ();
1085
1086       gtk_recent_filter_add_pattern (filter, "*.sav");
1087       gtk_recent_filter_add_pattern (filter, "*.SAV");
1088       gtk_recent_filter_add_pattern (filter, "*.por");
1089       gtk_recent_filter_add_pattern (filter, "*.POR");
1090
1091       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1092
1093       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1094     }
1095
1096     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1097
1098
1099     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1100
1101     {
1102       GtkRecentFilter *filter = gtk_recent_filter_new ();
1103
1104       gtk_recent_filter_add_pattern (filter, "*.sps");
1105       gtk_recent_filter_add_pattern (filter, "*.SPS");
1106
1107       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1108
1109       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1110     }
1111
1112     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1113
1114     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1115
1116   }
1117
1118   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1119
1120
1121   g_signal_connect (de->data_editor,
1122                     "cases-selected",
1123                     G_CALLBACK (enable_delete_cases),
1124                     de);
1125
1126   g_signal_connect (de->data_editor,
1127                     "variables-selected",
1128                     G_CALLBACK (enable_delete_variables),
1129                     de);
1130
1131
1132   g_signal_connect (de->data_editor,
1133                     "switch-page",
1134                     G_CALLBACK (on_switch_sheet), de);
1135
1136   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1137   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1138
1139   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1140
1141   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1142
1143   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1144
1145   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1146
1147   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1148
1149   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1150
1151   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1152
1153   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1154
1155   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1156
1157   {
1158     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1159
1160     merge_help_menu (uim);
1161   }
1162
1163   {
1164     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1165                                                                 "datasheet-cases-popup");
1166
1167     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1168                                                                   "varsheet-variable-popup");
1169
1170     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1171                                                                    "datasheet-variable-popup");
1172
1173     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1174                               G_CALLBACK (psppire_data_editor_sort_ascending),
1175                               de->data_editor);
1176
1177     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1178                               G_CALLBACK (psppire_data_editor_sort_descending),
1179                               de->data_editor);
1180
1181     g_object_set (de->data_editor,
1182                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1183                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1184                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1185                   NULL);
1186   }
1187
1188   gtk_widget_show (GTK_WIDGET (de->data_editor));
1189   gtk_widget_show (box);
1190
1191   ll_push_head (&all_data_windows, &de->ll);
1192 }
1193
1194 static void
1195 psppire_data_window_dispose (GObject *object)
1196 {
1197   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1198
1199   if (dw->builder != NULL)
1200     {
1201       g_object_unref (dw->builder);
1202       dw->builder = NULL;
1203     }
1204
1205   if (dw->var_store)
1206     {
1207       g_object_unref (dw->var_store);
1208       dw->var_store = NULL;
1209     }
1210
1211   if (dw->data_store)
1212     {
1213       g_object_unref (dw->data_store);
1214       dw->data_store = NULL;
1215     }
1216
1217   if (dw->ll.next != NULL)
1218     {
1219       ll_remove (&dw->ll);
1220       dw->ll.next = NULL;
1221     }
1222
1223   if (G_OBJECT_CLASS (parent_class)->dispose)
1224     G_OBJECT_CLASS (parent_class)->dispose (object);
1225 }
1226
1227 static void
1228 psppire_data_window_set_property (GObject         *object,
1229                                   guint            prop_id,
1230                                   const GValue    *value,
1231                                   GParamSpec      *pspec)
1232 {
1233   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1234
1235   switch (prop_id)
1236     {
1237     case PROP_DATASET:
1238       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1239       break;
1240     default:
1241       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1242       break;
1243     };
1244 }
1245
1246 static void
1247 psppire_data_window_get_property (GObject         *object,
1248                                   guint            prop_id,
1249                                   GValue          *value,
1250                                   GParamSpec      *pspec)
1251 {
1252   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1253
1254   switch (prop_id)
1255     {
1256     case PROP_DATASET:
1257       g_value_set_pointer (value, window->dataset);
1258       break;
1259     default:
1260       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1261       break;
1262     };
1263 }
1264
1265 GtkWidget*
1266 psppire_data_window_new (struct dataset *ds)
1267 {
1268   GtkWidget *dw;
1269
1270   if (the_session == NULL)
1271     the_session = session_create ();
1272
1273   if (ds == NULL)
1274     {
1275       static int n_datasets;
1276       char *dataset_name;
1277
1278       dataset_name = xasprintf ("DataSet%d", ++n_datasets);
1279       ds = dataset_create (the_session, dataset_name);
1280       free (dataset_name);
1281     }
1282   assert (dataset_session (ds) == the_session);
1283
1284   dw = GTK_WIDGET (
1285     g_object_new (
1286       psppire_data_window_get_type (),
1287       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1288       "description", _("Data Editor"),
1289       "dataset", ds,
1290       NULL));
1291
1292   if (dataset_name (ds) != NULL)
1293     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1294
1295   return dw;
1296 }
1297
1298 bool
1299 psppire_data_window_is_empty (PsppireDataWindow *dw)
1300 {
1301   return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1302 }
1303
1304 static void
1305 psppire_data_window_iface_init (PsppireWindowIface *iface)
1306 {
1307   iface->save = save_file;
1308   iface->pick_filename = data_pick_filename;
1309   iface->load = load_file;
1310 }
1311 \f
1312 PsppireDataWindow *
1313 psppire_default_data_window (void)
1314 {
1315   if (ll_is_empty (&all_data_windows))
1316     create_data_window ();
1317   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1318 }
1319
1320 void
1321 psppire_data_window_set_default (PsppireDataWindow *pdw)
1322 {
1323   ll_remove (&pdw->ll);
1324   ll_push_head (&all_data_windows, &pdw->ll);
1325 }
1326
1327 void
1328 psppire_data_window_undefault (PsppireDataWindow *pdw)
1329 {
1330   ll_remove (&pdw->ll);
1331   ll_push_tail (&all_data_windows, &pdw->ll);
1332 }
1333
1334 PsppireDataWindow *
1335 psppire_data_window_for_dataset (struct dataset *ds)
1336 {
1337   PsppireDataWindow *pdw;
1338
1339   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1340     if (pdw->dataset == ds)
1341       return pdw;
1342
1343   return NULL;
1344 }
1345
1346 void
1347 create_data_window (void)
1348 {
1349   gtk_widget_show (psppire_data_window_new (NULL));
1350 }