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