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