Addressed some of the issues raised by John McCabe-Dansted when building
[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 "transpose-dialog.h"
36 #include "dict-display.h"
37
38 #define _(msgid) gettext (msgid)
39 #define N_(msgid) msgid
40
41 #include "data-editor.h"
42 #include "syntax-editor.h"
43 #include <language/syntax-string-source.h>
44 #include "window-manager.h"
45
46 #include "psppire-data-store.h"
47 #include "psppire-var-store.h"
48
49 static void register_data_editor_actions (struct data_editor *de);
50
51 static void insert_variable (GtkCheckMenuItem *m, gpointer data);
52
53
54 /* Switch between the VAR SHEET and the DATA SHEET */
55 enum {PAGE_DATA_SHEET = 0, PAGE_VAR_SHEET};
56
57 static gboolean click2column (GtkWidget *w, gint col, gpointer data);
58
59 static gboolean click2row (GtkWidget *w, gint row, gpointer data);
60
61
62 static void select_sheet (struct data_editor *de, guint page_num);
63
64
65 /* Callback for when the dictionary changes properties*/
66 static void on_weight_change (GObject *, gint, gpointer);
67 static void on_filter_change (GObject *, gint, gpointer);
68 static void on_split_change (PsppireDict *, gpointer);
69
70 static void data_var_select (GtkNotebook *notebook,
71                             GtkNotebookPage *page,
72                             guint page_num,
73                             gpointer user_data);
74
75 static void status_bar_activate (GtkCheckMenuItem *, gpointer);
76
77 static void grid_lines_activate (GtkCheckMenuItem *, gpointer);
78
79 static void data_sheet_activate (GtkCheckMenuItem *, gpointer);
80
81 static void variable_sheet_activate (GtkCheckMenuItem *, gpointer );
82
83 static void fonts_activate (GtkMenuItem *, gpointer);
84
85 static void value_labels_activate (GtkCheckMenuItem *, gpointer);
86 static void value_labels_toggled (GtkToggleToolButton *, gpointer);
87
88
89 static void file_quit (GtkCheckMenuItem *, gpointer );
90
91 static void on_clear_activate (GtkMenuItem *, gpointer);
92
93 static void
94 enable_edit_clear (GtkWidget *w, gint row, gpointer data)
95 {
96   struct data_editor *de = data;
97
98   GtkWidget *menuitem = get_widget_assert (de->xml, "edit_clear");
99
100   gtk_widget_set_sensitive (menuitem, TRUE);
101 }
102
103 static gboolean
104 disable_edit_clear (GtkWidget *w, gint x, gint y, gpointer data)
105 {
106   struct data_editor *de = data;
107
108   GtkWidget *menuitem = get_widget_assert (de->xml, "edit_clear");
109
110   gtk_widget_set_sensitive (menuitem, FALSE);
111
112   return FALSE;
113 }
114
115
116 /*
117   Create a new data editor.
118 */
119 struct data_editor *
120 new_data_editor (void)
121 {
122   struct data_editor *de ;
123   struct editor_window *e;
124   GtkSheet *var_sheet ;
125   PsppireVarStore *vs;
126
127   de = g_malloc0 (sizeof (*de));
128
129   e = (struct editor_window *) de;
130
131   de->xml = glade_xml_new (PKGDATADIR "/data-editor.glade", NULL, NULL);
132
133
134   var_sheet = GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
135
136   vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
137
138   g_assert(vs); /* Traps a possible bug in win32 build */
139
140   g_signal_connect (vs->dict, "weight-changed",
141                     G_CALLBACK (on_weight_change),
142                     de);
143
144   g_signal_connect (vs->dict, "filter-changed",
145                     G_CALLBACK (on_filter_change),
146                     de);
147
148   g_signal_connect (vs->dict, "split-changed",
149                     G_CALLBACK (on_split_change),
150                     de);
151
152   connect_help (de->xml);
153
154
155   register_data_editor_actions (de);
156
157   de->invoke_weight_cases_dialog =
158     gtk_action_new ("weight-cases-dialog",
159                     _("Weights"),
160                     _("Weight cases by variable"),
161                     "pspp-weight-cases");
162
163   g_signal_connect (de->invoke_weight_cases_dialog, "activate",
164                     G_CALLBACK (weight_cases_dialog), de);
165
166
167   de->invoke_transpose_dialog =
168     gtk_action_new ("transpose-dialog",
169                     _("Transpose"),
170                     _("Transpose the cases with the variables"),
171                     NULL);
172
173
174   g_signal_connect (de->invoke_transpose_dialog, "activate",
175                     G_CALLBACK (transpose_dialog), de);
176
177
178   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
179
180   g_signal_connect_swapped (get_widget_assert (de->xml,"file_new_data"),
181                             "activate",
182                             G_CALLBACK (gtk_action_activate),
183                             de->action_data_new);
184
185   g_signal_connect_swapped (get_widget_assert (de->xml,"file_open_data"),
186                             "activate",
187                             G_CALLBACK (gtk_action_activate),
188                             de->action_data_open);
189
190   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
191                     "activate",
192                     G_CALLBACK (new_syntax_window),
193                     e->window);
194
195   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
196                     "activate",
197                     G_CALLBACK (open_syntax_window),
198                     e->window);
199
200   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save"),
201                             "activate",
202                             G_CALLBACK (gtk_action_activate),
203                             de->action_data_save);
204
205   g_signal_connect_swapped (get_widget_assert (de->xml,"file_save_as"),
206                             "activate",
207                             G_CALLBACK (gtk_action_activate),
208                             de->action_data_save_as);
209
210
211   g_signal_connect (get_widget_assert (de->xml,"edit_clear"),
212                     "activate",
213                     G_CALLBACK (on_clear_activate),
214                     de);
215
216
217   g_signal_connect (get_widget_assert (de->xml,"data_insert-variable"),
218                     "activate",
219                     G_CALLBACK (insert_variable),
220                     de);
221
222   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
223                             get_widget_assert (de->xml, "data_weight-cases")
224                             );
225
226   gtk_action_connect_proxy (de->invoke_transpose_dialog,
227                             get_widget_assert (de->xml, "data_transpose")
228                             );
229
230   g_signal_connect (get_widget_assert (de->xml,"help_about"),
231                     "activate",
232                     G_CALLBACK (about_new),
233                     e->window);
234
235
236   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
237                     "activate",
238                     G_CALLBACK (reference_manual),
239                     e->window);
240
241
242
243   g_signal_connect (get_widget_assert (de->xml,"data_sheet"),
244                     "double-click-column",
245                     G_CALLBACK (click2column),
246                     de);
247
248
249   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
250                     "double-click-row",
251                     GTK_SIGNAL_FUNC (click2row),
252                     de);
253
254
255   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
256                     "select-row",
257                     GTK_SIGNAL_FUNC (enable_edit_clear),
258                     de);
259
260   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
261                     "activate",
262                     GTK_SIGNAL_FUNC (disable_edit_clear),
263                     de);
264
265
266   g_signal_connect (get_widget_assert (de->xml, "notebook"),
267                     "switch-page",
268                     G_CALLBACK (data_var_select), de);
269
270
271
272   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
273                     "activate",
274                     G_CALLBACK (status_bar_activate), de);
275
276
277   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
278                     "activate",
279                     G_CALLBACK (grid_lines_activate), de);
280
281
282
283   g_signal_connect (get_widget_assert (de->xml, "view_data"),
284                     "activate",
285                     G_CALLBACK (data_sheet_activate), de);
286
287   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
288                     "activate",
289                     G_CALLBACK (variable_sheet_activate), de);
290
291
292
293   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
294                     "activate",
295                     G_CALLBACK (fonts_activate), de);
296
297
298
299   g_signal_connect (get_widget_assert (de->xml, "view_valuelabels"),
300                     "activate",
301                     G_CALLBACK (value_labels_activate), de);
302
303
304   g_signal_connect (get_widget_assert (de->xml, "togglebutton-value-labels"),
305                     "toggled",
306                     G_CALLBACK (value_labels_toggled), de);
307
308   gtk_action_connect_proxy (de->action_data_open,
309                             get_widget_assert (de->xml, "button-open")
310                             );
311
312   gtk_action_connect_proxy (de->action_data_save,
313                             get_widget_assert (de->xml, "button-save")
314                             );
315
316   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
317                             get_widget_assert (de->xml, "button-weight-cases")
318                             );
319
320   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
321                     "activate",
322                     G_CALLBACK (file_quit), de);
323
324
325   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
326                     "activate",
327                     G_CALLBACK (minimise_all_windows), NULL);
328
329
330   select_sheet (de, PAGE_DATA_SHEET);
331
332   return de;
333 }
334
335
336 /* Callback which occurs when the var sheet's row title
337    button is double clicked */
338 static gboolean
339 click2row (GtkWidget *w, gint row, gpointer data)
340 {
341   struct data_editor *de = data;
342
343   gint current_row, current_column;
344
345   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
346
347   data_editor_select_sheet (de, PAGE_DATA_SHEET);
348
349   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
350                              &current_row, &current_column);
351
352   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
353
354   return FALSE;
355 }
356
357
358 /* Callback which occurs when the data sheet's column title
359    is double clicked */
360 static gboolean
361 click2column (GtkWidget *w, gint col, gpointer data)
362 {
363   struct data_editor *de = data;
364
365   gint current_row, current_column;
366
367   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
368
369   data_editor_select_sheet (de, PAGE_VAR_SHEET);
370
371   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
372                              &current_row, &current_column);
373
374   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
375
376   return FALSE;
377 }
378
379
380 void
381 new_data_window (GtkMenuItem *menuitem, gpointer parent)
382 {
383   window_create (WINDOW_DATA, NULL);
384 }
385
386
387 static void
388 select_sheet (struct data_editor *de, guint page_num)
389 {
390   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
391   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
392
393   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
394   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
395
396   switch (page_num)
397     {
398     case PAGE_VAR_SHEET:
399       gtk_widget_hide (view_variables);
400       gtk_widget_show (view_data);
401       gtk_widget_set_sensitive (insert_variable, TRUE);
402       gtk_widget_set_sensitive (insert_cases, FALSE);
403       break;
404     case PAGE_DATA_SHEET:
405       gtk_widget_show (view_variables);
406       gtk_widget_hide (view_data);
407 #if 0
408       gtk_widget_set_sensitive (insert_cases, TRUE);
409 #endif
410       break;
411     default:
412       g_assert_not_reached ();
413       break;
414     }
415 }
416
417
418 static void
419 data_var_select (GtkNotebook *notebook,
420                 GtkNotebookPage *page,
421                 guint page_num,
422                 gpointer user_data)
423 {
424   struct data_editor *de = user_data;
425
426   select_sheet (de, page_num);
427 }
428
429
430
431
432 void
433 data_editor_select_sheet (struct data_editor *de, gint page)
434 {
435   gtk_notebook_set_current_page
436    (
437     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
438     );
439 }
440
441
442 static void
443 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
444 {
445   struct data_editor *de = data;
446   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
447
448   if ( gtk_check_menu_item_get_active (menuitem) )
449     gtk_widget_show (statusbar);
450   else
451     gtk_widget_hide (statusbar);
452 }
453
454
455 static void
456 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
457 {
458   struct data_editor *de = data;
459   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
460
461   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
462                                                      "variable_sheet")),
463                        grid_visible);
464
465   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
466                        grid_visible);
467 }
468
469
470
471 static void
472 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
473 {
474   struct data_editor *de = data;
475
476   data_editor_select_sheet (de, PAGE_DATA_SHEET);
477 }
478
479
480 static void
481 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
482 {
483   struct data_editor *de = data;
484
485   data_editor_select_sheet (de, PAGE_VAR_SHEET);
486 }
487
488
489 static void
490 fonts_activate (GtkMenuItem *menuitem, gpointer data)
491 {
492   struct data_editor *de = data;
493   GtkWidget *dialog =
494     gtk_font_selection_dialog_new (_("Font Selection"));
495
496   gtk_window_set_transient_for (GTK_WINDOW (dialog),
497                                 GTK_WINDOW (get_widget_assert (de->xml,
498                                                                "data_editor")));
499   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
500     {
501       GtkSheet *data_sheet =
502         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
503
504       GtkSheet *var_sheet =
505         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
506
507       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
508       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
509
510       const gchar *font = gtk_font_selection_dialog_get_font_name
511         (GTK_FONT_SELECTION_DIALOG (dialog));
512
513       PangoFontDescription* font_desc =
514         pango_font_description_from_string (font);
515
516       psppire_var_store_set_font (vs, font_desc);
517       psppire_data_store_set_font (ds, font_desc);
518     }
519
520   gtk_widget_hide (dialog);
521 }
522
523
524 /* The next two callbacks are mutually co-operative */
525
526 /* Callback for the value labels menu item */
527 static void
528 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
529 {
530   struct data_editor *de = data;
531
532   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
533
534   GtkToggleToolButton *tb =
535     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
536                                                "togglebutton-value-labels"));
537
538   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
539
540   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
541
542   gtk_toggle_tool_button_set_active (tb, show_value_labels);
543
544   psppire_data_store_show_labels (ds, show_value_labels);
545 }
546
547
548 /* Callback for the value labels tooglebutton */
549 static void
550 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
551                       gpointer data)
552 {
553   struct data_editor *de = data;
554
555   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
556
557   GtkCheckMenuItem *item =
558     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
559
560   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
561
562   gboolean show_value_labels =
563     gtk_toggle_tool_button_get_active (toggle_tool_button);
564
565   gtk_check_menu_item_set_active (item, show_value_labels);
566
567   psppire_data_store_show_labels (ds, show_value_labels);
568 }
569
570
571 static void
572 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
573 {
574   /* FIXME: Need to be more intelligent here.
575      Give the user the opportunity to save any unsaved data.
576   */
577   gtk_main_quit ();
578 }
579
580
581
582 /* Callback for when the Clear item in the edit menu is activated */
583 static void
584 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
585 {
586   struct data_editor *de = data;
587
588   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
589                                                            "notebook"));
590
591   switch ( gtk_notebook_get_current_page (notebook) )
592     {
593     case PAGE_VAR_SHEET:
594       {
595         GtkSheet *var_sheet =
596           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
597
598         PsppireVarStore *vs = PSPPIRE_VAR_STORE
599           (gtk_sheet_get_model (var_sheet) );
600
601         /* This shouldn't be able to happen, because the menuitem
602            should be disabled */
603         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
604
605         psppire_dict_delete_variables (vs->dict,
606                                        var_sheet->range.row0,
607                                        1 +
608                                        var_sheet->range.rowi -
609                                        var_sheet->range.row0 );
610       }
611       break;
612       case PAGE_DATA_SHEET:
613         break;
614       default:
615         g_assert_not_reached ();
616     }
617 }
618
619
620 /* Insert a new variable before the current row in the variable sheet,
621    or before the current column in the data sheet, whichever is selected */
622 static void
623 insert_variable (GtkCheckMenuItem *m, gpointer data)
624 {
625   struct data_editor *de = data;
626   gint posn;
627
628   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
629
630   GtkSheet *var_sheet =
631     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
632
633   PsppireVarStore *vs = PSPPIRE_VAR_STORE
634     (gtk_sheet_get_model (var_sheet) );
635
636   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
637     {
638     case PAGE_VAR_SHEET:
639       posn = var_sheet->active_cell.row;
640       break;
641     case PAGE_DATA_SHEET:
642       {
643         GtkSheet *data_sheet =
644           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
645
646         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
647           posn = data_sheet->range.col0;
648         else
649           posn = data_sheet->active_cell.col;
650       }
651       break;
652     default:
653       g_assert_not_reached ();
654     }
655
656   psppire_dict_insert_variable (vs->dict, posn, NULL);
657 }
658
659 /* Callback for when the dictionary changes its split variables */
660 static void
661 on_split_change (PsppireDict *dict, gpointer data)
662 {
663   struct data_editor *de = data;
664
665   size_t n_split_vars = dict_get_split_cnt (dict->dict);
666
667   GtkWidget *split_status_area =
668     get_widget_assert (de->xml, "split-file-status-area");
669
670   if ( n_split_vars == 0 )
671     {
672       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
673     }
674   else
675     {
676       gint i;
677       GString *text;
678       struct variable *const * split_vars = dict_get_split_vars (dict->dict);
679
680       text = g_string_new (_("Split by "));
681
682       for (i = 0 ; i < n_split_vars - 1; ++i )
683         {
684           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
685         }
686       g_string_append (text, var_get_name (split_vars[i]));
687
688       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
689
690       g_string_free (text, TRUE);
691     }
692 }
693
694
695 /* Callback for when the dictionary changes its filter variable */
696 static void
697 on_filter_change (GObject *o, gint filter_index, gpointer data)
698 {
699   struct data_editor *de = data;
700   GtkWidget *filter_status_area =
701     get_widget_assert (de->xml, "filter-use-status-area");
702
703   if ( filter_index == -1 )
704     {
705       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
706     }
707   else
708     {
709       GtkSheet *var_sheet =
710         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
711
712       PsppireVarStore *vs = PSPPIRE_VAR_STORE
713         (gtk_sheet_get_model (var_sheet) );
714
715       struct variable *var = psppire_dict_get_variable (vs->dict,
716                                                         filter_index);
717
718       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
719
720       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
721
722       g_free (text);
723     }
724 }
725
726 /* Callback for when the dictionary changes its weights */
727 static void
728 on_weight_change (GObject *o, gint weight_index, gpointer data)
729 {
730   struct data_editor *de = data;
731   GtkWidget *weight_status_area =
732     get_widget_assert (de->xml, "weight-status-area");
733
734   if ( weight_index == -1 )
735     {
736       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
737     }
738   else
739     {
740       GtkSheet *var_sheet =
741         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
742
743       PsppireVarStore *vs = PSPPIRE_VAR_STORE
744         (gtk_sheet_get_model (var_sheet) );
745
746       struct variable *var = psppire_dict_get_variable (vs->dict,
747                                                         weight_index);
748
749       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
750
751       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
752
753       g_free (text);
754     }
755 }
756
757
758
759 \f
760 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
761 static void new_file (GtkAction *, struct editor_window *de);
762 static void open_data_dialog (GtkAction *, struct data_editor *de);
763 static void data_save (GtkAction *action, struct data_editor *e);
764
765
766 /* Create the GtkActions and connect to their signals */
767 static void
768 register_data_editor_actions (struct data_editor *de)
769 {
770   de->action_data_open =
771     gtk_action_new ("data-open-dialog",
772                     _("Open"),
773                     _("Open a data file"),
774                     "gtk-open");
775
776   g_signal_connect (de->action_data_open, "activate",
777                     G_CALLBACK (open_data_dialog), de);
778
779
780   de->action_data_save = gtk_action_new ("data-save",
781                                             _("Save"),
782                                             _("Save data to file"),
783                                             "gtk-save");
784
785   g_signal_connect (de->action_data_save, "activate",
786                     G_CALLBACK (data_save), de);
787
788
789
790   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
791                                             _("Save As"),
792                                             _("Save data to file"),
793                                             "gtk-save");
794
795   g_signal_connect (de->action_data_save_as, "activate",
796                     G_CALLBACK (data_save_as_dialog), de);
797
798   de->action_data_new =
799     gtk_action_new ("data-new",
800                     _("New"),
801                     _("New data file"),
802                     NULL);
803
804   g_signal_connect (de->action_data_new, "activate",
805                     G_CALLBACK (new_file), de);
806 }
807
808 /* Returns true if NAME has a suffix which might denote a PSPP file */
809 static gboolean
810 name_has_suffix (const gchar *name)
811 {
812   if ( g_str_has_suffix (name, ".sav"))
813     return TRUE;
814   if ( g_str_has_suffix (name, ".SAV"))
815     return TRUE;
816   if ( g_str_has_suffix (name, ".por"))
817     return TRUE;
818   if ( g_str_has_suffix (name, ".POR"))
819     return TRUE;
820
821   return FALSE;
822 }
823
824 /* Append SUFFIX to the filename of DE */
825 static void
826 append_filename_suffix (struct data_editor *de, const gchar *suffix)
827 {
828   if ( ! name_has_suffix (de->file_name))
829     {
830       gchar *s = de->file_name;
831       de->file_name = g_strconcat (de->file_name, suffix, NULL);
832       g_free (s);
833     }
834 }
835
836 /* Save DE to file */
837 static void
838 save_file (struct data_editor *de)
839 {
840   struct getl_interface *sss;
841
842   g_assert (de->file_name);
843
844   if ( de->save_as_portable )
845     {
846       append_filename_suffix (de, ".por");
847       sss = create_syntax_string_source ("EXPORT OUTFILE='%s'.",
848                                          de->file_name);
849     }
850   else
851     {
852       append_filename_suffix (de, ".sav");
853       sss = create_syntax_string_source ("SAVE OUTFILE='%s'.",
854                                          de->file_name);
855     }
856
857   execute_syntax (sss);
858 }
859
860
861 /* Callback for data_save action.
862    If there's an existing file name, then just save,
863    otherwise prompt for a file name, then save */
864 static void
865 data_save (GtkAction *action, struct data_editor *de)
866 {
867   if ( de->file_name)
868     save_file (de);
869   else
870     data_save_as_dialog (action, de);
871 }
872
873
874 /* Callback for data_save_as action. Prompt for a filename and save */
875 static void
876 data_save_as_dialog (GtkAction *action, struct data_editor *de)
877 {
878   struct editor_window *e = (struct editor_window *) de;
879
880   GtkWidget *button_sys;
881   GtkWidget *dialog =
882     gtk_file_chooser_dialog_new (_("Save"),
883                                  GTK_WINDOW (e->window),
884                                  GTK_FILE_CHOOSER_ACTION_SAVE,
885                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
886                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
887                                  NULL);
888
889   GtkFileFilter *filter = gtk_file_filter_new ();
890   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
891   gtk_file_filter_add_pattern (filter, "*.sav");
892   gtk_file_filter_add_pattern (filter, "*.SAV");
893   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
894
895   filter = gtk_file_filter_new ();
896   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
897   gtk_file_filter_add_pattern (filter, "*.por");
898   gtk_file_filter_add_pattern (filter, "*.POR");
899   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
900
901   filter = gtk_file_filter_new ();
902   gtk_file_filter_set_name (filter, _("All Files"));
903   gtk_file_filter_add_pattern (filter, "*");
904   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
905
906   {
907     GtkWidget *button_por;
908     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
909     button_sys =
910       gtk_radio_button_new_with_label (NULL, _("System File"));
911
912     button_por =
913       gtk_radio_button_new_with_label
914       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
915        _("Portable File"));
916
917     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
918     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
919
920     gtk_widget_show_all (vbox);
921
922     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
923   }
924
925   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
926     {
927     case GTK_RESPONSE_ACCEPT:
928       {
929         g_free (de->file_name);
930
931         de->file_name =
932           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
933
934         de->save_as_portable =
935           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
936
937         save_file (de);
938
939         window_set_name_from_filename (e, de->file_name);
940       }
941       break;
942     default:
943       break;
944     }
945
946   gtk_widget_destroy (dialog);
947 }
948
949
950 /* Callback for data_new action.
951    Performs the NEW FILE command */
952 static void
953 new_file (GtkAction *action, struct editor_window *e)
954 {
955   struct data_editor *de = (struct data_editor *) e;
956
957   struct getl_interface *sss =
958     create_syntax_string_source ("NEW FILE.");
959
960   execute_syntax (sss);
961
962   g_free (de->file_name);
963   de->file_name = NULL;
964
965   default_window_name (e);
966 }
967
968
969 /* Callback for the data_open action.
970    Prompts for a filename and opens it */
971 static void
972 open_data_dialog (GtkAction *action, struct data_editor *de)
973 {
974   struct editor_window *e = (struct editor_window *) de;
975
976   GtkWidget *dialog =
977     gtk_file_chooser_dialog_new (_("Open"),
978                                  GTK_WINDOW (e->window),
979                                  GTK_FILE_CHOOSER_ACTION_OPEN,
980                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
981                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
982                                  NULL);
983
984   GtkFileFilter *filter = gtk_file_filter_new ();
985   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
986   gtk_file_filter_add_pattern (filter, "*.sav");
987   gtk_file_filter_add_pattern (filter, "*.SAV");
988   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
989
990   filter = gtk_file_filter_new ();
991   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
992   gtk_file_filter_add_pattern (filter, "*.por");
993   gtk_file_filter_add_pattern (filter, "*.POR");
994   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
995
996   filter = gtk_file_filter_new ();
997   gtk_file_filter_set_name (filter, _("All Files"));
998   gtk_file_filter_add_pattern (filter, "*");
999   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1000
1001   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1002     {
1003     case GTK_RESPONSE_ACCEPT:
1004       {
1005         struct getl_interface *sss;
1006         g_free (de->file_name);
1007         de->file_name =
1008           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1009
1010         sss = create_syntax_string_source ("GET FILE='%s'.", de->file_name);
1011
1012         execute_syntax (sss);
1013
1014         window_set_name_from_filename (e, de->file_name);
1015       }
1016       break;
1017     default:
1018       break;
1019     }
1020
1021   gtk_widget_destroy (dialog);
1022 }
1023
1024
1025
1026