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