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