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