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