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)
308 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
309 PSPPIRE_DATA_EDITOR_DATA_VIEW);
311 jmd_sheet_scroll_to (de->data_sheet, dict_index, -1);
315 on_data_sheet_var_double_clicked (JmdSheet *data_sheet, gint dict_index,
316 PsppireDataEditor *de)
318 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
319 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
321 jmd_sheet_scroll_to (de->var_sheet, -1, dict_index);
325 /* Refreshes 'de->cell_ref_label' and 'de->datum_entry' from the currently
326 active cell or cells. */
328 refresh_entry (PsppireDataEditor *de)
333 on_datum_entry_activate (PsppireValueEntry *entry, PsppireDataEditor *de)
339 disconnect_data_sheets (PsppireDataEditor *de)
344 static void set_font_recursively (GtkWidget *w, gpointer data);
347 psppire_data_editor_init (PsppireDataEditor *de)
350 gchar *fontname = NULL;
353 de->ui_manager = NULL;
354 de->old_vbox_widget = NULL;
356 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
358 de->cell_ref_label = gtk_label_new ("");
359 gtk_label_set_width_chars (GTK_LABEL (de->cell_ref_label), 25);
360 gtk_widget_set_valign (de->cell_ref_label, GTK_ALIGN_CENTER);
362 de->datum_entry = psppire_value_entry_new ();
363 g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (de->datum_entry))),
364 "activate", G_CALLBACK (on_datum_entry_activate), de);
366 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
367 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_label, FALSE, FALSE, 0);
368 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
371 de->data_sheet = g_object_new (JMD_TYPE_SHEET, NULL);
372 GtkWidget *data_button = jmd_sheet_get_button (JMD_SHEET (de->data_sheet));
373 gtk_button_set_label (GTK_BUTTON (data_button), _("Case"));
374 de->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
375 gtk_box_pack_start (GTK_BOX (de->vbox), hbox, FALSE, FALSE, 0);
376 gtk_box_pack_start (GTK_BOX (de->vbox), de->data_sheet, TRUE, TRUE, 0);
378 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->vbox,
379 gtk_label_new_with_mnemonic (_("Data View")));
381 gtk_widget_show_all (de->vbox);
383 de->var_sheet = g_object_new (JMD_TYPE_SHEET, NULL);
385 PsppireVarSheetHeader *vsh = g_object_new (PSPPIRE_TYPE_VAR_SHEET_HEADER, NULL);
387 g_object_set (de->var_sheet, "hmodel", vsh, NULL);
390 GtkWidget *var_button = jmd_sheet_get_button (JMD_SHEET (de->var_sheet));
391 gtk_button_set_label (GTK_BUTTON (var_button), _("Variable"));
393 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->var_sheet,
394 gtk_label_new_with_mnemonic (_("Variable View")));
396 gtk_widget_show_all (de->var_sheet);
398 g_signal_connect (de->var_sheet, "row-header-double-clicked",
399 G_CALLBACK (on_var_sheet_var_double_clicked), de);
401 g_signal_connect (de->data_sheet, "column-header-double-clicked",
402 G_CALLBACK (on_data_sheet_var_double_clicked), de);
404 g_object_set (de, "can-focus", FALSE, NULL);
406 if (psppire_conf_get_string (psppire_conf_new (),
407 "Data Editor", "font",
410 de->font = pango_font_description_from_string (fontname);
412 set_font_recursively (GTK_WIDGET (de), de->font);
415 psppire_data_editor_update_ui_manager (de);
419 psppire_data_editor_new (PsppireDict *dict,
420 PsppireDataStore *data_store)
422 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
424 "data-store", data_store,
428 /* Turns the visible grid on or off, according to GRID_VISIBLE, for DE's data
429 sheet(s) and variable sheet. */
431 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
433 GtkTreeViewGridLines grid;
436 ? GTK_TREE_VIEW_GRID_LINES_BOTH
437 : GTK_TREE_VIEW_GRID_LINES_NONE);
439 pspp_sheet_view_set_grid_lines (PSPP_SHEET_VIEW (de->var_sheet), grid);
444 set_font_recursively (GtkWidget *w, gpointer data)
446 PangoFontDescription *font_desc = data;
448 gtk_widget_override_font (w, font_desc);
450 if ( GTK_IS_CONTAINER (w))
451 gtk_container_foreach (GTK_CONTAINER (w), set_font_recursively, font_desc);
454 /* Sets FONT_DESC as the font used by the data sheet(s) and variable sheet. */
456 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
459 set_font_recursively (GTK_WIDGET (de), font_desc);
462 pango_font_description_free (de->font);
463 de->font = pango_font_description_copy (font_desc);
464 font_name = pango_font_description_to_string (de->font);
466 psppire_conf_set_string (psppire_conf_new (),
467 "Data Editor", "font",
472 /* If SPLIT is TRUE, splits DE's data sheet into four panes.
473 If SPLIT is FALSE, un-splits it into a single pane. */
475 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
477 if (split == de->split)
480 disconnect_data_sheets (de);
482 psppire_data_editor_refresh_model (de);
484 gtk_widget_show_all (de->vbox);
487 set_font_recursively (GTK_WIDGET (de), de->font);
490 g_object_notify (G_OBJECT (de), "split");
491 psppire_data_editor_update_ui_manager (de);
494 /* Makes the variable with dictionary index DICT_INDEX in DE's dictionary
495 visible and selected in the active view in DE. */
497 psppire_data_editor_goto_variable (PsppireDataEditor *de, gint dict_index)
502 /* Returns the UI manager that should be merged into DE's toplevel widget's UI
503 manager to display menu items and toolbar items specific to DE's current
506 DE's toplevel widget can watch for changes by connecting to DE's
507 notify::ui-manager signal. */
509 psppire_data_editor_get_ui_manager (PsppireDataEditor *de)
511 psppire_data_editor_update_ui_manager (de);
512 return de->ui_manager;
516 psppire_data_editor_update_ui_manager (PsppireDataEditor *de)
518 GtkUIManager *ui_manager;
522 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
524 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
527 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
531 /* This happens transiently in psppire_data_editor_init(). */
535 if (ui_manager != de->ui_manager)
538 g_object_unref (de->ui_manager);
540 g_object_ref (ui_manager);
541 de->ui_manager = ui_manager;
543 g_object_notify (G_OBJECT (de), "ui-manager");