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"
37 #define _(msgid) gettext (msgid)
39 #define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR) \
42 && ((DATA_SHEET) = PSPPIRE_DATA_SHEET ( \
43 (DATA_EDITOR)->data_sheets[IDX])) != NULL; \
46 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
47 static void psppire_data_editor_init (PsppireDataEditor *de);
49 static void disconnect_data_sheets (PsppireDataEditor *);
50 static void refresh_entry (PsppireDataEditor *);
51 static void psppire_data_editor_update_ui_manager (PsppireDataEditor *);
54 psppire_data_editor_get_type (void)
56 static GType de_type = 0;
60 static const GTypeInfo de_info =
62 sizeof (PsppireDataEditorClass),
64 NULL, /* base_finalize */
65 (GClassInitFunc) psppire_data_editor_class_init,
66 NULL, /* class_finalize */
67 NULL, /* class_data */
68 sizeof (PsppireDataEditor),
70 (GInstanceInitFunc) psppire_data_editor_init,
73 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
80 static GObjectClass * parent_class = NULL;
83 psppire_data_editor_dispose (GObject *obj)
85 PsppireDataEditor *de = (PsppireDataEditor *) obj;
87 disconnect_data_sheets (de);
91 g_object_unref (de->data_store);
92 de->data_store = NULL;
97 g_object_unref (de->dict);
101 if (de->font != NULL)
103 pango_font_description_free (de->font);
109 g_object_unref (de->ui_manager);
110 de->ui_manager = NULL;
113 /* Chain up to the parent class */
114 G_OBJECT_CLASS (parent_class)->dispose (obj);
128 psppire_data_editor_refresh_model (PsppireDataEditor *de)
130 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet);
131 PsppireDataSheet *data_sheet;
134 FOR_EACH_DATA_SHEET (data_sheet, i, de)
135 psppire_data_sheet_set_data_store (data_sheet, de->data_store);
136 psppire_var_sheet_set_dictionary (var_sheet, de->dict);
140 psppire_data_editor_set_property (GObject *object,
145 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
146 PsppireDataSheet *data_sheet;
151 case PROP_SPLIT_WINDOW:
152 psppire_data_editor_split_window (de, g_value_get_boolean (value));
154 case PROP_DATA_STORE:
157 g_signal_handlers_disconnect_by_func (de->data_store,
158 G_CALLBACK (refresh_entry),
160 g_object_unref (de->data_store);
163 de->data_store = g_value_get_pointer (value);
164 g_object_ref (de->data_store);
165 psppire_data_editor_refresh_model (de);
167 g_signal_connect_swapped (de->data_store, "case-changed",
168 G_CALLBACK (refresh_entry), de);
171 case PROP_DICTIONARY:
173 g_object_unref (de->dict);
174 de->dict = g_value_get_pointer (value);
175 g_object_ref (de->dict);
177 psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet),
180 case PROP_VALUE_LABELS:
181 FOR_EACH_DATA_SHEET (data_sheet, i, de)
182 psppire_data_sheet_set_value_labels (data_sheet,
183 g_value_get_boolean (value));
185 case PROP_UI_MANAGER:
187 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
193 psppire_data_editor_get_property (GObject *object,
198 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
202 case PROP_SPLIT_WINDOW:
203 g_value_set_boolean (value, de->split);
205 case PROP_DATA_STORE:
206 g_value_set_pointer (value, de->data_store);
208 case PROP_DICTIONARY:
209 g_value_set_pointer (value, de->dict);
211 case PROP_VALUE_LABELS:
212 g_value_set_boolean (value,
213 psppire_data_sheet_get_value_labels (
214 PSPPIRE_DATA_SHEET (de->data_sheets[0])));
216 case PROP_UI_MANAGER:
217 g_value_set_object (value, psppire_data_editor_get_ui_manager (de));
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226 psppire_data_editor_switch_page (GtkNotebook *notebook,
227 #if GTK_DISABLE_DEPRECATED && GTK_CHECK_VERSION(2,20,0)
230 GtkNotebookPage *page,
234 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, page, page_num);
235 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
239 psppire_data_editor_set_focus_child (GtkContainer *container,
242 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
243 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
247 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
249 GParamSpec *data_store_spec ;
250 GParamSpec *dict_spec ;
251 GParamSpec *value_labels_spec;
252 GParamSpec *split_window_spec;
253 GParamSpec *ui_manager_spec;
254 GObjectClass *object_class = G_OBJECT_CLASS (klass);
255 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
256 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
258 parent_class = g_type_class_peek_parent (klass);
260 object_class->dispose = psppire_data_editor_dispose;
261 object_class->set_property = psppire_data_editor_set_property;
262 object_class->get_property = psppire_data_editor_get_property;
264 container_class->set_focus_child = psppire_data_editor_set_focus_child;
266 notebook_class->switch_page = psppire_data_editor_switch_page;
269 g_param_spec_pointer ("data-store",
271 "A pointer to the data store associated with this editor",
272 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
274 g_object_class_install_property (object_class,
279 g_param_spec_pointer ("dictionary",
281 "A pointer to the dictionary associated with this editor",
282 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
284 g_object_class_install_property (object_class,
289 g_param_spec_boolean ("value-labels",
291 "Whether or not the data sheet should display labels instead of values",
293 G_PARAM_WRITABLE | G_PARAM_READABLE);
295 g_object_class_install_property (object_class,
301 g_param_spec_boolean ("split",
303 "True iff the data sheet is split",
305 G_PARAM_READABLE | G_PARAM_WRITABLE);
307 g_object_class_install_property (object_class,
312 g_param_spec_object ("ui-manager",
314 "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.",
317 g_object_class_install_property (object_class,
323 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
325 PsppireDataEditor *de)
327 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
328 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
330 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
337 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
338 PsppireDataEditor *de)
340 PsppireDataSheet *data_sheet;
342 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
343 PSPPIRE_DATA_EDITOR_DATA_VIEW);
345 data_sheet = psppire_data_editor_get_active_data_sheet (de);
346 psppire_data_sheet_goto_variable (data_sheet, dict_index);
351 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
352 active cell or cells. */
354 refresh_entry (PsppireDataEditor *de)
356 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
357 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
358 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
360 gchar *ref_cell_text;
361 GList *selected_columns, *iter;
362 struct variable *var;
366 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
369 for (iter = selected_columns; iter != NULL; iter = iter->next)
371 PsppSheetViewColumn *column = iter->data;
372 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
379 g_list_free (selected_columns);
381 n_cases = pspp_sheet_selection_count_selected_rows (selection);
384 /* The final row is selectable but it isn't a case (it's just used to add
385 more cases), so don't count it. */
389 case_count = psppire_data_store_get_case_count (de->data_store);
390 path = gtk_tree_path_new_from_indices (case_count, -1);
391 if (pspp_sheet_selection_path_is_selected (selection, path))
393 gtk_tree_path_free (path);
396 ref_cell_text = NULL;
397 if (n_cases == 1 && n_vars == 1)
399 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
400 struct range_set *selected_rows;
401 gboolean show_value_labels;
406 selected_rows = pspp_sheet_selection_get_range_set (selection);
407 row = range_set_scan (selected_rows, 0);
408 range_set_destroy (selected_rows);
410 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
412 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
414 psppire_value_entry_set_variable (value_entry, var);
415 psppire_value_entry_set_show_value_label (value_entry,
418 width = var_get_width (var);
419 value_init (&value, width);
420 datasheet_get_value (de->data_store->datasheet,
421 row, var_get_case_index (var), &value);
422 psppire_value_entry_set_value (value_entry, &value, width);
423 value_destroy (&value, width);
425 gtk_widget_set_sensitive (de->datum_entry, TRUE);
429 if (n_cases == 0 || n_vars == 0)
431 ref_cell_text = NULL;
437 /* The glib string library does not understand the ' printf modifier
438 on all platforms, but the "struct string" library does (because
439 Gnulib fixes that problem), so use the latter. */
441 ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
443 ds_put_byte (&s, ' ');
444 ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
445 ds_put_byte (&s, ' ');
446 ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
449 ref_cell_text = ds_steal_cstr (&s);
452 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
455 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
456 gtk_widget_set_sensitive (de->datum_entry, FALSE);
459 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
460 ref_cell_text ? ref_cell_text : "");
461 g_free (ref_cell_text);
465 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
467 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
468 struct variable *var;
473 row = psppire_data_sheet_get_current_case (data_sheet);
474 var = psppire_data_sheet_get_current_variable (data_sheet);
478 width = var_get_width (var);
479 value_init (&value, width);
480 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
482 psppire_data_store_set_value (de->data_store, row, var, &value);
483 value_destroy (&value, width);
487 on_data_sheet_selection_changed (PsppSheetSelection *selection,
488 PsppireDataEditor *de)
490 /* In a split view, ensure that only a single data sheet has a nonempty
493 && pspp_sheet_selection_count_selected_rows (selection)
494 && pspp_sheet_selection_count_selected_columns (selection))
496 PsppireDataSheet *ds;
499 FOR_EACH_DATA_SHEET (ds, i, de)
501 PsppSheetSelection *s;
503 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
505 pspp_sheet_selection_unselect_all (s);
512 /* Ensures that rows in the right-hand panes in the split view have the same
513 row height as the left-hand panes. Otherwise, the rows in the right-hand
514 pane tend to be smaller, because the right-hand pane doesn't have buttons
517 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
519 PsppireDataEditor *de)
523 TL = GTK_XPANED_TOP_LEFT,
524 TR = GTK_XPANED_TOP_RIGHT,
525 BL = GTK_XPANED_BOTTOM_LEFT,
526 BR = GTK_XPANED_BOTTOM_RIGHT
529 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
531 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
533 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
538 disconnect_data_sheets (PsppireDataEditor *de)
540 PsppireDataSheet *ds;
543 FOR_EACH_DATA_SHEET (ds, i, de)
545 PsppSheetSelection *selection;
549 /* This can only happen if 'dispose' runs more than once. */
553 if (i == GTK_XPANED_TOP_LEFT)
554 g_signal_handlers_disconnect_by_func (
555 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
557 g_signal_handlers_disconnect_by_func (
558 ds, G_CALLBACK (refresh_entry), de);
559 g_signal_handlers_disconnect_by_func (
560 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
562 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
563 g_signal_handlers_disconnect_by_func (
564 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
566 de->data_sheets[i] = NULL;
571 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
572 gboolean show_value_labels)
574 PsppSheetSelection *selection;
577 ds = psppire_data_sheet_new ();
578 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
579 psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
582 g_signal_connect_swapped (ds, "notify::value-labels",
583 G_CALLBACK (refresh_entry), de);
584 g_signal_connect (ds, "var-double-clicked",
585 G_CALLBACK (on_data_sheet_var_double_clicked), de);
587 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
588 g_signal_connect (selection, "changed",
589 G_CALLBACK (on_data_sheet_selection_changed), de);
595 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
596 gboolean show_value_labels)
598 GtkWidget *data_sheet_scroller;
600 de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
601 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
603 /* Put data sheet in scroller. */
604 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
605 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
606 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
607 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
609 return data_sheet_scroller;
613 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
614 gboolean show_value_labels)
616 /* Panes, in the order in which we want to create them. */
621 BL, /* bottom left */
622 BR /* bottom right */
625 PsppSheetView *ds[4];
629 xpaned = GTK_XPANED (gtk_xpaned_new ());
631 for (i = 0; i < 4; i++)
633 GtkAdjustment *hadjust, *vadjust;
634 GtkPolicyType hpolicy, vpolicy;
637 de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
638 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
641 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
643 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
648 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
650 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
654 scroller = gtk_scrolled_window_new (hadjust, vadjust);
655 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
656 GTK_SHADOW_ETCHED_IN);
657 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
658 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
659 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
661 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
666 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
670 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
674 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
678 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
682 g_warn_if_reached ();
686 /* Bottom sheets don't display variable names. */
687 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
688 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
690 /* Right sheets don't display case numbers. */
691 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
692 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
694 g_signal_connect (ds[TL], "notify::fixed-height",
695 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
697 return GTK_WIDGET (xpaned);
701 psppire_data_editor_init (PsppireDataEditor *de)
703 GtkWidget *var_sheet_scroller;
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 psppire_data_editor_update_ui_manager (de);
758 psppire_data_editor_new (PsppireDict *dict,
759 PsppireDataStore *data_store)
761 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
763 "data-store", data_store,
767 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
768 sheet(s) and variable sheet. */
770 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
772 GtkTreeViewGridLines grid;
773 PsppireDataSheet *data_sheet;
777 ? GTK_TREE_VIEW_GRID_LINES_BOTH
778 : GTK_TREE_VIEW_GRID_LINES_NONE);
780 FOR_EACH_DATA_SHEET (data_sheet, i, de)
781 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
782 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
787 set_font_recursively (GtkWidget *w, gpointer data)
789 PangoFontDescription *font_desc = data;
790 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
792 pango_font_description_free (style->font_desc);
793 style->font_desc = pango_font_description_copy (font_desc);
795 gtk_widget_modify_style (w, style);
797 if ( GTK_IS_CONTAINER (w))
798 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
801 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
803 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
805 set_font_recursively (GTK_WIDGET (de), font_desc);
808 pango_font_description_free (de->font);
809 de->font = pango_font_description_copy (font_desc);
812 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
813 If SPLIT is FALSE, un-splits it into a single pane. */
815 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
817 GtkTreeViewGridLines grid_lines;
820 if (split == de->split)
824 grid_lines = pspp_sheet_view_get_grid_lines (
825 PSPP_SHEET_VIEW (de->data_sheets[0]));
826 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
827 de->data_sheets[0]));
829 disconnect_data_sheets (de);
830 if (de->old_vbox_widget)
831 g_object_unref (de->old_vbox_widget);
832 de->old_vbox_widget = de->datasheet_vbox_widget;
833 g_object_ref (de->old_vbox_widget);
834 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
835 (currently it seems to provoke an error if I do that.
837 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
840 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
842 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
844 psppire_data_editor_refresh_model (de);
846 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
848 gtk_widget_show_all (de->vbox);
851 set_font_recursively (GTK_WIDGET (de), de->font);
854 g_object_notify (G_OBJECT (de), "split");
855 psppire_data_editor_update_ui_manager (de);
858 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
859 visible and selected in the active view in DE. */
861 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
863 PsppireDataSheet *data_sheet;
865 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
867 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
868 data_sheet = psppire_data_editor_get_active_data_sheet (de);
869 psppire_data_sheet_goto_variable (data_sheet, dict_index);
872 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
873 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
879 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
880 is the only data sheet. If DE is in split mode (showing four data sheets),
881 this is the focused data sheet or, if none is focused, the data sheet with
882 selected cells or, if none has selected cells, the upper-left data sheet. */
884 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
888 PsppireDataSheet *data_sheet;
892 /* If one of the datasheet's scrollers is focused, choose that one. */
893 scroller = gtk_container_get_focus_child (
894 GTK_CONTAINER (de->datasheet_vbox_widget));
895 if (scroller != NULL)
896 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
898 /* Otherwise if there's a nonempty selection in some data sheet, choose
900 FOR_EACH_DATA_SHEET (data_sheet, i, de)
902 PsppSheetSelection *selection;
904 selection = pspp_sheet_view_get_selection (
905 PSPP_SHEET_VIEW (data_sheet));
906 if (pspp_sheet_selection_count_selected_rows (selection)
907 && pspp_sheet_selection_count_selected_columns (selection))
912 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
915 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
916 manager to display menu items and toolbar items specific to DE's current
919 DE's toplevel widget can watch for changes by connecting to DE's
920 notify::ui-manager signal. */
922 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
924 psppire_data_editor_update_ui_manager (de);
925 return de->ui_manager;
929 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
931 PsppireDataSheet *data_sheet;
932 GtkUIManager *ui_manager;
936 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
938 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
939 data_sheet = psppire_data_editor_get_active_data_sheet (de);
940 if (data_sheet != NULL)
941 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
944 /* This happens transiently in psppire_data_editor_split_window(). */
948 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
949 ui_manager = psppire_var_sheet_get_ui_manager (
950 PSPPIRE_VAR_SHEET (de->var_sheet));
954 /* This happens transiently in psppire_data_editor_init(). */
958 if (ui_manager != de->ui_manager)
961 g_object_unref (de->ui_manager);
963 g_object_ref (ui_manager);
964 de->ui_manager = ui_manager;
966 g_object_notify (G_OBJECT (de), "ui-manager");