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,
230 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, 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_goto_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,
568 gboolean show_value_labels)
570 PsppSheetSelection *selection;
573 ds = psppire_data_sheet_new ();
574 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
575 psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
578 g_signal_connect_swapped (ds, "notify::value-labels",
579 G_CALLBACK (refresh_entry), de);
580 g_signal_connect (ds, "var-double-clicked",
581 G_CALLBACK (on_data_sheet_var_double_clicked), de);
583 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
584 g_signal_connect (selection, "changed",
585 G_CALLBACK (on_data_sheet_selection_changed), de);
591 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
592 gboolean show_value_labels)
594 GtkWidget *data_sheet_scroller;
596 de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
597 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
599 /* Put data sheet in scroller. */
600 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
601 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
602 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
603 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
605 return data_sheet_scroller;
609 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
610 gboolean show_value_labels)
612 /* Panes, in the order in which we want to create them. */
617 BL, /* bottom left */
618 BR /* bottom right */
621 PsppSheetView *ds[4];
625 xpaned = GTK_XPANED (gtk_xpaned_new ());
627 for (i = 0; i < 4; i++)
629 GtkAdjustment *hadjust, *vadjust;
630 GtkPolicyType hpolicy, vpolicy;
633 de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
634 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
637 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
639 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
644 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
646 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
650 scroller = gtk_scrolled_window_new (hadjust, vadjust);
651 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
652 GTK_SHADOW_ETCHED_IN);
653 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
654 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
655 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
657 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
662 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
666 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
670 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
674 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
678 g_warn_if_reached ();
682 /* Bottom sheets don't display variable names. */
683 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
684 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
686 /* Right sheets don't display case numbers. */
687 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
688 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
690 g_signal_connect (ds[TL], "notify::fixed-height",
691 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
693 return GTK_WIDGET (xpaned);
697 psppire_data_editor_init (PsppireDataEditor *de)
699 GtkWidget *var_sheet_scroller;
703 de->ui_manager = NULL;
704 de->old_vbox_widget = NULL;
706 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
708 de->cell_ref_label = gtk_label_new ("");
709 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
710 gtk_misc_set_alignment (GTK_MISC (de->cell_ref_label), 0.0, 0.5);
712 de->datum_entry = psppire_value_entry_new ();
713 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
714 "activate", G_CALLBACK (on_datum_entry_activate), de);
716 hbox = gtk_hbox_new (FALSE, 0);
717 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
718 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
721 de->datasheet_vbox_widget
722 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE);
724 de->vbox = gtk_vbox_new (FALSE, 0);
725 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
726 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
729 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
730 gtk_label_new_with_mnemonic (_("Data View")));
732 gtk_widget_show_all (de->vbox);
734 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
735 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
736 GTK_TREE_VIEW_GRID_LINES_BOTH);
737 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
738 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
739 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
740 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
741 gtk_widget_show_all (var_sheet_scroller);
742 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
743 gtk_label_new_with_mnemonic (_("Variable View")));
745 g_signal_connect (de->var_sheet, "var-double-clicked",
746 G_CALLBACK (on_var_sheet_var_double_clicked), de);
748 g_object_set (de, "can-focus", FALSE, NULL);
750 psppire_data_editor_update_ui_manager (de);
754 psppire_data_editor_new (PsppireDict *dict,
755 PsppireDataStore *data_store)
757 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
759 "data-store", data_store,
763 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
764 sheet(s) and variable sheet. */
766 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
768 GtkTreeViewGridLines grid;
769 PsppireDataSheet *data_sheet;
773 ? GTK_TREE_VIEW_GRID_LINES_BOTH
774 : GTK_TREE_VIEW_GRID_LINES_NONE);
776 FOR_EACH_DATA_SHEET (data_sheet, i, de)
777 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
778 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
783 set_font_recursively (GtkWidget *w, gpointer data)
785 PangoFontDescription *font_desc = data;
786 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
788 pango_font_description_free (style->font_desc);
789 style->font_desc = pango_font_description_copy (font_desc);
791 gtk_widget_modify_style (w, style);
793 if ( GTK_IS_CONTAINER (w))
794 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
797 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
799 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
801 set_font_recursively (GTK_WIDGET (de), font_desc);
804 pango_font_description_free (de->font);
805 de->font = pango_font_description_copy (font_desc);
808 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
809 If SPLIT is FALSE, un-splits it into a single pane. */
811 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
813 GtkTreeViewGridLines grid_lines;
816 if (split == de->split)
820 grid_lines = pspp_sheet_view_get_grid_lines (
821 PSPP_SHEET_VIEW (de->data_sheets[0]));
822 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
823 de->data_sheets[0]));
825 disconnect_data_sheets (de);
826 if (de->old_vbox_widget)
827 g_object_unref (de->old_vbox_widget);
828 de->old_vbox_widget = de->datasheet_vbox_widget;
829 g_object_ref (de->old_vbox_widget);
830 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
831 (currently it seems to provoke an error if I do that.
833 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
836 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
838 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
840 psppire_data_editor_refresh_model (de);
842 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
844 gtk_widget_show_all (de->vbox);
847 set_font_recursively (GTK_WIDGET (de), de->font);
850 g_object_notify (G_OBJECT (de), "split");
851 psppire_data_editor_update_ui_manager (de);
854 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
855 visible and selected in the active view in DE. */
857 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
859 PsppireDataSheet *data_sheet;
861 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
863 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
864 data_sheet = psppire_data_editor_get_active_data_sheet (de);
865 psppire_data_sheet_goto_variable (data_sheet, dict_index);
868 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
869 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
875 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
876 is the only data sheet. If DE is in split mode (showing four data sheets),
877 this is the focused data sheet or, if none is focused, the data sheet with
878 selected cells or, if none has selected cells, the upper-left data sheet. */
880 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
884 PsppireDataSheet *data_sheet;
888 /* If one of the datasheet's scrollers is focused, choose that one. */
889 scroller = gtk_container_get_focus_child (
890 GTK_CONTAINER (de->datasheet_vbox_widget));
891 if (scroller != NULL)
892 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
894 /* Otherwise if there's a nonempty selection in some data sheet, choose
896 FOR_EACH_DATA_SHEET (data_sheet, i, de)
898 PsppSheetSelection *selection;
900 selection = pspp_sheet_view_get_selection (
901 PSPP_SHEET_VIEW (data_sheet));
902 if (pspp_sheet_selection_count_selected_rows (selection)
903 && pspp_sheet_selection_count_selected_columns (selection))
908 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
911 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
912 manager to display menu items and toolbar items specific to DE's current
915 DE's toplevel widget can watch for changes by connecting to DE's
916 notify::ui-manager signal. */
918 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
920 psppire_data_editor_update_ui_manager (de);
921 return de->ui_manager;
925 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
927 PsppireDataSheet *data_sheet;
928 GtkUIManager *ui_manager;
932 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
934 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
935 data_sheet = psppire_data_editor_get_active_data_sheet (de);
936 if (data_sheet != NULL)
937 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
940 /* This happens transiently in psppire_data_editor_split_window(). */
944 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
945 ui_manager = psppire_var_sheet_get_ui_manager (
946 PSPPIRE_VAR_SHEET (de->var_sheet));
950 /* This happens transiently in psppire_data_editor_init(). */
954 if (ui_manager != de->ui_manager)
957 g_object_unref (de->ui_manager);
959 g_object_ref (ui_manager);
960 de->ui_manager = ui_manager;
962 g_object_notify (G_OBJECT (de), "ui-manager");