1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/psppire-data-editor.h"
22 #include <gtk-contrib/gtkxpaned.h>
24 #include "data/datasheet.h"
25 #include "data/value-labels.h"
26 #include "libpspp/range-set.h"
27 #include "libpspp/str.h"
28 #include "ui/gui/helper.h"
29 #include "ui/gui/pspp-sheet-selection.h"
30 #include "ui/gui/psppire-data-sheet.h"
31 #include "ui/gui/psppire-data-store.h"
32 #include "ui/gui/psppire-value-entry.h"
33 #include "ui/gui/psppire-var-sheet.h"
34 #include "ui/gui/psppire.h"
35 #include "ui/gui/psppire-conf.h"
38 #define _(msgid) gettext (msgid)
40 #define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR) \
43 && ((DATA_SHEET) = PSPPIRE_DATA_SHEET ( \
44 (DATA_EDITOR)->data_sheets[IDX])) != NULL; \
47 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
48 static void psppire_data_editor_init (PsppireDataEditor *de);
50 static void disconnect_data_sheets (PsppireDataEditor *);
51 static void refresh_entry (PsppireDataEditor *);
52 static void psppire_data_editor_update_ui_manager (PsppireDataEditor *);
55 psppire_data_editor_get_type (void)
57 static GType de_type = 0;
61 static const GTypeInfo de_info =
63 sizeof (PsppireDataEditorClass),
65 NULL, /* base_finalize */
66 (GClassInitFunc) psppire_data_editor_class_init,
67 NULL, /* class_finalize */
68 NULL, /* class_data */
69 sizeof (PsppireDataEditor),
71 (GInstanceInitFunc) psppire_data_editor_init,
74 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
81 static GObjectClass * parent_class = NULL;
84 psppire_data_editor_dispose (GObject *obj)
86 PsppireDataEditor *de = (PsppireDataEditor *) obj;
88 disconnect_data_sheets (de);
92 g_object_unref (de->data_store);
93 de->data_store = NULL;
98 g_object_unref (de->dict);
102 if (de->font != NULL)
104 pango_font_description_free (de->font);
110 g_object_unref (de->ui_manager);
111 de->ui_manager = NULL;
114 /* Chain up to the parent class */
115 G_OBJECT_CLASS (parent_class)->dispose (obj);
129 psppire_data_editor_refresh_model (PsppireDataEditor *de)
131 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet);
132 PsppireDataSheet *data_sheet;
135 FOR_EACH_DATA_SHEET (data_sheet, i, de)
136 psppire_data_sheet_set_data_store (data_sheet, de->data_store);
137 psppire_var_sheet_set_dictionary (var_sheet, de->dict);
141 psppire_data_editor_set_property (GObject *object,
146 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
147 PsppireDataSheet *data_sheet;
152 case PROP_SPLIT_WINDOW:
153 psppire_data_editor_split_window (de, g_value_get_boolean (value));
155 case PROP_DATA_STORE:
158 g_signal_handlers_disconnect_by_func (de->data_store,
159 G_CALLBACK (refresh_entry),
161 g_object_unref (de->data_store);
164 de->data_store = g_value_get_pointer (value);
165 g_object_ref (de->data_store);
166 psppire_data_editor_refresh_model (de);
168 g_signal_connect_swapped (de->data_store, "case-changed",
169 G_CALLBACK (refresh_entry), de);
172 case PROP_DICTIONARY:
174 g_object_unref (de->dict);
175 de->dict = g_value_get_pointer (value);
176 g_object_ref (de->dict);
178 psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet),
181 case PROP_VALUE_LABELS:
182 FOR_EACH_DATA_SHEET (data_sheet, i, de)
183 psppire_data_sheet_set_value_labels (data_sheet,
184 g_value_get_boolean (value));
186 case PROP_UI_MANAGER:
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 psppire_data_editor_get_property (GObject *object,
199 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
203 case PROP_SPLIT_WINDOW:
204 g_value_set_boolean (value, de->split);
206 case PROP_DATA_STORE:
207 g_value_set_pointer (value, de->data_store);
209 case PROP_DICTIONARY:
210 g_value_set_pointer (value, de->dict);
212 case PROP_VALUE_LABELS:
213 g_value_set_boolean (value,
214 psppire_data_sheet_get_value_labels (
215 PSPPIRE_DATA_SHEET (de->data_sheets[0])));
217 case PROP_UI_MANAGER:
218 g_value_set_object (value, psppire_data_editor_get_ui_manager (de));
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 psppire_data_editor_switch_page (GtkNotebook *notebook,
228 #if GTK_DISABLE_DEPRECATED && GTK_CHECK_VERSION(2,20,0)
231 GtkNotebookPage *page,
235 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, page, page_num);
236 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
240 psppire_data_editor_set_focus_child (GtkContainer *container,
243 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
244 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
248 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
250 GParamSpec *data_store_spec ;
251 GParamSpec *dict_spec ;
252 GParamSpec *value_labels_spec;
253 GParamSpec *split_window_spec;
254 GParamSpec *ui_manager_spec;
255 GObjectClass *object_class = G_OBJECT_CLASS (klass);
256 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
257 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
259 parent_class = g_type_class_peek_parent (klass);
261 object_class->dispose = psppire_data_editor_dispose;
262 object_class->set_property = psppire_data_editor_set_property;
263 object_class->get_property = psppire_data_editor_get_property;
265 container_class->set_focus_child = psppire_data_editor_set_focus_child;
267 notebook_class->switch_page = psppire_data_editor_switch_page;
270 g_param_spec_pointer ("data-store",
272 "A pointer to the data store associated with this editor",
273 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
275 g_object_class_install_property (object_class,
280 g_param_spec_pointer ("dictionary",
282 "A pointer to the dictionary associated with this editor",
283 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
285 g_object_class_install_property (object_class,
290 g_param_spec_boolean ("value-labels",
292 "Whether or not the data sheet should display labels instead of values",
294 G_PARAM_WRITABLE | G_PARAM_READABLE);
296 g_object_class_install_property (object_class,
302 g_param_spec_boolean ("split",
304 "True iff the data sheet is split",
306 G_PARAM_READABLE | G_PARAM_WRITABLE);
308 g_object_class_install_property (object_class,
313 g_param_spec_object ("ui-manager",
315 "UI manager for the active notebook tab. The client should merge this UI manager with the active UI manager to obtain menu items and tool bar items specific to the active notebook tab.",
318 g_object_class_install_property (object_class,
324 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
326 PsppireDataEditor *de)
328 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
329 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
331 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
338 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
339 PsppireDataEditor *de)
341 PsppireDataSheet *data_sheet;
343 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
344 PSPPIRE_DATA_EDITOR_DATA_VIEW);
346 data_sheet = psppire_data_editor_get_active_data_sheet (de);
347 psppire_data_sheet_goto_variable (data_sheet, dict_index);
352 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
353 active cell or cells. */
355 refresh_entry (PsppireDataEditor *de)
357 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
358 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
359 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
361 gchar *ref_cell_text;
362 GList *selected_columns, *iter;
363 struct variable *var;
367 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
370 for (iter = selected_columns; iter != NULL; iter = iter->next)
372 PsppSheetViewColumn *column = iter->data;
373 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
380 g_list_free (selected_columns);
382 n_cases = pspp_sheet_selection_count_selected_rows (selection);
385 /* The final row is selectable but it isn't a case (it's just used to add
386 more cases), so don't count it. */
390 case_count = psppire_data_store_get_case_count (de->data_store);
391 path = gtk_tree_path_new_from_indices (case_count, -1);
392 if (pspp_sheet_selection_path_is_selected (selection, path))
394 gtk_tree_path_free (path);
397 ref_cell_text = NULL;
398 if (n_cases == 1 && n_vars == 1)
400 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
401 struct range_set *selected_rows;
402 gboolean show_value_labels;
407 selected_rows = pspp_sheet_selection_get_range_set (selection);
408 row = range_set_scan (selected_rows, 0);
409 range_set_destroy (selected_rows);
411 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
413 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
415 psppire_value_entry_set_variable (value_entry, var);
416 psppire_value_entry_set_show_value_label (value_entry,
419 width = var_get_width (var);
420 value_init (&value, width);
421 datasheet_get_value (de->data_store->datasheet,
422 row, var_get_case_index (var), &value);
423 psppire_value_entry_set_value (value_entry, &value, width);
424 value_destroy (&value, width);
426 gtk_widget_set_sensitive (de->datum_entry, TRUE);
430 if (n_cases == 0 || n_vars == 0)
432 ref_cell_text = NULL;
438 /* The glib string library does not understand the ' printf modifier
439 on all platforms, but the "struct string" library does (because
440 Gnulib fixes that problem), so use the latter. */
442 ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
444 ds_put_byte (&s, ' ');
445 ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
446 ds_put_byte (&s, ' ');
447 ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
450 ref_cell_text = ds_steal_cstr (&s);
453 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
456 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
457 gtk_widget_set_sensitive (de->datum_entry, FALSE);
460 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
461 ref_cell_text ? ref_cell_text : "");
462 g_free (ref_cell_text);
466 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
468 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
469 struct variable *var;
474 row = psppire_data_sheet_get_current_case (data_sheet);
475 var = psppire_data_sheet_get_current_variable (data_sheet);
479 width = var_get_width (var);
480 value_init (&value, width);
481 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
483 psppire_data_store_set_value (de->data_store, row, var, &value);
484 value_destroy (&value, width);
488 on_data_sheet_selection_changed (PsppSheetSelection *selection,
489 PsppireDataEditor *de)
491 /* In a split view, ensure that only a single data sheet has a nonempty
494 && pspp_sheet_selection_count_selected_rows (selection)
495 && pspp_sheet_selection_count_selected_columns (selection))
497 PsppireDataSheet *ds;
500 FOR_EACH_DATA_SHEET (ds, i, de)
502 PsppSheetSelection *s;
504 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
506 pspp_sheet_selection_unselect_all (s);
513 /* Ensures that rows in the right-hand panes in the split view have the same
514 row height as the left-hand panes. Otherwise, the rows in the right-hand
515 pane tend to be smaller, because the right-hand pane doesn't have buttons
518 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
520 PsppireDataEditor *de)
524 TL = GTK_XPANED_TOP_LEFT,
525 TR = GTK_XPANED_TOP_RIGHT,
526 BL = GTK_XPANED_BOTTOM_LEFT,
527 BR = GTK_XPANED_BOTTOM_RIGHT
530 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
532 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
534 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
539 disconnect_data_sheets (PsppireDataEditor *de)
541 PsppireDataSheet *ds;
544 FOR_EACH_DATA_SHEET (ds, i, de)
546 PsppSheetSelection *selection;
550 /* This can only happen if 'dispose' runs more than once. */
554 if (i == GTK_XPANED_TOP_LEFT)
555 g_signal_handlers_disconnect_by_func (
556 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
558 g_signal_handlers_disconnect_by_func (
559 ds, G_CALLBACK (refresh_entry), de);
560 g_signal_handlers_disconnect_by_func (
561 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
563 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
564 g_signal_handlers_disconnect_by_func (
565 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
567 de->data_sheets[i] = NULL;
572 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
573 gboolean show_value_labels)
575 PsppSheetSelection *selection;
578 ds = psppire_data_sheet_new ();
579 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
580 psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
583 g_signal_connect_swapped (ds, "notify::value-labels",
584 G_CALLBACK (refresh_entry), de);
585 g_signal_connect (ds, "var-double-clicked",
586 G_CALLBACK (on_data_sheet_var_double_clicked), de);
588 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
589 g_signal_connect (selection, "changed",
590 G_CALLBACK (on_data_sheet_selection_changed), de);
596 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
597 gboolean show_value_labels)
599 GtkWidget *data_sheet_scroller;
601 de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
602 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
604 /* Put data sheet in scroller. */
605 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
606 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
607 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
608 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
610 return data_sheet_scroller;
614 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
615 gboolean show_value_labels)
617 /* Panes, in the order in which we want to create them. */
622 BL, /* bottom left */
623 BR /* bottom right */
626 PsppSheetView *ds[4];
630 xpaned = GTK_XPANED (gtk_xpaned_new ());
632 for (i = 0; i < 4; i++)
634 GtkAdjustment *hadjust, *vadjust;
635 GtkPolicyType hpolicy, vpolicy;
638 de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
639 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
642 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
644 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
649 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
651 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
655 scroller = gtk_scrolled_window_new (hadjust, vadjust);
656 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
657 GTK_SHADOW_ETCHED_IN);
658 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
659 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
660 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
662 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
667 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
671 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
675 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
679 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
683 g_warn_if_reached ();
687 /* Bottom sheets don't display variable names. */
688 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
689 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
691 /* Right sheets don't display case numbers. */
692 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
693 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
695 g_signal_connect (ds[TL], "notify::fixed-height",
696 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
698 return GTK_WIDGET (xpaned);
701 static void set_font_recursively (GtkWidget *w, gpointer data);
704 psppire_data_editor_init (PsppireDataEditor *de)
706 GtkWidget *var_sheet_scroller;
708 gchar *fontname = NULL;
711 de->ui_manager = NULL;
712 de->old_vbox_widget = NULL;
714 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
716 de->cell_ref_label = gtk_label_new ("");
717 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
718 gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
720 de->datum_entry = psppire_value_entry_new ();
721 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
722 "activate", G_CALLBACK (on_datum_entry_activate), de);
724 hbox = gtk_hbox_new (FALSE, 0);
725 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
726 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
729 de->datasheet_vbox_widget
730 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE);
732 de->vbox = gtk_vbox_new (FALSE, 0);
733 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
734 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
737 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
738 gtk_label_new_with_mnemonic (_("Data View")));
740 gtk_widget_show_all (de->vbox);
742 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
743 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
744 GTK_TREE_VIEW_GRID_LINES_BOTH);
745 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
746 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
747 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
748 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
749 gtk_widget_show_all (var_sheet_scroller);
750 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
751 gtk_label_new_with_mnemonic (_("Variable View")));
753 g_signal_connect (de->var_sheet, "var-double-clicked",
754 G_CALLBACK (on_var_sheet_var_double_clicked), de);
756 g_object_set (de, "can-focus", FALSE, NULL);
758 if (psppire_conf_get_string (psppire_conf_new (),
759 "Data Editor", "font",
762 de->font = pango_font_description_from_string (fontname);
764 set_font_recursively (GTK_WIDGET (de), de->font);
767 psppire_data_editor_update_ui_manager (de);
771 psppire_data_editor_new (PsppireDict *dict,
772 PsppireDataStore *data_store)
774 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
776 "data-store", data_store,
780 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
781 sheet(s) and variable sheet. */
783 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
785 GtkTreeViewGridLines grid;
786 PsppireDataSheet *data_sheet;
790 ? GTK_TREE_VIEW_GRID_LINES_BOTH
791 : GTK_TREE_VIEW_GRID_LINES_NONE);
793 FOR_EACH_DATA_SHEET (data_sheet, i, de)
794 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
795 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
800 set_font_recursively (GtkWidget *w, gpointer data)
802 PangoFontDescription *font_desc = data;
803 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
805 pango_font_description_free (style->font_desc);
806 style->font_desc = pango_font_description_copy (font_desc);
808 gtk_widget_modify_style (w, style);
810 if ( GTK_IS_CONTAINER (w))
811 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
814 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
816 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
819 set_font_recursively (GTK_WIDGET (de), font_desc);
822 pango_font_description_free (de->font);
823 de->font = pango_font_description_copy (font_desc);
824 font_name = pango_font_description_to_string (de->font);
826 psppire_conf_set_string (psppire_conf_new (),
827 "Data Editor", "font",
832 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
833 If SPLIT is FALSE, un-splits it into a single pane. */
835 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
837 GtkTreeViewGridLines grid_lines;
840 if (split == de->split)
844 grid_lines = pspp_sheet_view_get_grid_lines (
845 PSPP_SHEET_VIEW (de->data_sheets[0]));
846 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
847 de->data_sheets[0]));
849 disconnect_data_sheets (de);
850 if (de->old_vbox_widget)
851 g_object_unref (de->old_vbox_widget);
852 de->old_vbox_widget = de->datasheet_vbox_widget;
853 g_object_ref (de->old_vbox_widget);
854 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
855 (currently it seems to provoke an error if I do that.
857 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
860 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
862 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
864 psppire_data_editor_refresh_model (de);
866 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
868 gtk_widget_show_all (de->vbox);
871 set_font_recursively (GTK_WIDGET (de), de->font);
874 g_object_notify (G_OBJECT (de), "split");
875 psppire_data_editor_update_ui_manager (de);
878 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
879 visible and selected in the active view in DE. */
881 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
883 PsppireDataSheet *data_sheet;
885 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
887 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
888 data_sheet = psppire_data_editor_get_active_data_sheet (de);
889 psppire_data_sheet_goto_variable (data_sheet, dict_index);
892 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
893 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
899 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
900 is the only data sheet. If DE is in split mode (showing four data sheets),
901 this is the focused data sheet or, if none is focused, the data sheet with
902 selected cells or, if none has selected cells, the upper-left data sheet. */
904 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
908 PsppireDataSheet *data_sheet;
912 /* If one of the datasheet's scrollers is focused, choose that one. */
913 scroller = gtk_container_get_focus_child (
914 GTK_CONTAINER (de->datasheet_vbox_widget));
915 if (scroller != NULL)
916 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
918 /* Otherwise if there's a nonempty selection in some data sheet, choose
920 FOR_EACH_DATA_SHEET (data_sheet, i, de)
922 PsppSheetSelection *selection;
924 selection = pspp_sheet_view_get_selection (
925 PSPP_SHEET_VIEW (data_sheet));
926 if (pspp_sheet_selection_count_selected_rows (selection)
927 && pspp_sheet_selection_count_selected_columns (selection))
932 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
935 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
936 manager to display menu items and toolbar items specific to DE's current
939 DE's toplevel widget can watch for changes by connecting to DE's
940 notify::ui-manager signal. */
942 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
944 psppire_data_editor_update_ui_manager (de);
945 return de->ui_manager;
949 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
951 PsppireDataSheet *data_sheet;
952 GtkUIManager *ui_manager;
956 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
958 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
959 data_sheet = psppire_data_editor_get_active_data_sheet (de);
960 if (data_sheet != NULL)
961 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
964 /* This happens transiently in psppire_data_editor_split_window(). */
968 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
969 ui_manager = psppire_var_sheet_get_ui_manager (
970 PSPPIRE_VAR_SHEET (de->var_sheet));
974 /* This happens transiently in psppire_data_editor_init(). */
978 if (ui_manager != de->ui_manager)
981 g_object_unref (de->ui_manager);
983 g_object_ref (ui_manager);
984 de->ui_manager = ui_manager;
986 g_object_notify (G_OBJECT (de), "ui-manager");