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