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