undid accidental checkin of text import patch
[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 "crosstabs-dialog.h"
46 #include "frequencies-dialog.h"
47 #include "dict-display.h"
48 #include "clipboard.h"
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   de->invoke_crosstabs_dialog =
715     gtk_action_new ("crosstabs-dialog",
716                     _("_Crosstabs"),
717                     _("Generate crosstabulations"),
718                     "pspp-crosstabs");
719
720   g_signal_connect (de->invoke_crosstabs_dialog, "activate",
721                     G_CALLBACK (crosstabs_dialog), de);
722
723
724   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
725
726   g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
727                             "activate",
728                             G_CALLBACK (gtk_action_activate),
729                             de->action_data_new);
730
731   g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
732                             "activate",
733                             G_CALLBACK (gtk_action_activate),
734                             de->action_data_open);
735
736 #if RECENT_LISTS_AVAILABLE
737   {
738     GtkRecentManager *rm = gtk_recent_manager_get_default ();
739     GtkWidget *recent_data = get_widget_assert (de->xml, "file_recent-data");
740     GtkWidget *recent_files = get_widget_assert (de->xml, "file_recent-files");
741     GtkWidget *recent_separator = get_widget_assert (de->xml, "file_separator1");
742
743     GtkWidget *menu = gtk_recent_chooser_menu_new_for_manager (rm);
744
745     GtkRecentFilter *filter = gtk_recent_filter_new ();
746
747     gtk_widget_show (recent_data);
748     gtk_widget_show (recent_files);
749     gtk_widget_show (recent_separator);
750
751     gtk_recent_filter_add_pattern (filter, "*.sav");
752     gtk_recent_filter_add_pattern (filter, "*.SAV");
753
754     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
755
756     gtk_widget_set_sensitive (recent_data, TRUE);
757     g_signal_connect (menu, "selection-done",
758                       G_CALLBACK (on_recent_data_select), de);
759
760     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu);
761
762
763     filter = gtk_recent_filter_new ();
764     menu = gtk_recent_chooser_menu_new_for_manager (rm);
765
766     gtk_recent_filter_add_pattern (filter, "*.sps");
767     gtk_recent_filter_add_pattern (filter, "*.SPS");
768
769     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
770
771     gtk_widget_set_sensitive (recent_files, TRUE);
772     g_signal_connect (menu, "selection-done",
773                       G_CALLBACK (on_recent_files_select), de);
774
775     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu);
776   }
777 #endif
778
779   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
780                     "activate",
781                     G_CALLBACK (new_syntax_window),
782                     e->window);
783
784   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
785                     "activate",
786                     G_CALLBACK (open_syntax_window),
787                     e->window);
788
789   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
790                             "activate",
791                             G_CALLBACK (gtk_action_activate),
792                             de->action_data_save);
793
794   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
795                             "activate",
796                             G_CALLBACK (gtk_action_activate),
797                             de->action_data_save_as);
798
799   gtk_action_connect_proxy (de->invoke_find_dialog,
800                             get_widget_assert (de->xml, "edit_find")
801                             );
802
803   gtk_action_connect_proxy (de->invoke_find_dialog,
804                             get_widget_assert (de->xml, "button-find")
805                             );
806
807   gtk_action_connect_proxy (de->invoke_rank_dialog,
808                             get_widget_assert (de->xml, "transform_rank")
809                             );
810
811   gtk_action_connect_proxy (de->invoke_recode_same_dialog,
812                             get_widget_assert (de->xml,
813                                                "transform_recode-same")
814                             );
815
816   gtk_action_connect_proxy (de->invoke_recode_different_dialog,
817                             get_widget_assert (de->xml,
818                                                "transform_recode-different")
819                             );
820
821   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
822                             get_widget_assert (de->xml, "data_weight-cases")
823                             );
824
825   gtk_action_connect_proxy (de->invoke_transpose_dialog,
826                             get_widget_assert (de->xml, "data_transpose")
827                             );
828
829   gtk_action_connect_proxy (de->invoke_split_file_dialog,
830                             get_widget_assert (de->xml, "data_split-file")
831                             );
832
833   gtk_action_connect_proxy (de->invoke_sort_cases_dialog,
834                             get_widget_assert (de->xml, "data_sort-cases")
835                             );
836
837   gtk_action_connect_proxy (de->invoke_select_cases_dialog,
838                             get_widget_assert (de->xml, "data_select-cases")
839                             );
840
841   gtk_action_connect_proxy (de->invoke_compute_dialog,
842                             get_widget_assert (de->xml, "transform_compute")
843                             );
844
845   gtk_action_connect_proxy (de->invoke_t_test_independent_samples_dialog,
846                             get_widget_assert (de->xml,
847                                                "indep-t-test")
848                             );
849
850
851   gtk_action_connect_proxy (de->invoke_t_test_paired_samples_dialog,
852                             get_widget_assert (de->xml,
853                                                "paired-t-test")
854                             );
855
856
857   gtk_action_connect_proxy (de->invoke_t_test_one_sample_dialog,
858                             get_widget_assert (de->xml,
859                                                "one-sample-t-test")
860                             );
861
862
863   gtk_action_connect_proxy (de->invoke_oneway_anova_dialog,
864                             get_widget_assert (de->xml,
865                                                "oneway-anova")
866                             );
867
868
869   gtk_action_connect_proxy (de->invoke_comments_dialog,
870                             get_widget_assert (de->xml, "utilities_comments")
871                             );
872
873   gtk_action_connect_proxy (de->invoke_variable_info_dialog,
874                             get_widget_assert (de->xml, "utilities_variables")
875                             );
876
877   gtk_action_connect_proxy (de->invoke_descriptives_dialog,
878                             get_widget_assert (de->xml, "analyze_descriptives")
879                             );
880
881   gtk_action_connect_proxy (de->invoke_crosstabs_dialog,
882                             get_widget_assert (de->xml, "crosstabs")
883                             );
884
885   gtk_action_connect_proxy (de->invoke_frequencies_dialog,
886                             get_widget_assert (de->xml, "analyze_frequencies")
887                             );
888
889   g_signal_connect (get_widget_assert (de->xml,"help_about"),
890                     "activate",
891                     G_CALLBACK (about_new),
892                     e->window);
893
894
895   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
896                     "activate",
897                     G_CALLBACK (reference_manual),
898                     e->window);
899
900   g_signal_connect (data_sheet,
901                     "double-click-column",
902                     G_CALLBACK (click2column),
903                     de);
904
905   g_signal_connect (data_sheet,
906                     "select-column",
907                     G_CALLBACK (enable_delete_variables),
908                     de);
909
910   g_signal_connect (data_sheet,
911                     "select-row",
912                     G_CALLBACK (enable_delete_cases),
913                     de);
914
915
916   g_signal_connect (var_sheet,
917                     "double-click-row",
918                     GTK_SIGNAL_FUNC (click2row),
919                     de);
920
921   g_signal_connect_after (var_sheet,
922                     "select-row",
923                     G_CALLBACK (enable_delete_variables),
924                     de);
925
926   g_signal_connect (get_widget_assert (de->xml, "notebook"),
927                     "switch-page",
928                     G_CALLBACK (on_switch_sheet), de);
929
930
931   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
932                     "activate",
933                     G_CALLBACK (status_bar_activate), de);
934
935
936   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
937                     "activate",
938                     G_CALLBACK (grid_lines_activate), de);
939
940
941
942   g_signal_connect (get_widget_assert (de->xml, "view_data"),
943                     "activate",
944                     G_CALLBACK (data_sheet_activate), de);
945
946   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
947                     "activate",
948                     G_CALLBACK (variable_sheet_activate), de);
949
950
951
952   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
953                     "activate",
954                     G_CALLBACK (fonts_activate), de);
955
956
957
958
959   gtk_action_connect_proxy (de->action_data_open,
960                             get_widget_assert (de->xml, "button-open")
961                             );
962
963   gtk_action_connect_proxy (de->action_data_save,
964                             get_widget_assert (de->xml, "button-save")
965                             );
966
967   gtk_action_connect_proxy (de->invoke_variable_info_dialog,
968                             get_widget_assert (de->xml, "button-goto-variable")
969                             );
970
971   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
972                             get_widget_assert (de->xml, "button-weight-cases")
973                             );
974
975   gtk_action_connect_proxy (de->invoke_split_file_dialog,
976                             get_widget_assert (de->xml, "button-split-file")
977                             );
978
979   gtk_action_connect_proxy (de->invoke_select_cases_dialog,
980                             get_widget_assert (de->xml, "button-select-cases")
981                             );
982
983
984   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
985                     "activate",
986                     G_CALLBACK (file_quit), de);
987
988   g_signal_connect (get_widget_assert (de->xml, "transform_run-pending"),
989                     "activate",
990                     G_CALLBACK (execute), de);
991
992
993   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
994                     "activate",
995                     G_CALLBACK (minimise_all_windows), NULL);
996
997
998   create_data_sheet_variable_popup_menu (de);
999   create_data_sheet_cases_popup_menu (de);
1000
1001   g_signal_connect (G_OBJECT (data_sheet), "button-event-column",
1002                     G_CALLBACK (popup_variable_menu), de);
1003
1004   g_signal_connect (G_OBJECT (data_sheet), "button-event-row",
1005                     G_CALLBACK (popup_cases_menu), de);
1006
1007   return de;
1008 }
1009
1010
1011 /* Callback which occurs when the var sheet's row title
1012    button is double clicked */
1013 static gboolean
1014 click2row (GtkWidget *w, gint row, gpointer data)
1015 {
1016   struct data_editor *de = data;
1017   GtkSheetRange visible_range;
1018
1019   gint current_row, current_column;
1020
1021   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
1022
1023   data_editor_select_sheet (de, PAGE_DATA_SHEET);
1024
1025   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
1026                              &current_row, &current_column);
1027
1028   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
1029
1030   gtk_sheet_get_visible_range (GTK_SHEET (data_sheet), &visible_range);
1031
1032   if ( row < visible_range.col0 || row > visible_range.coli)
1033     {
1034       gtk_sheet_moveto (GTK_SHEET (data_sheet),
1035                         current_row, row, 0, 0);
1036     }
1037
1038   return FALSE;
1039 }
1040
1041
1042 /* Callback which occurs when the data sheet's column title
1043    is double clicked */
1044 static gboolean
1045 click2column (GtkWidget *w, gint col, gpointer data)
1046 {
1047   struct data_editor *de = data;
1048
1049   gint current_row, current_column;
1050
1051   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
1052
1053   data_editor_select_sheet (de, PAGE_VAR_SHEET);
1054
1055   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
1056                              &current_row, &current_column);
1057
1058   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
1059
1060   return FALSE;
1061 }
1062
1063
1064 void
1065 new_data_window (GtkMenuItem *menuitem, gpointer parent)
1066 {
1067   window_create (WINDOW_DATA, NULL);
1068 }
1069
1070 /* Callback for when the datasheet/varsheet is selected */
1071 static void
1072 on_switch_sheet (GtkNotebook *notebook,
1073                 GtkNotebookPage *page,
1074                 guint page_num,
1075                 gpointer user_data)
1076 {
1077   struct data_editor *de = user_data;
1078
1079   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
1080   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
1081
1082   switch (page_num)
1083     {
1084     case PAGE_VAR_SHEET:
1085       gtk_widget_hide (view_variables);
1086       gtk_widget_show (view_data);
1087       gtk_action_set_sensitive (de->insert_variable, TRUE);
1088       gtk_action_set_sensitive (de->insert_case, FALSE);
1089       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
1090       break;
1091     case PAGE_DATA_SHEET:
1092       gtk_widget_show (view_variables);
1093       gtk_widget_show (view_data);
1094       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
1095       gtk_action_set_sensitive (de->insert_case, TRUE);
1096       break;
1097     default:
1098       g_assert_not_reached ();
1099       break;
1100     }
1101
1102   update_paste_menuitem (de, page_num);
1103   update_cut_copy_menuitem (de, page_num);
1104 }
1105
1106
1107 void
1108 data_editor_select_sheet (struct data_editor *de, gint page)
1109 {
1110   gtk_notebook_set_current_page
1111    (
1112     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
1113     );
1114 }
1115
1116
1117
1118 static void
1119 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
1120 {
1121   struct data_editor *de = data;
1122   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
1123
1124   if ( gtk_check_menu_item_get_active (menuitem) )
1125     gtk_widget_show (statusbar);
1126   else
1127     gtk_widget_hide (statusbar);
1128 }
1129
1130
1131 static void
1132 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
1133 {
1134   struct data_editor *de = data;
1135   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
1136
1137   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
1138                                                      "variable_sheet")),
1139                        grid_visible);
1140
1141   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
1142                        grid_visible);
1143 }
1144
1145
1146
1147 static void
1148 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
1149 {
1150   struct data_editor *de = data;
1151
1152   data_editor_select_sheet (de, PAGE_DATA_SHEET);
1153 }
1154
1155
1156 static void
1157 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
1158 {
1159   struct data_editor *de = data;
1160
1161   data_editor_select_sheet (de, PAGE_VAR_SHEET);
1162 }
1163
1164
1165 static void
1166 fonts_activate (GtkMenuItem *menuitem, gpointer data)
1167 {
1168   struct data_editor *de = data;
1169   GtkWidget *dialog =
1170     gtk_font_selection_dialog_new (_("Font Selection"));
1171
1172   gtk_window_set_transient_for (GTK_WINDOW (dialog),
1173                                 GTK_WINDOW (get_widget_assert (de->xml,
1174                                                                "data_editor")));
1175   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
1176     {
1177       GtkSheet *data_sheet =
1178         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1179
1180       GtkSheet *var_sheet =
1181         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1182
1183       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
1184       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
1185
1186       const gchar *font = gtk_font_selection_dialog_get_font_name
1187         (GTK_FONT_SELECTION_DIALOG (dialog));
1188
1189       PangoFontDescription* font_desc =
1190         pango_font_description_from_string (font);
1191
1192       psppire_var_store_set_font (vs, font_desc);
1193       psppire_data_store_set_font (ds, font_desc);
1194     }
1195
1196   gtk_widget_hide (dialog);
1197 }
1198
1199
1200
1201 /* Callback for the value labels action */
1202 static void
1203 toggle_value_labels (GtkToggleAction *ta, gpointer data)
1204 {
1205   struct data_editor *de = data;
1206
1207   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1208
1209   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
1210
1211
1212   psppire_data_store_show_labels (ds,
1213                                   gtk_toggle_action_get_active (ta));
1214 }
1215
1216 extern PsppireDataStore *the_data_store ;
1217
1218 static void
1219 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
1220 {
1221   /* FIXME: Need to be more intelligent here.
1222      Give the user the opportunity to save any unsaved data.
1223   */
1224   g_object_unref (the_data_store);
1225   gtk_main_quit ();
1226 }
1227
1228 static void
1229 delete_cases (GtkAction *action, gpointer data)
1230 {
1231   struct data_editor *de = data;
1232   GtkSheet *data_sheet =
1233     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1234
1235   GtkSheetRange range;
1236
1237   PsppireDataStore *data_store = PSPPIRE_DATA_STORE
1238     (gtk_sheet_get_model (data_sheet) );
1239
1240
1241   /* This shouldn't be able to happen, because the action
1242      should be disabled */
1243   g_return_if_fail (gtk_sheet_get_state (data_sheet)
1244                     ==  GTK_SHEET_ROW_SELECTED );
1245
1246   gtk_sheet_get_selected_range (data_sheet, &range);
1247
1248   gtk_sheet_unselect_range (data_sheet);
1249
1250   psppire_data_store_delete_cases (data_store, range.row0,
1251                                    1 + range.rowi - range.row0);
1252
1253 }
1254
1255 static void
1256 delete_variables (GtkAction *a, gpointer data)
1257 {
1258   struct data_editor *de = data;
1259   GtkSheetRange range;
1260
1261   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
1262                                                            "notebook"));
1263
1264   const gint page = gtk_notebook_get_current_page (notebook);
1265
1266   GtkSheet *sheet = GTK_SHEET (get_widget_assert (de->xml,
1267                                                   (page == PAGE_VAR_SHEET) ?
1268                                                   "variable_sheet" :
1269                                                   "data_sheet"));
1270
1271
1272   gtk_sheet_get_selected_range (sheet, &range);
1273
1274   switch ( page )
1275     {
1276     case PAGE_VAR_SHEET:
1277       {
1278         PsppireVarStore *vs =
1279           PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
1280
1281         psppire_dict_delete_variables (vs->dict,
1282                                        range.row0,
1283                                        1 +
1284                                        range.rowi -
1285                                        range.row0 );
1286       }
1287       break;
1288     case PAGE_DATA_SHEET:
1289       {
1290         PsppireDataStore *ds =
1291           PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1292
1293         psppire_dict_delete_variables (ds->dict,
1294                                        range.col0,
1295                                        1 +
1296                                        range.coli -
1297                                        range.col0 );
1298       }
1299       break;
1300     };
1301
1302   gtk_sheet_unselect_range (sheet);
1303 }
1304
1305 static void
1306 insert_case (GtkAction *action, gpointer data)
1307 {
1308   gint current_row ;
1309   struct data_editor *de = data;
1310
1311   GtkSheet *data_sheet =
1312     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1313
1314   PsppireDataStore *ds = PSPPIRE_DATA_STORE
1315     (gtk_sheet_get_model (data_sheet) );
1316
1317
1318   gtk_sheet_get_active_cell (data_sheet, &current_row, NULL);
1319
1320   if (current_row < 0) current_row = 0;
1321
1322   psppire_data_store_insert_new_case (ds, current_row);
1323 }
1324
1325 /* Insert a new variable before the current row in the variable sheet,
1326    or before the current column in the data sheet, whichever is selected */
1327 static void
1328 insert_variable (GtkAction *action, gpointer data)
1329 {
1330   struct data_editor *de = data;
1331   gint posn = -1;
1332
1333   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
1334
1335   GtkSheet *var_sheet =
1336     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1337
1338   PsppireVarStore *vs = PSPPIRE_VAR_STORE
1339     (gtk_sheet_get_model (var_sheet) );
1340
1341   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
1342     {
1343     case PAGE_VAR_SHEET:
1344       posn = var_sheet->active_cell.row;
1345       break;
1346     case PAGE_DATA_SHEET:
1347       {
1348         GtkSheet *data_sheet =
1349           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1350
1351         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
1352           posn = data_sheet->range.col0;
1353         else
1354           posn = data_sheet->active_cell.col;
1355       }
1356       break;
1357     default:
1358       g_assert_not_reached ();
1359     }
1360
1361   if ( posn == -1 ) posn = 0;
1362
1363   psppire_dict_insert_variable (vs->dict, posn, NULL);
1364 }
1365
1366 /* Callback for when the dictionary changes its split variables */
1367 static void
1368 on_split_change (PsppireDict *dict, gpointer data)
1369 {
1370   struct data_editor *de = data;
1371
1372   size_t n_split_vars = dict_get_split_cnt (dict->dict);
1373
1374   GtkWidget *split_status_area =
1375     get_widget_assert (de->xml, "split-file-status-area");
1376
1377   if ( n_split_vars == 0 )
1378     {
1379       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
1380     }
1381   else
1382     {
1383       gint i;
1384       GString *text;
1385       const struct variable *const * split_vars =
1386         dict_get_split_vars (dict->dict);
1387
1388       text = g_string_new (_("Split by "));
1389
1390       for (i = 0 ; i < n_split_vars - 1; ++i )
1391         {
1392           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
1393         }
1394       g_string_append (text, var_get_name (split_vars[i]));
1395
1396       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
1397
1398       g_string_free (text, TRUE);
1399     }
1400 }
1401
1402
1403 /* Callback for when the dictionary changes its filter variable */
1404 static void
1405 on_filter_change (GObject *o, gint filter_index, gpointer data)
1406 {
1407   struct data_editor *de = data;
1408   GtkWidget *filter_status_area =
1409     get_widget_assert (de->xml, "filter-use-status-area");
1410
1411   if ( filter_index == -1 )
1412     {
1413       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
1414     }
1415   else
1416     {
1417       GtkSheet *var_sheet =
1418         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1419
1420       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1421         (gtk_sheet_get_model (var_sheet) );
1422
1423       struct variable *var = psppire_dict_get_variable (vs->dict,
1424                                                         filter_index);
1425
1426       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
1427
1428       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
1429
1430       g_free (text);
1431     }
1432 }
1433
1434 /* Callback for when the dictionary changes its weights */
1435 static void
1436 on_weight_change (GObject *o, gint weight_index, gpointer data)
1437 {
1438   struct data_editor *de = data;
1439   GtkWidget *weight_status_area =
1440     get_widget_assert (de->xml, "weight-status-area");
1441
1442   if ( weight_index == -1 )
1443     {
1444       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
1445     }
1446   else
1447     {
1448       GtkSheet *var_sheet =
1449         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1450
1451       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1452         (gtk_sheet_get_model (var_sheet) );
1453
1454       struct variable *var = psppire_dict_get_variable (vs->dict,
1455                                                         weight_index);
1456
1457       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
1458
1459       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
1460
1461       g_free (text);
1462     }
1463 }
1464
1465
1466
1467 \f
1468 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
1469 static void new_file (GtkAction *, struct editor_window *de);
1470 static void open_data_dialog (GtkAction *, struct data_editor *de);
1471 static void data_save (GtkAction *action, struct data_editor *e);
1472
1473
1474 /* Create the GtkActions and connect to their signals */
1475 static void
1476 register_data_editor_actions (struct data_editor *de)
1477 {
1478   de->action_data_open =
1479     gtk_action_new ("data-open-dialog",
1480                     _("Open"),
1481                     _("Open a data file"),
1482                     "gtk-open");
1483
1484   g_signal_connect (de->action_data_open, "activate",
1485                     G_CALLBACK (open_data_dialog), de);
1486
1487
1488   de->action_data_save = gtk_action_new ("data-save",
1489                                             _("Save"),
1490                                             _("Save data to file"),
1491                                             "gtk-save");
1492
1493   g_signal_connect (de->action_data_save, "activate",
1494                     G_CALLBACK (data_save), de);
1495
1496
1497
1498   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
1499                                             _("Save As"),
1500                                             _("Save data to file"),
1501                                             "gtk-save");
1502
1503   g_signal_connect (de->action_data_save_as, "activate",
1504                     G_CALLBACK (data_save_as_dialog), de);
1505
1506   de->action_data_new =
1507     gtk_action_new ("data-new",
1508                     _("New"),
1509                     _("New data file"),
1510                     NULL);
1511
1512   g_signal_connect (de->action_data_new, "activate",
1513                     G_CALLBACK (new_file), de);
1514 }
1515
1516 /* Returns true if NAME has a suffix which might denote a PSPP file */
1517 static gboolean
1518 name_has_suffix (const gchar *name)
1519 {
1520   if ( g_str_has_suffix (name, ".sav"))
1521     return TRUE;
1522   if ( g_str_has_suffix (name, ".SAV"))
1523     return TRUE;
1524   if ( g_str_has_suffix (name, ".por"))
1525     return TRUE;
1526   if ( g_str_has_suffix (name, ".POR"))
1527     return TRUE;
1528
1529   return FALSE;
1530 }
1531
1532 /* Append SUFFIX to the filename of DE */
1533 static void
1534 append_filename_suffix (struct data_editor *de, const gchar *suffix)
1535 {
1536   if ( ! name_has_suffix (de->file_name))
1537     {
1538       gchar *s = de->file_name;
1539       de->file_name = g_strconcat (de->file_name, suffix, NULL);
1540       g_free (s);
1541     }
1542 }
1543
1544 /* Save DE to file */
1545 static void
1546 save_file (struct data_editor *de)
1547 {
1548   struct getl_interface *sss;
1549   struct string file_name ;
1550
1551   g_assert (de->file_name);
1552
1553   ds_init_cstr (&file_name, de->file_name);
1554   gen_quoted_string (&file_name);
1555
1556   if ( de->save_as_portable )
1557     {
1558       append_filename_suffix (de, ".por");
1559       sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
1560                                          ds_cstr (&file_name));
1561     }
1562   else
1563     {
1564       append_filename_suffix (de, ".sav");
1565       sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
1566                                          ds_cstr (&file_name));
1567     }
1568
1569   ds_destroy (&file_name);
1570
1571   execute_syntax (sss);
1572 }
1573
1574
1575 /* Callback for data_save action.
1576    If there's an existing file name, then just save,
1577    otherwise prompt for a file name, then save */
1578 static void
1579 data_save (GtkAction *action, struct data_editor *de)
1580 {
1581   if ( de->file_name)
1582     save_file (de);
1583   else
1584     data_save_as_dialog (action, de);
1585 }
1586
1587
1588 /* Callback for data_save_as action. Prompt for a filename and save */
1589 static void
1590 data_save_as_dialog (GtkAction *action, struct data_editor *de)
1591 {
1592   struct editor_window *e = (struct editor_window *) de;
1593
1594   GtkWidget *button_sys;
1595   GtkWidget *dialog =
1596     gtk_file_chooser_dialog_new (_("Save"),
1597                                  GTK_WINDOW (e->window),
1598                                  GTK_FILE_CHOOSER_ACTION_SAVE,
1599                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1600                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1601                                  NULL);
1602
1603   GtkFileFilter *filter = gtk_file_filter_new ();
1604   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1605   gtk_file_filter_add_pattern (filter, "*.sav");
1606   gtk_file_filter_add_pattern (filter, "*.SAV");
1607   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1608
1609   filter = gtk_file_filter_new ();
1610   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1611   gtk_file_filter_add_pattern (filter, "*.por");
1612   gtk_file_filter_add_pattern (filter, "*.POR");
1613   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1614
1615   filter = gtk_file_filter_new ();
1616   gtk_file_filter_set_name (filter, _("All Files"));
1617   gtk_file_filter_add_pattern (filter, "*");
1618   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1619
1620   {
1621     GtkWidget *button_por;
1622     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
1623     button_sys =
1624       gtk_radio_button_new_with_label (NULL, _("System File"));
1625
1626     button_por =
1627       gtk_radio_button_new_with_label
1628       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
1629        _("Portable File"));
1630
1631     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
1632     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
1633
1634     gtk_widget_show_all (vbox);
1635
1636     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
1637   }
1638
1639   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1640     {
1641     case GTK_RESPONSE_ACCEPT:
1642       {
1643         g_free (de->file_name);
1644
1645         de->file_name =
1646           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1647
1648         de->save_as_portable =
1649           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
1650
1651         save_file (de);
1652
1653         window_set_name_from_filename (e, de->file_name);
1654       }
1655       break;
1656     default:
1657       break;
1658     }
1659
1660   gtk_widget_destroy (dialog);
1661 }
1662
1663
1664 /* Callback for data_new action.
1665    Performs the NEW FILE command */
1666 static void
1667 new_file (GtkAction *action, struct editor_window *e)
1668 {
1669   struct data_editor *de = (struct data_editor *) e;
1670
1671   struct getl_interface *sss =
1672     create_syntax_string_source ("NEW FILE.");
1673
1674   execute_syntax (sss);
1675
1676   g_free (de->file_name);
1677   de->file_name = NULL;
1678
1679   default_window_name (e);
1680 }
1681
1682
1683 static void
1684 open_data_file (const gchar *file_name, struct data_editor *de)
1685 {
1686   struct getl_interface *sss;
1687   struct string filename;
1688
1689   ds_init_cstr (&filename, file_name);
1690
1691   gen_quoted_string (&filename);
1692
1693   sss = create_syntax_string_source ("GET FILE=%s.",
1694                                      ds_cstr (&filename));
1695   ds_destroy (&filename);
1696
1697   if (execute_syntax (sss) )
1698   {
1699     window_set_name_from_filename ((struct editor_window *) de, file_name);
1700     add_most_recent (file_name);
1701   }
1702 }
1703
1704
1705
1706 /* Callback for the data_open action.
1707    Prompts for a filename and opens it */
1708 static void
1709 open_data_dialog (GtkAction *action, struct data_editor *de)
1710 {
1711   struct editor_window *e = (struct editor_window *) de;
1712
1713   GtkWidget *dialog =
1714     gtk_file_chooser_dialog_new (_("Open"),
1715                                  GTK_WINDOW (e->window),
1716                                  GTK_FILE_CHOOSER_ACTION_OPEN,
1717                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1718                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1719                                  NULL);
1720
1721   GtkFileFilter *filter = gtk_file_filter_new ();
1722   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1723   gtk_file_filter_add_pattern (filter, "*.sav");
1724   gtk_file_filter_add_pattern (filter, "*.SAV");
1725   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1726
1727   filter = gtk_file_filter_new ();
1728   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1729   gtk_file_filter_add_pattern (filter, "*.por");
1730   gtk_file_filter_add_pattern (filter, "*.POR");
1731   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1732
1733   filter = gtk_file_filter_new ();
1734   gtk_file_filter_set_name (filter, _("All Files"));
1735   gtk_file_filter_add_pattern (filter, "*");
1736   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1737
1738
1739   if ( de->file_name)
1740     {
1741       gchar *dir_name = g_path_get_dirname (de->file_name);
1742       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1743                                            dir_name);
1744       free (dir_name);
1745     }
1746
1747   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1748     {
1749     case GTK_RESPONSE_ACCEPT:
1750       {
1751         g_free (de->file_name);
1752         de->file_name =
1753           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1754
1755         open_data_file (de->file_name, de);
1756       }
1757       break;
1758     default:
1759       break;
1760     }
1761
1762   gtk_widget_destroy (dialog);
1763 }
1764
1765
1766
1767 /* Update the data_ref_entry with the reference of the active cell */
1768 static gint
1769 update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
1770 {
1771   GladeXML *data_editor_xml = data;
1772
1773   PsppireDataStore *data_store =
1774     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1775
1776   g_return_val_if_fail (data_editor_xml, FALSE);
1777
1778   if (data_store)
1779     {
1780       const struct variable *var =
1781         psppire_dict_get_variable (data_store->dict, col);
1782
1783       /* The entry where the reference to the current cell is displayed */
1784       GtkEntry *cell_ref_entry =
1785         GTK_ENTRY (get_widget_assert (data_editor_xml,
1786                                       "cell_ref_entry"));
1787       GtkEntry *datum_entry =
1788         GTK_ENTRY (get_widget_assert (data_editor_xml,
1789                                       "datum_entry"));
1790
1791       if ( var )
1792         {
1793           gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
1794                                          var_get_name (var));
1795
1796           gchar *s = pspp_locale_to_utf8 (text, -1, 0);
1797
1798           g_free (text);
1799
1800           gtk_entry_set_text (cell_ref_entry, s);
1801
1802           g_free (s);
1803         }
1804       else
1805         gtk_entry_set_text (cell_ref_entry, "");
1806
1807
1808       if ( var )
1809         {
1810           gchar *text =
1811             psppire_data_store_get_string (data_store, row,
1812                                            var_get_dict_index(var));
1813           g_strchug (text);
1814
1815           gtk_entry_set_text (datum_entry, text);
1816
1817           free (text);
1818         }
1819       else
1820         gtk_entry_set_text (datum_entry, "");
1821     }
1822
1823   return FALSE;
1824 }
1825
1826
1827
1828
1829
1830 static void
1831 do_sort (PsppireDataStore *ds, int var, gboolean descend)
1832 {
1833   GString *string = g_string_new ("SORT CASES BY ");
1834
1835   const struct variable *v =
1836     psppire_dict_get_variable (ds->dict, var);
1837
1838   g_string_append_printf (string, "%s", var_get_name (v));
1839
1840   if ( descend )
1841     g_string_append (string, " (D)");
1842
1843   g_string_append (string, ".");
1844
1845   execute_syntax (create_syntax_string_source (string->str));
1846
1847   g_string_free (string, TRUE);
1848 }
1849
1850
1851 static void
1852 sort_up (GtkMenuItem *item, gpointer data)
1853 {
1854   GtkSheet *sheet  = data;
1855   GtkSheetRange range;
1856   gtk_sheet_get_selected_range (sheet, &range);
1857
1858   do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)),
1859            range.col0, FALSE);
1860
1861 }
1862
1863 static void
1864 sort_down (GtkMenuItem *item, gpointer data)
1865 {
1866   GtkSheet *sheet  = data;
1867   GtkSheetRange range;
1868   gtk_sheet_get_selected_range (sheet, &range);
1869
1870   do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)),
1871            range.col0, TRUE);
1872 }
1873
1874
1875
1876
1877 static void
1878 create_data_sheet_variable_popup_menu (struct data_editor *de)
1879 {
1880   GtkSheet *sheet  = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1881   GtkWidget *menu = gtk_menu_new ();
1882
1883   GtkWidget *sort_ascending =
1884     gtk_menu_item_new_with_label (_("Sort Ascending"));
1885
1886   GtkWidget *sort_descending =
1887     gtk_menu_item_new_with_label (_("Sort Descending"));
1888
1889
1890   GtkWidget *insert_variable =
1891     gtk_menu_item_new_with_label (_("Insert Variable"));
1892
1893   GtkWidget *clear_variable =
1894     gtk_menu_item_new_with_label (_("Clear"));
1895
1896
1897   gtk_action_connect_proxy (de->insert_variable,
1898                             insert_variable );
1899
1900
1901   gtk_action_connect_proxy (de->delete_variables,
1902                             clear_variable );
1903
1904
1905   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
1906
1907
1908   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1909                          gtk_separator_menu_item_new ());
1910
1911
1912   gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable);
1913
1914
1915   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1916                          gtk_separator_menu_item_new ());
1917
1918
1919   g_signal_connect (G_OBJECT (sort_ascending), "activate",
1920                     G_CALLBACK (sort_up), sheet);
1921
1922   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending);
1923
1924
1925   g_signal_connect (G_OBJECT (sort_descending), "activate",
1926                     G_CALLBACK (sort_down), sheet);
1927
1928
1929   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending);
1930
1931   gtk_widget_show_all (menu);
1932
1933
1934   de->data_sheet_variable_popup_menu = GTK_MENU(menu);
1935 }
1936
1937
1938 static void
1939 create_data_sheet_cases_popup_menu (struct data_editor *de)
1940 {
1941   GtkWidget *menu = gtk_menu_new ();
1942
1943   GtkWidget *insert_case =
1944     gtk_menu_item_new_with_label (_("Insert Case"));
1945
1946   GtkWidget *delete_case =
1947     gtk_menu_item_new_with_label (_("Clear"));
1948
1949
1950   gtk_action_connect_proxy (de->insert_case,
1951                             insert_case);
1952
1953
1954   gtk_action_connect_proxy (de->delete_cases,
1955                             delete_case);
1956
1957
1958   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case);
1959
1960
1961   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1962                          gtk_separator_menu_item_new ());
1963
1964
1965   gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case);
1966
1967
1968   gtk_widget_show_all (menu);
1969
1970
1971   de->data_sheet_cases_popup_menu = GTK_MENU (menu);
1972 }
1973
1974
1975 static void
1976 popup_variable_menu (GtkSheet *sheet, gint column,
1977                      GdkEventButton *event, gpointer data)
1978 {
1979   struct data_editor *de = data;
1980
1981   PsppireDataStore *data_store =
1982     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1983
1984   const struct variable *v =
1985     psppire_dict_get_variable (data_store->dict, column);
1986
1987   if ( v && event->button == 3)
1988     {
1989
1990       gtk_sheet_select_column (sheet, column);
1991
1992       gtk_menu_popup (GTK_MENU (de->data_sheet_variable_popup_menu),
1993                       NULL, NULL, NULL, NULL,
1994                       event->button, event->time);
1995     }
1996 }
1997
1998
1999 static void
2000 popup_cases_menu (GtkSheet *sheet, gint row,
2001                   GdkEventButton *event, gpointer data)
2002 {
2003   struct data_editor *de = data;
2004
2005   PsppireDataStore *data_store =
2006     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
2007
2008   if ( row <= psppire_data_store_get_case_count (data_store) &&
2009        event->button == 3)
2010     {
2011       gtk_sheet_select_row (sheet, row);
2012
2013       gtk_menu_popup (GTK_MENU (de->data_sheet_cases_popup_menu),
2014                       NULL, NULL, NULL, NULL,
2015                       event->button, event->time);
2016     }
2017 }
2018
2019
2020 static void
2021 on_edit_paste (GtkAction *a, gpointer data)
2022 {
2023   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2024
2025   gtk_clipboard_request_contents (clipboard,
2026                                   gdk_atom_intern ("UTF8_STRING", TRUE),
2027                                   data_sheet_contents_received_callback,
2028                                   data);
2029 }
2030
2031
2032 static void
2033 on_edit_copy (GtkMenuItem *m, gpointer data)
2034 {
2035   struct data_editor *de = data;
2036
2037   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2038                                                        "data_sheet"));
2039
2040   data_sheet_set_clip (data_sheet);
2041 }
2042
2043
2044
2045 static void
2046 on_edit_cut (GtkMenuItem *m, gpointer data)
2047 {
2048   struct data_editor *de = data;
2049   gint max_rows, max_columns;
2050   gint r;
2051   GtkSheetRange range;
2052   PsppireDataStore *ds;
2053   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2054                                                        "data_sheet"));
2055
2056   data_sheet_set_clip (data_sheet);
2057
2058
2059   /* Now blank all the cells */
2060   gtk_sheet_get_selected_range (data_sheet, &range);
2061
2062   ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
2063
2064
2065    /* If nothing selected, then use active cell */
2066   if ( range.row0 < 0 || range.col0 < 0 )
2067     {
2068       gint row, col;
2069       gtk_sheet_get_active_cell (data_sheet, &row, &col);
2070
2071       range.row0 = range.rowi = row;
2072       range.col0 = range.coli = col;
2073     }
2074
2075   /* The sheet range can include cells that do not include data.
2076      Exclude them from the range. */
2077   max_rows = psppire_data_store_get_case_count (ds);
2078   if (range.rowi >= max_rows)
2079     {
2080       if (max_rows == 0)
2081         return;
2082       range.rowi = max_rows - 1;
2083     }
2084
2085   max_columns = dict_get_var_cnt (ds->dict->dict);
2086   if (range.coli >= max_columns)
2087     {
2088       if (max_columns == 0)
2089         return;
2090       range.coli = max_columns - 1;
2091     }
2092
2093   g_return_if_fail (range.rowi >= range.row0);
2094   g_return_if_fail (range.row0 >= 0);
2095   g_return_if_fail (range.coli >= range.col0);
2096   g_return_if_fail (range.col0 >= 0);
2097
2098
2099   for (r = range.row0; r <= range.rowi ; ++r )
2100     {
2101       gint c;
2102
2103       for (c = range.col0 ; c <= range.coli; ++c)
2104         {
2105           psppire_data_store_set_string (ds, "", r, c);
2106         }
2107     }
2108
2109   /* and remove the selection */
2110   gtk_sheet_unselect_range (data_sheet);
2111 }
2112
2113