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