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