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 *);
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->dict);
100 if (de->font != NULL)
102 pango_font_description_free (de->font);
106 /* Chain up to the parent class */
107 G_OBJECT_CLASS (parent_class)->dispose (obj);
120 psppire_data_editor_refresh_model (PsppireDataEditor *de)
122 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (de->var_sheet);
123 PsppireDataSheet *data_sheet;
126 FOR_EACH_DATA_SHEET (data_sheet, i, de)
127 psppire_data_sheet_set_data_store (data_sheet, de->data_store);
128 psppire_var_sheet_set_dictionary (var_sheet, de->dict);
132 psppire_data_editor_set_property (GObject *object,
137 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
138 PsppireDataSheet *data_sheet;
143 case PROP_SPLIT_WINDOW:
144 psppire_data_editor_split_window (de, g_value_get_boolean (value));
146 case PROP_DATA_STORE:
149 g_signal_handlers_disconnect_by_func (de->data_store,
150 G_CALLBACK (refresh_entry),
152 g_object_unref (de->data_store);
155 de->data_store = g_value_get_pointer (value);
156 g_object_ref (de->data_store);
157 psppire_data_editor_refresh_model (de);
159 g_signal_connect_swapped (de->data_store, "case-changed",
160 G_CALLBACK (refresh_entry), de);
163 case PROP_DICTIONARY:
165 g_object_unref (de->dict);
166 de->dict = g_value_get_pointer (value);
167 g_object_ref (de->dict);
169 psppire_var_sheet_set_dictionary (PSPPIRE_VAR_SHEET (de->var_sheet),
172 case PROP_VALUE_LABELS:
173 FOR_EACH_DATA_SHEET (data_sheet, i, de)
174 psppire_data_sheet_set_value_labels (data_sheet,
175 g_value_get_boolean (value));
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185 psppire_data_editor_get_property (GObject *object,
190 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
194 case PROP_SPLIT_WINDOW:
195 g_value_set_boolean (value, de->split);
197 case PROP_DATA_STORE:
198 g_value_set_pointer (value, de->data_store);
200 case PROP_DICTIONARY:
201 g_value_set_pointer (value, de->dict);
203 case PROP_VALUE_LABELS:
204 g_value_set_boolean (value,
205 psppire_data_sheet_get_value_labels (
206 PSPPIRE_DATA_SHEET (de->data_sheets[0])));
209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215 psppire_data_editor_switch_page (GtkNotebook *notebook,
219 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, page_num);
224 psppire_data_editor_set_focus_child (GtkContainer *container,
227 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
232 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
234 GParamSpec *data_store_spec ;
235 GParamSpec *dict_spec ;
236 GParamSpec *value_labels_spec;
237 GParamSpec *split_window_spec;
239 GObjectClass *object_class = G_OBJECT_CLASS (klass);
240 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
241 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
243 parent_class = g_type_class_peek_parent (klass);
245 object_class->dispose = psppire_data_editor_dispose;
246 object_class->set_property = psppire_data_editor_set_property;
247 object_class->get_property = psppire_data_editor_get_property;
249 container_class->set_focus_child = psppire_data_editor_set_focus_child;
251 notebook_class->switch_page = psppire_data_editor_switch_page;
254 g_param_spec_pointer ("data-store",
256 "A pointer to the data store associated with this editor",
257 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
259 g_object_class_install_property (object_class,
264 g_param_spec_pointer ("dictionary",
266 "A pointer to the dictionary associated with this editor",
267 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
269 g_object_class_install_property (object_class,
274 g_param_spec_boolean ("value-labels",
276 "Whether or not the data sheet should display labels instead of values",
278 G_PARAM_WRITABLE | G_PARAM_READABLE);
280 g_object_class_install_property (object_class,
286 g_param_spec_boolean ("split",
288 "True iff the data sheet is split",
290 G_PARAM_READABLE | G_PARAM_WRITABLE);
292 g_object_class_install_property (object_class,
299 on_data_sheet_var_double_clicked (PsppireDataSheet *data_sheet,
301 PsppireDataEditor *de)
303 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
304 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
306 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
313 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
314 PsppireDataEditor *de)
316 PsppireDataSheet *data_sheet;
318 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
319 PSPPIRE_DATA_EDITOR_DATA_VIEW);
321 data_sheet = psppire_data_editor_get_active_data_sheet (de);
322 psppire_data_sheet_goto_variable (data_sheet, dict_index);
327 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
328 active cell or cells. */
330 refresh_entry (PsppireDataEditor *de)
332 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
333 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (data_sheet);
334 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
336 gchar *ref_cell_text;
337 GList *selected_columns, *iter;
338 struct variable *var;
342 selected_columns = pspp_sheet_selection_get_selected_columns (selection);
345 for (iter = selected_columns; iter != NULL; iter = iter->next)
347 PsppSheetViewColumn *column = iter->data;
348 struct variable *v = g_object_get_data (G_OBJECT (column), "variable");
355 g_list_free (selected_columns);
357 n_cases = pspp_sheet_selection_count_selected_rows (selection);
360 /* The final row is selectable but it isn't a case (it's just used to add
361 more cases), so don't count it. */
365 case_count = psppire_data_store_get_case_count (de->data_store);
366 path = gtk_tree_path_new_from_indices (case_count, -1);
367 if (pspp_sheet_selection_path_is_selected (selection, path))
369 gtk_tree_path_free (path);
372 ref_cell_text = NULL;
373 if (n_cases == 1 && n_vars == 1)
375 PsppireValueEntry *value_entry = PSPPIRE_VALUE_ENTRY (de->datum_entry);
376 struct range_set *selected_rows;
377 gboolean show_value_labels;
382 selected_rows = pspp_sheet_selection_get_range_set (selection);
383 row = range_set_scan (selected_rows, 0);
384 range_set_destroy (selected_rows);
386 ref_cell_text = g_strdup_printf ("%d : %s", row + 1, var_get_name (var));
388 show_value_labels = psppire_data_sheet_get_value_labels (data_sheet);
390 psppire_value_entry_set_variable (value_entry, var);
391 psppire_value_entry_set_show_value_label (value_entry,
394 width = var_get_width (var);
395 value_init (&value, width);
396 datasheet_get_value (de->data_store->datasheet,
397 row, var_get_case_index (var), &value);
398 psppire_value_entry_set_value (value_entry, &value, width);
399 value_destroy (&value, width);
401 gtk_widget_set_sensitive (de->datum_entry, TRUE);
405 if (n_cases == 0 || n_vars == 0)
407 ref_cell_text = NULL;
413 /* The glib string library does not understand the ' printf modifier
414 on all platforms, but the "struct string" library does (because
415 Gnulib fixes that problem), so use the latter. */
417 ds_put_format (&s, ngettext ("%'d case", "%'d cases", n_cases),
419 ds_put_byte (&s, ' ');
420 ds_put_unichar (&s, 0xd7); /* U+00D7 MULTIPLICATION SIGN */
421 ds_put_byte (&s, ' ');
422 ds_put_format (&s, ngettext ("%'d variable", "%'d variables",
425 ref_cell_text = ds_steal_cstr (&s);
428 psppire_value_entry_set_variable (PSPPIRE_VALUE_ENTRY (de->datum_entry),
431 GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))), "");
432 gtk_widget_set_sensitive (de->datum_entry, FALSE);
435 gtk_label_set_label (GTK_LABEL (de->cell_ref_label),
436 ref_cell_text ? ref_cell_text : "");
437 g_free (ref_cell_text);
441 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
443 PsppireDataSheet *data_sheet = psppire_data_editor_get_active_data_sheet (de);
444 struct variable *var;
449 row = psppire_data_sheet_get_current_case (data_sheet);
450 var = psppire_data_sheet_get_current_variable (data_sheet);
454 width = var_get_width (var);
455 value_init (&value, width);
456 if (psppire_value_entry_get_value (PSPPIRE_VALUE_ENTRY (de->datum_entry),
458 psppire_data_store_set_value (de->data_store, row, var, &value);
459 value_destroy (&value, width);
463 on_data_sheet_selection_changed (PsppSheetSelection *selection,
464 PsppireDataEditor *de)
466 /* In a split view, ensure that only a single data sheet has a nonempty
469 && pspp_sheet_selection_count_selected_rows (selection)
470 && pspp_sheet_selection_count_selected_columns (selection))
472 PsppireDataSheet *ds;
475 FOR_EACH_DATA_SHEET (ds, i, de)
477 PsppSheetSelection *s;
479 s = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
481 pspp_sheet_selection_unselect_all (s);
488 /* Ensures that rows in the right-hand panes in the split view have the same
489 row height as the left-hand panes. Otherwise, the rows in the right-hand
490 pane tend to be smaller, because the right-hand pane doesn't have buttons
493 on_data_sheet_fixed_height_notify (PsppireDataSheet *ds,
495 PsppireDataEditor *de)
499 TL = GTK_XPANED_TOP_LEFT,
500 TR = GTK_XPANED_TOP_RIGHT,
501 BL = GTK_XPANED_BOTTOM_LEFT,
502 BR = GTK_XPANED_BOTTOM_RIGHT
505 int fixed_height = pspp_sheet_view_get_fixed_height (PSPP_SHEET_VIEW (ds));
507 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[TR]),
509 pspp_sheet_view_set_fixed_height (PSPP_SHEET_VIEW (de->data_sheets[BR]),
514 disconnect_data_sheets (PsppireDataEditor *de)
516 PsppireDataSheet *ds;
519 FOR_EACH_DATA_SHEET (ds, i, de)
521 PsppSheetSelection *selection;
525 /* This can only happen if 'dispose' runs more than once. */
529 if (i == GTK_XPANED_TOP_LEFT)
530 g_signal_handlers_disconnect_by_func (
531 ds, G_CALLBACK (on_data_sheet_fixed_height_notify), de);
533 g_signal_handlers_disconnect_by_func (
534 ds, G_CALLBACK (refresh_entry), de);
535 g_signal_handlers_disconnect_by_func (
536 ds, G_CALLBACK (on_data_sheet_var_double_clicked), de);
538 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
539 g_signal_handlers_disconnect_by_func (
540 selection, G_CALLBACK (on_data_sheet_selection_changed), de);
542 de->data_sheets[i] = NULL;
547 make_data_sheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
548 gboolean show_value_labels)
550 PsppSheetSelection *selection;
553 ds = psppire_data_sheet_new ();
554 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (ds), grid_lines);
555 psppire_data_sheet_set_value_labels (PSPPIRE_DATA_SHEET (ds),
558 g_signal_connect_swapped (ds, "notify::value-labels",
559 G_CALLBACK (refresh_entry), de);
560 g_signal_connect (ds, "var-double-clicked",
561 G_CALLBACK (on_data_sheet_var_double_clicked), de);
563 selection = pspp_sheet_view_get_selection (PSPP_SHEET_VIEW (ds));
564 g_signal_connect (selection, "changed",
565 G_CALLBACK (on_data_sheet_selection_changed), de);
571 make_single_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
572 gboolean show_value_labels)
574 GtkWidget *data_sheet_scroller;
576 de->data_sheets[0] = make_data_sheet (de, grid_lines, show_value_labels);
577 de->data_sheets[1] = de->data_sheets[2] = de->data_sheets[3] = NULL;
579 /* Put data sheet in scroller. */
580 data_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
581 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (data_sheet_scroller),
582 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
583 gtk_container_add (GTK_CONTAINER (data_sheet_scroller), de->data_sheets[0]);
585 return data_sheet_scroller;
589 make_split_datasheet (PsppireDataEditor *de, GtkTreeViewGridLines grid_lines,
590 gboolean show_value_labels)
592 /* Panes, in the order in which we want to create them. */
597 BL, /* bottom left */
598 BR /* bottom right */
601 PsppSheetView *ds[4];
605 xpaned = GTK_XPANED (gtk_xpaned_new ());
607 for (i = 0; i < 4; i++)
609 GtkAdjustment *hadjust, *vadjust;
610 GtkPolicyType hpolicy, vpolicy;
613 de->data_sheets[i] = make_data_sheet (de, grid_lines, show_value_labels);
614 ds[i] = PSPP_SHEET_VIEW (de->data_sheets[i]);
617 hadjust = pspp_sheet_view_get_hadjustment (ds[TL]);
619 hadjust = pspp_sheet_view_get_hadjustment (ds[TR]);
624 vadjust = pspp_sheet_view_get_vadjustment (ds[TL]);
626 vadjust = pspp_sheet_view_get_vadjustment (ds[BL]);
630 scroller = gtk_scrolled_window_new (hadjust, vadjust);
631 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
632 GTK_SHADOW_ETCHED_IN);
633 hpolicy = i == TL || i == TR ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
634 vpolicy = i == TL || i == BL ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS;
635 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
637 gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (ds[i]));
642 gtk_xpaned_pack_top_left (xpaned, scroller, TRUE, TRUE);
646 gtk_xpaned_pack_top_right (xpaned, scroller, TRUE, TRUE);
650 gtk_xpaned_pack_bottom_left (xpaned, scroller, TRUE, TRUE);
654 gtk_xpaned_pack_bottom_right (xpaned, scroller, TRUE, TRUE);
658 g_warn_if_reached ();
662 /* Bottom sheets don't display variable names. */
663 pspp_sheet_view_set_headers_visible (ds[BL], FALSE);
664 pspp_sheet_view_set_headers_visible (ds[BR], FALSE);
666 /* Right sheets don't display case numbers. */
667 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[TR]), FALSE);
668 psppire_data_sheet_set_case_numbers (PSPPIRE_DATA_SHEET (ds[BR]), FALSE);
670 g_signal_connect (ds[TL], "notify::fixed-height",
671 G_CALLBACK (on_data_sheet_fixed_height_notify), de);
673 return GTK_WIDGET (xpaned);
676 static void set_font_recursively (GtkWidget *w, gpointer data);
679 psppire_data_editor_init (PsppireDataEditor *de)
681 GtkWidget *var_sheet_scroller;
683 gchar *fontname = NULL;
685 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (de));
686 gtk_style_context_add_class (context, "psppire-data-editor");
689 de->old_vbox_widget = NULL;
691 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
693 de->cell_ref_label = gtk_label_new ("");
694 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
695 gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER);
697 de->datum_entry = psppire_value_entry_new ();
698 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
699 "activate", G_CALLBACK (on_datum_entry_activate), de);
701 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
702 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
703 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
706 de->datasheet_vbox_widget
707 = make_single_datasheet (de, GTK_TREE_VIEW_GRID_LINES_BOTH, FALSE);
709 de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
710 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
711 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
714 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
715 gtk_label_new_with_mnemonic (_("Data View")));
717 gtk_widget_show_all (de->vbox);
719 de->var_sheet = GTK_WIDGET (psppire_var_sheet_new ());
720 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet),
721 GTK_TREE_VIEW_GRID_LINES_BOTH);
722 var_sheet_scroller = gtk_scrolled_window_new (NULL, NULL);
723 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (var_sheet_scroller),
724 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
725 gtk_container_add (GTK_CONTAINER (var_sheet_scroller), de->var_sheet);
726 gtk_widget_show_all (var_sheet_scroller);
727 gtk_notebook_append_page (GTK_NOTEBOOK (de), var_sheet_scroller,
728 gtk_label_new_with_mnemonic (_("Variable View")));
730 g_signal_connect (de->var_sheet, "var-double-clicked",
731 G_CALLBACK (on_var_sheet_var_double_clicked), de);
733 g_object_set (de, "can-focus", FALSE, NULL);
735 if (psppire_conf_get_string (psppire_conf_new (),
736 "Data Editor", "font",
739 de->font = pango_font_description_from_string (fontname);
741 set_font_recursively (GTK_WIDGET (de), de->font);
747 psppire_data_editor_new (PsppireDict *dict,
748 PsppireDataStore *data_store)
750 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
752 "data-store", data_store,
756 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
757 sheet(s) and variable sheet. */
759 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
761 GtkTreeViewGridLines grid;
762 PsppireDataSheet *data_sheet;
766 ? GTK_TREE_VIEW_GRID_LINES_BOTH
767 : GTK_TREE_VIEW_GRID_LINES_NONE);
769 FOR_EACH_DATA_SHEET (data_sheet, i, de)
770 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (data_sheet), grid);
771 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
776 set_font_recursively (GtkWidget *w, gpointer data)
778 PangoFontDescription *font_desc = data;
780 GtkStyleContext *style = gtk_widget_get_style_context (w);
781 GtkCssProvider *cssp = gtk_css_provider_new ();
783 gchar *str = pango_font_description_to_string (font_desc);
785 g_strdup_printf ("* {font: %s}", str);
789 gtk_css_provider_load_from_data (cssp, css, -1, &err);
792 g_warning ("Failed to load font css \"%s\": %s", css, err->message);
797 gtk_style_context_add_provider (style,
798 GTK_STYLE_PROVIDER (cssp),
799 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
800 g_object_unref (cssp);
803 if ( GTK_IS_CONTAINER (w))
804 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
807 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
809 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
812 set_font_recursively (GTK_WIDGET (de), font_desc);
815 pango_font_description_free (de->font);
816 de->font = pango_font_description_copy (font_desc);
817 font_name = pango_font_description_to_string (de->font);
819 psppire_conf_set_string (psppire_conf_new (),
820 "Data Editor", "font",
825 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
826 If SPLIT is FALSE, un-splits it into a single pane. */
828 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
830 GtkTreeViewGridLines grid_lines;
833 if (split == de->split)
837 grid_lines = pspp_sheet_view_get_grid_lines (
838 PSPP_SHEET_VIEW (de->data_sheets[0]));
839 labels = psppire_data_sheet_get_value_labels (PSPPIRE_DATA_SHEET (
840 de->data_sheets[0]));
842 disconnect_data_sheets (de);
843 if (de->old_vbox_widget)
844 g_object_unref (de->old_vbox_widget);
845 de->old_vbox_widget = de->datasheet_vbox_widget;
846 g_object_ref (de->old_vbox_widget);
847 /* FIXME: old_vbox_widget needs to be unreffed in dispose.
848 (currently it seems to provoke an error if I do that.
850 gtk_container_remove (GTK_CONTAINER (de->vbox), de->datasheet_vbox_widget);
853 de->datasheet_vbox_widget = make_split_datasheet (de, grid_lines, labels);
855 de->datasheet_vbox_widget = make_single_datasheet (de, grid_lines, labels);
857 psppire_data_editor_refresh_model (de);
859 gtk_box_pack_start (GTK_BOX (de->vbox), de->datasheet_vbox_widget,
861 gtk_widget_show_all (de->vbox);
864 set_font_recursively (GTK_WIDGET (de), de->font);
867 g_object_notify (G_OBJECT (de), "split");
870 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
871 visible and selected in the active view in DE. */
873 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
875 PsppireDataSheet *data_sheet;
877 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
879 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
880 data_sheet = psppire_data_editor_get_active_data_sheet (de);
881 psppire_data_sheet_goto_variable (data_sheet, dict_index);
884 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
885 psppire_var_sheet_goto_variable (PSPPIRE_VAR_SHEET (de->var_sheet),
891 /* Returns the "active" data sheet in DE. If DE is in single-paned mode, this
892 is the only data sheet. If DE is in split mode (showing four data sheets),
893 this is the focused data sheet or, if none is focused, the data sheet with
894 selected cells or, if none has selected cells, the upper-left data sheet. */
896 psppire_data_editor_get_active_data_sheet (PsppireDataEditor *de)
900 PsppireDataSheet *data_sheet;
904 /* If one of the datasheet's scrollers is focused, choose that one. */
905 scroller = gtk_container_get_focus_child (
906 GTK_CONTAINER (de->datasheet_vbox_widget));
907 if (scroller != NULL)
908 return PSPPIRE_DATA_SHEET (gtk_bin_get_child (GTK_BIN (scroller)));
910 /* Otherwise if there's a nonempty selection in some data sheet, choose
912 FOR_EACH_DATA_SHEET (data_sheet, i, de)
914 PsppSheetSelection *selection;
916 selection = pspp_sheet_view_get_selection (
917 PSPP_SHEET_VIEW (data_sheet));
918 if (pspp_sheet_selection_count_selected_rows (selection)
919 && pspp_sheet_selection_count_selected_columns (selection))
924 return PSPPIRE_DATA_SHEET (de->data_sheets[0]);