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