6e05d27183b254815bc85d99bf751b7997c3fa1f
[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 extern PsppireDataStore *the_data_store ;
1205
1206 static void
1207 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
1208 {
1209   /* FIXME: Need to be more intelligent here.
1210      Give the user the opportunity to save any unsaved data.
1211   */
1212   g_object_unref (the_data_store);
1213   gtk_main_quit ();
1214 }
1215
1216 static void
1217 delete_cases (GtkAction *action, gpointer data)
1218 {
1219   struct data_editor *de = data;
1220   GtkSheet *data_sheet =
1221     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1222
1223   GtkSheetRange range;
1224
1225   PsppireDataStore *data_store = PSPPIRE_DATA_STORE
1226     (gtk_sheet_get_model (data_sheet) );
1227
1228
1229   /* This shouldn't be able to happen, because the action
1230      should be disabled */
1231   g_return_if_fail (gtk_sheet_get_state (data_sheet)
1232                     ==  GTK_SHEET_ROW_SELECTED );
1233
1234   gtk_sheet_get_selected_range (data_sheet, &range);
1235
1236   gtk_sheet_unselect_range (data_sheet);
1237
1238   psppire_data_store_delete_cases (data_store, range.row0,
1239                                    1 + range.rowi - range.row0);
1240
1241 }
1242
1243 static void
1244 delete_variables (GtkAction *a, gpointer data)
1245 {
1246   struct data_editor *de = data;
1247   GtkSheetRange range;
1248
1249   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
1250                                                            "notebook"));
1251
1252   const gint page = gtk_notebook_get_current_page (notebook);
1253
1254   GtkSheet *sheet = GTK_SHEET (get_widget_assert (de->xml,
1255                                                   (page == PAGE_VAR_SHEET) ?
1256                                                   "variable_sheet" :
1257                                                   "data_sheet"));
1258
1259
1260   gtk_sheet_get_selected_range (sheet, &range);
1261
1262   switch ( page )
1263     {
1264     case PAGE_VAR_SHEET:
1265       {
1266         PsppireVarStore *vs =
1267           PSPPIRE_VAR_STORE (gtk_sheet_get_model (sheet));
1268
1269         psppire_dict_delete_variables (vs->dict,
1270                                        range.row0,
1271                                        1 +
1272                                        range.rowi -
1273                                        range.row0 );
1274       }
1275       break;
1276     case PAGE_DATA_SHEET:
1277       {
1278         PsppireDataStore *ds =
1279           PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1280
1281         psppire_dict_delete_variables (ds->dict,
1282                                        range.col0,
1283                                        1 +
1284                                        range.coli -
1285                                        range.col0 );
1286       }
1287       break;
1288     };
1289
1290   gtk_sheet_unselect_range (sheet);
1291 }
1292
1293 static void
1294 insert_case (GtkAction *action, gpointer data)
1295 {
1296   gint current_row ;
1297   struct data_editor *de = data;
1298
1299   GtkSheet *data_sheet =
1300     GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1301
1302   PsppireDataStore *ds = PSPPIRE_DATA_STORE
1303     (gtk_sheet_get_model (data_sheet) );
1304
1305
1306   gtk_sheet_get_active_cell (data_sheet, &current_row, NULL);
1307
1308   if (current_row < 0) current_row = 0;
1309
1310   psppire_data_store_insert_new_case (ds, current_row);
1311 }
1312
1313 /* Insert a new variable before the current row in the variable sheet,
1314    or before the current column in the data sheet, whichever is selected */
1315 static void
1316 insert_variable (GtkAction *action, gpointer data)
1317 {
1318   struct data_editor *de = data;
1319   gint posn = -1;
1320
1321   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
1322
1323   GtkSheet *var_sheet =
1324     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1325
1326   PsppireVarStore *vs = PSPPIRE_VAR_STORE
1327     (gtk_sheet_get_model (var_sheet) );
1328
1329   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
1330     {
1331     case PAGE_VAR_SHEET:
1332       posn = var_sheet->active_cell.row;
1333       break;
1334     case PAGE_DATA_SHEET:
1335       {
1336         GtkSheet *data_sheet =
1337           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1338
1339         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
1340           posn = data_sheet->range.col0;
1341         else
1342           posn = data_sheet->active_cell.col;
1343       }
1344       break;
1345     default:
1346       g_assert_not_reached ();
1347     }
1348
1349   if ( posn == -1 ) posn = 0;
1350
1351   psppire_dict_insert_variable (vs->dict, posn, NULL);
1352 }
1353
1354 /* Callback for when the dictionary changes its split variables */
1355 static void
1356 on_split_change (PsppireDict *dict, gpointer data)
1357 {
1358   struct data_editor *de = data;
1359
1360   size_t n_split_vars = dict_get_split_cnt (dict->dict);
1361
1362   GtkWidget *split_status_area =
1363     get_widget_assert (de->xml, "split-file-status-area");
1364
1365   if ( n_split_vars == 0 )
1366     {
1367       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
1368     }
1369   else
1370     {
1371       gint i;
1372       GString *text;
1373       const struct variable *const * split_vars =
1374         dict_get_split_vars (dict->dict);
1375
1376       text = g_string_new (_("Split by "));
1377
1378       for (i = 0 ; i < n_split_vars - 1; ++i )
1379         {
1380           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
1381         }
1382       g_string_append (text, var_get_name (split_vars[i]));
1383
1384       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
1385
1386       g_string_free (text, TRUE);
1387     }
1388 }
1389
1390
1391 /* Callback for when the dictionary changes its filter variable */
1392 static void
1393 on_filter_change (GObject *o, gint filter_index, gpointer data)
1394 {
1395   struct data_editor *de = data;
1396   GtkWidget *filter_status_area =
1397     get_widget_assert (de->xml, "filter-use-status-area");
1398
1399   if ( filter_index == -1 )
1400     {
1401       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
1402     }
1403   else
1404     {
1405       GtkSheet *var_sheet =
1406         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1407
1408       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1409         (gtk_sheet_get_model (var_sheet) );
1410
1411       struct variable *var = psppire_dict_get_variable (vs->dict,
1412                                                         filter_index);
1413
1414       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
1415
1416       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
1417
1418       g_free (text);
1419     }
1420 }
1421
1422 /* Callback for when the dictionary changes its weights */
1423 static void
1424 on_weight_change (GObject *o, gint weight_index, gpointer data)
1425 {
1426   struct data_editor *de = data;
1427   GtkWidget *weight_status_area =
1428     get_widget_assert (de->xml, "weight-status-area");
1429
1430   if ( weight_index == -1 )
1431     {
1432       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
1433     }
1434   else
1435     {
1436       GtkSheet *var_sheet =
1437         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
1438
1439       PsppireVarStore *vs = PSPPIRE_VAR_STORE
1440         (gtk_sheet_get_model (var_sheet) );
1441
1442       struct variable *var = psppire_dict_get_variable (vs->dict,
1443                                                         weight_index);
1444
1445       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
1446
1447       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
1448
1449       g_free (text);
1450     }
1451 }
1452
1453
1454
1455 \f
1456 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
1457 static void new_file (GtkAction *, struct editor_window *de);
1458 static void open_data_dialog (GtkAction *, struct data_editor *de);
1459 static void data_save (GtkAction *action, struct data_editor *e);
1460
1461
1462 /* Create the GtkActions and connect to their signals */
1463 static void
1464 register_data_editor_actions (struct data_editor *de)
1465 {
1466   de->action_data_open =
1467     gtk_action_new ("data-open-dialog",
1468                     _("Open"),
1469                     _("Open a data file"),
1470                     "gtk-open");
1471
1472   g_signal_connect (de->action_data_open, "activate",
1473                     G_CALLBACK (open_data_dialog), de);
1474
1475
1476   de->action_data_save = gtk_action_new ("data-save",
1477                                             _("Save"),
1478                                             _("Save data to file"),
1479                                             "gtk-save");
1480
1481   g_signal_connect (de->action_data_save, "activate",
1482                     G_CALLBACK (data_save), de);
1483
1484
1485
1486   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
1487                                             _("Save As"),
1488                                             _("Save data to file"),
1489                                             "gtk-save");
1490
1491   g_signal_connect (de->action_data_save_as, "activate",
1492                     G_CALLBACK (data_save_as_dialog), de);
1493
1494   de->action_data_new =
1495     gtk_action_new ("data-new",
1496                     _("New"),
1497                     _("New data file"),
1498                     NULL);
1499
1500   g_signal_connect (de->action_data_new, "activate",
1501                     G_CALLBACK (new_file), de);
1502 }
1503
1504 /* Returns true if NAME has a suffix which might denote a PSPP file */
1505 static gboolean
1506 name_has_suffix (const gchar *name)
1507 {
1508   if ( g_str_has_suffix (name, ".sav"))
1509     return TRUE;
1510   if ( g_str_has_suffix (name, ".SAV"))
1511     return TRUE;
1512   if ( g_str_has_suffix (name, ".por"))
1513     return TRUE;
1514   if ( g_str_has_suffix (name, ".POR"))
1515     return TRUE;
1516
1517   return FALSE;
1518 }
1519
1520 /* Append SUFFIX to the filename of DE */
1521 static void
1522 append_filename_suffix (struct data_editor *de, const gchar *suffix)
1523 {
1524   if ( ! name_has_suffix (de->file_name))
1525     {
1526       gchar *s = de->file_name;
1527       de->file_name = g_strconcat (de->file_name, suffix, NULL);
1528       g_free (s);
1529     }
1530 }
1531
1532 /* Save DE to file */
1533 static void
1534 save_file (struct data_editor *de)
1535 {
1536   struct getl_interface *sss;
1537   struct string file_name ;
1538
1539   g_assert (de->file_name);
1540
1541   ds_init_cstr (&file_name, de->file_name);
1542   gen_quoted_string (&file_name);
1543
1544   if ( de->save_as_portable )
1545     {
1546       append_filename_suffix (de, ".por");
1547       sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
1548                                          ds_cstr (&file_name));
1549     }
1550   else
1551     {
1552       append_filename_suffix (de, ".sav");
1553       sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
1554                                          ds_cstr (&file_name));
1555     }
1556
1557   ds_destroy (&file_name);
1558
1559   execute_syntax (sss);
1560 }
1561
1562
1563 /* Callback for data_save action.
1564    If there's an existing file name, then just save,
1565    otherwise prompt for a file name, then save */
1566 static void
1567 data_save (GtkAction *action, struct data_editor *de)
1568 {
1569   if ( de->file_name)
1570     save_file (de);
1571   else
1572     data_save_as_dialog (action, de);
1573 }
1574
1575
1576 /* Callback for data_save_as action. Prompt for a filename and save */
1577 static void
1578 data_save_as_dialog (GtkAction *action, struct data_editor *de)
1579 {
1580   struct editor_window *e = (struct editor_window *) de;
1581
1582   GtkWidget *button_sys;
1583   GtkWidget *dialog =
1584     gtk_file_chooser_dialog_new (_("Save"),
1585                                  GTK_WINDOW (e->window),
1586                                  GTK_FILE_CHOOSER_ACTION_SAVE,
1587                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1588                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1589                                  NULL);
1590
1591   GtkFileFilter *filter = gtk_file_filter_new ();
1592   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1593   gtk_file_filter_add_pattern (filter, "*.sav");
1594   gtk_file_filter_add_pattern (filter, "*.SAV");
1595   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1596
1597   filter = gtk_file_filter_new ();
1598   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1599   gtk_file_filter_add_pattern (filter, "*.por");
1600   gtk_file_filter_add_pattern (filter, "*.POR");
1601   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1602
1603   filter = gtk_file_filter_new ();
1604   gtk_file_filter_set_name (filter, _("All Files"));
1605   gtk_file_filter_add_pattern (filter, "*");
1606   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1607
1608   {
1609     GtkWidget *button_por;
1610     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
1611     button_sys =
1612       gtk_radio_button_new_with_label (NULL, _("System File"));
1613
1614     button_por =
1615       gtk_radio_button_new_with_label
1616       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
1617        _("Portable File"));
1618
1619     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
1620     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
1621
1622     gtk_widget_show_all (vbox);
1623
1624     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
1625   }
1626
1627   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1628     {
1629     case GTK_RESPONSE_ACCEPT:
1630       {
1631         g_free (de->file_name);
1632
1633         de->file_name =
1634           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1635
1636         de->save_as_portable =
1637           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
1638
1639         save_file (de);
1640
1641         window_set_name_from_filename (e, de->file_name);
1642       }
1643       break;
1644     default:
1645       break;
1646     }
1647
1648   gtk_widget_destroy (dialog);
1649 }
1650
1651
1652 /* Callback for data_new action.
1653    Performs the NEW FILE command */
1654 static void
1655 new_file (GtkAction *action, struct editor_window *e)
1656 {
1657   struct data_editor *de = (struct data_editor *) e;
1658
1659   struct getl_interface *sss =
1660     create_syntax_string_source ("NEW FILE.");
1661
1662   execute_syntax (sss);
1663
1664   g_free (de->file_name);
1665   de->file_name = NULL;
1666
1667   default_window_name (e);
1668 }
1669
1670
1671 static void
1672 open_data_file (const gchar *file_name, struct data_editor *de)
1673 {
1674   struct getl_interface *sss;
1675   struct string filename;
1676
1677   ds_init_cstr (&filename, file_name);
1678
1679   gen_quoted_string (&filename);
1680
1681   sss = create_syntax_string_source ("GET FILE=%s.",
1682                                      ds_cstr (&filename));
1683   ds_destroy (&filename);
1684
1685   if (execute_syntax (sss) )
1686   {
1687     window_set_name_from_filename ((struct editor_window *) de, file_name);
1688     add_most_recent (file_name);
1689   }
1690 }
1691
1692
1693
1694 /* Callback for the data_open action.
1695    Prompts for a filename and opens it */
1696 static void
1697 open_data_dialog (GtkAction *action, struct data_editor *de)
1698 {
1699   struct editor_window *e = (struct editor_window *) de;
1700
1701   GtkWidget *dialog =
1702     gtk_file_chooser_dialog_new (_("Open"),
1703                                  GTK_WINDOW (e->window),
1704                                  GTK_FILE_CHOOSER_ACTION_OPEN,
1705                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1706                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1707                                  NULL);
1708
1709   GtkFileFilter *filter = gtk_file_filter_new ();
1710   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1711   gtk_file_filter_add_pattern (filter, "*.sav");
1712   gtk_file_filter_add_pattern (filter, "*.SAV");
1713   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1714
1715   filter = gtk_file_filter_new ();
1716   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1717   gtk_file_filter_add_pattern (filter, "*.por");
1718   gtk_file_filter_add_pattern (filter, "*.POR");
1719   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1720
1721   filter = gtk_file_filter_new ();
1722   gtk_file_filter_set_name (filter, _("All Files"));
1723   gtk_file_filter_add_pattern (filter, "*");
1724   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1725
1726
1727   if ( de->file_name)
1728     {
1729       gchar *dir_name = g_path_get_dirname (de->file_name);
1730       gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1731                                            dir_name);
1732       free (dir_name);
1733     }
1734
1735   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1736     {
1737     case GTK_RESPONSE_ACCEPT:
1738       {
1739         g_free (de->file_name);
1740         de->file_name =
1741           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1742
1743         open_data_file (de->file_name, de);
1744       }
1745       break;
1746     default:
1747       break;
1748     }
1749
1750   gtk_widget_destroy (dialog);
1751 }
1752
1753
1754
1755 /* Update the data_ref_entry with the reference of the active cell */
1756 static gint
1757 update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
1758 {
1759   GladeXML *data_editor_xml = data;
1760
1761   PsppireDataStore *data_store =
1762     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1763
1764   g_return_val_if_fail (data_editor_xml, FALSE);
1765
1766   if (data_store)
1767     {
1768       const struct variable *var =
1769         psppire_dict_get_variable (data_store->dict, col);
1770
1771       /* The entry where the reference to the current cell is displayed */
1772       GtkEntry *cell_ref_entry =
1773         GTK_ENTRY (get_widget_assert (data_editor_xml,
1774                                       "cell_ref_entry"));
1775       GtkEntry *datum_entry =
1776         GTK_ENTRY (get_widget_assert (data_editor_xml,
1777                                       "datum_entry"));
1778
1779       if ( var )
1780         {
1781           gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
1782                                          var_get_name (var));
1783
1784           gchar *s = pspp_locale_to_utf8 (text, -1, 0);
1785
1786           g_free (text);
1787
1788           gtk_entry_set_text (cell_ref_entry, s);
1789
1790           g_free (s);
1791         }
1792       else
1793         gtk_entry_set_text (cell_ref_entry, "");
1794
1795
1796       if ( var )
1797         {
1798           gchar *text =
1799             psppire_data_store_get_string (data_store, row,
1800                                            var_get_dict_index(var));
1801           g_strchug (text);
1802
1803           gtk_entry_set_text (datum_entry, text);
1804
1805           free (text);
1806         }
1807       else
1808         gtk_entry_set_text (datum_entry, "");
1809     }
1810
1811   return FALSE;
1812 }
1813
1814
1815
1816
1817
1818 static void
1819 do_sort (PsppireDataStore *ds, int var, gboolean descend)
1820 {
1821   GString *string = g_string_new ("SORT CASES BY ");
1822
1823   const struct variable *v =
1824     psppire_dict_get_variable (ds->dict, var);
1825
1826   g_string_append_printf (string, "%s", var_get_name (v));
1827
1828   if ( descend )
1829     g_string_append (string, " (D)");
1830
1831   g_string_append (string, ".");
1832
1833   execute_syntax (create_syntax_string_source (string->str));
1834
1835   g_string_free (string, TRUE);
1836 }
1837
1838
1839 static void
1840 sort_up (GtkMenuItem *item, gpointer data)
1841 {
1842   GtkSheet *sheet  = data;
1843   GtkSheetRange range;
1844   gtk_sheet_get_selected_range (sheet, &range);
1845
1846   do_sort (PSPPIRE_DATA_STORE (gtk_sheet_get_model(sheet)),
1847            range.col0, FALSE);
1848
1849 }
1850
1851 static void
1852 sort_down (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, TRUE);
1860 }
1861
1862
1863
1864
1865 static void
1866 create_data_sheet_variable_popup_menu (struct data_editor *de)
1867 {
1868   GtkSheet *sheet  = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
1869   GtkWidget *menu = gtk_menu_new ();
1870
1871   GtkWidget *sort_ascending =
1872     gtk_menu_item_new_with_label (_("Sort Ascending"));
1873
1874   GtkWidget *sort_descending =
1875     gtk_menu_item_new_with_label (_("Sort Descending"));
1876
1877
1878   GtkWidget *insert_variable =
1879     gtk_menu_item_new_with_label (_("Insert Variable"));
1880
1881   GtkWidget *clear_variable =
1882     gtk_menu_item_new_with_label (_("Clear"));
1883
1884
1885   gtk_action_connect_proxy (de->insert_variable,
1886                             insert_variable );
1887
1888
1889   gtk_action_connect_proxy (de->delete_variables,
1890                             clear_variable );
1891
1892
1893   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_variable);
1894
1895
1896   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1897                          gtk_separator_menu_item_new ());
1898
1899
1900   gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_variable);
1901
1902
1903   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1904                          gtk_separator_menu_item_new ());
1905
1906
1907   g_signal_connect (G_OBJECT (sort_ascending), "activate",
1908                     G_CALLBACK (sort_up), sheet);
1909
1910   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_ascending);
1911
1912
1913   g_signal_connect (G_OBJECT (sort_descending), "activate",
1914                     G_CALLBACK (sort_down), sheet);
1915
1916
1917   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sort_descending);
1918
1919   gtk_widget_show_all (menu);
1920
1921
1922   de->data_sheet_variable_popup_menu = GTK_MENU(menu);
1923 }
1924
1925
1926 static void
1927 create_data_sheet_cases_popup_menu (struct data_editor *de)
1928 {
1929   GtkWidget *menu = gtk_menu_new ();
1930
1931   GtkWidget *insert_case =
1932     gtk_menu_item_new_with_label (_("Insert Case"));
1933
1934   GtkWidget *delete_case =
1935     gtk_menu_item_new_with_label (_("Clear"));
1936
1937
1938   gtk_action_connect_proxy (de->insert_case,
1939                             insert_case);
1940
1941
1942   gtk_action_connect_proxy (de->delete_cases,
1943                             delete_case);
1944
1945
1946   gtk_menu_shell_append (GTK_MENU_SHELL (menu), insert_case);
1947
1948
1949   gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1950                          gtk_separator_menu_item_new ());
1951
1952
1953   gtk_menu_shell_append (GTK_MENU_SHELL (menu), delete_case);
1954
1955
1956   gtk_widget_show_all (menu);
1957
1958
1959   de->data_sheet_cases_popup_menu = GTK_MENU (menu);
1960 }
1961
1962
1963 static void
1964 popup_variable_menu (GtkSheet *sheet, gint column,
1965                      GdkEventButton *event, gpointer data)
1966 {
1967   struct data_editor *de = data;
1968
1969   PsppireDataStore *data_store =
1970     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1971
1972   const struct variable *v =
1973     psppire_dict_get_variable (data_store->dict, column);
1974
1975   if ( v && event->button == 3)
1976     {
1977
1978       gtk_sheet_select_column (sheet, column);
1979
1980       gtk_menu_popup (GTK_MENU (de->data_sheet_variable_popup_menu),
1981                       NULL, NULL, NULL, NULL,
1982                       event->button, event->time);
1983     }
1984 }
1985
1986
1987 static void
1988 popup_cases_menu (GtkSheet *sheet, gint row,
1989                   GdkEventButton *event, gpointer data)
1990 {
1991   struct data_editor *de = data;
1992
1993   PsppireDataStore *data_store =
1994     PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1995
1996   if ( row <= psppire_data_store_get_case_count (data_store) &&
1997        event->button == 3)
1998     {
1999       gtk_sheet_select_row (sheet, row);
2000
2001       gtk_menu_popup (GTK_MENU (de->data_sheet_cases_popup_menu),
2002                       NULL, NULL, NULL, NULL,
2003                       event->button, event->time);
2004     }
2005 }
2006
2007
2008 static void
2009 on_edit_paste (GtkAction *a, gpointer data)
2010 {
2011   GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2012
2013   gtk_clipboard_request_contents (clipboard,
2014                                   gdk_atom_intern ("UTF8_STRING", TRUE),
2015                                   data_sheet_contents_received_callback,
2016                                   data);
2017 }
2018
2019
2020 static void
2021 on_edit_copy (GtkMenuItem *m, gpointer data)
2022 {
2023   struct data_editor *de = data;
2024
2025   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2026                                                        "data_sheet"));
2027
2028   data_sheet_set_clip (data_sheet);
2029 }
2030
2031
2032
2033 static void
2034 on_edit_cut (GtkMenuItem *m, gpointer data)
2035 {
2036   struct data_editor *de = data;
2037   gint max_rows, max_columns;
2038   gint r;
2039   GtkSheetRange range;
2040   PsppireDataStore *ds;
2041   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml,
2042                                                        "data_sheet"));
2043
2044   data_sheet_set_clip (data_sheet);
2045
2046
2047   /* Now blank all the cells */
2048   gtk_sheet_get_selected_range (data_sheet, &range);
2049
2050   ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
2051
2052
2053    /* If nothing selected, then use active cell */
2054   if ( range.row0 < 0 || range.col0 < 0 )
2055     {
2056       gint row, col;
2057       gtk_sheet_get_active_cell (data_sheet, &row, &col);
2058
2059       range.row0 = range.rowi = row;
2060       range.col0 = range.coli = col;
2061     }
2062
2063   /* The sheet range can include cells that do not include data.
2064      Exclude them from the range. */
2065   max_rows = psppire_data_store_get_case_count (ds);
2066   if (range.rowi >= max_rows)
2067     {
2068       if (max_rows == 0)
2069         return;
2070       range.rowi = max_rows - 1;
2071     }
2072
2073   max_columns = dict_get_var_cnt (ds->dict->dict);
2074   if (range.coli >= max_columns)
2075     {
2076       if (max_columns == 0)
2077         return;
2078       range.coli = max_columns - 1;
2079     }
2080
2081   g_return_if_fail (range.rowi >= range.row0);
2082   g_return_if_fail (range.row0 >= 0);
2083   g_return_if_fail (range.coli >= range.col0);
2084   g_return_if_fail (range.col0 >= 0);
2085
2086
2087   for (r = range.row0; r <= range.rowi ; ++r )
2088     {
2089       gint c;
2090
2091       for (c = range.col0 ; c <= range.coli; ++c)
2092         {
2093           psppire_data_store_set_string (ds, "", r, c);
2094         }
2095     }
2096
2097   /* and remove the selection */
2098   gtk_sheet_unselect_range (data_sheet);
2099 }
2100
2101