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