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