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