diff options
author | Owen Taylor <otaylor@src.gnome.org> | 2001-05-03 00:46:41 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2001-05-03 00:46:41 +0000 |
commit | ff73253c8dddcf5bc7de1ccdffe00ac296e6ea20 (patch) | |
tree | 95fcfbdaeb8089b89f5251a37b22c7522f3ac851 /tests/prop-editor.c | |
parent | 4c2a74b79d0513ae9accfb8652b0ca47f5eab894 (diff) | |
download | gdk-pixbuf-ff73253c8dddcf5bc7de1ccdffe00ac296e6ea20.tar.gz |
Split the property editor code out for reuse, improve.
* tests/prop-editor.[ch] test/testtreeview.c tests/Makefile.am:
Split the property editor code out for reuse, improve.
Diffstat (limited to 'tests/prop-editor.c')
-rw-r--r-- | tests/prop-editor.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/tests/prop-editor.c b/tests/prop-editor.c new file mode 100644 index 000000000..ac96abf9d --- /dev/null +++ b/tests/prop-editor.c @@ -0,0 +1,586 @@ +/* prop-editor.c + * Copyright (C) 2000 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include <gtk/gtk.h> + +#include "prop-editor.h" + +static void +get_param_specs (GObject *object, + GParamSpec ***specs, + gint *n_specs) +{ + /* Use private interface for now, fix later */ + *specs = G_OBJECT_GET_CLASS (object)->property_specs; + *n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs; +} + +typedef struct +{ + gpointer instance; + GObject *alive_object; + guint id; +} DisconnectData; + +static void +disconnect_func (gpointer data) +{ + DisconnectData *dd = data; + + g_signal_handler_disconnect (dd->instance, dd->id); +} + +static void +signal_removed (gpointer data, + GClosure *closure) +{ + DisconnectData *dd = data; + + g_object_steal_data (dd->alive_object, "alive-object-data"); + g_free (dd); +} + +static void +g_object_connect_property (GObject *object, + const gchar *prop_name, + GtkSignalFunc func, + gpointer data, + GObject *alive_object) +{ + GClosure *closure; + gchar *with_detail = g_strconcat ("notify::", prop_name, NULL); + DisconnectData *dd; + + dd = g_new (DisconnectData, 1); + + closure = g_cclosure_new (func, data, NULL); + + g_closure_add_invalidate_notifier (closure, dd, signal_removed); + + dd->id = g_signal_connect_closure (object, with_detail, + closure, FALSE); + + dd->instance = object; + dd->alive_object = alive_object; + + g_object_set_data_full (G_OBJECT (alive_object), + "alive-object-data", + dd, + disconnect_func); + + g_free (with_detail); +} + +typedef struct +{ + GObject *obj; + gchar *prop; +} ObjectProperty; + +static void +free_object_property (ObjectProperty *p) +{ + g_free (p->prop); + g_free (p); +} + +static void +connect_controller (GObject *controller, + const gchar *signal, + GObject *model, + const gchar *prop_name, + GtkSignalFunc func) +{ + ObjectProperty *p; + + p = g_new (ObjectProperty, 1); + p->obj = model; + p->prop = g_strdup (prop_name); + + g_signal_connect_data (controller, signal, func, p, + (GClosureNotify)free_object_property, + FALSE, FALSE); +} + +static void +int_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + g_object_set (p->obj, p->prop, (int) adj->value, NULL); +} + +static void +int_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_INT); + g_object_get_property (object, pspec->name, &val); + + if (g_value_get_int (&val) != (int)adj->value) + gtk_adjustment_set_value (adj, g_value_get_int (&val)); + + g_value_unset (&val); +} + +static void +float_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + g_object_set (p->obj, p->prop, (float) adj->value, NULL); +} + +static void +float_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_FLOAT); + g_object_get_property (object, pspec->name, &val); + + if (g_value_get_float (&val) != (float) adj->value) + gtk_adjustment_set_value (adj, g_value_get_float (&val)); + + g_value_unset (&val); +} + +static void +string_modified (GtkEntry *entry, gpointer data) +{ + ObjectProperty *p = data; + const gchar *text; + + text = gtk_entry_get_text (entry); + + g_object_set (p->obj, p->prop, text, NULL); +} + +static void +string_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + GValue val = { 0, }; + const gchar *str; + const gchar *text; + + g_value_init (&val, G_TYPE_STRING); + g_object_get_property (object, pspec->name, &val); + + str = g_value_get_string (&val); + if (str == NULL) + str = ""; + text = gtk_entry_get_text (entry); + + if (strcmp (str, text) != 0) + gtk_entry_set_text (entry, str); + + g_value_unset (&val); +} + +static void +bool_modified (GtkToggleButton *tb, gpointer data) +{ + ObjectProperty *p = data; + + g_object_set (p->obj, p->prop, (int) tb->active, NULL); +} + +static void +bool_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data); + GValue val = { 0, }; + + g_value_init (&val, G_TYPE_BOOLEAN); + g_object_get_property (object, pspec->name, &val); + + if (g_value_get_boolean (&val) != tb->active) + gtk_toggle_button_set_active (tb, g_value_get_boolean (&val)); + + gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ? + "TRUE" : "FALSE"); + + g_value_unset (&val); +} + + +static void +enum_modified (GtkOptionMenu *om, gpointer data) +{ + ObjectProperty *p = data; + gint i; + GParamSpec *spec; + GEnumClass *eclass; + + spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj), + p->prop); + + eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type)); + + i = gtk_option_menu_get_history (om); + + g_object_set (p->obj, p->prop, eclass->values[i].value, NULL); +} + +static void +enum_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkOptionMenu *om = GTK_OPTION_MENU (data); + GValue val = { 0, }; + GEnumClass *eclass; + gint i; + + eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type)); + + g_value_init (&val, pspec->value_type); + g_object_get_property (object, pspec->name, &val); + + i = 0; + while (i < eclass->n_values) + { + if (eclass->values[i].value == g_value_get_enum (&val)) + break; + ++i; + } + + if (gtk_option_menu_get_history (om) != i) + gtk_option_menu_set_history (om, i); + + g_value_unset (&val); + +} + +static gunichar +unichar_get_value (GtkEntry *entry) +{ + const gchar *text = gtk_entry_get_text (entry); + + if (text[0]) + return g_utf8_get_char (text); + else + return 0; +} + +static void +unichar_modified (GtkEntry *entry, gpointer data) +{ + ObjectProperty *p = data; + gunichar val = unichar_get_value (entry); + + g_object_set (p->obj, p->prop, val, NULL); +} + +static void +unichar_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkEntry *entry = GTK_ENTRY (data); + gunichar new_val; + gunichar old_val = unichar_get_value (entry); + gchar buf[7]; + gint len; + + g_object_get (object, pspec->name, &new_val, NULL); + + if (new_val != old_val) + { + if (!new_val) + len = 0; + else + len = g_unichar_to_utf8 (new_val, buf); + + buf[len] = '\0'; + + gtk_entry_set_text (entry, buf); + } +} + +void +model_destroy (gpointer data) +{ + g_object_steal_data (data, "model-object"); + gtk_widget_destroy (data); +} + +void +window_destroy (gpointer data) +{ + g_object_steal_data (data, "prop-editor-win"); +} + +GtkWidget* +create_prop_editor (GObject *object) +{ + GtkWidget *win; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *prop_edit; + GtkWidget *sw; + gint n_specs = 0; + GParamSpec **specs = NULL; + gint i; + GtkAdjustment *adj; + GtkTooltips *tips; + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + tips = gtk_tooltips_new (); + gtk_signal_connect_object (GTK_OBJECT (win), "destroy", + GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (tips)); + + /* hold a weak ref to the object we're editing */ + g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy); + g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy); + + vbox = gtk_vbox_new (TRUE, 2); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox); + gtk_container_add (GTK_CONTAINER (win), sw); + + get_param_specs (object, &specs, &n_specs); + + i = 0; + while (i < n_specs) + { + GParamSpec *spec = specs[i]; + gboolean can_modify; + + prop_edit = NULL; + + can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 && + (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0); + + if ((spec->flags & G_PARAM_READABLE) == 0) + { + /* can't display unreadable properties */ + ++i; + continue; + } + + switch (G_PARAM_SPEC_TYPE (spec)) + { + case G_TYPE_PARAM_INT: + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value, + G_PARAM_SPEC_INT (spec)->minimum, + G_PARAM_SPEC_INT (spec)->maximum, + 1, + MAX ((G_PARAM_SPEC_INT (spec)->maximum - + G_PARAM_SPEC_INT (spec)->minimum) / 10, 1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 1.0, 0); + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (int_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec->name, (GtkSignalFunc) int_modified); + break; + + case G_TYPE_PARAM_FLOAT: + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value, + G_PARAM_SPEC_FLOAT (spec)->minimum, + G_PARAM_SPEC_FLOAT (spec)->maximum, + 0.1, + MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum - + G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1), + 0.0)); + + prop_edit = gtk_spin_button_new (adj, 0.1, 2); + + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (float_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec->name, (GtkSignalFunc) float_modified); + break; + + case G_TYPE_PARAM_STRING: + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + prop_edit = gtk_entry_new (); + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (string_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec->name, (GtkSignalFunc) string_modified); + break; + + case G_TYPE_PARAM_BOOLEAN: + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + prop_edit = gtk_toggle_button_new_with_label (""); + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (bool_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "toggled", + object, spec->name, (GtkSignalFunc) bool_modified); + break; + + case G_TYPE_PARAM_ENUM: + { + GtkWidget *menu; + GEnumClass *eclass; + gint i; + + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + prop_edit = gtk_option_menu_new (); + + menu = gtk_menu_new (); + + eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type)); + + i = 0; + while (i < eclass->n_values) + { + GtkWidget *mi; + + mi = gtk_menu_item_new_with_label (eclass->values[i].value_name); + + gtk_widget_show (mi); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi); + + ++i; + } + + g_type_class_unref (eclass); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu); + + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (enum_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec->name, (GtkSignalFunc) enum_modified); + } + + case G_TYPE_PARAM_UNICHAR: + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (spec->nick); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + prop_edit = gtk_entry_new (); + gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1); + gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + g_object_connect_property (object, spec->name, + GTK_SIGNAL_FUNC (unichar_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec->name, (GtkSignalFunc) unichar_modified); + break; + + default: + { + gchar *msg = g_strdup_printf ("%s: don't know how to edit property type %s", + spec->nick, g_type_name (G_PARAM_SPEC_TYPE (spec))); + hbox = gtk_hbox_new (FALSE, 10); + label = gtk_label_new (msg); + g_free (msg); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + } + } + + if (prop_edit) + { + if (!can_modify) + gtk_widget_set_sensitive (prop_edit, FALSE); + + if (spec->blurb) + gtk_tooltips_set_tip (tips, prop_edit, spec->blurb, NULL); + + /* set initial value */ + g_object_notify (object, spec->name); + } + + ++i; + } + + gtk_window_set_default_size (GTK_WINDOW (win), 300, 500); + + gtk_widget_show_all (win); + + return win; +} + |