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-store.h"
31 #include "ui/gui/psppire-value-entry.h"
32 #include "ui/gui/psppire-var-sheet.h"
33 #include "ui/gui/psppire-conf.h"
34 #include "ui/gui/psppire-var-sheet-header.h"
36 #include "ui/gui/efficient-sheet/jmd-sheet.h"
39 #define _(msgid) gettext (msgid)
42 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
43 static void psppire_data_editor_init (PsppireDataEditor *de);
45 static void disconnect_data_sheets (PsppireDataEditor *);
46 static void refresh_entry (PsppireDataEditor *);
47 static void psppire_data_editor_update_ui_manager (PsppireDataEditor *);
50 psppire_data_editor_get_type (void)
52 static GType de_type = 0;
56 static const GTypeInfo de_info =
58 sizeof (PsppireDataEditorClass),
60 NULL, /* base_finalize */
61 (GClassInitFunc) psppire_data_editor_class_init,
62 NULL, /* class_finalize */
63 NULL, /* class_data */
64 sizeof (PsppireDataEditor),
66 (GInstanceInitFunc) psppire_data_editor_init,
69 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
76 static GObjectClass * parent_class = NULL;
79 psppire_data_editor_dispose (GObject *obj)
81 PsppireDataEditor *de = (PsppireDataEditor *) obj;
83 disconnect_data_sheets (de);
87 g_object_unref (de->data_store);
88 de->data_store = NULL;
93 g_object_unref (de->dict);
99 pango_font_description_free (de->font);
105 g_object_unref (de->ui_manager);
106 de->ui_manager = NULL;
109 /* Chain up to the parent class */
110 G_OBJECT_CLASS (parent_class)->dispose (obj);
124 psppire_data_editor_refresh_model (PsppireDataEditor *de)
129 psppire_data_editor_set_property (GObject *object,
134 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
138 case PROP_SPLIT_WINDOW:
139 psppire_data_editor_split_window (de, g_value_get_boolean (value));
141 case PROP_DATA_STORE:
144 g_signal_handlers_disconnect_by_func (de->data_store,
145 G_CALLBACK (refresh_entry),
147 g_object_unref (de->data_store);
150 de->data_store = g_value_get_pointer (value);
151 g_object_ref (de->data_store);
152 g_print ("NEW STORE\n");
154 g_object_set (de->data_sheet, "data-model", de->data_store, NULL);
155 psppire_data_editor_refresh_model (de);
157 g_signal_connect_swapped (de->data_store, "case-changed",
158 G_CALLBACK (refresh_entry), de);
161 case PROP_DICTIONARY:
163 g_object_unref (de->dict);
164 de->dict = g_value_get_pointer (value);
165 g_object_ref (de->dict);
167 g_object_set (de->data_sheet, "hmodel", de->dict, NULL);
168 g_object_set (de->var_sheet, "data-model", de->dict, NULL);
171 case PROP_VALUE_LABELS:
173 case PROP_UI_MANAGER:
175 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181 psppire_data_editor_get_property (GObject *object,
186 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
190 case PROP_SPLIT_WINDOW:
191 g_value_set_boolean (value, de->split);
193 case PROP_DATA_STORE:
194 g_value_set_pointer (value, de->data_store);
196 case PROP_DICTIONARY:
197 g_value_set_pointer (value, de->dict);
199 case PROP_VALUE_LABELS:
201 case PROP_UI_MANAGER:
202 g_value_set_object (value, psppire_data_editor_get_ui_manager (de));
205 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211 psppire_data_editor_switch_page (GtkNotebook *notebook,
215 GTK_NOTEBOOK_CLASS (parent_class)->switch_page (notebook, w, page_num);
216 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (notebook));
220 psppire_data_editor_set_focus_child (GtkContainer *container,
223 GTK_CONTAINER_CLASS (parent_class)->set_focus_child (container, widget);
224 psppire_data_editor_update_ui_manager (PSPPIRE_DATA_EDITOR (container));
228 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
230 GParamSpec *data_store_spec ;
231 GParamSpec *dict_spec ;
232 GParamSpec *value_labels_spec;
233 GParamSpec *split_window_spec;
234 GParamSpec *ui_manager_spec;
235 GObjectClass *object_class = G_OBJECT_CLASS (klass);
236 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
237 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
239 parent_class = g_type_class_peek_parent (klass);
241 object_class->dispose = psppire_data_editor_dispose;
242 object_class->set_property = psppire_data_editor_set_property;
243 object_class->get_property = psppire_data_editor_get_property;
245 container_class->set_focus_child = psppire_data_editor_set_focus_child;
247 notebook_class->switch_page = psppire_data_editor_switch_page;
250 g_param_spec_pointer ("data-store",
252 "A pointer to the data store associated with this editor",
253 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
255 g_object_class_install_property (object_class,
260 g_param_spec_pointer ("dictionary",
262 "A pointer to the dictionary associated with this editor",
263 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
265 g_object_class_install_property (object_class,
270 g_param_spec_boolean ("value-labels",
272 "Whether or not the data sheet should display labels instead of values",
274 G_PARAM_WRITABLE | G_PARAM_READABLE);
276 g_object_class_install_property (object_class,
282 g_param_spec_boolean ("split",
284 "True iff the data sheet is split",
286 G_PARAM_READABLE | G_PARAM_WRITABLE);
288 g_object_class_install_property (object_class,
293 g_param_spec_object ("ui-manager",
295 "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.",
298 g_object_class_install_property (object_class,
305 on_var_sheet_var_double_clicked (PsppireVarSheet *var_sheet, gint dict_index,
306 PsppireDataEditor *de)
309 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
310 PSPPIRE_DATA_EDITOR_DATA_VIEW);
316 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
317 active cell or cells. */
319 refresh_entry (PsppireDataEditor *de)
324 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
330 disconnect_data_sheets (PsppireDataEditor *de)
335 static void set_font_recursively (GtkWidget *w, gpointer data);
338 psppire_data_editor_init (PsppireDataEditor *de)
341 gchar *fontname = NULL;
344 de->ui_manager = NULL;
345 de->old_vbox_widget = NULL;
347 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
349 de->cell_ref_label = gtk_label_new ("");
350 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
351 gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER);
353 de->datum_entry = psppire_value_entry_new ();
354 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
355 "activate", G_CALLBACK (on_datum_entry_activate), de);
357 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
358 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
359 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
362 de->data_sheet = g_object_new (JMD_TYPE_SHEET, NULL);
363 GtkWidget *data_button = jmd_sheet_get_button (JMD_SHEET (de->data_sheet));
364 gtk_button_set_label (GTK_BUTTON (data_button), _("Case"));
365 de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
366 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
367 gtk_box_pack_start (GTK_BOX (de->vbox), de->data_sheet, TRUE, TRUE, 0);
369 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
370 gtk_label_new_with_mnemonic (_("Data View")));
372 gtk_widget_show_all (de->vbox);
374 de->var_sheet = g_object_new (JMD_TYPE_SHEET, NULL);
376 PsppireVarSheetHeader *vsh = g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
378 g_object_set (de->var_sheet, "hmodel", vsh, NULL);
381 GtkWidget *var_button = jmd_sheet_get_button (JMD_SHEET (de->var_sheet));
382 gtk_button_set_label (GTK_BUTTON (var_button), _("Variable"));
384 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->var_sheet,
385 gtk_label_new_with_mnemonic (_("Variable View")));
387 gtk_widget_show_all (de->var_sheet);
389 g_signal_connect (de->var_sheet, "var-double-clicked",
390 G_CALLBACK (on_var_sheet_var_double_clicked), de);
392 g_object_set (de, "can-focus", FALSE, NULL);
394 if (psppire_conf_get_string (psppire_conf_new (),
395 "Data Editor", "font",
398 de->font = pango_font_description_from_string (fontname);
400 set_font_recursively (GTK_WIDGET (de), de->font);
403 psppire_data_editor_update_ui_manager (de);
407 psppire_data_editor_new (PsppireDict *dict,
408 PsppireDataStore *data_store)
410 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
412 "data-store", data_store,
416 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
417 sheet(s) and variable sheet. */
419 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
421 GtkTreeViewGridLines grid;
424 ? GTK_TREE_VIEW_GRID_LINES_BOTH
425 : GTK_TREE_VIEW_GRID_LINES_NONE);
427 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
432 set_font_recursively (GtkWidget *w, gpointer data)
434 PangoFontDescription *font_desc = data;
436 gtk_widget_override_font (w, font_desc);
438 if ( GTK_IS_CONTAINER (w))
439 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
442 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
444 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
447 set_font_recursively (GTK_WIDGET (de), font_desc);
450 pango_font_description_free (de->font);
451 de->font = pango_font_description_copy (font_desc);
452 font_name = pango_font_description_to_string (de->font);
454 psppire_conf_set_string (psppire_conf_new (),
455 "Data Editor", "font",
460 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
461 If SPLIT is FALSE, un-splits it into a single pane. */
463 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
465 if (split == de->split)
468 disconnect_data_sheets (de);
470 psppire_data_editor_refresh_model (de);
472 gtk_widget_show_all (de->vbox);
475 set_font_recursively (GTK_WIDGET (de), de->font);
478 g_object_notify (G_OBJECT (de), "split");
479 psppire_data_editor_update_ui_manager (de);
482 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
483 visible and selected in the active view in DE. */
485 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
490 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
491 manager to display menu items and toolbar items specific to DE's current
494 DE's toplevel widget can watch for changes by connecting to DE's
495 notify::ui-manager signal. */
497 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
499 psppire_data_editor_update_ui_manager (de);
500 return de->ui_manager;
504 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
506 GtkUIManager *ui_manager;
510 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
512 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
515 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
519 /* This happens transiently in psppire_data_editor_init(). */
523 if (ui_manager != de->ui_manager)
526 g_object_unref (de->ui_manager);
528 g_object_ref (ui_manager);
529 de->ui_manager = ui_manager;
531 g_object_notify (G_OBJECT (de), "ui-manager");