(Re)enabled cell reference entry and datum entry widgets.
[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
437
438   g_signal_connect (get_widget_assert (de->xml,"data_sheet"),
439                     "double-click-column",
440                     G_CALLBACK (click2column),
441                     de);
442
443
444   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
445                     "double-click-row",
446                     GTK_SIGNAL_FUNC (click2row),
447                     de);
448
449
450   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
451                     "select-row",
452                     GTK_SIGNAL_FUNC (enable_edit_clear),
453                     de);
454
455   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
456                     "activate",
457                     GTK_SIGNAL_FUNC (disable_edit_clear),
458                     de);
459
460
461   g_signal_connect (get_widget_assert (de->xml, "notebook"),
462                     "switch-page",
463                     G_CALLBACK (data_var_select), de);
464
465
466
467   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
468                     "activate",
469                     G_CALLBACK (status_bar_activate), de);
470
471
472   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
473                     "activate",
474                     G_CALLBACK (grid_lines_activate), de);
475
476
477
478   g_signal_connect (get_widget_assert (de->xml, "view_data"),
479                     "activate",
480                     G_CALLBACK (data_sheet_activate), de);
481
482   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
483                     "activate",
484                     G_CALLBACK (variable_sheet_activate), de);
485
486
487
488   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
489                     "activate",
490                     G_CALLBACK (fonts_activate), de);
491
492
493
494   g_signal_connect (get_widget_assert (de->xml, "view_valuelabels"),
495                     "activate",
496                     G_CALLBACK (value_labels_activate), de);
497
498
499   g_signal_connect (get_widget_assert (de->xml, "togglebutton-value-labels"),
500                     "toggled",
501                     G_CALLBACK (value_labels_toggled), de);
502
503   gtk_action_connect_proxy (de->action_data_open,
504                             get_widget_assert (de->xml, "button-open")
505                             );
506
507   gtk_action_connect_proxy (de->action_data_save,
508                             get_widget_assert (de->xml, "button-save")
509                             );
510
511   gtk_action_connect_proxy (de->invoke_variable_info_dialog,
512                             get_widget_assert (de->xml, "button-goto-variable")
513                             );
514
515   gtk_action_connect_proxy (de->invoke_weight_cases_dialog,
516                             get_widget_assert (de->xml, "button-weight-cases")
517                             );
518
519   gtk_action_connect_proxy (de->invoke_split_file_dialog,
520                             get_widget_assert (de->xml, "button-split-file")
521                             );
522
523   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
524                     "activate",
525                     G_CALLBACK (file_quit), de);
526
527
528   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
529                     "activate",
530                     G_CALLBACK (minimise_all_windows), NULL);
531
532
533   select_sheet (de, PAGE_DATA_SHEET);
534
535   return de;
536 }
537
538
539 /* Callback which occurs when the var sheet's row title
540    button is double clicked */
541 static gboolean
542 click2row (GtkWidget *w, gint row, gpointer data)
543 {
544   struct data_editor *de = data;
545
546   gint current_row, current_column;
547
548   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
549
550   data_editor_select_sheet (de, PAGE_DATA_SHEET);
551
552   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
553                              &current_row, &current_column);
554
555   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
556
557   return FALSE;
558 }
559
560
561 /* Callback which occurs when the data sheet's column title
562    is double clicked */
563 static gboolean
564 click2column (GtkWidget *w, gint col, gpointer data)
565 {
566   struct data_editor *de = data;
567
568   gint current_row, current_column;
569
570   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
571
572   data_editor_select_sheet (de, PAGE_VAR_SHEET);
573
574   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
575                              &current_row, &current_column);
576
577   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
578
579   return FALSE;
580 }
581
582
583 void
584 new_data_window (GtkMenuItem *menuitem, gpointer parent)
585 {
586   window_create (WINDOW_DATA, NULL);
587 }
588
589
590 static void
591 select_sheet (struct data_editor *de, guint page_num)
592 {
593   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
594   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
595
596   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
597   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
598
599   switch (page_num)
600     {
601     case PAGE_VAR_SHEET:
602       gtk_widget_hide (view_variables);
603       gtk_widget_show (view_data);
604       gtk_widget_set_sensitive (insert_variable, TRUE);
605       gtk_widget_set_sensitive (insert_cases, FALSE);
606       break;
607     case PAGE_DATA_SHEET:
608       gtk_widget_show (view_variables);
609       gtk_widget_hide (view_data);
610 #if 0
611       gtk_widget_set_sensitive (insert_cases, TRUE);
612 #endif
613       break;
614     default:
615       g_assert_not_reached ();
616       break;
617     }
618 }
619
620
621 static void
622 data_var_select (GtkNotebook *notebook,
623                 GtkNotebookPage *page,
624                 guint page_num,
625                 gpointer user_data)
626 {
627   struct data_editor *de = user_data;
628
629   select_sheet (de, page_num);
630 }
631
632
633
634
635 void
636 data_editor_select_sheet (struct data_editor *de, gint page)
637 {
638   gtk_notebook_set_current_page
639    (
640     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
641     );
642 }
643
644
645 static void
646 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
647 {
648   struct data_editor *de = data;
649   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
650
651   if ( gtk_check_menu_item_get_active (menuitem) )
652     gtk_widget_show (statusbar);
653   else
654     gtk_widget_hide (statusbar);
655 }
656
657
658 static void
659 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
660 {
661   struct data_editor *de = data;
662   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
663
664   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
665                                                      "variable_sheet")),
666                        grid_visible);
667
668   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
669                        grid_visible);
670 }
671
672
673
674 static void
675 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
676 {
677   struct data_editor *de = data;
678
679   data_editor_select_sheet (de, PAGE_DATA_SHEET);
680 }
681
682
683 static void
684 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
685 {
686   struct data_editor *de = data;
687
688   data_editor_select_sheet (de, PAGE_VAR_SHEET);
689 }
690
691
692 static void
693 fonts_activate (GtkMenuItem *menuitem, gpointer data)
694 {
695   struct data_editor *de = data;
696   GtkWidget *dialog =
697     gtk_font_selection_dialog_new (_("Font Selection"));
698
699   gtk_window_set_transient_for (GTK_WINDOW (dialog),
700                                 GTK_WINDOW (get_widget_assert (de->xml,
701                                                                "data_editor")));
702   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
703     {
704       GtkSheet *data_sheet =
705         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
706
707       GtkSheet *var_sheet =
708         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
709
710       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
711       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
712
713       const gchar *font = gtk_font_selection_dialog_get_font_name
714         (GTK_FONT_SELECTION_DIALOG (dialog));
715
716       PangoFontDescription* font_desc =
717         pango_font_description_from_string (font);
718
719       psppire_var_store_set_font (vs, font_desc);
720       psppire_data_store_set_font (ds, font_desc);
721     }
722
723   gtk_widget_hide (dialog);
724 }
725
726
727 /* The next two callbacks are mutually co-operative */
728
729 /* Callback for the value labels menu item */
730 static void
731 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
732 {
733   struct data_editor *de = data;
734
735   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
736
737   GtkToggleToolButton *tb =
738     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
739                                                "togglebutton-value-labels"));
740
741   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
742
743   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
744
745   gtk_toggle_tool_button_set_active (tb, show_value_labels);
746
747   psppire_data_store_show_labels (ds, show_value_labels);
748 }
749
750
751 /* Callback for the value labels tooglebutton */
752 static void
753 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
754                       gpointer data)
755 {
756   struct data_editor *de = data;
757
758   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
759
760   GtkCheckMenuItem *item =
761     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
762
763   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
764
765   gboolean show_value_labels =
766     gtk_toggle_tool_button_get_active (toggle_tool_button);
767
768   gtk_check_menu_item_set_active (item, show_value_labels);
769
770   psppire_data_store_show_labels (ds, show_value_labels);
771 }
772
773
774 static void
775 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
776 {
777   /* FIXME: Need to be more intelligent here.
778      Give the user the opportunity to save any unsaved data.
779   */
780   gtk_main_quit ();
781 }
782
783
784
785 /* Callback for when the Clear item in the edit menu is activated */
786 static void
787 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
788 {
789   struct data_editor *de = data;
790
791   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
792                                                            "notebook"));
793
794   switch ( gtk_notebook_get_current_page (notebook) )
795     {
796     case PAGE_VAR_SHEET:
797       {
798         GtkSheet *var_sheet =
799           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
800
801         PsppireVarStore *vs = PSPPIRE_VAR_STORE
802           (gtk_sheet_get_model (var_sheet) );
803
804         /* This shouldn't be able to happen, because the menuitem
805            should be disabled */
806         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
807
808         psppire_dict_delete_variables (vs->dict,
809                                        var_sheet->range.row0,
810                                        1 +
811                                        var_sheet->range.rowi -
812                                        var_sheet->range.row0 );
813       }
814       break;
815       case PAGE_DATA_SHEET:
816         break;
817       default:
818         g_assert_not_reached ();
819     }
820 }
821
822
823 /* Insert a new variable before the current row in the variable sheet,
824    or before the current column in the data sheet, whichever is selected */
825 static void
826 insert_variable (GtkCheckMenuItem *m, gpointer data)
827 {
828   struct data_editor *de = data;
829   gint posn;
830
831   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
832
833   GtkSheet *var_sheet =
834     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
835
836   PsppireVarStore *vs = PSPPIRE_VAR_STORE
837     (gtk_sheet_get_model (var_sheet) );
838
839   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
840     {
841     case PAGE_VAR_SHEET:
842       posn = var_sheet->active_cell.row;
843       break;
844     case PAGE_DATA_SHEET:
845       {
846         GtkSheet *data_sheet =
847           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
848
849         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
850           posn = data_sheet->range.col0;
851         else
852           posn = data_sheet->active_cell.col;
853       }
854       break;
855     default:
856       g_assert_not_reached ();
857     }
858
859   psppire_dict_insert_variable (vs->dict, posn, NULL);
860 }
861
862 /* Callback for when the dictionary changes its split variables */
863 static void
864 on_split_change (PsppireDict *dict, gpointer data)
865 {
866   struct data_editor *de = data;
867
868   size_t n_split_vars = dict_get_split_cnt (dict->dict);
869
870   GtkWidget *split_status_area =
871     get_widget_assert (de->xml, "split-file-status-area");
872
873   if ( n_split_vars == 0 )
874     {
875       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
876     }
877   else
878     {
879       gint i;
880       GString *text;
881       const struct variable *const * split_vars = dict_get_split_vars (dict->dict);
882
883       text = g_string_new (_("Split by "));
884
885       for (i = 0 ; i < n_split_vars - 1; ++i )
886         {
887           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
888         }
889       g_string_append (text, var_get_name (split_vars[i]));
890
891       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
892
893       g_string_free (text, TRUE);
894     }
895 }
896
897
898 /* Callback for when the dictionary changes its filter variable */
899 static void
900 on_filter_change (GObject *o, gint filter_index, gpointer data)
901 {
902   struct data_editor *de = data;
903   GtkWidget *filter_status_area =
904     get_widget_assert (de->xml, "filter-use-status-area");
905
906   if ( filter_index == -1 )
907     {
908       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
909     }
910   else
911     {
912       GtkSheet *var_sheet =
913         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
914
915       PsppireVarStore *vs = PSPPIRE_VAR_STORE
916         (gtk_sheet_get_model (var_sheet) );
917
918       struct variable *var = psppire_dict_get_variable (vs->dict,
919                                                         filter_index);
920
921       gchar *text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
922
923       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
924
925       g_free (text);
926     }
927 }
928
929 /* Callback for when the dictionary changes its weights */
930 static void
931 on_weight_change (GObject *o, gint weight_index, gpointer data)
932 {
933   struct data_editor *de = data;
934   GtkWidget *weight_status_area =
935     get_widget_assert (de->xml, "weight-status-area");
936
937   if ( weight_index == -1 )
938     {
939       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
940     }
941   else
942     {
943       GtkSheet *var_sheet =
944         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
945
946       PsppireVarStore *vs = PSPPIRE_VAR_STORE
947         (gtk_sheet_get_model (var_sheet) );
948
949       struct variable *var = psppire_dict_get_variable (vs->dict,
950                                                         weight_index);
951
952       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
953
954       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
955
956       g_free (text);
957     }
958 }
959
960
961
962 \f
963 static void data_save_as_dialog (GtkAction *, struct data_editor *de);
964 static void new_file (GtkAction *, struct editor_window *de);
965 static void open_data_dialog (GtkAction *, struct data_editor *de);
966 static void data_save (GtkAction *action, struct data_editor *e);
967
968
969 /* Create the GtkActions and connect to their signals */
970 static void
971 register_data_editor_actions (struct data_editor *de)
972 {
973   de->action_data_open =
974     gtk_action_new ("data-open-dialog",
975                     _("Open"),
976                     _("Open a data file"),
977                     "gtk-open");
978
979   g_signal_connect (de->action_data_open, "activate",
980                     G_CALLBACK (open_data_dialog), de);
981
982
983   de->action_data_save = gtk_action_new ("data-save",
984                                             _("Save"),
985                                             _("Save data to file"),
986                                             "gtk-save");
987
988   g_signal_connect (de->action_data_save, "activate",
989                     G_CALLBACK (data_save), de);
990
991
992
993   de->action_data_save_as = gtk_action_new ("data-save-as-dialog",
994                                             _("Save As"),
995                                             _("Save data to file"),
996                                             "gtk-save");
997
998   g_signal_connect (de->action_data_save_as, "activate",
999                     G_CALLBACK (data_save_as_dialog), de);
1000
1001   de->action_data_new =
1002     gtk_action_new ("data-new",
1003                     _("New"),
1004                     _("New data file"),
1005                     NULL);
1006
1007   g_signal_connect (de->action_data_new, "activate",
1008                     G_CALLBACK (new_file), de);
1009 }
1010
1011 /* Returns true if NAME has a suffix which might denote a PSPP file */
1012 static gboolean
1013 name_has_suffix (const gchar *name)
1014 {
1015   if ( g_str_has_suffix (name, ".sav"))
1016     return TRUE;
1017   if ( g_str_has_suffix (name, ".SAV"))
1018     return TRUE;
1019   if ( g_str_has_suffix (name, ".por"))
1020     return TRUE;
1021   if ( g_str_has_suffix (name, ".POR"))
1022     return TRUE;
1023
1024   return FALSE;
1025 }
1026
1027 /* Append SUFFIX to the filename of DE */
1028 static void
1029 append_filename_suffix (struct data_editor *de, const gchar *suffix)
1030 {
1031   if ( ! name_has_suffix (de->file_name))
1032     {
1033       gchar *s = de->file_name;
1034       de->file_name = g_strconcat (de->file_name, suffix, NULL);
1035       g_free (s);
1036     }
1037 }
1038
1039 /* Save DE to file */
1040 static void
1041 save_file (struct data_editor *de)
1042 {
1043   struct getl_interface *sss;
1044   struct string file_name ;
1045
1046   g_assert (de->file_name);
1047
1048   ds_init_cstr (&file_name, de->file_name);
1049   gen_quoted_string (&file_name);
1050
1051   if ( de->save_as_portable )
1052     {
1053       append_filename_suffix (de, ".por");
1054       sss = create_syntax_string_source ("EXPORT OUTFILE=%s.",
1055                                          ds_cstr (&file_name));
1056     }
1057   else
1058     {
1059       append_filename_suffix (de, ".sav");
1060       sss = create_syntax_string_source ("SAVE OUTFILE=%s.",
1061                                          ds_cstr (&file_name));
1062     }
1063
1064   ds_destroy (&file_name);
1065
1066   execute_syntax (sss);
1067 }
1068
1069
1070 /* Callback for data_save action.
1071    If there's an existing file name, then just save,
1072    otherwise prompt for a file name, then save */
1073 static void
1074 data_save (GtkAction *action, struct data_editor *de)
1075 {
1076   if ( de->file_name)
1077     save_file (de);
1078   else
1079     data_save_as_dialog (action, de);
1080 }
1081
1082
1083 /* Callback for data_save_as action. Prompt for a filename and save */
1084 static void
1085 data_save_as_dialog (GtkAction *action, struct data_editor *de)
1086 {
1087   struct editor_window *e = (struct editor_window *) de;
1088
1089   GtkWidget *button_sys;
1090   GtkWidget *dialog =
1091     gtk_file_chooser_dialog_new (_("Save"),
1092                                  GTK_WINDOW (e->window),
1093                                  GTK_FILE_CHOOSER_ACTION_SAVE,
1094                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1095                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1096                                  NULL);
1097
1098   GtkFileFilter *filter = gtk_file_filter_new ();
1099   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1100   gtk_file_filter_add_pattern (filter, "*.sav");
1101   gtk_file_filter_add_pattern (filter, "*.SAV");
1102   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1103
1104   filter = gtk_file_filter_new ();
1105   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1106   gtk_file_filter_add_pattern (filter, "*.por");
1107   gtk_file_filter_add_pattern (filter, "*.POR");
1108   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1109
1110   filter = gtk_file_filter_new ();
1111   gtk_file_filter_set_name (filter, _("All Files"));
1112   gtk_file_filter_add_pattern (filter, "*");
1113   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1114
1115   {
1116     GtkWidget *button_por;
1117     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
1118     button_sys =
1119       gtk_radio_button_new_with_label (NULL, _("System File"));
1120
1121     button_por =
1122       gtk_radio_button_new_with_label
1123       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
1124        _("Portable File"));
1125
1126     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
1127     gtk_box_pack_start_defaults (GTK_BOX (vbox), button_por);
1128
1129     gtk_widget_show_all (vbox);
1130
1131     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
1132   }
1133
1134   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1135     {
1136     case GTK_RESPONSE_ACCEPT:
1137       {
1138         g_free (de->file_name);
1139
1140         de->file_name =
1141           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1142
1143         de->save_as_portable =
1144           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
1145
1146         save_file (de);
1147
1148         window_set_name_from_filename (e, de->file_name);
1149       }
1150       break;
1151     default:
1152       break;
1153     }
1154
1155   gtk_widget_destroy (dialog);
1156 }
1157
1158
1159 /* Callback for data_new action.
1160    Performs the NEW FILE command */
1161 static void
1162 new_file (GtkAction *action, struct editor_window *e)
1163 {
1164   struct data_editor *de = (struct data_editor *) e;
1165
1166   struct getl_interface *sss =
1167     create_syntax_string_source ("NEW FILE.");
1168
1169   execute_syntax (sss);
1170
1171   g_free (de->file_name);
1172   de->file_name = NULL;
1173
1174   default_window_name (e);
1175 }
1176
1177
1178 static void
1179 open_data_file (const gchar *file_name, struct data_editor *de)
1180 {
1181   struct getl_interface *sss;
1182   struct string filename;
1183
1184   ds_init_cstr (&filename, file_name);
1185
1186   gen_quoted_string (&filename);
1187
1188   sss = create_syntax_string_source ("GET FILE=%s.",
1189                                      ds_cstr (&filename));
1190
1191   execute_syntax (sss);
1192   ds_destroy (&filename);
1193
1194   window_set_name_from_filename ((struct editor_window *) de, file_name);
1195 }
1196
1197
1198 /* Callback for the data_open action.
1199    Prompts for a filename and opens it */
1200 static void
1201 open_data_dialog (GtkAction *action, struct data_editor *de)
1202 {
1203   struct editor_window *e = (struct editor_window *) de;
1204
1205   GtkWidget *dialog =
1206     gtk_file_chooser_dialog_new (_("Open"),
1207                                  GTK_WINDOW (e->window),
1208                                  GTK_FILE_CHOOSER_ACTION_OPEN,
1209                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1210                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1211                                  NULL);
1212
1213   GtkFileFilter *filter = gtk_file_filter_new ();
1214   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
1215   gtk_file_filter_add_pattern (filter, "*.sav");
1216   gtk_file_filter_add_pattern (filter, "*.SAV");
1217   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1218
1219   filter = gtk_file_filter_new ();
1220   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
1221   gtk_file_filter_add_pattern (filter, "*.por");
1222   gtk_file_filter_add_pattern (filter, "*.POR");
1223   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1224
1225   filter = gtk_file_filter_new ();
1226   gtk_file_filter_set_name (filter, _("All Files"));
1227   gtk_file_filter_add_pattern (filter, "*");
1228   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1229
1230   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1231     {
1232     case GTK_RESPONSE_ACCEPT:
1233       {
1234         g_free (de->file_name);
1235         de->file_name =
1236           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1237
1238         open_data_file (de->file_name, de);
1239
1240 #if RECENT_LISTS_AVAILABLE
1241         {
1242           GtkRecentManager *manager = gtk_recent_manager_get_default();
1243           gchar *uri = g_filename_to_uri (de->file_name, NULL, NULL);
1244
1245           if ( ! gtk_recent_manager_add_item (manager, uri))
1246             g_warning ("Could not add item %s to recent list\n",uri);
1247
1248           g_free (uri);
1249         }
1250 #endif
1251
1252       }
1253       break;
1254     default:
1255       break;
1256     }
1257
1258   gtk_widget_destroy (dialog);
1259 }
1260
1261
1262
1263 /* Update the data_ref_entry with the reference of the active cell */
1264 static gint
1265 update_data_ref_entry (const GtkSheet *sheet, gint row, gint col, gpointer data)
1266 {
1267   GladeXML *data_editor_xml = data;
1268
1269   PsppireDataStore *data_store = PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
1270
1271   g_return_val_if_fail (data_editor_xml, FALSE);
1272
1273   if (data_store)
1274     {
1275       const struct variable *var =
1276         psppire_dict_get_variable (data_store->dict, col);
1277
1278       {
1279         /* The entry where the reference to the current cell is displayed */
1280         GtkEntry *cell_ref_entry =
1281           GTK_ENTRY (get_widget_assert (data_editor_xml,
1282                                         "cell_ref_entry"));
1283
1284         gchar *text = g_strdup_printf ("%d: %s", row,
1285                                        var ? var_get_name (var) : "");
1286
1287         gchar *s = pspp_locale_to_utf8 (text, -1, 0);
1288
1289         g_free (text);
1290
1291         gtk_entry_set_text (cell_ref_entry, s);
1292
1293         g_free (s);
1294       }
1295
1296       {
1297         GtkEntry *datum_entry =
1298           GTK_ENTRY (get_widget_assert (data_editor_xml,
1299                                         "datum_entry"));
1300
1301         gchar *text =
1302           psppire_data_store_get_string (data_store, row,
1303                                          var_get_dict_index(var));
1304         g_strchug (text);
1305
1306         gtk_entry_set_text (datum_entry, text);
1307
1308         free (text);
1309       }
1310     }
1311
1312   return FALSE;
1313 }