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