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