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