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