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