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 GtkNotebookPage *page,
230 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, page, page_num);
231 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
235 psppire_data_editor_set_focus_child (GtkContainer *container,
238 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
239 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
243 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
245 GParamSpec *data_store_spec ;
246 GParamSpec *dict_spec ;
247 GParamSpec *value_labels_spec;
248 GParamSpec *split_window_spec;
249 GParamSpec *ui_manager_spec;
250 GObjectClass *object_class = G_OBJECT_CLASS (klass);
251 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
252 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
254 parent_class = g_type_class_peek_parent (klass);
256 object_class->dispose = psppire_data_editor_dispose;
257 object_class->set_property = psppire_data_editor_set_property;
258 object_class->get_property = psppire_data_editor_get_property;
260 container_class->set_focus_child = psppire_data_editor_set_focus_child;
262 notebook_class->switch_page = psppire_data_editor_switch_page;
265 g_param_spec_pointer ("data-store",
267 "A pointer to the data store associated with this editor",
268 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
270 g_object_class_install_property (object_class,
275 g_param_spec_pointer ("dictionary",
277 "A pointer to the dictionary associated with this editor",
278 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
280 g_object_class_install_property (object_class,
285 g_param_spec_boolean ("value-labels",
287 "Whether or not the data sheet should display labels instead of values",
289 G_PARAM_WRITABLE | G_PARAM_READABLE);
291 g_object_class_install_property (object_class,
297 g_param_spec_boolean ("split",
299 "True iff the data sheet is split",
301 G_PARAM_READABLE | G_PARAM_WRITABLE);
303 g_object_class_install_property (object_class,
308 g_param_spec_object ("ui-manager",
310 "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.",
313 g_object_class_install_property (object_class,
319 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
321 PsppireDataEditor *de)
323 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
324 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
326 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
333 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
334 PsppireDataEditor *de)
336 PsppireDataSheet *data_sheet;
338 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
339 PSPPIRE_DATA_EDITOR_DATA_VIEW);
341 data_sheet = psppire_data_editor_get_active_data_sheet (de);
342 psppire_data_sheet_show_variable (data_sheet, dict_index);
347 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
348 active cell or cells. */
350 refresh_entry (PsppireDataEditor *de)
352 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
353 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
354 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
356 gchar *ref_cell_text;
357 GList *selected_columns, *iter;
358 struct variable *var;
362 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
365 for (iter = selected_columns; iter != NULL; iter = iter->next)
367 PsppSheetViewColumn *column = iter->data;
368 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
375 g_list_free (selected_columns);
377 n_cases = pspp_sheet_selection_count_selected_rows (selection);
380 /* The final row is selectable but it isn't a case (it's just used to add
381 more cases), so don't count it. */
385 case_count = psppire_data_store_get_case_count (de->data_store);
386 path = gtk_tree_path_new_from_indices (case_count, -1);
387 if (pspp_sheet_selection_path_is_selected (selection, path))
389 gtk_tree_path_free (path);
392 ref_cell_text = NULL;
393 if (n_cases == 1 && n_vars == 1)
395 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
396 struct range_set *selected_rows;
397 gboolean show_value_labels;
402 selected_rows = pspp_sheet_selection_get_range_set (selection);
403 row = range_set_scan (selected_rows, 0);
404 range_set_destroy (selected_rows);
406 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
408 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
410 psppire_value_entry_set_variable (value_entry, var);
411 psppire_value_entry_set_show_value_label (value_entry,
414 width = var_get_width (var);
415 value_init (&value, width);
416 datasheet_get_value (de->data_store->datasheet,
417 row, var_get_case_index (var), &value);
418 psppire_value_entry_set_value (value_entry, &value, width);
419 value_destroy (&value, width);
421 gtk_widget_set_sensitive (de->datum_entry, TRUE);
425 if (n_cases == 0 || n_vars == 0)
427 ref_cell_text = NULL;
433 /* The glib string library does not understand the ' printf modifier
434 on all platforms, but the "struct string" library does (because
435 Gnulib fixes that problem), so use the latter. */
437 ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
439 ds_put_byte (&s, ' ');
440 ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
441 ds_put_byte (&s, ' ');
442 ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
445 ref_cell_text = ds_steal_cstr (&s);
448 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
451 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
452 gtk_widget_set_sensitive (de->datum_entry, FALSE);
455 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
456 ref_cell_text ? ref_cell_text : "");
457 g_free (ref_cell_text);
461 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
463 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
464 struct variable *var;
469 row = psppire_data_sheet_get_current_case (data_sheet);
470 var = psppire_data_sheet_get_current_variable (data_sheet);
474 width = var_get_width (var);
475 value_init (&value, width);
476 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
478 psppire_data_store_set_value (de->data_store, row, var, &value);
479 value_destroy (&value, width);
483 on_data_sheet_selection_changed (PsppSheetSelection *selection,
484 PsppireDataEditor *de)
486 /* In a split view, ensure that only a single data sheet has a nonempty
489 && pspp_sheet_selection_count_selected_rows (selection)
490 && pspp_sheet_selection_count_selected_columns (selection))
492 PsppireDataSheet *ds;
495 FOR_EACH_DATA_SHEET (ds, i, de)
497 PsppSheetSelection *s;
499 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
501 pspp_sheet_selection_unselect_all (s);
508 /* Ensures that rows in the right-hand panes in the split view have the same
509 row height as the left-hand panes. Otherwise, the rows in the right-hand
510 pane tend to be smaller, because the right-hand pane doesn't have buttons
513 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
515 PsppireDataEditor *de)
519 TL = GTK_XPANED_TOP_LEFT,
520 TR = GTK_XPANED_TOP_RIGHT,
521 BL = GTK_XPANED_BOTTOM_LEFT,
522 BR = GTK_XPANED_BOTTOM_RIGHT
525 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
527 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
529 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
534 disconnect_data_sheets (PsppireDataEditor *de)
536 PsppireDataSheet *ds;
539 FOR_EACH_DATA_SHEET (ds, i, de)
541 PsppSheetSelection *selection;
545 /* This can only happen if 'dispose' runs more than once. */
549 if (i == GTK_XPANED_TOP_LEFT)
550 g_signal_handlers_disconnect_by_func (
551 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
553 g_signal_handlers_disconnect_by_func (
554 ds, G_CALLBACK (refresh_entry), de);
555 g_signal_handlers_disconnect_by_func (
556 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
558 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
559 g_signal_handlers_disconnect_by_func (
560 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
562 de->data_sheets[i] = NULL;
567 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
569 PsppSheetSelection *selection;
572 ds = psppire_data_sheet_new ();
573 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
575 g_signal_connect_swapped (ds, "notify::value-labels",
576 G_CALLBACK (refresh_entry), de);
577 g_signal_connect (ds, "var-double-clicked",
578 G_CALLBACK (on_data_sheet_var_double_clicked), de);
580 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
581 g_signal_connect (selection, "changed",
582 G_CALLBACK (on_data_sheet_selection_changed), de);
588 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
590 GtkWidget *data_sheet_scroller;
592 de->data_sheets[0] = make_data_sheet (de, grid_lines);
593 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
595 /* Put data sheet in scroller. */
596 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
597 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
598 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
599 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
601 return data_sheet_scroller;
605 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines)
607 /* Panes, in the order in which we want to create them. */
612 BL, /* bottom left */
613 BR /* bottom right */
616 PsppSheetView *ds[4];
620 xpaned = GTK_XPANED (gtk_xpaned_new ());
622 for (i = 0; i < 4; i++)
624 GtkAdjustment *hadjust, *vadjust;
625 GtkPolicyType hpolicy, vpolicy;
628 de->data_sheets[i] = make_data_sheet (de, grid_lines);
629 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
632 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
634 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
639 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
641 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
645 scroller = gtk_scrolled_window_new (hadjust, vadjust);
646 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
647 GTK_SHADOW_ETCHED_IN);
648 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
649 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
650 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
652 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
657 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
661 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
665 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
669 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
673 g_warn_if_reached ();
677 /* Bottom sheets don't display variable names. */
678 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
679 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
681 /* Right sheets don't display case numbers. */
682 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
683 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
685 g_signal_connect (ds[TL], "notify::fixed-height",
686 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
688 return GTK_WIDGET (xpaned);
692 psppire_data_editor_init (PsppireDataEditor *de)
694 GtkWidget *var_sheet_scroller;
698 de->ui_manager = NULL;
699 de->old_vbox_widget = NULL;
701 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
703 de->cell_ref_label = gtk_label_new ("");
704 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
705 gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
707 de->datum_entry = psppire_value_entry_new ();
708 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
709 "activate", G_CALLBACK (on_datum_entry_activate), de);
711 hbox = gtk_hbox_new (FALSE, 0);
712 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
713 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
716 de->datasheet_vbox_widget
717 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH);
719 de->vbox = gtk_vbox_new (FALSE, 0);
720 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
721 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
724 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
725 gtk_label_new_with_mnemonic (_("Data View")));
727 gtk_widget_show_all (de->vbox);
729 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
730 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
731 GTK_TREE_VIEW_GRID_LINES_BOTH);
732 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
733 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
734 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
735 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
736 gtk_widget_show_all (var_sheet_scroller);
737 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
738 gtk_label_new_with_mnemonic (_("Variable View")));
740 g_signal_connect (de->var_sheet, "var-double-clicked",
741 G_CALLBACK (on_var_sheet_var_double_clicked), de);
743 g_object_set (de, "can-focus", FALSE, NULL);
745 psppire_data_editor_update_ui_manager (de);
749 psppire_data_editor_new (PsppireDict *dict,
750 PsppireDataStore *data_store)
752 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
754 "data-store", data_store,
758 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
759 sheet(s) and variable sheet. */
761 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
763 GtkTreeViewGridLines grid;
764 PsppireDataSheet *data_sheet;
768 ? GTK_TREE_VIEW_GRID_LINES_BOTH
769 : GTK_TREE_VIEW_GRID_LINES_NONE);
771 FOR_EACH_DATA_SHEET (data_sheet, i, de)
772 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
773 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
778 set_font_recursively (GtkWidget *w, gpointer data)
780 PangoFontDescription *font_desc = data;
781 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
783 pango_font_description_free (style->font_desc);
784 style->font_desc = pango_font_description_copy (font_desc);
786 gtk_widget_modify_style (w, style);
788 if ( GTK_IS_CONTAINER (w))
789 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
792 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
794 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
796 set_font_recursively (GTK_WIDGET (de), font_desc);
799 pango_font_description_free (de->font);
800 de->font = pango_font_description_copy (font_desc);
803 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
804 If SPLIT is FALSE, un-splits it into a single pane. */
806 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
808 GtkTreeViewGridLines grid_lines;
810 if (split == de->split)
814 grid_lines = pspp_sheet_view_get_grid_lines (
815 PSPP_SHEET_VIEW (de->data_sheets[0]));
817 disconnect_data_sheets (de);
818 if (de->old_vbox_widget)
819 g_object_unref (de->old_vbox_widget);
820 de->old_vbox_widget = de->datasheet_vbox_widget;
821 g_object_ref (de->old_vbox_widget);
822 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
823 (currently it seems to provoke an error if I do that.
825 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
828 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines);
830 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines);
832 psppire_data_editor_refresh_model (de);
834 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
836 gtk_widget_show_all (de->vbox);
839 set_font_recursively (GTK_WIDGET (de), de->font);
842 g_object_notify (G_OBJECT (de), "split");
843 psppire_data_editor_update_ui_manager (de);
846 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
847 visible and selected in the active view in DE. */
849 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
851 PsppireDataSheet *data_sheet;
853 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
855 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
856 data_sheet = psppire_data_editor_get_active_data_sheet (de);
857 psppire_data_sheet_show_variable (data_sheet, dict_index);
860 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
861 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
867 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
868 is the only data sheet. If DE is in split mode (showing four data sheets),
869 this is the focused data sheet or, if none is focused, the data sheet with
870 selected cells or, if none has selected cells, the upper-left data sheet. */
872 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
876 PsppireDataSheet *data_sheet;
880 /* If one of the datasheet's scrollers is focused, choose that one. */
881 scroller = gtk_container_get_focus_child (
882 GTK_CONTAINER (de->datasheet_vbox_widget));
883 if (scroller != NULL)
884 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
886 /* Otherwise if there's a nonempty selection in some data sheet, choose
888 FOR_EACH_DATA_SHEET (data_sheet, i, de)
890 PsppSheetSelection *selection;
892 selection = pspp_sheet_view_get_selection (
893 PSPP_SHEET_VIEW (data_sheet));
894 if (pspp_sheet_selection_count_selected_rows (selection)
895 && pspp_sheet_selection_count_selected_columns (selection))
900 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
903 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
904 manager to display menu items and toolbar items specific to DE's current
907 DE's toplevel widget can watch for changes by connecting to DE's
908 notify::ui-manager signal. */
910 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
912 psppire_data_editor_update_ui_manager (de);
913 return de->ui_manager;
917 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
919 PsppireDataSheet *data_sheet;
920 GtkUIManager *ui_manager;
924 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
926 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
927 data_sheet = psppire_data_editor_get_active_data_sheet (de);
928 if (data_sheet != NULL)
929 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
932 /* This happens transiently in psppire_data_editor_split_window(). */
936 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
937 ui_manager = psppire_var_sheet_get_ui_manager (
938 PSPPIRE_VAR_SHEET (de->var_sheet));
942 /* This happens transiently in psppire_data_editor_init(). */
946 if (ui_manager != de->ui_manager)
949 g_object_unref (de->ui_manager);
951 g_object_ref (ui_manager);
952 de->ui_manager = ui_manager;
954 g_object_notify (G_OBJECT (de), "ui-manager");