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