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