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 "ui/gui/helper.h"
28 #include "ui/gui/pspp-sheet-selection.h"
29 #include "ui/gui/psppire-data-sheet.h"
30 #include "ui/gui/psppire-data-store.h"
31 #include "ui/gui/psppire-value-entry.h"
32 #include "ui/gui/psppire-var-sheet.h"
33 #include "ui/gui/psppire.h"
36 #define _(msgid) gettext (msgid)
38 #define FOR_EACH_DATA_SHEET(DATA_SHEET, IDX, DATA_EDITOR) \
41 && ((DATA_SHEET) = PSPPIRE_DATA_SHEET ( \
42 (DATA_EDITOR)->data_sheets[IDX])) != NULL; \
45 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
46 static void psppire_data_editor_init (PsppireDataEditor *de);
48 static void disconnect_data_sheets (PsppireDataEditor *);
49 static void refresh_entry (PsppireDataEditor *);
50 static void psppire_data_editor_update_ui_manager (PsppireDataEditor *);
53 psppire_data_editor_get_type (void)
55 static GType de_type = 0;
59 static const GTypeInfo de_info =
61 sizeof (PsppireDataEditorClass),
63 NULL, /* base_finalize */
64 (GClassInitFunc) psppire_data_editor_class_init,
65 NULL, /* class_finalize */
66 NULL, /* class_data */
67 sizeof (PsppireDataEditor),
69 (GInstanceInitFunc) psppire_data_editor_init,
72 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
79 static GObjectClass * parent_class = NULL;
82 psppire_data_editor_dispose (GObject *obj)
84 PsppireDataEditor *de = (PsppireDataEditor *) obj;
86 disconnect_data_sheets (de);
90 g_object_unref (de->data_store);
91 de->data_store = NULL;
96 g_object_unref (de->var_store);
100 if (de->font != NULL)
102 pango_font_description_free (de->font);
108 g_object_unref (de->ui_manager);
109 de->ui_manager = NULL;
112 /* Chain up to the parent class */
113 G_OBJECT_CLASS (parent_class)->dispose (obj);
127 psppire_data_editor_refresh_model (PsppireDataEditor *de)
129 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet);
130 PsppireDataSheet *data_sheet;
133 FOR_EACH_DATA_SHEET (data_sheet, i, de)
134 psppire_data_sheet_set_data_store (data_sheet, de->data_store);
135 psppire_var_sheet_set_dictionary (var_sheet, de->var_store->dictionary);
139 psppire_data_editor_set_property (GObject *object,
144 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
145 PsppireDataSheet *data_sheet;
150 case PROP_SPLIT_WINDOW:
151 psppire_data_editor_split_window (de, g_value_get_boolean (value));
153 case PROP_DATA_STORE:
156 g_signal_handlers_disconnect_by_func (de->data_store,
157 G_CALLBACK (refresh_entry),
159 g_object_unref (de->data_store);
162 de->data_store = g_value_get_pointer (value);
163 g_object_ref (de->data_store);
164 psppire_data_editor_refresh_model (de);
166 g_signal_connect_swapped (de->data_store, "case-changed",
167 G_CALLBACK (refresh_entry), de);
171 if ( de->var_store) g_object_unref (de->var_store);
172 de->var_store = g_value_get_pointer (value);
173 g_object_ref (de->var_store);
175 psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet),
176 de->var_store->dictionary);
178 case PROP_VALUE_LABELS:
179 FOR_EACH_DATA_SHEET (data_sheet, i, de)
180 psppire_data_sheet_set_value_labels (data_sheet,
181 g_value_get_boolean (value));
183 case PROP_UI_MANAGER:
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191 psppire_data_editor_get_property (GObject *object,
196 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
200 case PROP_SPLIT_WINDOW:
201 g_value_set_boolean (value, de->split);
203 case PROP_DATA_STORE:
204 g_value_set_pointer (value, de->data_store);
207 g_value_set_pointer (value, de->var_store);
209 case PROP_VALUE_LABELS:
210 g_value_set_boolean (value,
211 psppire_data_sheet_get_value_labels (
212 PSPPIRE_DATA_SHEET (de->data_sheets[0])));
214 case PROP_UI_MANAGER:
215 g_value_set_object (value, psppire_data_editor_get_ui_manager (de));
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 psppire_data_editor_switch_page (GtkNotebook *notebook,
225 GtkNotebookPage *page,
228 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, page, page_num);
229 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
233 psppire_data_editor_set_focus_child (GtkContainer *container,
236 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
237 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
241 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
243 GParamSpec *data_store_spec ;
244 GParamSpec *var_store_spec ;
245 GParamSpec *value_labels_spec;
246 GParamSpec *split_window_spec;
247 GParamSpec *ui_manager_spec;
248 GObjectClass *object_class = G_OBJECT_CLASS (klass);
249 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
250 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
252 parent_class = g_type_class_peek_parent (klass);
254 object_class->dispose = psppire_data_editor_dispose;
255 object_class->set_property = psppire_data_editor_set_property;
256 object_class->get_property = psppire_data_editor_get_property;
258 container_class->set_focus_child = psppire_data_editor_set_focus_child;
260 notebook_class->switch_page = psppire_data_editor_switch_page;
263 g_param_spec_pointer ("data-store",
265 "A pointer to the data store associated with this editor",
266 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
268 g_object_class_install_property (object_class,
273 g_param_spec_pointer ("var-store",
275 "A pointer to the variable store associated with this editor",
276 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
278 g_object_class_install_property (object_class,
283 g_param_spec_boolean ("value-labels",
285 "Whether or not the data sheet should display labels instead of values",
287 G_PARAM_WRITABLE | G_PARAM_READABLE);
289 g_object_class_install_property (object_class,
295 g_param_spec_boolean ("split",
297 "True iff the data sheet is split",
299 G_PARAM_READABLE | G_PARAM_WRITABLE);
301 g_object_class_install_property (object_class,
306 g_param_spec_object ("ui-manager",
308 "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.",
311 g_object_class_install_property (object_class,
317 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
319 PsppireDataEditor *de)
321 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
322 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
324 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
331 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
332 PsppireDataEditor *de)
334 PsppireDataSheet *data_sheet;
336 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
337 PSPPIRE_DATA_EDITOR_DATA_VIEW);
339 data_sheet = psppire_data_editor_get_active_data_sheet (de);
340 psppire_data_sheet_show_variable (data_sheet, dict_index);
345 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
346 active cell or cells. */
348 refresh_entry (PsppireDataEditor *de)
350 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
351 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
352 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
354 gchar *ref_cell_text;
355 GList *selected_columns, *iter;
356 struct variable *var;
360 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
363 for (iter = selected_columns; iter != NULL; iter = iter->next)
365 PsppSheetViewColumn *column = iter->data;
366 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
373 g_list_free (selected_columns);
375 n_cases = pspp_sheet_selection_count_selected_rows (selection);
378 /* The final row is selectable but it isn't a case (it's just used to add
379 more cases), so don't count it. */
383 case_count = psppire_data_store_get_case_count (de->data_store);
384 path = gtk_tree_path_new_from_indices (case_count, -1);
385 if (pspp_sheet_selection_path_is_selected (selection, path))
387 gtk_tree_path_free (path);
390 ref_cell_text = NULL;
391 if (n_cases == 1 && n_vars == 1)
393 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
394 struct range_set *selected_rows;
395 gboolean show_value_labels;
400 selected_rows = pspp_sheet_selection_get_range_set (selection);
401 row = range_set_scan (selected_rows, 0);
402 range_set_destroy (selected_rows);
404 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
406 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
408 psppire_value_entry_set_variable (value_entry, var);
409 psppire_value_entry_set_show_value_label (value_entry,
412 width = var_get_width (var);
413 value_init (&value, width);
414 datasheet_get_value (de->data_store->datasheet,
415 row, var_get_case_index (var), &value);
416 psppire_value_entry_set_value (value_entry, &value, width);
417 value_destroy (&value, width);
419 gtk_widget_set_sensitive (de->datum_entry, TRUE);
423 if (n_cases == 0 || n_vars == 0)
425 ref_cell_text = NULL;
431 string = g_string_sized_new (25);
432 g_string_append_printf (string,
433 ngettext ("%'d case", "%'d cases", n_cases),
435 g_string_append_c (string, ' ');
436 g_string_append_unichar (string, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
437 g_string_append_c (string, ' ');
438 g_string_append_printf (string,
439 ngettext ("%'d variable", "%'d variables",
442 ref_cell_text = string->str;
443 g_string_free (string, FALSE);
446 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
449 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
450 gtk_widget_set_sensitive (de->datum_entry, FALSE);
453 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
454 ref_cell_text ? ref_cell_text : "");
455 g_free (ref_cell_text);
459 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
461 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
462 struct variable *var;
467 row = psppire_data_sheet_get_current_case (data_sheet);
468 var = psppire_data_sheet_get_current_variable (data_sheet);
472 width = var_get_width (var);
473 value_init (&value, width);
474 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
476 psppire_data_store_set_value (de->data_store, row, var, &value);
477 value_destroy (&value, width);
481 on_data_sheet_selection_changed (PsppSheetSelection *selection,
482 PsppireDataEditor *de)
484 /* In a split view, ensure that only a single data sheet has a nonempty
487 && pspp_sheet_selection_count_selected_rows (selection)
488 && pspp_sheet_selection_count_selected_columns (selection))
490 PsppireDataSheet *ds;
493 FOR_EACH_DATA_SHEET (ds, i, de)
495 PsppSheetSelection *s;
497 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
499 pspp_sheet_selection_unselect_all (s);
506 /* Ensures that rows in the right-hand panes in the split view have the same
507 row height as the left-hand panes. Otherwise, the rows in the right-hand
508 pane tend to be smaller, because the right-hand pane doesn't have buttons
511 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
513 PsppireDataEditor *de)
517 TL = GTK_XPANED_TOP_LEFT,
518 TR = GTK_XPANED_TOP_RIGHT,
519 BL = GTK_XPANED_BOTTOM_LEFT,
520 BR = GTK_XPANED_BOTTOM_RIGHT
523 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
525 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
527 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
532 disconnect_data_sheets (PsppireDataEditor *de)
534 PsppireDataSheet *ds;
537 FOR_EACH_DATA_SHEET (ds, i, de)
539 PsppSheetSelection *selection;
543 /* This can only happen if 'dispose' runs more than once. */
547 if (i == GTK_XPANED_TOP_LEFT)
548 g_signal_handlers_disconnect_by_func (
549 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
551 g_signal_handlers_disconnect_by_func (
552 ds, G_CALLBACK (refresh_entry), de);
553 g_signal_handlers_disconnect_by_func (
554 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
556 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
557 g_signal_handlers_disconnect_by_func (
558 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
560 de->data_sheets[i] = NULL;
565 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
567 PsppSheetSelection *selection;
570 ds = psppire_data_sheet_new ();
571 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
573 g_signal_connect_swapped (ds, "notify::value-labels",
574 G_CALLBACK (refresh_entry), de);
575 g_signal_connect (ds, "var-double-clicked",
576 G_CALLBACK (on_data_sheet_var_double_clicked), de);
578 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
579 g_signal_connect (selection, "changed",
580 G_CALLBACK (on_data_sheet_selection_changed), de);
586 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
588 GtkWidget *data_sheet_scroller;
590 de->data_sheets[0] = make_data_sheet (de, grid_lines);
591 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
593 /* Put data sheet in scroller. */
594 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
595 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
596 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
597 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
599 return data_sheet_scroller;
603 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
605 /* Panes, in the order in which we want to create them. */
610 BL, /* bottom left */
611 BR /* bottom right */
614 PsppSheetView *ds[4];
618 xpaned = GTK_XPANED (gtk_xpaned_new ());
620 for (i = 0; i < 4; i++)
622 GtkAdjustment *hadjust, *vadjust;
623 GtkPolicyType hpolicy, vpolicy;
626 de->data_sheets[i] = make_data_sheet (de, grid_lines);
627 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
630 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
632 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
637 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
639 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
643 scroller = gtk_scrolled_window_new (hadjust, vadjust);
644 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
645 GTK_SHADOW_ETCHED_IN);
646 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
647 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
648 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
650 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
655 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
659 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
663 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
667 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
671 g_warn_if_reached ();
675 /* Bottom sheets don't display variable names. */
676 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
677 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
679 /* Right sheets don't display case numbers. */
680 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
681 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
683 g_signal_connect (ds[TL], "notify::fixed-height",
684 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
686 return GTK_WIDGET (xpaned);
690 psppire_data_editor_init (PsppireDataEditor *de)
692 GtkWidget *var_sheet_scroller;
696 de->ui_manager = NULL;
698 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
700 de->cell_ref_label = gtk_label_new ("");
701 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
702 gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
704 de->datum_entry = psppire_value_entry_new ();
705 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
706 "activate", G_CALLBACK (on_datum_entry_activate), de);
708 hbox = gtk_hbox_new (FALSE, 0);
709 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
710 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
713 de->datasheet_vbox_widget
714 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH);
716 de->vbox = gtk_vbox_new (FALSE, 0);
717 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
718 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
721 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
722 gtk_label_new_with_mnemonic (_("Data View")));
724 gtk_widget_show_all (de->vbox);
726 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
727 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
728 GTK_TREE_VIEW_GRID_LINES_BOTH);
729 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
730 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
731 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
732 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
733 gtk_widget_show_all (var_sheet_scroller);
734 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
735 gtk_label_new_with_mnemonic (_("Variable View")));
737 g_signal_connect (de->var_sheet, "var-double-clicked",
738 G_CALLBACK (on_var_sheet_var_double_clicked), de);
740 g_object_set (de, "can-focus", FALSE, NULL);
742 psppire_data_editor_update_ui_manager (de);
746 psppire_data_editor_new (PsppireVarStore *var_store,
747 PsppireDataStore *data_store)
749 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
750 "var-store", var_store,
751 "data-store", data_store,
755 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
756 sheet(s) and variable sheet. */
758 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
760 GtkTreeViewGridLines grid;
761 PsppireDataSheet *data_sheet;
765 ? GTK_TREE_VIEW_GRID_LINES_BOTH
766 : GTK_TREE_VIEW_GRID_LINES_NONE);
768 FOR_EACH_DATA_SHEET (data_sheet, i, de)
769 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
770 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
775 set_font_recursively (GtkWidget *w, gpointer data)
777 PangoFontDescription *font_desc = data;
778 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
780 pango_font_description_free (style->font_desc);
781 style->font_desc = pango_font_description_copy (font_desc);
783 gtk_widget_modify_style (w, style);
785 if ( GTK_IS_CONTAINER (w))
786 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
789 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
791 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
793 set_font_recursively (GTK_WIDGET (de), font_desc);
796 pango_font_description_free (de->font);
797 de->font = pango_font_description_copy (font_desc);
800 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
801 If SPLIT is FALSE, un-splits it into a single pane. */
803 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
805 GtkTreeViewGridLines grid_lines;
807 if (split == de->split)
811 grid_lines = pspp_sheet_view_get_grid_lines (
812 PSPP_SHEET_VIEW (de->data_sheets[0]));
814 disconnect_data_sheets (de);
815 gtk_widget_destroy (de->datasheet_vbox_widget);
818 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines);
820 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines);
821 psppire_data_editor_refresh_model (de);
823 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
825 gtk_widget_show_all (de->vbox);
828 set_font_recursively (GTK_WIDGET (de), de->font);
831 g_object_notify (G_OBJECT (de), "split");
832 psppire_data_editor_update_ui_manager (de);
835 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
836 visible and selected in the active view in DE. */
838 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
840 PsppireDataSheet *data_sheet;
842 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
844 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
845 data_sheet = psppire_data_editor_get_active_data_sheet (de);
846 psppire_data_sheet_show_variable (data_sheet, dict_index);
849 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
850 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
856 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
857 is the only data sheet. If DE is in split mode (showing four data sheets),
858 this is the focused data sheet or, if none is focused, the data sheet with
859 selected cells or, if none has selected cells, the upper-left data sheet. */
861 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
865 PsppireDataSheet *data_sheet;
869 /* If one of the datasheet's scrollers is focused, choose that one. */
870 scroller = gtk_container_get_focus_child (
871 GTK_CONTAINER (de->datasheet_vbox_widget));
872 if (scroller != NULL)
873 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
875 /* Otherwise if there's a nonempty selection in some data sheet, choose
877 FOR_EACH_DATA_SHEET (data_sheet, i, de)
879 PsppSheetSelection *selection;
881 selection = pspp_sheet_view_get_selection (
882 PSPP_SHEET_VIEW (data_sheet));
883 if (pspp_sheet_selection_count_selected_rows (selection)
884 && pspp_sheet_selection_count_selected_columns (selection))
889 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
892 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
893 manager to display menu items and toolbar items specific to DE's current
896 DE's toplevel widget can watch for changes by connecting to DE's
897 notify::ui-manager signal. */
899 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
901 psppire_data_editor_update_ui_manager (de);
902 return de->ui_manager;
906 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
908 PsppireDataSheet *data_sheet;
909 GtkUIManager *ui_manager;
913 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
915 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
916 data_sheet = psppire_data_editor_get_active_data_sheet (de);
917 if (data_sheet != NULL)
918 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
921 /* This happens transiently in psppire_data_editor_split_window(). */
925 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
926 ui_manager = psppire_var_sheet_get_ui_manager (
927 PSPPIRE_VAR_SHEET (de->var_sheet));
931 /* This happens transiently in psppire_data_editor_init(). */
935 if (ui_manager != de->ui_manager)
938 g_object_unref (de->ui_manager);
940 g_object_ref (ui_manager);
941 de->ui_manager = ui_manager;
943 g_object_notify (G_OBJECT (de), "ui-manager");