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-conf.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);
696 static void set_font_recursively (GtkWidget *w, gpointer data);
699 psppire_data_editor_init (PsppireDataEditor *de)
701 GtkWidget *var_sheet_scroller;
703 gchar *fontname = NULL;
706 de->ui_manager = NULL;
707 de->old_vbox_widget = NULL;
709 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
711 de->cell_ref_label = gtk_label_new ("");
712 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
713 gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER);
715 de->datum_entry = psppire_value_entry_new ();
716 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
717 "activate", G_CALLBACK (on_datum_entry_activate), de);
719 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
720 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
721 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
724 de->datasheet_vbox_widget
725 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE);
727 de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
728 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
729 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
732 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
733 gtk_label_new_with_mnemonic (_("Data View")));
735 gtk_widget_show_all (de->vbox);
737 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
738 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
739 GTK_TREE_VIEW_GRID_LINES_BOTH);
740 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
741 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
742 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
743 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
744 gtk_widget_show_all (var_sheet_scroller);
745 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
746 gtk_label_new_with_mnemonic (_("Variable View")));
748 g_signal_connect (de->var_sheet, "var-double-clicked",
749 G_CALLBACK (on_var_sheet_var_double_clicked), de);
751 g_object_set (de, "can-focus", FALSE, NULL);
753 if (psppire_conf_get_string (psppire_conf_new (),
754 "Data Editor", "font",
757 de->font = pango_font_description_from_string (fontname);
759 set_font_recursively (GTK_WIDGET (de), de->font);
762 psppire_data_editor_update_ui_manager (de);
766 psppire_data_editor_new (PsppireDict *dict,
767 PsppireDataStore *data_store)
769 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
771 "data-store", data_store,
775 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
776 sheet(s) and variable sheet. */
778 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
780 GtkTreeViewGridLines grid;
781 PsppireDataSheet *data_sheet;
785 ? GTK_TREE_VIEW_GRID_LINES_BOTH
786 : GTK_TREE_VIEW_GRID_LINES_NONE);
788 FOR_EACH_DATA_SHEET (data_sheet, i, de)
789 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
790 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
795 set_font_recursively (GtkWidget *w, gpointer data)
797 PangoFontDescription *font_desc = data;
799 gtk_widget_override_font (w, font_desc);
801 if ( GTK_IS_CONTAINER (w))
802 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
805 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
807 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
810 set_font_recursively (GTK_WIDGET (de), font_desc);
813 pango_font_description_free (de->font);
814 de->font = pango_font_description_copy (font_desc);
815 font_name = pango_font_description_to_string (de->font);
817 psppire_conf_set_string (psppire_conf_new (),
818 "Data Editor", "font",
823 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
824 If SPLIT is FALSE, un-splits it into a single pane. */
826 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
828 GtkTreeViewGridLines grid_lines;
831 if (split == de->split)
835 grid_lines = pspp_sheet_view_get_grid_lines (
836 PSPP_SHEET_VIEW (de->data_sheets[0]));
837 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
838 de->data_sheets[0]));
840 disconnect_data_sheets (de);
841 if (de->old_vbox_widget)
842 g_object_unref (de->old_vbox_widget);
843 de->old_vbox_widget = de->datasheet_vbox_widget;
844 g_object_ref (de->old_vbox_widget);
845 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
846 (currently it seems to provoke an error if I do that.
848 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
851 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
853 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
855 psppire_data_editor_refresh_model (de);
857 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
859 gtk_widget_show_all (de->vbox);
862 set_font_recursively (GTK_WIDGET (de), de->font);
865 g_object_notify (G_OBJECT (de), "split");
866 psppire_data_editor_update_ui_manager (de);
869 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
870 visible and selected in the active view in DE. */
872 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
874 PsppireDataSheet *data_sheet;
876 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
878 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
879 data_sheet = psppire_data_editor_get_active_data_sheet (de);
880 psppire_data_sheet_goto_variable (data_sheet, dict_index);
883 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
884 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
890 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
891 is the only data sheet. If DE is in split mode (showing four data sheets),
892 this is the focused data sheet or, if none is focused, the data sheet with
893 selected cells or, if none has selected cells, the upper-left data sheet. */
895 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
899 PsppireDataSheet *data_sheet;
903 /* If one of the datasheet's scrollers is focused, choose that one. */
904 scroller = gtk_container_get_focus_child (
905 GTK_CONTAINER (de->datasheet_vbox_widget));
906 if (scroller != NULL)
907 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
909 /* Otherwise if there's a nonempty selection in some data sheet, choose
911 FOR_EACH_DATA_SHEET (data_sheet, i, de)
913 PsppSheetSelection *selection;
915 selection = pspp_sheet_view_get_selection (
916 PSPP_SHEET_VIEW (data_sheet));
917 if (pspp_sheet_selection_count_selected_rows (selection)
918 && pspp_sheet_selection_count_selected_columns (selection))
923 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);
926 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
927 manager to display menu items and toolbar items specific to DE's current
930 DE's toplevel widget can watch for changes by connecting to DE's
931 notify::ui-manager signal. */
933 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
935 psppire_data_editor_update_ui_manager (de);
936 return de->ui_manager;
940 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
942 PsppireDataSheet *data_sheet;
943 GtkUIManager *ui_manager;
947 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
949 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
950 data_sheet = psppire_data_editor_get_active_data_sheet (de);
951 if (data_sheet != NULL)
952 ui_manager = psppire_data_sheet_get_ui_manager (data_sheet);
955 /* This happens transiently in psppire_data_editor_split_window(). */
959 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
960 ui_manager = psppire_var_sheet_get_ui_manager (
961 PSPPIRE_VAR_SHEET (de->var_sheet));
965 /* This happens transiently in psppire_data_editor_init(). */
969 if (ui_manager != de->ui_manager)
972 g_object_unref (de->ui_manager);
974 g_object_ref (ui_manager);
975 de->ui_manager = ui_manager;
977 g_object_notify (G_OBJECT (de), "ui-manager");