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,
231 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, page_num);
232 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
236 psppire_data_editor_set_focus_child (GtkContainer *container,
239 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
240 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
244 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
246 GParamSpec *data_store_spec ;
247 GParamSpec *dict_spec ;
248 GParamSpec *value_labels_spec;
249 GParamSpec *split_window_spec;
250 GParamSpec *ui_manager_spec;
251 GObjectClass *object_class = G_OBJECT_CLASS (klass);
252 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
253 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
255 parent_class = g_type_class_peek_parent (klass);
257 object_class->dispose = psppire_data_editor_dispose;
258 object_class->set_property = psppire_data_editor_set_property;
259 object_class->get_property = psppire_data_editor_get_property;
261 container_class->set_focus_child = psppire_data_editor_set_focus_child;
263 notebook_class->switch_page = psppire_data_editor_switch_page;
266 g_param_spec_pointer ("data-store",
268 "A pointer to the data store associated with this editor",
269 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
271 g_object_class_install_property (object_class,
276 g_param_spec_pointer ("dictionary",
278 "A pointer to the dictionary associated with this editor",
279 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
281 g_object_class_install_property (object_class,
286 g_param_spec_boolean ("value-labels",
288 "Whether or not the data sheet should display labels instead of values",
290 G_PARAM_WRITABLE | G_PARAM_READABLE);
292 g_object_class_install_property (object_class,
298 g_param_spec_boolean ("split",
300 "True iff the data sheet is split",
302 G_PARAM_READABLE | G_PARAM_WRITABLE);
304 g_object_class_install_property (object_class,
309 g_param_spec_object ("ui-manager",
311 "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.",
314 g_object_class_install_property (object_class,
320 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
322 PsppireDataEditor *de)
324 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
325 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
327 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
334 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
335 PsppireDataEditor *de)
337 PsppireDataSheet *data_sheet;
339 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
340 PSPPIRE_DATA_EDITOR_DATA_VIEW);
342 data_sheet = psppire_data_editor_get_active_data_sheet (de);
343 psppire_data_sheet_goto_variable (data_sheet, dict_index);
348 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
349 active cell or cells. */
351 refresh_entry (PsppireDataEditor *de)
353 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
354 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
355 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
357 gchar *ref_cell_text;
358 GList *selected_columns, *iter;
359 struct variable *var;
363 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
366 for (iter = selected_columns; iter != NULL; iter = iter->next)
368 PsppSheetViewColumn *column = iter->data;
369 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
376 g_list_free (selected_columns);
378 n_cases = pspp_sheet_selection_count_selected_rows (selection);
381 /* The final row is selectable but it isn't a case (it's just used to add
382 more cases), so don't count it. */
386 case_count = psppire_data_store_get_case_count (de->data_store);
387 path = gtk_tree_path_new_from_indices (case_count, -1);
388 if (pspp_sheet_selection_path_is_selected (selection, path))
390 gtk_tree_path_free (path);
393 ref_cell_text = NULL;
394 if (n_cases == 1 && n_vars == 1)
396 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
397 struct range_set *selected_rows;
398 gboolean show_value_labels;
403 selected_rows = pspp_sheet_selection_get_range_set (selection);
404 row = range_set_scan (selected_rows, 0);
405 range_set_destroy (selected_rows);
407 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
409 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
411 psppire_value_entry_set_variable (value_entry, var);
412 psppire_value_entry_set_show_value_label (value_entry,
415 width = var_get_width (var);
416 value_init (&value, width);
417 datasheet_get_value (de->data_store->datasheet,
418 row, var_get_case_index (var), &value);
419 psppire_value_entry_set_value (value_entry, &value, width);
420 value_destroy (&value, width);
422 gtk_widget_set_sensitive (de->datum_entry, TRUE);
426 if (n_cases == 0 || n_vars == 0)
428 ref_cell_text = NULL;
434 /* The glib string library does not understand the ' printf modifier
435 on all platforms, but the "struct string" library does (because
436 Gnulib fixes that problem), so use the latter. */
438 ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
440 ds_put_byte (&s, ' ');
441 ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
442 ds_put_byte (&s, ' ');
443 ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
446 ref_cell_text = ds_steal_cstr (&s);
449 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
452 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
453 gtk_widget_set_sensitive (de->datum_entry, FALSE);
456 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
457 ref_cell_text ? ref_cell_text : "");
458 g_free (ref_cell_text);
462 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
464 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
465 struct variable *var;
470 row = psppire_data_sheet_get_current_case (data_sheet);
471 var = psppire_data_sheet_get_current_variable (data_sheet);
475 width = var_get_width (var);
476 value_init (&value, width);
477 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
479 psppire_data_store_set_value (de->data_store, row, var, &value);
480 value_destroy (&value, width);
484 on_data_sheet_selection_changed (PsppSheetSelection *selection,
485 PsppireDataEditor *de)
487 /* In a split view, ensure that only a single data sheet has a nonempty
490 && pspp_sheet_selection_count_selected_rows (selection)
491 && pspp_sheet_selection_count_selected_columns (selection))
493 PsppireDataSheet *ds;
496 FOR_EACH_DATA_SHEET (ds, i, de)
498 PsppSheetSelection *s;
500 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
502 pspp_sheet_selection_unselect_all (s);
509 /* Ensures that rows in the right-hand panes in the split view have the same
510 row height as the left-hand panes. Otherwise, the rows in the right-hand
511 pane tend to be smaller, because the right-hand pane doesn't have buttons
514 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
516 PsppireDataEditor *de)
520 TL = GTK_XPANED_TOP_LEFT,
521 TR = GTK_XPANED_TOP_RIGHT,
522 BL = GTK_XPANED_BOTTOM_LEFT,
523 BR = GTK_XPANED_BOTTOM_RIGHT
526 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
528 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
530 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
535 disconnect_data_sheets (PsppireDataEditor *de)
537 PsppireDataSheet *ds;
540 FOR_EACH_DATA_SHEET (ds, i, de)
542 PsppSheetSelection *selection;
546 /* This can only happen if 'dispose' runs more than once. */
550 if (i == GTK_XPANED_TOP_LEFT)
551 g_signal_handlers_disconnect_by_func (
552 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
554 g_signal_handlers_disconnect_by_func (
555 ds, G_CALLBACK (refresh_entry), de);
556 g_signal_handlers_disconnect_by_func (
557 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
559 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
560 g_signal_handlers_disconnect_by_func (
561 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
563 de->data_sheets[i] = NULL;
568 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
569 gboolean show_value_labels)
571 PsppSheetSelection *selection;
574 ds = psppire_data_sheet_new ();
575 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
576 psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
579 g_signal_connect_swapped (ds, "notify::value-labels",
580 G_CALLBACK (refresh_entry), de);
581 g_signal_connect (ds, "var-double-clicked",
582 G_CALLBACK (on_data_sheet_var_double_clicked), de);
584 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
585 g_signal_connect (selection, "changed",
586 G_CALLBACK (on_data_sheet_selection_changed), de);
592 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
593 gboolean show_value_labels)
595 GtkWidget *data_sheet_scroller;
597 de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
598 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
600 /* Put data sheet in scroller. */
601 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
602 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
603 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
604 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
606 return data_sheet_scroller;
610 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
611 gboolean show_value_labels)
613 /* Panes, in the order in which we want to create them. */
618 BL, /* bottom left */
619 BR /* bottom right */
622 PsppSheetView *ds[4];
626 xpaned = GTK_XPANED (gtk_xpaned_new ());
628 for (i = 0; i < 4; i++)
630 GtkAdjustment *hadjust, *vadjust;
631 GtkPolicyType hpolicy, vpolicy;
634 de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
635 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
638 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
640 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
645 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
647 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
651 scroller = gtk_scrolled_window_new (hadjust, vadjust);
652 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
653 GTK_SHADOW_ETCHED_IN);
654 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
655 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
656 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
658 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
663 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
667 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
671 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
675 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
679 g_warn_if_reached ();
683 /* Bottom sheets don't display variable names. */
684 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
685 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
687 /* Right sheets don't display case numbers. */
688 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
689 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
691 g_signal_connect (ds[TL], "notify::fixed-height",
692 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
694 return GTK_WIDGET (xpaned);
697 static void set_font_recursively (GtkWidget *w, gpointer data);
700 psppire_data_editor_init (PsppireDataEditor *de)
702 GtkWidget *var_sheet_scroller;
704 gchar *fontname = NULL;
707 de->ui_manager = NULL;
708 de->old_vbox_widget = NULL;
710 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
712 de->cell_ref_label = gtk_label_new ("");
713 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
714 gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
716 de->datum_entry = psppire_value_entry_new ();
717 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
718 "activate", G_CALLBACK (on_datum_entry_activate), de);
720 hbox = gtk_hbox_new (FALSE, 0);
721 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
722 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
725 de->datasheet_vbox_widget
726 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE);
728 de->vbox = gtk_vbox_new (FALSE, 0);
729 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
730 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
733 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
734 gtk_label_new_with_mnemonic (_("Data View")));
736 gtk_widget_show_all (de->vbox);
738 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
739 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
740 GTK_TREE_VIEW_GRID_LINES_BOTH);
741 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
742 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
743 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
744 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
745 gtk_widget_show_all (var_sheet_scroller);
746 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
747 gtk_label_new_with_mnemonic (_("Variable View")));
749 g_signal_connect (de->var_sheet, "var-double-clicked",
750 G_CALLBACK (on_var_sheet_var_double_clicked), de);
752 g_object_set (de, "can-focus", FALSE, NULL);
754 if (psppire_conf_get_string (psppire_conf_new (),
755 "Data Editor", "font",
758 de->font = pango_font_description_from_string (fontname);
760 set_font_recursively (GTK_WIDGET (de), de->font);
763 psppire_data_editor_update_ui_manager (de);
767 psppire_data_editor_new (PsppireDict *dict,
768 PsppireDataStore *data_store)
770 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
772 "data-store", data_store,
776 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
777 sheet(s) and variable sheet. */
779 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
781 GtkTreeViewGridLines grid;
782 PsppireDataSheet *data_sheet;
786 ? GTK_TREE_VIEW_GRID_LINES_BOTH
787 : GTK_TREE_VIEW_GRID_LINES_NONE);
789 FOR_EACH_DATA_SHEET (data_sheet, i, de)
790 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
791 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
796 set_font_recursively (GtkWidget *w, gpointer data)
798 PangoFontDescription *font_desc = data;
799 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
801 pango_font_description_free (style->font_desc);
802 style->font_desc = pango_font_description_copy (font_desc);
804 gtk_widget_modify_style (w, style);
806 if ( GTK_IS_CONTAINER (w))
807 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
810 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
812 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
815 set_font_recursively (GTK_WIDGET (de), font_desc);
818 pango_font_description_free (de->font);
819 de->font = pango_font_description_copy (font_desc);
820 font_name = pango_font_description_to_string (de->font);
822 psppire_conf_set_string (psppire_conf_new (),
823 "Data Editor", "font",
828 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
829 If SPLIT is FALSE, un-splits it into a single pane. */
831 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
833 GtkTreeViewGridLines grid_lines;
836 if (split == de->split)
840 grid_lines = pspp_sheet_view_get_grid_lines (
841 PSPP_SHEET_VIEW (de->data_sheets[0]));
842 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
843 de->data_sheets[0]));
845 disconnect_data_sheets (de);
846 if (de->old_vbox_widget)
847 g_object_unref (de->old_vbox_widget);
848 de->old_vbox_widget = de->datasheet_vbox_widget;
849 g_object_ref (de->old_vbox_widget);
850 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
851 (currently it seems to provoke an error if I do that.
853 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
856 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
858 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
860 psppire_data_editor_refresh_model (de);
862 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
864 gtk_widget_show_all (de->vbox);
867 set_font_recursively (GTK_WIDGET (de), de->font);
870 g_object_notify (G_OBJECT (de), "split");
871 psppire_data_editor_update_ui_manager (de);
874 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
875 visible and selected in the active view in DE. */
877 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
879 PsppireDataSheet *data_sheet;
881 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
883 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
884 data_sheet = psppire_data_editor_get_active_data_sheet (de);
885 psppire_data_sheet_goto_variable (data_sheet, dict_index);
888 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
889 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
895 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
896 is the only data sheet. If DE is in split mode (showing four data sheets),
897 this is the focused data sheet or, if none is focused, the data sheet with
898 selected cells or, if none has selected cells, the upper-left data sheet. */
900 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
904 PsppireDataSheet *data_sheet;
908 /* If one of the datasheet's scrollers is focused, choose that one. */
909 scroller = gtk_container_get_focus_child (
910 GTK_CONTAINER (de->datasheet_vbox_widget));
911 if (scroller != NULL)
912 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
914 /* Otherwise if there's a nonempty selection in some data sheet, choose
916 FOR_EACH_DATA_SHEET (data_sheet, i, de)
918 PsppSheetSelection *selection;
920 selection = pspp_sheet_view_get_selection (
921 PSPP_SHEET_VIEW (data_sheet));
922 if (pspp_sheet_selection_count_selected_rows (selection)
923 && pspp_sheet_selection_count_selected_columns (selection))
928 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
931 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
932 manager to display menu items and toolbar items specific to DE's current
935 DE's toplevel widget can watch for changes by connecting to DE's
936 notify::ui-manager signal. */
938 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
940 psppire_data_editor_update_ui_manager (de);
941 return de->ui_manager;
945 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
947 PsppireDataSheet *data_sheet;
948 GtkUIManager *ui_manager;
952 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
954 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
955 data_sheet = psppire_data_editor_get_active_data_sheet (de);
956 if (data_sheet != NULL)
957 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
960 /* This happens transiently in psppire_data_editor_split_window(). */
964 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
965 ui_manager = psppire_var_sheet_get_ui_manager (
966 PSPPIRE_VAR_SHEET (de->var_sheet));
970 /* This happens transiently in psppire_data_editor_init(). */
974 if (ui_manager != de->ui_manager)
977 g_object_unref (de->ui_manager);
979 g_object_ref (ui_manager);
980 de->ui_manager = ui_manager;
982 g_object_notify (G_OBJECT (de), "ui-manager");