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