Implemented the paired samples t test dialog. Closes patch #6378
[pspp-builds.git] / src / ui / gui / data-editor.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2006, 2007  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 #include <stdlib.h>
19 #include <gettext.h>
20
21 #include <glade/glade.h>
22 #include <gtk/gtk.h>
23
24 #include "window-manager.h"
25 #include <gtksheet/gtksheet.h>
26
27 #include "helper.h"
28 #include "about.h"
29 #include <data/procedure.h>
30 #include "psppire-dialog.h"
31 #include "psppire-selector.h"
32 #include "weight-cases-dialog.h"
33 #include "split-file-dialog.h"
34 #include "transpose-dialog.h"
35 #include "sort-cases-dialog.h"
36 #include "select-cases-dialog.h"
37 #include "compute-dialog.h"
38 #include "goto-case-dialog.h"
39 #include "find-dialog.h"
40 #include "rank-dialog.h"
41 #include "recode-dialog.h"
42 #include "comments-dialog.h"
43 #include "variable-info-dialog.h"
44 #include "descriptives-dialog.h"
45 #include "frequencies-dialog.h"
46 #include "dict-display.h"
47 #include "clipboard.h"
48
49
50 #include "oneway-anova-dialog.h"
51 #include "t-test-independent-samples-dialog.h"
52 #include "t-test-one-sample.h"
53 #include "t-test-paired-samples.h"
54
55 #define _(msgid) gettext (msgid)
56 #define N_(msgid) msgid
57
58 #include "data-editor.h"
59 #include "syntax-editor.h"
60 #include <language/syntax-string-source.h>
61 #include <language/command.h>
62 #include <libpspp/syntax-gen.h>
63 #include "window-manager.h"
64
65 #include "psppire-data-store.h"
66 #include "psppire-var-store.h"
67
68 static void on_edit_copy (GtkMenuItem *, gpointer);
69 static void on_edit_cut (GtkMenuItem *, gpointer);
70 static void on_edit_paste (GtkAction *a, gpointer data);
71
72
73 static void create_data_sheet_variable_popup_menu (struct data_editor *);
74 static void create_data_sheet_cases_popup_menu (struct data_editor *);
75
76 static void popup_variable_menu (GtkSheet *, gint,
77                                  GdkEventButton *, gpointer data);
78
79 static void popup_cases_menu (GtkSheet *, gint,
80                                  GdkEventButton *, gpointer data);
81
82 /* Update the data_ref_entry with the reference of the active cell */
83 static gint update_data_ref_entry (const GtkSheet *sheet,
84                                    gint row, gint col, gpointer data);
85
86 static void register_data_editor_actions (struct data_editor *de);
87
88 static void insert_variable (GtkAction *, gpointer data);
89 static void insert_case (GtkAction *a, gpointer data);
90 static void delete_cases (GtkAction *a, gpointer data);
91 static void delete_variables (GtkAction *a, gpointer data);
92
93 static void toggle_value_labels (GtkToggleAction *a, gpointer data);
94
95 /* Switch between the VAR SHEET and the DATA SHEET */
96
97 static gboolean click2column (GtkWidget *w, gint col, gpointer data);
98 static gboolean click2row (GtkWidget *w, gint row, gpointer data);
99
100
101 /* Callback for when the dictionary changes properties*/
102 static void on_weight_change (GObject *, gint, gpointer);
103 static void on_filter_change (GObject *, gint, gpointer);
104 static void on_split_change (PsppireDict *, gpointer);
105
106 static void on_switch_sheet (GtkNotebook *notebook,
107                             GtkNotebookPage *page,
108                             guint page_num,
109                             gpointer user_data);
110
111 static void status_bar_activate (GtkCheckMenuItem *, gpointer);
112
113 static void grid_lines_activate (GtkCheckMenuItem *, gpointer);
114
115 static void data_sheet_activate (GtkCheckMenuItem *, gpointer);
116
117 static void variable_sheet_activate (GtkCheckMenuItem *, gpointer );
118
119 static void fonts_activate (GtkMenuItem *, gpointer);
120
121 static void file_quit (GtkCheckMenuItem *, gpointer );
122
123 static void
124 enable_delete_cases (GtkWidget *w, gint var, gpointer data)
125 {
126   struct data_editor *de = data;
127
128   gtk_action_set_visible (de->delete_cases, var != -1);
129 }
130
131
132 static void
133 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
134 {
135   struct data_editor *de = data;
136
137   gtk_action_set_visible (de->delete_variables, var != -1);
138 }
139
140
141
142 /* Run the EXECUTE command. */
143 static void
144 execute (GtkMenuItem *mi, gpointer data)
145 {
146   struct getl_interface *sss = create_syntax_string_source ("EXECUTE.");
147
148   execute_syntax (sss);
149 }
150
151 static void
152 transformation_change_callback (bool transformations_pending,
153                                 gpointer data)
154 {
155   struct data_editor *de = data;
156   GtkWidget *menuitem =
157     get_widget_assert (de->xml, "transform_run-pending");
158   GtkWidget *status_label  =
159     get_widget_assert (de->xml, "case-counter-area");
160
161   gtk_widget_set_sensitive (menuitem, transformations_pending);
162
163
164   if ( transformations_pending)
165     gtk_label_set_text (GTK_LABEL (status_label),
166                         _("Transformations Pending"));
167   else
168     gtk_label_set_text (GTK_LABEL (status_label), "");
169 }
170
171
172 static void open_data_file (const gchar *, struct data_editor *);
173
174
175 /* Puts FILE_NAME into the recent list.
176    If it's already in the list, it moves it to the top
177 */
178 static void
179 add_most_recent (const char *file_name)
180 {
181 #if RECENT_LISTS_AVAILABLE
182
183   GtkRecentManager *manager = gtk_recent_manager_get_default();
184   gchar *uri = g_filename_to_uri (file_name, NULL, NULL);
185
186   gtk_recent_manager_remove_item (manager, uri, NULL);
187
188   if ( ! gtk_recent_manager_add_item (manager, uri))
189     g_warning ("Could not add item %s to recent list\n",uri);
190
191   g_free (uri);
192 #endif
193 }
194
195
196
197 #if RECENT_LISTS_AVAILABLE
198
199 static void
200 on_recent_data_select (GtkMenuShell *menushell,   gpointer user_data)
201 {
202   gchar *file;
203   struct data_editor *de = user_data;
204
205   gchar *uri =
206     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
207
208   file = g_filename_from_uri (uri, NULL, NULL);
209
210   g_free (uri);
211
212   open_data_file (file, de);
213
214   g_free (file);
215 }
216
217 static void
218 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
219 {
220   gchar *file;
221
222   struct syntax_editor *se ;
223
224   gchar *uri =
225     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
226
227   file = g_filename_from_uri (uri, NULL, NULL);
228
229   g_free (uri);
230
231   se = (struct syntax_editor *)
232     window_create (WINDOW_SYNTAX, file);
233
234   load_editor_from_file (se, file, NULL);
235
236   g_free (file);
237 }
238
239 #endif
240
241 static void
242 datum_entry_activate (GtkEntry *entry, gpointer data)
243 {
244   gint row, column;
245   GtkSheet *data_sheet = GTK_SHEET (data);
246   PsppireDataStore *store = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
247
248   const char *text = gtk_entry_get_text (entry);
249
250   gtk_sheet_get_active_cell (data_sheet, &row, &column);
251
252   if ( row == -1 || column == -1)
253     return;
254
255   psppire_data_store_set_string (store, text, row, column);
256 }
257
258
259 /* Update the Edit->Paste menuitem
260    If PAGE is not -1 , then it should be set to the current page of
261    the data editors notebook widget.
262    If -1, then it'll be queried.
263 */
264 static void
265 update_paste_menuitem (struct data_editor *de, gint page)
266 {
267   GtkWidget * edit_paste = get_widget_assert (de->xml, "edit_paste");
268   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
269   GtkSheet * data_sheet ;
270   gint row, column;
271
272   if ( page < 0 )
273     page = gtk_notebook_get_current_page (GTK_NOTEBOOK(notebook));
274
275
276   if ( PAGE_VAR_SHEET == page )
277     {
278       /* We don't yet support pasting to the var sheet */
279       gtk_widget_set_sensitive (edit_paste, FALSE);
280       return;
281     }
282
283   data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
284
285   gtk_sheet_get_active_cell (data_sheet, &row, &column);
286
287   if ( row < 0 || column < 0 )
288       gtk_widget_set_sensitive (edit_paste, FALSE);
289   else
290       gtk_widget_set_sensitive (edit_paste, TRUE);
291 }
292
293 /* Update the Edit->Cut and Edit->Copy menuitems
294    If PAGE is not -1 , then it should be set to the current page of
295    the data editors notebook widget.
296    If -1, then it'll be queried.
297 */
298 static void
299 update_cut_copy_menuitem (struct data_editor *de, gint page)
300 {
301   GtkWidget * edit_copy = get_widget_assert (de->xml, "edit_copy");
302   GtkWidget * edit_cut = get_widget_assert (de->xml, "edit_cut");
303   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
304   GtkSheet * data_sheet ;
305   gint row, column;
306
307   if ( page < 0 )
308     page = gtk_notebook_get_current_page (GTK_NOTEBOOK(notebook));
309
310
311   if ( PAGE_VAR_SHEET == page )
312     {
313       /* We don't yet support copying from the var sheet */
314       gtk_widget_set_sensitive (edit_copy, FALSE);
315       gtk_widget_set_sensitive (edit_cut, FALSE);
316       return;
317     }
318
319   data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
320
321   gtk_sheet_get_active_cell (data_sheet, &row, &column);
322
323   if ( row < 0 || column < 0 )
324     {
325       gtk_widget_set_sensitive (edit_copy, FALSE);
326       gtk_widget_set_sensitive (edit_cut, FALSE);
327       return;
328     }
329
330   gtk_widget_set_sensitive (edit_copy, TRUE);
331   gtk_widget_set_sensitive (edit_cut, TRUE);
332 }
333
334
335 /* Callback for when the datasheet's active cell becomes active/inactive */
336 static gboolean
337 on_data_sheet_activate_change (GtkSheet *sheet,
338                                gint row, gint column, gpointer data)
339 {
340   struct data_editor *de = data;
341
342   update_paste_menuitem (de, -1);
343   update_cut_copy_menuitem (de, -1);
344
345   return TRUE;
346 }
347
348 extern struct dataset *the_dataset;
349
350 /*
351   Create a new data editor.
352 */
353 struct data_editor *
354 new_data_editor (void)
355 {
356   struct data_editor *de ;
357   struct editor_window *e;
358   GtkSheet *var_sheet ;
359   GtkSheet *data_sheet ;
360   PsppireVarStore *vs;
361   GtkWidget *datum_entry;
362
363   de = g_malloc0 (sizeof (*de));
364
365   e = (struct editor_window *) de;
366
367   de->xml = XML_NEW ("data-editor.glade");
368
369
370   dataset_add_transform_change_callback (the_dataset,
371                                          transformation_change_callback,
372                                          de);
373
374   var_sheet = GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
375   data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
376
377
378   g_signal_connect (G_OBJECT (data_sheet), "activate",
379                     G_CALLBACK (on_data_sheet_activate_change), de);
380
381   g_signal_connect (G_OBJECT (data_sheet), "deactivate",
382                     G_CALLBACK (on_data_sheet_activate_change), de);
383
384
385   vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
386
387   g_assert(vs); /* Traps a possible bug in win32 build */
388
389   g_signal_connect (G_OBJECT (data_sheet), "activate",
390                     G_CALLBACK (update_data_ref_entry),
391                     de->xml);
392
393   datum_entry = get_widget_assert (de->xml, "datum_entry");
394
395   g_signal_connect (G_OBJECT (datum_entry), "activate",
396                     G_CALLBACK (datum_entry_activate),
397                     data_sheet);
398
399   g_signal_connect (vs->dict, "weight-changed",
400                     G_CALLBACK (on_weight_change),
401                     de);
402
403   g_signal_connect (vs->dict, "filter-changed",
404                     G_CALLBACK (on_filter_change),
405                     de);
406
407   g_signal_connect (vs->dict, "split-changed",
408                     G_CALLBACK (on_split_change),
409                     de);
410
411   connect_help (de->xml);
412
413
414
415   g_signal_connect (get_widget_assert (de->xml, "edit_copy"),
416                     "activate",
417                     G_CALLBACK (on_edit_copy), de);
418
419   g_signal_connect (get_widget_assert (de->xml, "edit_cut"),
420                     "activate",
421                     G_CALLBACK (on_edit_cut), de);
422
423
424   register_data_editor_actions (de);
425
426   de->toggle_value_labels =
427     gtk_toggle_action_new ("toggle-value-labels",
428                            _("Labels"),
429                            _("Show/hide value labels"),
430                            "pspp-value-labels");
431
432   g_signal_connect (de->toggle_value_labels, "activate",
433                     G_CALLBACK (toggle_value_labels), de);
434
435
436   gtk_action_connect_proxy (GTK_ACTION (de->toggle_value_labels),
437                             get_widget_assert (de->xml,
438                                                "togglebutton-value-labels"));
439
440
441   gtk_action_connect_proxy (GTK_ACTION (de->toggle_value_labels),
442                             get_widget_assert (de->xml,
443                                                "view_value-labels"));
444
445   de->delete_cases =
446     gtk_action_new ("clear-cases",
447                     _("Clear"),
448                     _("Delete the cases at the selected position(s)"),
449                     "pspp-clear-cases");
450
451   g_signal_connect (de->delete_cases, "activate",
452                     G_CALLBACK (delete_cases), de);
453
454   gtk_action_connect_proxy (de->delete_cases,
455                             get_widget_assert (de->xml, "edit_clear-cases"));
456
457   g_signal_connect (get_widget_assert (de->xml, "edit_paste"), "activate",
458                     G_CALLBACK (on_edit_paste), de);
459
460   gtk_action_set_visible (de->delete_cases, FALSE);
461
462   de->delete_variables =
463     gtk_action_new ("clear-variables",
464                     _("Clear"),
465                     _("Delete the variables at the selected position(s)"),
466                     "pspp-clear-variables");
467
468   g_signal_connect (de->delete_variables, "activate",
469                     G_CALLBACK (delete_variables), de);
470
471   gtk_action_connect_proxy (de->delete_variables,
472                             get_widget_assert (de->xml, "edit_clear-variables")
473                             );
474
475   gtk_action_set_visible (de->delete_variables, FALSE);
476
477   de->insert_variable =
478     gtk_action_new ("insert-variable",
479                     _("Insert Variable"),
480                     _("Create a new variable at the current position"),
481                     "pspp-insert-variable");
482
483   g_signal_connect (de->insert_variable, "activate",
484                     G_CALLBACK (insert_variable), de);
485
486
487   gtk_action_connect_proxy (de->insert_variable,
488                             get_widget_assert (de->xml, "button-insert-variable")
489                             );
490
491   gtk_action_connect_proxy (de->insert_variable,
492                             get_widget_assert (de->xml, "edit_insert-variable")
493                             );
494
495
496   de->insert_case =
497     gtk_action_new ("insert-case",
498                     _("Insert Case"),
499                     _("Create a new case at the current position"),
500                     "pspp-insert-case");
501
502   g_signal_connect (de->insert_case, "activate",
503                     G_CALLBACK (insert_case), de);
504
505
506   gtk_action_connect_proxy (de->insert_case,
507                             get_widget_assert (de->xml, "button-insert-case")
508                             );
509
510
511   gtk_action_connect_proxy (de->insert_case,
512                             get_widget_assert (de->xml, "edit_insert-case")
513                             );
514
515
516
517   de->invoke_goto_dialog =
518     gtk_action_new ("goto-case-dialog",
519                     _("Goto Case"),
520                     _("Jump to a Case in the Data Sheet"),
521                     "gtk-jump-to");
522
523
524   gtk_action_connect_proxy (de->invoke_goto_dialog,
525                             get_widget_assert (de->xml, "button-goto-case")
526                             );
527
528   gtk_action_connect_proxy (de->invoke_goto_dialog,
529                             get_widget_assert (de->xml, "edit_goto-case")
530                             );
531
532
533   g_signal_connect (de->invoke_goto_dialog, "activate",
534                     G_CALLBACK (goto_case_dialog), de);
535
536
537   de->invoke_weight_cases_dialog =
538     gtk_action_new ("weight-cases-dialog",
539                     _("Weights"),
540                     _("Weight cases by variable"),
541                     "pspp-weight-cases");
542
543   g_signal_connect (de->invoke_weight_cases_dialog, "activate",
544                     G_CALLBACK (weight_cases_dialog), de);
545
546
547   de->invoke_transpose_dialog =
548     gtk_action_new ("transpose-dialog",
549                     _("Transpose"),
550                     _("Transpose the cases with the variables"),
551                     NULL);
552
553
554   g_signal_connect (de->invoke_transpose_dialog, "activate",
555                     G_CALLBACK (transpose_dialog), de);
556
557
558
559   de->invoke_split_file_dialog =
560     gtk_action_new ("split-file-dialog",
561                     _("Split"),
562                     _("Split the active file"),
563                     "pspp-split-file");
564
565   g_signal_connect (de->invoke_split_file_dialog, "activate",
566                     G_CALLBACK (split_file_dialog), de);
567
568
569
570   de->invoke_sort_cases_dialog =
571     gtk_action_new ("sort-cases-dialog",
572                     _("Sort"),
573                     _("Sort cases in the active file"),
574                     "pspp-sort-cases");
575
576   g_signal_connect (de->invoke_sort_cases_dialog, "activate",
577                     G_CALLBACK (sort_cases_dialog), de);
578
579   de->invoke_select_cases_dialog =
580     gtk_action_new ("select-cases-dialog",
581                     _("Select Cases"),
582                     _("Select cases from the active file"),
583                     "pspp-select-cases");
584
585   g_signal_connect (de->invoke_select_cases_dialog, "activate",
586                     G_CALLBACK (select_cases_dialog), de);
587
588
589   de->invoke_compute_dialog =
590     gtk_action_new ("compute-dialog",
591                     _("Compute"),
592                     _("Compute new values for a variable"),
593                     "pspp-compute");
594
595   g_signal_connect (de->invoke_compute_dialog, "activate",
596                     G_CALLBACK (compute_dialog), de);
597
598   de->invoke_oneway_anova_dialog =
599     gtk_action_new ("oneway-anova",
600                     _("Oneway _ANOVA"),
601                     _("Perform one way analysis of variance"),
602                     NULL);
603
604   g_signal_connect (de->invoke_oneway_anova_dialog, "activate",
605                     G_CALLBACK (oneway_anova_dialog), de);
606
607   de->invoke_t_test_independent_samples_dialog =
608     gtk_action_new ("t-test-independent-samples",
609                     _("_Independent Samples T Test"),
610                     _("Calculate T Test for samples from independent groups"),
611                     NULL);
612
613   g_signal_connect (de->invoke_t_test_independent_samples_dialog, "activate",
614                     G_CALLBACK (t_test_independent_samples_dialog), de);
615
616
617   de->invoke_t_test_paired_samples_dialog =
618     gtk_action_new ("t-test-paired-samples",
619                     _("_Paired Samples T Test"),
620                     _("Calculate T Test for paired samples"),
621                     NULL);
622
623   g_signal_connect (de->invoke_t_test_paired_samples_dialog, "activate",
624                     G_CALLBACK (t_test_paired_samples_dialog), de);
625
626
627   de->invoke_t_test_one_sample_dialog =
628     gtk_action_new ("t-test-one-sample",
629                     _("One _Sample T Test"),
630                     _("Calculate T Test for sample from a single distribution"),
631                     NULL);
632
633   g_signal_connect (de->invoke_t_test_one_sample_dialog, "activate",
634                     G_CALLBACK (t_test_one_sample_dialog), de);
635
636
637   de->invoke_comments_dialog =
638     gtk_action_new ("commments-dialog",
639                     _("Data File Comments"),
640                     _("Commentary text for the data file"),
641                     NULL);
642
643   g_signal_connect (de->invoke_comments_dialog, "activate",
644                     G_CALLBACK (comments_dialog), de);
645
646   de->invoke_find_dialog  =
647     gtk_action_new ("find-dialog",
648                     _("Find"),
649                     _("Find Case"),
650                     "gtk-find");
651
652   g_signal_connect (de->invoke_find_dialog, "activate",
653                     G_CALLBACK (find_dialog), de);
654
655
656   de->invoke_rank_dialog  =
657     gtk_action_new ("rank-dialog",
658                     _("Ran_k Cases"),
659                     _("Rank Cases"),
660                     "pspp-rank-cases");
661
662   g_signal_connect (de->invoke_rank_dialog, "activate",
663                     G_CALLBACK (rank_dialog), de);
664
665
666   de->invoke_recode_same_dialog  =
667     gtk_action_new ("recode-same-dialog",
668                     _("Recode into _Same Variables"),
669                     _("Recode values into the same Variables"),
670                     "pspp-recode-same");
671
672   g_signal_connect (de->invoke_recode_same_dialog, "activate",
673                     G_CALLBACK (recode_same_dialog), de);
674
675
676   de->invoke_recode_different_dialog  =
677     gtk_action_new ("recode-different-dialog",
678                     _("Recode into _Different Variables"),
679                     _("Recode values into different Variables"),
680                     "pspp-recode-different");
681
682   g_signal_connect (de->invoke_recode_different_dialog, "activate",
683                     G_CALLBACK (recode_different_dialog), de);
684
685
686   de->invoke_variable_info_dialog  =
687     gtk_action_new ("variable-info-dialog",
688                     _("Variables"),
689                     _("Jump to Variable"),
690                     "pspp-goto-variable");
691
692   g_signal_connect (de->invoke_variable_info_dialog, "activate",
693                     G_CALLBACK (variable_info_dialog), de);
694
695   de->invoke_descriptives_dialog =
696     gtk_action_new ("descriptives-dialog",
697                     _("_Descriptives"),
698                     _("Calculate descriptive statistics (mean, variance, ...)"),
699                     "pspp-descriptives");
700
701   g_signal_connect (de->invoke_descriptives_dialog, "activate",
702                     G_CALLBACK (descriptives_dialog), de);
703
704
705   de->invoke_frequencies_dialog =
706     gtk_action_new ("frequencies-dialog",
707                     _("_Frequencies"),
708                     _("Generate frequency statistics"),
709                     "pspp-frequencies");
710
711   g_signal_connect (de->invoke_frequencies_dialog, "activate",
712                     G_CALLBACK (frequencies_dialog), de);
713
714
715   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
716
717   g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
718                             "activate",
719                             G_CALLBACK (gtk_action_activate),
720                             de->action_data_new);
721
722   g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
723                             "activate",
724                             G_CALLBACK (gtk_action_activate),
725                             de->action_data_open);
726
727
728 #if RECENT_LISTS_AVAILABLE
729   {
730     GtkRecentManager *rm = gtk_recent_manager_get_default ();
731     GtkWidget *recent_data = get_widget_assert (de->xml, "file_recent-data");
732     GtkWidget *recent_files = get_widget_assert (de->xml, "file_recent-files");
733     GtkWidget *recent_separator = get_widget_assert (de->xml, "file_separator1");
734
735     GtkWidget *menu = gtk_recent_chooser_menu_new_for_manager (rm);
736
737     GtkRecentFilter *filter = gtk_recent_filter_new ();
738
739     gtk_widget_show (recent_data);
740     gtk_widget_show (recent_files);
741     gtk_widget_show (recent_separator);
742
743     gtk_recent_filter_add_pattern (filter, "*.sav");
744     gtk_recent_filter_add_pattern (filter, "*.SAV");
745
746     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
747
748     gtk_widget_set_sensitive (recent_data, TRUE);
749     g_signal_connect (menu, "selection-done",
750                       G_CALLBACK (on_recent_data_select), de);
751
752     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu);
753
754
755     filter = gtk_recent_filter_new ();
756     menu = gtk_recent_chooser_menu_new_for_manager (rm);
757
758     gtk_recent_filter_add_pattern (filter, "*.sps");
759     gtk_recent_filter_add_pattern (filter, "*.SPS");
760
761     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
762
763     gtk_widget_set_sensitive (recent_files, TRUE);
764     g_signal_connect (menu, "selection-done",
765                       G_CALLBACK (on_recent_files_select), de);
766
767     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu);
768   }
769 #endif
770
771   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
772                     "activate",
773                     G_CALLBACK (new_syntax_window),
774                     e->window);
775
776   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
777                     "activate",
778                     G_CALLBACK (open_syntax_window),
779                     e->window);
780
781   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
782                             "activate",
783                             G_CALLBACK (gtk_action_activate),
784                             de->action_data_save);
785
786   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
787                             "activate",
788                             G_CALLBACK (gtk_action_activate),
789                             de->action_data_save_as);
790
791   gtk_action_connect_proxy (de->invoke_find_dialog,
792                             get_widget_assert (de->xml, "edit_find")
793                             );
794
795   gtk_action_connect_proxy (de->invoke_find_dialog,
796                             get_widget_assert (de->xml, "button-find")
797                             );
798
799   gtk_action_connect_proxy (de->invoke_rank_dialog,
800                             get_widget_assert (de->xml, "transform_rank")
801                             );
802
803   gtk_action_connect_proxy (de->invoke_recode_same_dialog,
804                             get_widget_assert (de->xml,
805                                                "transform_recode-same")
806                             );
807
808   gtk_action_connect_proxy (de->invoke_recode_different_dialog,
809                             get_widget_assert (de->xml,
810                                                "transform_recode-different")
811                             );
812
813   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
814                             get_widget_assert (de->xml, "data_weight-cases")
815                             );
816
817   gtk_action_connect_proxy (de->invoke_transpose_dialog,
818                             get_widget_assert (de->xml, "data_transpose")
819                             );
820
821   gtk_action_connect_proxy (de->invoke_split_file_dialog,
822                             get_widget_assert (de->xml, "data_split-file")
823                             );
824
825   gtk_action_connect_proxy (de->invoke_sort_cases_dialog,
826                             get_widget_assert (de->xml, "data_sort-cases")
827                             );
828
829   gtk_action_connect_proxy (de->invoke_select_cases_dialog,
830                             get_widget_assert (de->xml, "data_select-cases")
831                             );
832
833   gtk_action_connect_proxy (de->invoke_compute_dialog,
834                             get_widget_assert (de->xml, "transform_compute")
835                             );
836
837   gtk_action_connect_proxy (de->invoke_t_test_independent_samples_dialog,
838                             get_widget_assert (de->xml,
839                                                "indep-t-test")
840                             );
841
842
843   gtk_action_connect_proxy (de->invoke_t_test_paired_samples_dialog,
844                             get_widget_assert (de->xml,
845                                                "paired-t-test")
846                             );
847
848
849   gtk_action_connect_proxy (de->invoke_t_test_one_sample_dialog,
850                             get_widget_assert (de->xml,
851                                                "one-sample-t-test")
852                             );
853
854
855   gtk_action_connect_proxy (de->invoke_oneway_anova_dialog,
856                             get_widget_assert (de->xml,
857                                                "oneway-anova")
858                             );
859
860
861   gtk_action_connect_proxy (de->invoke_comments_dialog,
862                             get_widget_assert (de->xml, "utilities_comments")
863                             );
864
865   gtk_action_connect_proxy (de->invoke_variable_info_dialog,
866                             get_widget_assert (de->xml, "utilities_variables")
867                             );
868
869   gtk_action_connect_proxy (de->invoke_descriptives_dialog,
870                             get_widget_assert (de->xml, "analyze_descriptives")
871                             );
872
873   gtk_action_connect_proxy (de->invoke_frequencies_dialog,
874                             get_widget_assert (de->xml, "analyze_frequencies")
875                             );
876
877   g_signal_connect (get_widget_assert (de->xml,"help_about"),
878                     "activate",
879                     G_CALLBACK (about_new),
880                     e->window);
881
882
883   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
884                     "activate",
885                     G_CALLBACK (reference_manual),
886                     e->window);
887
888   g_signal_connect (data_sheet,
889                     "double-click-column",
890                     G_CALLBACK (click2column),
891                     de);
892
893   g_signal_connect (data_sheet,
894                     "select-column",
895                     G_CALLBACK (enable_delete_variables),
896                     de);
897
898   g_signal_connect (data_sheet,
899                     "select-row",
900                     G_CALLBACK (enable_delete_cases),
901                     de);
902
903
904   g_signal_connect (var_sheet,
905                     "double-click-row",
906                     GTK_SIGNAL_FUNC (click2row),
907                     de);
908
909   g_signal_connect_after (var_sheet,
910                     "select-row",
911                     G_CALLBACK (enable_delete_variables),
912                     de);
913
914   g_signal_connect (get_widget_assert (de->xml, "notebook"),
915                     "switch-page",
916                     G_CALLBACK (on_switch_sheet), de);
917
918
919   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
920                     "activate",
921                     G_CALLBACK (status_bar_activate), de);
922
923
924   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
925                     "activate",
926                     G_CALLBACK (grid_lines_activate), de);
927
928
929
930   g_signal_connect (get_widget_assert (de->xml, "view_data"),
931                     "activate",
932                     G_CALLBACK (data_sheet_activate), de);
933
934   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
935                     "activate",
936                     G_CALLBACK (variable_sheet_activate), de);
937
938
939
940   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
941                     "activate",
942                     G_CALLBACK (fonts_activate), de);
943
944
945
946
947   gtk_action_connect_proxy (de->action_data_open,
948                             get_widget_assert (de->xml, "button-open")
949                             );
950
951   gtk_action_connect_proxy (de->action_data_save,
952                             get_widget_assert (de->xml, "button-save")
953                             );
954
955   gtk_action_connect_proxy (de->invoke_variable_info_dialog,
956                             get_widget_assert (de->xml, "button-goto-variable")
957                             );
958
959   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
960                             get_widget_assert (de->xml, "button-weight-cases")
961                             );
962
963   gtk_action_connect_proxy (de->invoke_split_file_dialog,
964                             get_widget_assert (de->xml, "button-split-file")
965                             );
966
967   gtk_action_connect_proxy (de->invoke_select_cases_dialog,
968                             get_widget_assert (de->xml, "button-select-cases")
969                             );
970
971
972   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
973                     "activate",
974                     G_CALLBACK (file_quit), de);
975
976   g_signal_connect (get_widget_assert (de->xml, "transform_run-pending"),
977                     "activate",
978                     G_CALLBACK (execute), de);
979
980
981   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
982                     "activate",
983                     G_CALLBACK (minimise_all_windows), NULL);
984
985
986   create_data_sheet_variable_popup_menu (de);
987   create_data_sheet_cases_popup_menu (de);
988
989   g_signal_connect (G_OBJECT (data_sheet), "button-event-column",
990                     G_CALLBACK (popup_variable_menu), de);
991
992   g_signal_connect (G_OBJECT (data_sheet), "button-event-row",
993                     G_CALLBACK (popup_cases_menu), de);
994
995   return de;
996 }
997
998
999 /* Callback which occurs when the var sheet's row title
1000    button is double clicked */
1001 static gboolean
1002 click2row (GtkWidget *w, gint row, gpointer data)
1003 {
1004   struct data_editor *de = data;
1005   GtkSheetRange visible_range;
1006
1007   gint current_row, current_column;
1008
1009   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
1010
1011   data_editor_select_sheet (de, PAGE_DATA_SHEET);
1012
1013   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
1014                              &current_row, &current_column);
1015
1016   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
1017
1018   gtk_sheet_get_visible_range (GTK_SHEET (data_sheet), &visible_range);
1019
1020   if ( row < visible_range.col0 || row > visible_range.coli)
1021     {
1022       gtk_sheet_moveto (GTK_SHEET (data_sheet),
1023                         current_row, row, 0, 0);
1024     }
1025
1026   return FALSE;
1027 }
1028
1029
1030 /* Callback which occurs when the data sheet's column title
1031    is double clicked */
1032 static gboolean
1033 click2column (GtkWidget *w, gint col, gpointer data)
1034 {
1035   struct data_editor *de = data;
1036
1037   gint current_row, current_column;
1038
1039   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
1040
1041   data_editor_select_sheet (de, PAGE_VAR_SHEET);
1042
1043   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
1044                              &current_row, &current_column);
1045
1046   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
1047
1048   return FALSE;
1049 }
1050
1051
1052 void
1053 new_data_window (GtkMenuItem *menuitem, gpointer parent)
1054 {
1055   window_create (WINDOW_DATA, NULL);
1056 }
1057
1058 /* Callback for when the datasheet/varsheet is selected */
1059 static void
1060 on_switch_sheet (GtkNotebook *notebook,
1061                 GtkNotebookPage *page,
1062                 guint page_num,
1063                 gpointer user_data)
1064 {
1065   struct data_editor *de = user_data;
1066
1067   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
1068   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
1069
1070   switch (page_num)
1071     {
1072     case PAGE_VAR_SHEET:
1073       gtk_widget_hide (view_variables);
1074       gtk_widget_show (view_data);
1075       gtk_action_set_sensitive (de->insert_variable, TRUE);
1076       gtk_action_set_sensitive (de->insert_case, FALSE);
1077       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
1078       break;
1079     case PAGE_DATA_SHEET:
1080       gtk_widget_show (view_variables);
1081       gtk_widget_show (view_data);
1082       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
1083       gtk_action_set_sensitive (de->insert_case, TRUE);
1084       break;
1085     default:
1086       g_assert_not_reached ();
1087       break;
1088     }
1089
1090   update_paste_menuitem (de, page_num);
1091   update_cut_copy_menuitem (de, page_num);
1092 }
1093
1094
1095 void
1096 data_editor_select_sheet (struct data_editor *de, gint page)
1097 {
1098   gtk_notebook_set_current_page
1099    (
1100     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
1101     );
1102 }
1103
1104
1105
1106 static void
1107 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
1108 {
1109   struct data_editor *de = data;
1110   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
1111
1112   if ( gtk_check_menu_item_get_active (menuitem) )
1113     gtk_widget_show (statusbar);
1114   else
1115     gtk_widget_hide (statusbar);
1116 }
1117
1118
1119 static void
1120 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
1121 {
1122   struct data_editor *de = data;
1123   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
1124
1125   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
1126                                                      "variable_sheet")),
1127                        grid_visible);
1128
1129   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
1130                        grid_visible);
1131 }
1132
1133
1134
1135 static void
1136 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
1137 {
1138   struct data_editor *de = data;
1139
1140   data_editor_select_sheet (de, PAGE_DATA_SHEET);
1141 }
1142
1143
1144 static void
1145 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
1146 {
1147   struct data_editor *de = data;
1148
1149   data_editor_select_sheet (de, PAGE_VAR_SHEET);
1150 }
1151
1152
1153 static void
1154 fonts_activate (GtkMenuItem *menuitem, gpointer data)
1155 {
1156   struct data_editor *de = data;
1157   GtkWidget *dialog =
1158     gtk_font_selection_dialog_new (_("Font Selection"));
1159
1160   gtk_window_set_transient_for (GTK_WINDOW (dialog),
1161                                 GTK_WINDOW (get_widget_assert (de->xml,
1162                                                                "data_editor")));
1163   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
1164     {
1165       GtkSheet *data_sheet =
1166         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1167
1168       GtkSheet *var_sheet =
1169         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1170
1171       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
1172       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
1173
1174       const gchar *font = gtk_font_selection_dialog_get_font_name
1175         (GTK_FONT_SELECTION_DIALOG (dialog));
1176
1177       PangoFontDescription* font_desc =
1178         pango_font_description_from_string (font);
1179
1180       psppire_var_store_set_font (vs, font_desc);
1181       psppire_data_store_set_font (ds, font_desc);
1182     }
1183
1184   gtk_widget_hide (dialog);
1185 }
1186
1187
1188
1189 /* Callback for the value labels action */
1190 static void
1191 toggle_value_labels (GtkToggleAction *ta, gpointer data)
1192 {
1193   struct data_editor *de = data;
1194
1195   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1196
1197   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
1198
1199
1200   psppire_data_store_show_labels (ds,
1201                                   gtk_toggle_action_get_active (ta));
1202 }
1203
1204
1205 static void
1206 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
1207 {
1208   /* FIXME: Need to be more intelligent here.
1209      Give the user the opportunity to save any unsaved data.
1210   */
1211   gtk_main_quit ();
1212 }
1213
1214 static void
1215 delete_cases (GtkAction *action, gpointer data)
1216 {
1217   struct data_editor *de = data;
1218   GtkSheet *data_sheet =
1219     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1220
1221   GtkSheetRange range;
1222
1223   PsppireDataStore *data_store = PSPPIRE_DATA_STORE
1224     (gtk_sheet_get_model (data_sheet) );
1225
1226
1227   /* This shouldn't be able to happen, because the action
1228      should be disabled */
1229   g_return_if_fail (gtk_sheet_get_state (data_sheet)
1230                     ==  GTK_SHEET_ROW_SELECTED );
1231
1232   gtk_sheet_get_selected_range (data_sheet, &range);
1233
1234   gtk_sheet_unselect_range (data_sheet);
1235
1236   psppire_data_store_delete_cases (data_store, range.row0,
1237                                    1 + range.rowi - range.row0);
1238
1239 }
1240
1241 static void
1242 delete_variables (GtkAction *a, gpointer data)
1243 {
1244   struct data_editor *de = data;
1245   GtkSheetRange range;
1246
1247   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
1248                                                            "notebook"));
1249
1250   const gint page = gtk_notebook_get_current_page (notebook);
1251
1252   GtkSheet *sheet = GTK_SHEET (get_widget_assert (de->xml,
1253                                                   (page == PAGE_VAR_SHEET) ?
1254                                                   "variable_sheet" :
1255                                                   "data_sheet"));
1256
1257
1258   gtk_sheet_get_selected_range (sheet, &range);
1259
1260   switch ( page )
1261     {
1262     case PAGE_VAR_SHEET:
1263       {
1264         PsppireVarStore *vs =
1265           PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
1266
1267         psppire_dict_delete_variables (vs->dict,
1268                                        range.row0,
1269                                        1 +
1270                                        range.rowi -
1271                                        range.row0 );
1272       }
1273       break;
1274     case PAGE_DATA_SHEET:
1275       {
1276         PsppireDataStore *ds =
1277           PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1278
1279         psppire_dict_delete_variables (ds->dict,
1280                                        range.col0,
1281                                        1 +
1282                                        range.coli -
1283                                        range.col0 );
1284       }
1285       break;
1286     };
1287
1288   gtk_sheet_unselect_range (sheet);
1289 }
1290
1291 static void
1292 insert_case (GtkAction *action, gpointer data)
1293 {
1294   gint current_row ;
1295   struct data_editor *de = data;
1296
1297   GtkSheet *data_sheet =
1298     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1299
1300   PsppireDataStore *ds = PSPPIRE_DATA_STORE
1301     (gtk_sheet_get_model (data_sheet) );
1302
1303
1304   gtk_sheet_get_active_cell (data_sheet, &current_row, NULL);
1305
1306   if (current_row < 0) current_row = 0;
1307
1308   psppire_data_store_insert_new_case (ds, current_row);
1309 }
1310
1311 /* Insert a new variable before the current row in the variable sheet,
1312    or before the current column in the data sheet, whichever is selected */
1313 static void
1314 insert_variable (GtkAction *action, gpointer data)
1315 {
1316   struct data_editor *de = data;
1317   gint posn = -1;
1318
1319   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
1320
1321   GtkSheet *var_sheet =
1322     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1323
1324   PsppireVarStore *vs = PSPPIRE_VAR_STORE
1325     (gtk_sheet_get_model (var_sheet) );
1326
1327   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
1328     {
1329     case PAGE_VAR_SHEET:
1330       posn = var_sheet->active_cell.row;
1331       break;
1332     case PAGE_DATA_SHEET:
1333       {
1334         GtkSheet *data_sheet =
1335           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1336
1337         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
1338           posn = data_sheet->range.col0;
1339         else
1340           posn = data_sheet->active_cell.col;
1341       }
1342       break;
1343     default:
1344       g_assert_not_reached ();
1345     }
1346
1347   if ( posn == -1 ) posn = 0;
1348
1349   psppire_dict_insert_variable (vs->dict, posn, NULL);
1350 }
1351
1352 /* Callback for when the dictionary changes its split variables */
1353 static void
1354 on_split_change (PsppireDict *dict, gpointer data)
1355 {
1356   struct data_editor *de = data;
1357
1358   size_t n_split_vars = dict_get_split_cnt (dict->dict);
1359
1360   GtkWidget *split_status_area =
1361     get_widget_assert (de->xml, "split-file-status-area");
1362
1363   if ( n_split_vars == 0 )
1364     {
1365       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
1366     }
1367   else
1368     {
1369       gint i;
1370       GString *text;
1371       const struct variable *const * split_vars =
1372         dict_get_split_vars (dict->dict);
1373
1374       text = g_string_new (_("Split by "));
1375
1376       for (i = 0 ; i < n_split_vars - 1; ++i )
1377         {
1378           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
1379         }
1380       g_string_append (text, var_get_name (split_vars[i]));
1381
1382       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
1383
1384       g_string_free (text, TRUE);
1385     }
1386 }
1387
1388
1389 /* Callback for when the dictionary changes its filter variable */
1390 static void
1391 on_filter_change (GObject *o, gint filter_index, gpointer data)
1392 {
1393   struct data_editor *de = data;
1394   GtkWidget *filter_status_area =
1395     get_widget_assert (de->xml, "filter-use-status-area");
1396
1397   if ( filter_index == -1 )
1398     {
1399       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
1400     }
1401   else
1402     {
1403       GtkSheet *var_sheet =
1404         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1405
1406       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1407         (gtk_sheet_get_model (var_sheet) );
1408
1409       struct variable *var = psppire_dict_get_variable (vs->dict,
1410                                                         filter_index);
1411
1412       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
1413
1414       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
1415
1416       g_free (text);
1417     }
1418 }
1419
1420 /* Callback for when the dictionary changes its weights */
1421 static void
1422 on_weight_change (GObject *o, gint weight_index, gpointer data)
1423 {
1424   struct data_editor *de = data;
1425   GtkWidget *weight_status_area =
1426     get_widget_assert (de->xml, "weight-status-area");
1427
1428   if ( weight_index == -1 )
1429     {
1430       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
1431     }
1432   else
1433     {
1434       GtkSheet *var_sheet =
1435         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1436
1437       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1438         (gtk_sheet_get_model (var_sheet) );
1439
1440       struct variable *var = psppire_dict_get_variable (vs->dict,
1441                                                         weight_index);
1442
1443       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
1444
1445       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
1446
1447       g_free (text);
1448     }
1449 }
1450
1451
1452
1453 \f
1454 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
1455 static void new_file (GtkAction *, struct editor_window *de);
1456 static void open_data_dialog (GtkAction *, struct data_editor *de);
1457 static void data_save (GtkAction *action, struct data_editor *e);
1458
1459
1460 /* Create the GtkActions and connect to their signals */
1461 static void
1462 register_data_editor_actions (struct data_editor *de)
1463 {
1464   de->action_data_open =
1465     gtk_action_new ("data-open-dialog",
1466                     _("Open"),
1467                     _("Open a data file"),
1468                     "gtk-open");
1469
1470   g_signal_connect (de->action_data_open, "activate",
1471                     G_CALLBACK (open_data_dialog), de);
1472
1473
1474   de->action_data_save = gtk_action_new ("data-save",
1475                                             _("Save"),
1476                                             _("Save data to file"),
1477                                             "gtk-save");
1478
1479   g_signal_connect (de->action_data_save, "activate",
1480                     G_CALLBACK (data_save), de);
1481
1482
1483
1484   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
1485                                             _("Save As"),
1486                                             _("Save data to file"),
1487                                             "gtk-save");
1488
1489   g_signal_connect (de->action_data_save_as, "activate",
1490                     G_CALLBACK (data_save_as_dialog), de);
1491
1492   de->action_data_new =
1493     gtk_action_new ("data-new",
1494                     _("New"),
1495                     _("New data file"),
1496                     NULL);
1497
1498   g_signal_connect (de->action_data_new, "activate",
1499                     G_CALLBACK (new_file), de);
1500 }
1501
1502 /* Returns true if NAME has a suffix which might denote a PSPP file */
1503 static gboolean
1504 name_has_suffix (const gchar *name)
1505 {
1506   if ( g_str_has_suffix (name, ".sav"))
1507     return TRUE;
1508   if ( g_str_has_suffix (name, ".SAV"))
1509     return TRUE;
1510   if ( g_str_has_suffix (name, ".por"))
1511     return TRUE;
1512   if ( g_str_has_suffix (name, ".POR"))
1513     return TRUE;
1514
1515   return FALSE;
1516 }
1517
1518 /* Append SUFFIX to the filename of DE */
1519 static void
1520 append_filename_suffix (struct data_editor *de, const gchar *suffix)
1521 {
1522   if ( ! name_has_suffix (de->file_name))
1523     {
1524       gchar *s = de->file_name;
1525       de->file_name = g_strconcat (de->file_name, suffix, NULL);
1526       g_free (s);
1527     }
1528 }
1529
1530 /* Save DE to file */
1531 static void
1532 save_file (struct data_editor *de)
1533 {
1534   struct getl_interface *sss;
1535   struct string file_name ;
1536
1537   g_assert (de->file_name);
1538
1539   ds_init_cstr (&file_name, de->file_name);
1540   gen_quoted_string (&file_name);
1541
1542   if ( de->save_as_portable )
1543     {
1544       append_filename_suffix (de, ".por");
1545       sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
1546                                          ds_cstr (&file_name));
1547     }
1548   else
1549     {
1550       append_filename_suffix (de, ".sav");
1551       sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
1552                                          ds_cstr (&file_name));
1553     }
1554
1555   ds_destroy (&file_name);
1556
1557   execute_syntax (sss);
1558 }
1559
1560
1561 /* Callback for data_save action.
1562    If there's an existing file name, then just save,
1563    otherwise prompt for a file name, then save */
1564 static void
1565 data_save (GtkAction *action, struct data_editor *de)
1566 {
1567   if ( de->file_name)
1568     save_file (de);
1569   else
1570     data_save_as_dialog (action, de);
1571 }
1572
1573
1574 /* Callback for data_save_as action. Prompt for a filename and save */
1575 static void
1576 data_save_as_dialog (GtkAction *action, struct data_editor *de)
1577 {
1578   struct editor_window *e = (struct editor_window *) de;
1579
1580   GtkWidget *button_sys;
1581   GtkWidget *dialog =
1582     gtk_file_chooser_dialog_new (_("Save"),
1583                                  GTK_WINDOW (e->window),
1584                                  GTK_FILE_CHOOSER_ACTION_SAVE,
1585                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1586                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1587                                  NULL);
1588
1589   GtkFileFilter *filter = gtk_file_filter_new ();
1590   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1591   gtk_file_filter_add_pattern (filter, "*.sav");
1592   gtk_file_filter_add_pattern (filter, "*.SAV");
1593   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1594
1595   filter = gtk_file_filter_new ();
1596   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1597   gtk_file_filter_add_pattern (filter, "*.por");
1598   gtk_file_filter_add_pattern (filter, "*.POR");
1599   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1600
1601   filter = gtk_file_filter_new ();
1602   gtk_file_filter_set_name (filter, _("All Files"));
1603   gtk_file_filter_add_pattern (filter, "*");
1604   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1605
1606   {
1607     GtkWidget *button_por;
1608     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
1609     button_sys =
1610       gtk_radio_button_new_with_label (NULL, _("System File"));
1611
1612     button_por =
1613       gtk_radio_button_new_with_label
1614       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
1615        _("Portable File"));
1616
1617     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
1618     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
1619
1620     gtk_widget_show_all (vbox);
1621
1622     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
1623   }
1624
1625   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1626     {
1627     case GTK_RESPONSE_ACCEPT:
1628       {
1629         g_free (de->file_name);
1630
1631         de->file_name =
1632           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1633
1634         de->save_as_portable =
1635           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
1636
1637         save_file (de);
1638
1639         window_set_name_from_filename (e, de->file_name);
1640       }
1641       break;
1642     default:
1643       break;
1644     }
1645
1646   gtk_widget_destroy (dialog);
1647 }
1648
1649
1650 /* Callback for data_new action.
1651    Performs the NEW FILE command */
1652 static void
1653 new_file (GtkAction *action, struct editor_window *e)
1654 {
1655   struct data_editor *de = (struct data_editor *) e;
1656
1657   struct getl_interface *sss =
1658     create_syntax_string_source ("NEW FILE.");
1659
1660   execute_syntax (sss);
1661
1662   g_free (de->file_name);
1663   de->file_name = NULL;
1664
1665   default_window_name (e);
1666 }
1667
1668
1669 static void
1670 open_data_file (const gchar *file_name, struct data_editor *de)
1671 {
1672   struct getl_interface *sss;
1673   struct string filename;
1674
1675   ds_init_cstr (&filename, file_name);
1676
1677   gen_quoted_string (&filename);
1678
1679   sss = create_syntax_string_source ("GET FILE=%s.",
1680                                      ds_cstr (&filename));
1681   ds_destroy (&filename);
1682
1683   if (execute_syntax (sss) )
1684   {
1685     window_set_name_from_filename ((struct editor_window *) de, file_name);
1686     add_most_recent (file_name);
1687   }
1688 }
1689
1690
1691
1692 /* Callback for the data_open action.
1693    Prompts for a filename and opens it */
1694 static void
1695 open_data_dialog (GtkAction *action, struct data_editor *de)
1696 {
1697   struct editor_window *e = (struct editor_window *) de;
1698
1699   GtkWidget *dialog =
1700     gtk_file_chooser_dialog_new (_("Open"),
1701                                  GTK_WINDOW (e->window),
1702                                  GTK_FILE_CHOOSER_ACTION_OPEN,
1703                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1704                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1705                                  NULL);
1706
1707   GtkFileFilter *filter = gtk_file_filter_new ();
1708   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1709   gtk_file_filter_add_pattern (filter, "*.sav");
1710   gtk_file_filter_add_pattern (filter, "*.SAV");
1711   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1712
1713   filter = gtk_file_filter_new ();
1714   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1715   gtk_file_filter_add_pattern (filter, "*.por");
1716   gtk_file_filter_add_pattern (filter, "*.POR");
1717   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1718
1719   filter = gtk_file_filter_new ();
1720   gtk_file_filter_set_name (filter, _("All Files"));
1721   gtk_file_filter_add_pattern (filter, "*");
1722   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1723
1724
1725   if ( de->file_name)
1726     {
1727       gchar *dir_name = g_path_get_dirname (de->file_name);
1728       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1729                                            dir_name);
1730       free (dir_name);
1731     }
1732
1733   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1734     {
1735     case GTK_RESPONSE_ACCEPT:
1736       {
1737         g_free (de->file_name);
1738         de->file_name =
1739           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1740
1741         open_data_file (de->file_name, de);
1742       }
1743       break;
1744     default:
1745       break;
1746     }
1747
1748   gtk_widget_destroy (dialog);
1749 }
1750
1751
1752
1753 /* Update the data_ref_entry with the reference of the active cell */
1754 static gint
1755 update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
1756 {
1757   GladeXML *data_editor_xml = data;
1758
1759   PsppireDataStore *data_store =
1760     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1761
1762   g_return_val_if_fail (data_editor_xml, FALSE);
1763
1764   if (data_store)
1765     {
1766       const struct variable *var =
1767         psppire_dict_get_variable (data_store->dict, col);
1768
1769       /* The entry where the reference to the current cell is displayed */
1770       GtkEntry *cell_ref_entry =
1771         GTK_ENTRY (get_widget_assert (data_editor_xml,
1772                                       "cell_ref_entry"));
1773       GtkEntry *datum_entry =
1774         GTK_ENTRY (get_widget_assert (data_editor_xml,
1775                                       "datum_entry"));
1776
1777       if ( var )
1778         {
1779           gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
1780                                          var_get_name (var));
1781
1782           gchar *s = pspp_locale_to_utf8 (text, -1, 0);
1783
1784           g_free (text);
1785
1786           gtk_entry_set_text (cell_ref_entry, s);
1787
1788           g_free (s);
1789         }
1790       else
1791         gtk_entry_set_text (cell_ref_entry, "");
1792
1793
1794       if ( var )
1795         {
1796           gchar *text =
1797             psppire_data_store_get_string (data_store, row,
1798                                            var_get_dict_index(var));
1799           g_strchug (text);
1800
1801           gtk_entry_set_text (datum_entry, text);
1802
1803           free (text);
1804         }
1805       else
1806         gtk_entry_set_text (datum_entry, "");
1807     }
1808
1809   return FALSE;
1810 }
1811
1812
1813
1814
1815
1816 static void
1817 do_sort (PsppireDataStore *ds, int var, gboolean descend)
1818 {
1819   GString *string = g_string_new ("SORT CASES BY ");
1820
1821   const struct variable *v =
1822     psppire_dict_get_variable (ds->dict, var);
1823
1824   g_string_append_printf (string, "%s", var_get_name (v));
1825
1826   if ( descend )
1827     g_string_append (string, " (D)");
1828
1829   g_string_append (string, ".");
1830
1831   execute_syntax (create_syntax_string_source (string->str));
1832
1833   g_string_free (string, TRUE);
1834 }
1835
1836
1837 static void
1838 sort_up (GtkMenuItem *item, gpointer data)
1839 {
1840   GtkSheet *sheet  = data;
1841   GtkSheetRange range;
1842   gtk_sheet_get_selected_range (sheet, &range);
1843
1844   do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)),
1845            range.col0, FALSE);
1846
1847 }
1848
1849 static void
1850 sort_down (GtkMenuItem *item, gpointer data)
1851 {
1852   GtkSheet *sheet  = data;
1853   GtkSheetRange range;
1854   gtk_sheet_get_selected_range (sheet, &range);
1855
1856   do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)),
1857            range.col0, TRUE);
1858 }
1859
1860
1861
1862
1863 static void
1864 create_data_sheet_variable_popup_menu (struct data_editor *de)
1865 {
1866   GtkSheet *sheet  = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1867   GtkWidget *menu = gtk_menu_new ();
1868
1869   GtkWidget *sort_ascending =
1870     gtk_menu_item_new_with_label (_("Sort Ascending"));
1871
1872   GtkWidget *sort_descending =
1873     gtk_menu_item_new_with_label (_("Sort Descending"));
1874
1875
1876   GtkWidget *insert_variable =
1877     gtk_menu_item_new_with_label (_("Insert Variable"));
1878
1879   GtkWidget *clear_variable =
1880     gtk_menu_item_new_with_label (_("Clear"));
1881
1882
1883   gtk_action_connect_proxy (de->insert_variable,
1884                             insert_variable );
1885
1886
1887   gtk_action_connect_proxy (de->delete_variables,
1888                             clear_variable );
1889
1890
1891   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
1892
1893
1894   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1895                          gtk_separator_menu_item_new ());
1896
1897
1898   gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable);
1899
1900
1901   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1902                          gtk_separator_menu_item_new ());
1903
1904
1905   g_signal_connect (G_OBJECT (sort_ascending), "activate",
1906                     G_CALLBACK (sort_up), sheet);
1907
1908   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending);
1909
1910
1911   g_signal_connect (G_OBJECT (sort_descending), "activate",
1912                     G_CALLBACK (sort_down), sheet);
1913
1914
1915   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending);
1916
1917   gtk_widget_show_all (menu);
1918
1919
1920   de->data_sheet_variable_popup_menu = GTK_MENU(menu);
1921 }
1922
1923
1924 static void
1925 create_data_sheet_cases_popup_menu (struct data_editor *de)
1926 {
1927   GtkWidget *menu = gtk_menu_new ();
1928
1929   GtkWidget *insert_case =
1930     gtk_menu_item_new_with_label (_("Insert Case"));
1931
1932   GtkWidget *delete_case =
1933     gtk_menu_item_new_with_label (_("Clear"));
1934
1935
1936   gtk_action_connect_proxy (de->insert_case,
1937                             insert_case);
1938
1939
1940   gtk_action_connect_proxy (de->delete_cases,
1941                             delete_case);
1942
1943
1944   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case);
1945
1946
1947   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1948                          gtk_separator_menu_item_new ());
1949
1950
1951   gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case);
1952
1953
1954   gtk_widget_show_all (menu);
1955
1956
1957   de->data_sheet_cases_popup_menu = GTK_MENU (menu);
1958 }
1959
1960
1961 static void
1962 popup_variable_menu (GtkSheet *sheet, gint column,
1963                      GdkEventButton *event, gpointer data)
1964 {
1965   struct data_editor *de = data;
1966
1967   PsppireDataStore *data_store =
1968     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1969
1970   const struct variable *v =
1971     psppire_dict_get_variable (data_store->dict, column);
1972
1973   if ( v && event->button == 3)
1974     {
1975
1976       gtk_sheet_select_column (sheet, column);
1977
1978       gtk_menu_popup (GTK_MENU (de->data_sheet_variable_popup_menu),
1979                       NULL, NULL, NULL, NULL,
1980                       event->button, event->time);
1981     }
1982 }
1983
1984
1985 static void
1986 popup_cases_menu (GtkSheet *sheet, gint row,
1987                   GdkEventButton *event, gpointer data)
1988 {
1989   struct data_editor *de = data;
1990
1991   PsppireDataStore *data_store =
1992     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1993
1994   if ( row <= psppire_data_store_get_case_count (data_store) &&
1995        event->button == 3)
1996     {
1997       gtk_sheet_select_row (sheet, row);
1998
1999       gtk_menu_popup (GTK_MENU (de->data_sheet_cases_popup_menu),
2000                       NULL, NULL, NULL, NULL,
2001                       event->button, event->time);
2002     }
2003 }
2004
2005
2006 static void
2007 on_edit_paste (GtkAction *a, gpointer data)
2008 {
2009   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2010
2011   gtk_clipboard_request_contents (clipboard,
2012                                   gdk_atom_intern ("UTF8_STRING", TRUE),
2013                                   data_sheet_contents_received_callback,
2014                                   data);
2015 }
2016
2017
2018 static void
2019 on_edit_copy (GtkMenuItem *m, gpointer data)
2020 {
2021   struct data_editor *de = data;
2022
2023   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2024                                                        "data_sheet"));
2025
2026   data_sheet_set_clip (data_sheet);
2027 }
2028
2029
2030
2031 static void
2032 on_edit_cut (GtkMenuItem *m, gpointer data)
2033 {
2034   struct data_editor *de = data;
2035   gint max_rows, max_columns;
2036   gint r;
2037   GtkSheetRange range;
2038   PsppireDataStore *ds;
2039   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2040                                                        "data_sheet"));
2041
2042   data_sheet_set_clip (data_sheet);
2043
2044
2045   /* Now blank all the cells */
2046   gtk_sheet_get_selected_range (data_sheet, &range);
2047
2048   ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
2049
2050
2051    /* If nothing selected, then use active cell */
2052   if ( range.row0 < 0 || range.col0 < 0 )
2053     {
2054       gint row, col;
2055       gtk_sheet_get_active_cell (data_sheet, &row, &col);
2056
2057       range.row0 = range.rowi = row;
2058       range.col0 = range.coli = col;
2059     }
2060
2061   /* The sheet range can include cells that do not include data.
2062      Exclude them from the range. */
2063   max_rows = psppire_data_store_get_case_count (ds);
2064   if (range.rowi >= max_rows)
2065     {
2066       if (max_rows == 0)
2067         return;
2068       range.rowi = max_rows - 1;
2069     }
2070
2071   max_columns = dict_get_var_cnt (ds->dict->dict);
2072   if (range.coli >= max_columns)
2073     {
2074       if (max_columns == 0)
2075         return;
2076       range.coli = max_columns - 1;
2077     }
2078
2079   g_return_if_fail (range.rowi >= range.row0);
2080   g_return_if_fail (range.row0 >= 0);
2081   g_return_if_fail (range.coli >= range.col0);
2082   g_return_if_fail (range.col0 >= 0);
2083
2084
2085   for (r = range.row0; r <= range.rowi ; ++r )
2086     {
2087       gint c;
2088
2089       for (c = range.col0 ; c <= range.coli; ++c)
2090         {
2091           psppire_data_store_set_string (ds, "", r, c);
2092         }
2093     }
2094
2095   /* and remove the selection */
2096   gtk_sheet_unselect_range (data_sheet);
2097 }
2098
2099