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