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