Fixed bug in internationalization.
[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 "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 static void register_data_editor_actions (struct data_editor *de);
54
55 static void insert_variable (GtkCheckMenuItem *m, gpointer data);
56
57
58
59 /* Switch between the VAR SHEET and the DATA SHEET */
60 enum {PAGE_DATA_SHEET = 0, PAGE_VAR_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
264   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
265
266   g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
267                             "activate",
268                             G_CALLBACK (gtk_action_activate),
269                             de->action_data_new);
270
271   g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
272                             "activate",
273                             G_CALLBACK (gtk_action_activate),
274                             de->action_data_open);
275
276
277 #if RECENT_LISTS_AVAILABLE
278   {
279     GtkRecentManager *rm = gtk_recent_manager_get_default ();
280     GtkWidget *recent_data = get_widget_assert (de->xml, "file_recent-data");
281     GtkWidget *recent_files = get_widget_assert (de->xml, "file_recent-files");
282
283     GtkWidget *menu = gtk_recent_chooser_menu_new_for_manager (rm);
284
285     GtkRecentFilter *filter = gtk_recent_filter_new ();
286
287     gtk_recent_filter_add_pattern (filter, "*.sav");
288     gtk_recent_filter_add_pattern (filter, "*.SAV");
289
290     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
291
292     gtk_widget_set_sensitive (recent_data, TRUE);
293     g_signal_connect (menu, "selection-done",
294                       G_CALLBACK (on_recent_data_select), de);
295
296     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu);
297
298
299     filter = gtk_recent_filter_new ();
300     menu = gtk_recent_chooser_menu_new_for_manager (rm);
301
302     gtk_recent_filter_add_pattern (filter, "*.sps");
303     gtk_recent_filter_add_pattern (filter, "*.SPS");
304
305     gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
306
307     gtk_widget_set_sensitive (recent_files, TRUE);
308     g_signal_connect (menu, "selection-done",
309                       G_CALLBACK (on_recent_files_select), de);
310
311     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu);
312   }
313 #endif
314
315   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
316                     "activate",
317                     G_CALLBACK (new_syntax_window),
318                     e->window);
319
320   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
321                     "activate",
322                     G_CALLBACK (open_syntax_window),
323                     e->window);
324
325   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
326                             "activate",
327                             G_CALLBACK (gtk_action_activate),
328                             de->action_data_save);
329
330   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
331                             "activate",
332                             G_CALLBACK (gtk_action_activate),
333                             de->action_data_save_as);
334
335
336   g_signal_connect (get_widget_assert (de->xml,"edit_clear"),
337                     "activate",
338                     G_CALLBACK (on_clear_activate),
339                     de);
340
341
342   g_signal_connect (get_widget_assert (de->xml,"data_insert-variable"),
343                     "activate",
344                     G_CALLBACK (insert_variable),
345                     de);
346
347   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
348                             get_widget_assert (de->xml, "data_weight-cases")
349                             );
350
351   gtk_action_connect_proxy (de->invoke_transpose_dialog,
352                             get_widget_assert (de->xml, "data_transpose")
353                             );
354
355   gtk_action_connect_proxy (de->invoke_split_file_dialog,
356                             get_widget_assert (de->xml, "data_split-file")
357                             );
358
359   gtk_action_connect_proxy (de->invoke_sort_cases_dialog,
360                             get_widget_assert (de->xml, "data_sort-cases")
361                             );
362
363   gtk_action_connect_proxy (de->invoke_compute_dialog,
364                             get_widget_assert (de->xml, "transform_compute")
365                             );
366
367
368   g_signal_connect (get_widget_assert (de->xml,"help_about"),
369                     "activate",
370                     G_CALLBACK (about_new),
371                     e->window);
372
373
374   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
375                     "activate",
376                     G_CALLBACK (reference_manual),
377                     e->window);
378
379
380
381   g_signal_connect (get_widget_assert (de->xml,"data_sheet"),
382                     "double-click-column",
383                     G_CALLBACK (click2column),
384                     de);
385
386
387   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
388                     "double-click-row",
389                     GTK_SIGNAL_FUNC (click2row),
390                     de);
391
392
393   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
394                     "select-row",
395                     GTK_SIGNAL_FUNC (enable_edit_clear),
396                     de);
397
398   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
399                     "activate",
400                     GTK_SIGNAL_FUNC (disable_edit_clear),
401                     de);
402
403
404   g_signal_connect (get_widget_assert (de->xml, "notebook"),
405                     "switch-page",
406                     G_CALLBACK (data_var_select), de);
407
408
409
410   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
411                     "activate",
412                     G_CALLBACK (status_bar_activate), de);
413
414
415   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
416                     "activate",
417                     G_CALLBACK (grid_lines_activate), de);
418
419
420
421   g_signal_connect (get_widget_assert (de->xml, "view_data"),
422                     "activate",
423                     G_CALLBACK (data_sheet_activate), de);
424
425   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
426                     "activate",
427                     G_CALLBACK (variable_sheet_activate), de);
428
429
430
431   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
432                     "activate",
433                     G_CALLBACK (fonts_activate), de);
434
435
436
437   g_signal_connect (get_widget_assert (de->xml, "view_valuelabels"),
438                     "activate",
439                     G_CALLBACK (value_labels_activate), de);
440
441
442   g_signal_connect (get_widget_assert (de->xml, "togglebutton-value-labels"),
443                     "toggled",
444                     G_CALLBACK (value_labels_toggled), de);
445
446   gtk_action_connect_proxy (de->action_data_open,
447                             get_widget_assert (de->xml, "button-open")
448                             );
449
450   gtk_action_connect_proxy (de->action_data_save,
451                             get_widget_assert (de->xml, "button-save")
452                             );
453
454   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
455                             get_widget_assert (de->xml, "button-weight-cases")
456                             );
457
458   gtk_action_connect_proxy (de->invoke_split_file_dialog,
459                             get_widget_assert (de->xml, "button-split-file")
460                             );
461
462   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
463                     "activate",
464                     G_CALLBACK (file_quit), de);
465
466
467   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
468                     "activate",
469                     G_CALLBACK (minimise_all_windows), NULL);
470
471
472   select_sheet (de, PAGE_DATA_SHEET);
473
474   return de;
475 }
476
477
478 /* Callback which occurs when the var sheet's row title
479    button is double clicked */
480 static gboolean
481 click2row (GtkWidget *w, gint row, gpointer data)
482 {
483   struct data_editor *de = data;
484
485   gint current_row, current_column;
486
487   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
488
489   data_editor_select_sheet (de, PAGE_DATA_SHEET);
490
491   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
492                              &current_row, &current_column);
493
494   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
495
496   return FALSE;
497 }
498
499
500 /* Callback which occurs when the data sheet's column title
501    is double clicked */
502 static gboolean
503 click2column (GtkWidget *w, gint col, gpointer data)
504 {
505   struct data_editor *de = data;
506
507   gint current_row, current_column;
508
509   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
510
511   data_editor_select_sheet (de, PAGE_VAR_SHEET);
512
513   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
514                              &current_row, &current_column);
515
516   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
517
518   return FALSE;
519 }
520
521
522 void
523 new_data_window (GtkMenuItem *menuitem, gpointer parent)
524 {
525   window_create (WINDOW_DATA, NULL);
526 }
527
528
529 static void
530 select_sheet (struct data_editor *de, guint page_num)
531 {
532   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
533   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
534
535   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
536   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
537
538   switch (page_num)
539     {
540     case PAGE_VAR_SHEET:
541       gtk_widget_hide (view_variables);
542       gtk_widget_show (view_data);
543       gtk_widget_set_sensitive (insert_variable, TRUE);
544       gtk_widget_set_sensitive (insert_cases, FALSE);
545       break;
546     case PAGE_DATA_SHEET:
547       gtk_widget_show (view_variables);
548       gtk_widget_hide (view_data);
549 #if 0
550       gtk_widget_set_sensitive (insert_cases, TRUE);
551 #endif
552       break;
553     default:
554       g_assert_not_reached ();
555       break;
556     }
557 }
558
559
560 static void
561 data_var_select (GtkNotebook *notebook,
562                 GtkNotebookPage *page,
563                 guint page_num,
564                 gpointer user_data)
565 {
566   struct data_editor *de = user_data;
567
568   select_sheet (de, page_num);
569 }
570
571
572
573
574 void
575 data_editor_select_sheet (struct data_editor *de, gint page)
576 {
577   gtk_notebook_set_current_page
578    (
579     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
580     );
581 }
582
583
584 static void
585 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
586 {
587   struct data_editor *de = data;
588   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
589
590   if ( gtk_check_menu_item_get_active (menuitem) )
591     gtk_widget_show (statusbar);
592   else
593     gtk_widget_hide (statusbar);
594 }
595
596
597 static void
598 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
599 {
600   struct data_editor *de = data;
601   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
602
603   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
604                                                      "variable_sheet")),
605                        grid_visible);
606
607   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
608                        grid_visible);
609 }
610
611
612
613 static void
614 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
615 {
616   struct data_editor *de = data;
617
618   data_editor_select_sheet (de, PAGE_DATA_SHEET);
619 }
620
621
622 static void
623 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
624 {
625   struct data_editor *de = data;
626
627   data_editor_select_sheet (de, PAGE_VAR_SHEET);
628 }
629
630
631 static void
632 fonts_activate (GtkMenuItem *menuitem, gpointer data)
633 {
634   struct data_editor *de = data;
635   GtkWidget *dialog =
636     gtk_font_selection_dialog_new (_("Font Selection"));
637
638   gtk_window_set_transient_for (GTK_WINDOW (dialog),
639                                 GTK_WINDOW (get_widget_assert (de->xml,
640                                                                "data_editor")));
641   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
642     {
643       GtkSheet *data_sheet =
644         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
645
646       GtkSheet *var_sheet =
647         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
648
649       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
650       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
651
652       const gchar *font = gtk_font_selection_dialog_get_font_name
653         (GTK_FONT_SELECTION_DIALOG (dialog));
654
655       PangoFontDescription* font_desc =
656         pango_font_description_from_string (font);
657
658       psppire_var_store_set_font (vs, font_desc);
659       psppire_data_store_set_font (ds, font_desc);
660     }
661
662   gtk_widget_hide (dialog);
663 }
664
665
666 /* The next two callbacks are mutually co-operative */
667
668 /* Callback for the value labels menu item */
669 static void
670 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
671 {
672   struct data_editor *de = data;
673
674   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
675
676   GtkToggleToolButton *tb =
677     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
678                                                "togglebutton-value-labels"));
679
680   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
681
682   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
683
684   gtk_toggle_tool_button_set_active (tb, show_value_labels);
685
686   psppire_data_store_show_labels (ds, show_value_labels);
687 }
688
689
690 /* Callback for the value labels tooglebutton */
691 static void
692 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
693                       gpointer data)
694 {
695   struct data_editor *de = data;
696
697   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
698
699   GtkCheckMenuItem *item =
700     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
701
702   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
703
704   gboolean show_value_labels =
705     gtk_toggle_tool_button_get_active (toggle_tool_button);
706
707   gtk_check_menu_item_set_active (item, show_value_labels);
708
709   psppire_data_store_show_labels (ds, show_value_labels);
710 }
711
712
713 static void
714 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
715 {
716   /* FIXME: Need to be more intelligent here.
717      Give the user the opportunity to save any unsaved data.
718   */
719   gtk_main_quit ();
720 }
721
722
723
724 /* Callback for when the Clear item in the edit menu is activated */
725 static void
726 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
727 {
728   struct data_editor *de = data;
729
730   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
731                                                            "notebook"));
732
733   switch ( gtk_notebook_get_current_page (notebook) )
734     {
735     case PAGE_VAR_SHEET:
736       {
737         GtkSheet *var_sheet =
738           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
739
740         PsppireVarStore *vs = PSPPIRE_VAR_STORE
741           (gtk_sheet_get_model (var_sheet) );
742
743         /* This shouldn't be able to happen, because the menuitem
744            should be disabled */
745         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
746
747         psppire_dict_delete_variables (vs->dict,
748                                        var_sheet->range.row0,
749                                        1 +
750                                        var_sheet->range.rowi -
751                                        var_sheet->range.row0 );
752       }
753       break;
754       case PAGE_DATA_SHEET:
755         break;
756       default:
757         g_assert_not_reached ();
758     }
759 }
760
761
762 /* Insert a new variable before the current row in the variable sheet,
763    or before the current column in the data sheet, whichever is selected */
764 static void
765 insert_variable (GtkCheckMenuItem *m, gpointer data)
766 {
767   struct data_editor *de = data;
768   gint posn;
769
770   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
771
772   GtkSheet *var_sheet =
773     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
774
775   PsppireVarStore *vs = PSPPIRE_VAR_STORE
776     (gtk_sheet_get_model (var_sheet) );
777
778   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
779     {
780     case PAGE_VAR_SHEET:
781       posn = var_sheet->active_cell.row;
782       break;
783     case PAGE_DATA_SHEET:
784       {
785         GtkSheet *data_sheet =
786           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
787
788         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
789           posn = data_sheet->range.col0;
790         else
791           posn = data_sheet->active_cell.col;
792       }
793       break;
794     default:
795       g_assert_not_reached ();
796     }
797
798   psppire_dict_insert_variable (vs->dict, posn, NULL);
799 }
800
801 /* Callback for when the dictionary changes its split variables */
802 static void
803 on_split_change (PsppireDict *dict, gpointer data)
804 {
805   struct data_editor *de = data;
806
807   size_t n_split_vars = dict_get_split_cnt (dict->dict);
808
809   GtkWidget *split_status_area =
810     get_widget_assert (de->xml, "split-file-status-area");
811
812   if ( n_split_vars == 0 )
813     {
814       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
815     }
816   else
817     {
818       gint i;
819       GString *text;
820       const struct variable *const * split_vars = dict_get_split_vars (dict->dict);
821
822       text = g_string_new (_("Split by "));
823
824       for (i = 0 ; i < n_split_vars - 1; ++i )
825         {
826           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
827         }
828       g_string_append (text, var_get_name (split_vars[i]));
829
830       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
831
832       g_string_free (text, TRUE);
833     }
834 }
835
836
837 /* Callback for when the dictionary changes its filter variable */
838 static void
839 on_filter_change (GObject *o, gint filter_index, gpointer data)
840 {
841   struct data_editor *de = data;
842   GtkWidget *filter_status_area =
843     get_widget_assert (de->xml, "filter-use-status-area");
844
845   if ( filter_index == -1 )
846     {
847       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
848     }
849   else
850     {
851       GtkSheet *var_sheet =
852         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
853
854       PsppireVarStore *vs = PSPPIRE_VAR_STORE
855         (gtk_sheet_get_model (var_sheet) );
856
857       struct variable *var = psppire_dict_get_variable (vs->dict,
858                                                         filter_index);
859
860       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
861
862       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
863
864       g_free (text);
865     }
866 }
867
868 /* Callback for when the dictionary changes its weights */
869 static void
870 on_weight_change (GObject *o, gint weight_index, gpointer data)
871 {
872   struct data_editor *de = data;
873   GtkWidget *weight_status_area =
874     get_widget_assert (de->xml, "weight-status-area");
875
876   if ( weight_index == -1 )
877     {
878       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights 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                                                         weight_index);
890
891       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
892
893       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
894
895       g_free (text);
896     }
897 }
898
899
900
901 \f
902 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
903 static void new_file (GtkAction *, struct editor_window *de);
904 static void open_data_dialog (GtkAction *, struct data_editor *de);
905 static void data_save (GtkAction *action, struct data_editor *e);
906
907
908 /* Create the GtkActions and connect to their signals */
909 static void
910 register_data_editor_actions (struct data_editor *de)
911 {
912   de->action_data_open =
913     gtk_action_new ("data-open-dialog",
914                     _("Open"),
915                     _("Open a data file"),
916                     "gtk-open");
917
918   g_signal_connect (de->action_data_open, "activate",
919                     G_CALLBACK (open_data_dialog), de);
920
921
922   de->action_data_save = gtk_action_new ("data-save",
923                                             _("Save"),
924                                             _("Save data to file"),
925                                             "gtk-save");
926
927   g_signal_connect (de->action_data_save, "activate",
928                     G_CALLBACK (data_save), de);
929
930
931
932   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
933                                             _("Save As"),
934                                             _("Save data to file"),
935                                             "gtk-save");
936
937   g_signal_connect (de->action_data_save_as, "activate",
938                     G_CALLBACK (data_save_as_dialog), de);
939
940   de->action_data_new =
941     gtk_action_new ("data-new",
942                     _("New"),
943                     _("New data file"),
944                     NULL);
945
946   g_signal_connect (de->action_data_new, "activate",
947                     G_CALLBACK (new_file), de);
948 }
949
950 /* Returns true if NAME has a suffix which might denote a PSPP file */
951 static gboolean
952 name_has_suffix (const gchar *name)
953 {
954   if ( g_str_has_suffix (name, ".sav"))
955     return TRUE;
956   if ( g_str_has_suffix (name, ".SAV"))
957     return TRUE;
958   if ( g_str_has_suffix (name, ".por"))
959     return TRUE;
960   if ( g_str_has_suffix (name, ".POR"))
961     return TRUE;
962
963   return FALSE;
964 }
965
966 /* Append SUFFIX to the filename of DE */
967 static void
968 append_filename_suffix (struct data_editor *de, const gchar *suffix)
969 {
970   if ( ! name_has_suffix (de->file_name))
971     {
972       gchar *s = de->file_name;
973       de->file_name = g_strconcat (de->file_name, suffix, NULL);
974       g_free (s);
975     }
976 }
977
978 /* Save DE to file */
979 static void
980 save_file (struct data_editor *de)
981 {
982   struct getl_interface *sss;
983   struct string file_name ;
984
985   g_assert (de->file_name);
986
987   ds_init_cstr (&file_name, de->file_name);
988   gen_quoted_string (&file_name);
989
990   if ( de->save_as_portable )
991     {
992       append_filename_suffix (de, ".por");
993       sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
994                                          ds_cstr (&file_name));
995     }
996   else
997     {
998       append_filename_suffix (de, ".sav");
999       sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
1000                                          ds_cstr (&file_name));
1001     }
1002
1003   ds_destroy (&file_name);
1004
1005   execute_syntax (sss);
1006 }
1007
1008
1009 /* Callback for data_save action.
1010    If there's an existing file name, then just save,
1011    otherwise prompt for a file name, then save */
1012 static void
1013 data_save (GtkAction *action, struct data_editor *de)
1014 {
1015   if ( de->file_name)
1016     save_file (de);
1017   else
1018     data_save_as_dialog (action, de);
1019 }
1020
1021
1022 /* Callback for data_save_as action. Prompt for a filename and save */
1023 static void
1024 data_save_as_dialog (GtkAction *action, struct data_editor *de)
1025 {
1026   struct editor_window *e = (struct editor_window *) de;
1027
1028   GtkWidget *button_sys;
1029   GtkWidget *dialog =
1030     gtk_file_chooser_dialog_new (_("Save"),
1031                                  GTK_WINDOW (e->window),
1032                                  GTK_FILE_CHOOSER_ACTION_SAVE,
1033                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1034                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1035                                  NULL);
1036
1037   GtkFileFilter *filter = gtk_file_filter_new ();
1038   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1039   gtk_file_filter_add_pattern (filter, "*.sav");
1040   gtk_file_filter_add_pattern (filter, "*.SAV");
1041   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1042
1043   filter = gtk_file_filter_new ();
1044   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1045   gtk_file_filter_add_pattern (filter, "*.por");
1046   gtk_file_filter_add_pattern (filter, "*.POR");
1047   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1048
1049   filter = gtk_file_filter_new ();
1050   gtk_file_filter_set_name (filter, _("All Files"));
1051   gtk_file_filter_add_pattern (filter, "*");
1052   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1053
1054   {
1055     GtkWidget *button_por;
1056     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
1057     button_sys =
1058       gtk_radio_button_new_with_label (NULL, _("System File"));
1059
1060     button_por =
1061       gtk_radio_button_new_with_label
1062       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
1063        _("Portable File"));
1064
1065     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
1066     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
1067
1068     gtk_widget_show_all (vbox);
1069
1070     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
1071   }
1072
1073   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1074     {
1075     case GTK_RESPONSE_ACCEPT:
1076       {
1077         g_free (de->file_name);
1078
1079         de->file_name =
1080           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1081
1082         de->save_as_portable =
1083           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
1084
1085         save_file (de);
1086
1087         window_set_name_from_filename (e, de->file_name);
1088       }
1089       break;
1090     default:
1091       break;
1092     }
1093
1094   gtk_widget_destroy (dialog);
1095 }
1096
1097
1098 /* Callback for data_new action.
1099    Performs the NEW FILE command */
1100 static void
1101 new_file (GtkAction *action, struct editor_window *e)
1102 {
1103   struct data_editor *de = (struct data_editor *) e;
1104
1105   struct getl_interface *sss =
1106     create_syntax_string_source ("NEW FILE.");
1107
1108   execute_syntax (sss);
1109
1110   g_free (de->file_name);
1111   de->file_name = NULL;
1112
1113   default_window_name (e);
1114 }
1115
1116
1117 static void
1118 open_data_file (const gchar *file_name, struct data_editor *de)
1119 {
1120   struct getl_interface *sss;
1121   struct string filename;
1122
1123   ds_init_cstr (&filename, file_name);
1124
1125   gen_quoted_string (&filename);
1126
1127   sss = create_syntax_string_source ("GET FILE=%s.",
1128                                      ds_cstr (&filename));
1129
1130   execute_syntax (sss);
1131   ds_destroy (&filename);
1132
1133   window_set_name_from_filename ((struct editor_window *) de, file_name);
1134 }
1135
1136
1137 /* Callback for the data_open action.
1138    Prompts for a filename and opens it */
1139 static void
1140 open_data_dialog (GtkAction *action, struct data_editor *de)
1141 {
1142   struct editor_window *e = (struct editor_window *) de;
1143
1144   GtkWidget *dialog =
1145     gtk_file_chooser_dialog_new (_("Open"),
1146                                  GTK_WINDOW (e->window),
1147                                  GTK_FILE_CHOOSER_ACTION_OPEN,
1148                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1149                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1150                                  NULL);
1151
1152   GtkFileFilter *filter = gtk_file_filter_new ();
1153   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1154   gtk_file_filter_add_pattern (filter, "*.sav");
1155   gtk_file_filter_add_pattern (filter, "*.SAV");
1156   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1157
1158   filter = gtk_file_filter_new ();
1159   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1160   gtk_file_filter_add_pattern (filter, "*.por");
1161   gtk_file_filter_add_pattern (filter, "*.POR");
1162   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1163
1164   filter = gtk_file_filter_new ();
1165   gtk_file_filter_set_name (filter, _("All Files"));
1166   gtk_file_filter_add_pattern (filter, "*");
1167   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1168
1169   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1170     {
1171     case GTK_RESPONSE_ACCEPT:
1172       {
1173         g_free (de->file_name);
1174         de->file_name =
1175           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1176
1177         open_data_file (de->file_name, de);
1178
1179 #if RECENT_LISTS_AVAILABLE
1180         {
1181           GtkRecentManager *manager = gtk_recent_manager_get_default();
1182           gchar *uri = g_filename_to_uri (de->file_name, NULL, NULL);
1183
1184           if ( ! gtk_recent_manager_add_item (manager, uri))
1185             g_warning ("Could not add item %s to recent list\n",uri);
1186
1187           g_free (uri);
1188         }
1189 #endif
1190
1191       }
1192       break;
1193     default:
1194       break;
1195     }
1196
1197   gtk_widget_destroy (dialog);
1198 }