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