X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=tests%2Fprop-editor.c;h=e9afe6f767b8b4b6f63c576e926b8dc03230a047;hb=HEAD;hp=2c915781550da7d170131a460b123a0becfb9aa0;hpb=6d2ce70689fd2f08c6565f092f582ff8cc7bdcc3;p=~andy%2Fgtk diff --git a/tests/prop-editor.c b/tests/prop-editor.c index 2c9157815..e9afe6f76 100644 --- a/tests/prop-editor.c +++ b/tests/prop-editor.c @@ -12,9 +12,7 @@ * 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. + * License along with this library. If not, see . */ #include @@ -23,40 +21,25 @@ #include "prop-editor.h" -static void -get_param_specs (GType type, - GParamSpec ***specs, - gint *n_specs) -{ - GObjectClass *class = g_type_class_peek (type); - - /* We count on the fact we have an instance, or else we'd have - * to use g_type_class_ref (); - */ - - /* Use private interface for now, fix later */ - *specs = class->property_specs; - *n_specs = class->n_property_specs; -} typedef struct { gpointer instance; GObject *alive_object; - guint id; + gulong 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) + GClosure *closure) { DisconnectData *dd = data; @@ -64,17 +47,35 @@ signal_removed (gpointer data, g_free (dd); } +static gboolean +is_child_property (GParamSpec *pspec) +{ + return g_param_spec_get_qdata (pspec, g_quark_from_string ("is-child-prop")) != NULL; +} + static void -g_object_connect_property (GObject *object, - const gchar *prop_name, - GtkSignalFunc func, - gpointer data, - GObject *alive_object) +mark_child_property (GParamSpec *pspec) +{ + g_param_spec_set_qdata (pspec, g_quark_from_string ("is-child-prop"), + GINT_TO_POINTER (TRUE)); +} + +static void +g_object_connect_property (GObject *object, + GParamSpec *spec, + GCallback func, + gpointer data, + GObject *alive_object) { GClosure *closure; - gchar *with_detail = g_strconcat ("notify::", prop_name, NULL); + gchar *with_detail; DisconnectData *dd; + if (is_child_property (spec)) + with_detail = g_strconcat ("child-notify::", spec->name, NULL); + else + with_detail = g_strconcat ("notify::", spec->name, NULL); + dd = g_new (DisconnectData, 1); closure = g_cclosure_new (func, data, NULL); @@ -82,49 +83,48 @@ g_object_connect_property (GObject *object, g_closure_add_invalidate_notifier (closure, dd, signal_removed); dd->id = g_signal_connect_closure (object, with_detail, - closure, FALSE); + 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 +typedef struct { GObject *obj; - gchar *prop; - gint modified_id; + GParamSpec *spec; + gulong modified_id; } ObjectProperty; static void free_object_property (ObjectProperty *p) { - g_free (p->prop); g_free (p); } static void -connect_controller (GObject *controller, +connect_controller (GObject *controller, const gchar *signal, - GObject *model, - const gchar *prop_name, - GtkSignalFunc func) + GObject *model, + GParamSpec *spec, + GCallback func) { ObjectProperty *p; p = g_new (ObjectProperty, 1); p->obj = model; - p->prop = g_strdup (prop_name); + p->spec = spec; p->modified_id = g_signal_connect_data (controller, signal, func, p, - (GClosureNotify)free_object_property, - FALSE, FALSE); + (GClosureNotify)free_object_property, + 0); g_object_set_data (controller, "object-property", p); } @@ -133,7 +133,8 @@ block_controller (GObject *controller) { ObjectProperty *p = g_object_get_data (controller, "object-property"); - g_signal_handler_block (controller, p->modified_id); + if (p) + g_signal_handler_block (controller, p->modified_id); } static void @@ -141,7 +142,8 @@ unblock_controller (GObject *controller) { ObjectProperty *p = g_object_get_data (controller, "object-property"); - g_signal_handler_unblock (controller, p->modified_id); + if (p) + g_signal_handler_unblock (controller, p->modified_id); } static void @@ -149,19 +151,44 @@ int_modified (GtkAdjustment *adj, gpointer data) { ObjectProperty *p = data; - g_object_set (p->obj, p->prop, (int) adj->value, NULL); + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL); + } + else + g_object_set (p->obj, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL); +} + +static void +get_property_value (GObject *object, GParamSpec *pspec, GValue *value) +{ + if (is_child_property (pspec)) + { + GtkWidget *widget = GTK_WIDGET (object); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_get_property (GTK_CONTAINER (parent), + widget, pspec->name, value); + } + else + g_object_get_property (object, pspec->name, value); } static void int_changed (GObject *object, GParamSpec *pspec, gpointer data) { GtkAdjustment *adj = GTK_ADJUSTMENT (data); - GValue val = { 0, }; + GValue val = G_VALUE_INIT; g_value_init (&val, G_TYPE_INT); - g_object_get_property (object, pspec->name, &val); - if (g_value_get_int (&val) != (int)adj->value) + get_property_value (object, pspec, &val); + + if (g_value_get_int (&val) != (int)gtk_adjustment_get_value (adj)) { block_controller (G_OBJECT (adj)); gtk_adjustment_set_value (adj, g_value_get_int (&val)); @@ -171,24 +198,69 @@ int_changed (GObject *object, GParamSpec *pspec, gpointer data) g_value_unset (&val); } +static void +uint_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL); + } + else + g_object_set (p->obj, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL); +} + +static void +uint_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = G_VALUE_INIT; + + g_value_init (&val, G_TYPE_UINT); + get_property_value (object, pspec, &val); + + if (g_value_get_uint (&val) != (guint)gtk_adjustment_get_value (adj)) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_uint (&val)); + unblock_controller (G_OBJECT (adj)); + } + + 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); + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL); + } + else + g_object_set (p->obj, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL); } static void float_changed (GObject *object, GParamSpec *pspec, gpointer data) { GtkAdjustment *adj = GTK_ADJUSTMENT (data); - GValue val = { 0, }; + GValue val = G_VALUE_INIT; g_value_init (&val, G_TYPE_FLOAT); - g_object_get_property (object, pspec->name, &val); + get_property_value (object, pspec, &val); - if (g_value_get_float (&val) != (float) adj->value) + if (g_value_get_float (&val) != (float) gtk_adjustment_get_value (adj)) { block_controller (G_OBJECT (adj)); gtk_adjustment_set_value (adj, g_value_get_float (&val)); @@ -198,6 +270,42 @@ float_changed (GObject *object, GParamSpec *pspec, gpointer data) g_value_unset (&val); } +static void +double_modified (GtkAdjustment *adj, gpointer data) +{ + ObjectProperty *p = data; + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL); + } + else + g_object_set (p->obj, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL); +} + +static void +double_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkAdjustment *adj = GTK_ADJUSTMENT (data); + GValue val = G_VALUE_INIT; + + g_value_init (&val, G_TYPE_DOUBLE); + get_property_value (object, pspec, &val); + + if (g_value_get_double (&val) != gtk_adjustment_get_value (adj)) + { + block_controller (G_OBJECT (adj)); + gtk_adjustment_set_value (adj, g_value_get_double (&val)); + unblock_controller (G_OBJECT (adj)); + } + + g_value_unset (&val); +} + static void string_modified (GtkEntry *entry, gpointer data) { @@ -206,19 +314,28 @@ string_modified (GtkEntry *entry, gpointer data) text = gtk_entry_get_text (entry); - g_object_set (p->obj, p->prop, text, NULL); + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, text, NULL); + } + else + g_object_set (p->obj, p->spec->name, text, NULL); } static void string_changed (GObject *object, GParamSpec *pspec, gpointer data) { GtkEntry *entry = GTK_ENTRY (data); - GValue val = { 0, }; + GValue val = G_VALUE_INIT; const gchar *str; const gchar *text; - + g_value_init (&val, G_TYPE_STRING); - g_object_get_property (object, pspec->name, &val); + get_property_value (object, pspec, &val); str = g_value_get_string (&val); if (str == NULL) @@ -231,7 +348,7 @@ string_changed (GObject *object, GParamSpec *pspec, gpointer data) gtk_entry_set_text (entry, str); unblock_controller (G_OBJECT (entry)); } - + g_value_unset (&val); } @@ -240,62 +357,80 @@ bool_modified (GtkToggleButton *tb, gpointer data) { ObjectProperty *p = data; - g_object_set (p->obj, p->prop, (int) tb->active, NULL); + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), widget, + p->spec->name, (int) gtk_toggle_button_get_active (tb), + NULL); + } + else + g_object_set (p->obj, p->spec->name, (int) gtk_toggle_button_get_active (tb), NULL); } static void bool_changed (GObject *object, GParamSpec *pspec, gpointer data) { GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data); - GValue val = { 0, }; - + GtkWidget *child; + GValue val = G_VALUE_INIT; + g_value_init (&val, G_TYPE_BOOLEAN); - g_object_get_property (object, pspec->name, &val); + get_property_value (object, pspec, &val); - if (g_value_get_boolean (&val) != tb->active) + if (g_value_get_boolean (&val) != gtk_toggle_button_get_active (tb)) { block_controller (G_OBJECT (tb)); gtk_toggle_button_set_active (tb, g_value_get_boolean (&val)); unblock_controller (G_OBJECT (tb)); } - gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ? - "TRUE" : "FALSE"); - + child = gtk_bin_get_child (GTK_BIN (tb)); + gtk_label_set_text (GTK_LABEL (child), + g_value_get_boolean (&val) ? "TRUE" : "FALSE"); + g_value_unset (&val); } static void -enum_modified (GtkOptionMenu *om, gpointer data) +enum_modified (GtkComboBox *cb, 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); + eclass = G_ENUM_CLASS (g_type_class_peek (p->spec->value_type)); + + i = gtk_combo_box_get_active (cb); - g_object_set (p->obj, p->prop, eclass->values[i].value, NULL); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, eclass->values[i].value, NULL); + } + else + g_object_set (p->obj, p->spec->name, eclass->values[i].value, NULL); } static void enum_changed (GObject *object, GParamSpec *pspec, gpointer data) { - GtkOptionMenu *om = GTK_OPTION_MENU (data); - GValue val = { 0, }; + GtkComboBox *cb = GTK_COMBO_BOX (data); + GValue val = G_VALUE_INIT; 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); + get_property_value (object, pspec, &val); i = 0; while (i < eclass->n_values) @@ -304,23 +439,94 @@ enum_changed (GObject *object, GParamSpec *pspec, gpointer data) break; ++i; } - - if (gtk_option_menu_get_history (om) != i) + + if (gtk_combo_box_get_active (cb) != i) + { + block_controller (G_OBJECT (cb)); + gtk_combo_box_set_active (cb, i); + unblock_controller (G_OBJECT (cb)); + } + + g_value_unset (&val); + +} + +static void +flags_modified (GtkCheckButton *button, gpointer data) +{ + ObjectProperty *p = data; + gboolean active; + GFlagsClass *fclass; + guint flags; + gint i; + + fclass = G_FLAGS_CLASS (g_type_class_peek (p->spec->value_type)); + + active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); + i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index")); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_get (GTK_CONTAINER (parent), + widget, p->spec->name, &flags, NULL); + if (active) + flags |= fclass->values[i].value; + else + flags &= ~fclass->values[i].value; + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, flags, NULL); + } + else { - block_controller (G_OBJECT (om)); - gtk_option_menu_set_history (om, i); - unblock_controller (G_OBJECT (om)); + g_object_get (p->obj, p->spec->name, &flags, NULL); + + if (active) + flags |= fclass->values[i].value; + else + flags &= ~fclass->values[i].value; + + g_object_set (p->obj, p->spec->name, flags, NULL); } - +} + +static void +flags_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GList *children, *c; + GValue val = G_VALUE_INIT; + GFlagsClass *fclass; + guint flags; + gint i; + + fclass = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type)); + + g_value_init (&val, pspec->value_type); + get_property_value (object, pspec, &val); + flags = g_value_get_flags (&val); g_value_unset (&val); + children = gtk_container_get_children (GTK_CONTAINER (data)); + + for (c = children, i = 0; c; c = c->next, i++) + { + block_controller (G_OBJECT (c->data)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data), + (fclass->values[i].value & flags) != 0); + unblock_controller (G_OBJECT (c->data)); + } + + g_list_free (children); } 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 @@ -333,7 +539,16 @@ unichar_modified (GtkEntry *entry, gpointer data) ObjectProperty *p = data; gunichar val = unichar_get_value (entry); - g_object_set (p->obj, p->prop, val, NULL); + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, val, NULL); + } + else + g_object_set (p->obj, p->spec->name, val, NULL); } static void @@ -342,290 +557,928 @@ unichar_changed (GObject *object, GParamSpec *pspec, gpointer data) GtkEntry *entry = GTK_ENTRY (data); gunichar new_val; gunichar old_val = unichar_get_value (entry); + GValue val = G_VALUE_INIT; gchar buf[7]; gint len; - g_object_get (object, pspec->name, &new_val, NULL); + g_value_init (&val, pspec->value_type); + get_property_value (object, pspec, &val); + new_val = (gunichar)g_value_get_uint (&val); if (new_val != old_val) { if (!new_val) - len = 0; + len = 0; else - len = g_unichar_to_utf8 (new_val, buf); - + len = g_unichar_to_utf8 (new_val, buf); + buf[len] = '\0'; - + block_controller (G_OBJECT (entry)); gtk_entry_set_text (entry, buf); unblock_controller (G_OBJECT (entry)); } } -void +static void +pointer_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkLabel *label = GTK_LABEL (data); + gchar *str; + gpointer ptr; + + g_object_get (object, pspec->name, &ptr, NULL); + + str = g_strdup_printf ("Pointer: %p", ptr); + gtk_label_set_text (label, str); + g_free (str); +} + +static gchar * +object_label (GObject *obj, GParamSpec *pspec) +{ + const gchar *name; + + if (obj) + name = g_type_name (G_TYPE_FROM_INSTANCE (obj)); + else if (pspec) + name = g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)); + else + name = "unknown"; + return g_strdup_printf ("Object: %p (%s)", obj, name); +} + +static void +object_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkWidget *label, *button; + gchar *str; + GObject *obj; + + GList *children = gtk_container_get_children (GTK_CONTAINER (data)); + label = GTK_WIDGET (children->data); + button = GTK_WIDGET (children->next->data); + g_object_get (object, pspec->name, &obj, NULL); + g_list_free (children); + + str = object_label (obj, pspec); + + gtk_label_set_text (GTK_LABEL (label), str); + gtk_widget_set_sensitive (button, G_IS_OBJECT (obj)); + + if (obj) + g_object_unref (obj); + + g_free (str); +} + +static void model_destroy (gpointer data) { g_object_steal_data (data, "model-object"); gtk_widget_destroy (data); } -void +static void window_destroy (gpointer data) { g_object_steal_data (data, "prop-editor-win"); } -GtkWidget* -create_prop_editor (GObject *object, - GType type) +static void +object_properties (GtkWidget *button, + GObject *object) +{ + gchar *name; + GObject *obj; + + name = (gchar *) g_object_get_data (G_OBJECT (button), "property-name"); + g_object_get (object, name, &obj, NULL); + if (G_IS_OBJECT (obj)) + create_prop_editor (obj, 0); +} + +static void +rgba_modified (GtkColorButton *cb, gpointer data) +{ + ObjectProperty *p = data; + GdkRGBA color; + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &color); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, &color, NULL); + } + else + g_object_set (p->obj, p->spec->name, &color, NULL); +} + +static void +rgba_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkColorButton *cb = GTK_COLOR_BUTTON (data); + GValue val = G_VALUE_INIT; + GdkRGBA *color; + GdkRGBA cb_color; + + g_value_init (&val, GDK_TYPE_RGBA); + get_property_value (object, pspec, &val); + + color = g_value_get_boxed (&val); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &cb_color); + + if (color != NULL && !gdk_rgba_equal (color, &cb_color)) + { + block_controller (G_OBJECT (cb)); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cb), color); + unblock_controller (G_OBJECT (cb)); + } + + g_value_unset (&val); +} + +static void +color_modified (GtkColorButton *cb, gpointer data) +{ + ObjectProperty *p = data; + GValue val = G_VALUE_INIT; + + g_value_init (&val, GDK_TYPE_COLOR); + g_object_get_property (G_OBJECT (cb), "color", &val); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set_property (GTK_CONTAINER (parent), + widget, p->spec->name, &val); + } + else + g_object_set_property (p->obj, p->spec->name, &val); + + g_value_unset (&val); +} + +static void +color_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkColorButton *cb = GTK_COLOR_BUTTON (data); + GValue val = G_VALUE_INIT; + + g_value_init (&val, GDK_TYPE_COLOR); + get_property_value (object, pspec, &val); + + if (g_value_get_boxed (&val)) + { + block_controller (G_OBJECT (cb)); + g_object_set_property (G_OBJECT (cb), "color", &val); + unblock_controller (G_OBJECT (cb)); + } + + g_value_unset (&val); +} + +static void +font_modified (GtkFontChooser *fb, GParamSpec *pspec, gpointer data) +{ + ObjectProperty *p = data; + PangoFontDescription *font_desc; + + font_desc = gtk_font_chooser_get_font_desc (fb); + + if (is_child_property (p->spec)) + { + GtkWidget *widget = GTK_WIDGET (p->obj); + GtkWidget *parent = gtk_widget_get_parent (widget); + + gtk_container_child_set (GTK_CONTAINER (parent), + widget, p->spec->name, font_desc, NULL); + } + else + g_object_set (p->obj, p->spec->name, font_desc, NULL); + + pango_font_description_free (font_desc); +} + +static void +font_changed (GObject *object, GParamSpec *pspec, gpointer data) +{ + GtkFontChooser *fb = GTK_FONT_CHOOSER (data); + GValue val = G_VALUE_INIT; + const PangoFontDescription *font_desc; + PangoFontDescription *fb_font_desc; + + g_value_init (&val, PANGO_TYPE_FONT_DESCRIPTION); + get_property_value (object, pspec, &val); + + font_desc = g_value_get_boxed (&val); + fb_font_desc = gtk_font_chooser_get_font_desc (fb); + + if (font_desc == NULL || + (fb_font_desc != NULL && + !pango_font_description_equal (fb_font_desc, font_desc))) + { + block_controller (G_OBJECT (fb)); + gtk_font_chooser_set_font_desc (fb, font_desc); + unblock_controller (G_OBJECT (fb)); + } + + g_value_unset (&val); + pango_font_description_free (fb_font_desc); +} + + +static GtkWidget * +property_widget (GObject *object, + GParamSpec *spec, + gboolean can_modify) { - 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; + gchar *msg; + GType type = G_PARAM_SPEC_TYPE (spec); - win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + if (type == G_TYPE_PARAM_INT) + { + adj = 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); + + g_object_connect_property (object, spec, + G_CALLBACK (int_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (int_modified)); + } + else if (type == G_TYPE_PARAM_UINT) + { + adj = gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value, + G_PARAM_SPEC_UINT (spec)->minimum, + G_PARAM_SPEC_UINT (spec)->maximum, + 1, + MAX ((G_PARAM_SPEC_UINT (spec)->maximum - G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1), + 0.0); + + prop_edit = gtk_spin_button_new (adj, 1.0, 0); + + g_object_connect_property (object, spec, + G_CALLBACK (uint_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (uint_modified)); + } + else if (type == G_TYPE_PARAM_FLOAT) + { + adj = 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); + + g_object_connect_property (object, spec, + G_CALLBACK (float_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (float_modified)); + } + else if (type == G_TYPE_PARAM_DOUBLE) + { + adj = gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value, + G_PARAM_SPEC_DOUBLE (spec)->minimum, + G_PARAM_SPEC_DOUBLE (spec)->maximum, + 0.1, + MAX ((G_PARAM_SPEC_DOUBLE (spec)->maximum - G_PARAM_SPEC_DOUBLE (spec)->minimum) / 10, 0.1), + 0.0); + + prop_edit = gtk_spin_button_new (adj, 0.1, 2); + + g_object_connect_property (object, spec, + G_CALLBACK (double_changed), + adj, G_OBJECT (adj)); + + if (can_modify) + connect_controller (G_OBJECT (adj), "value_changed", + object, spec, G_CALLBACK (double_modified)); + } + else if (type == G_TYPE_PARAM_STRING) + { + prop_edit = gtk_entry_new (); - tips = gtk_tooltips_new (); - gtk_signal_connect_object (GTK_OBJECT (win), "destroy", - GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (tips)); + g_object_connect_property (object, spec, + G_CALLBACK (string_changed), + prop_edit, G_OBJECT (prop_edit)); - /* 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); + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (string_modified)); + } + else if (type == G_TYPE_PARAM_BOOLEAN) + { + prop_edit = gtk_toggle_button_new_with_label (""); + + g_object_connect_property (object, spec, + G_CALLBACK (bool_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "toggled", + object, spec, G_CALLBACK (bool_modified)); + } + else if (type == G_TYPE_PARAM_ENUM) + { + { + GEnumClass *eclass; + gint j; + + prop_edit = gtk_combo_box_text_new (); + + eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type)); + + j = 0; + while (j < eclass->n_values) + { + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (prop_edit), + eclass->values[j].value_name); + ++j; + } + + g_type_class_unref (eclass); + + g_object_connect_property (object, spec, + G_CALLBACK (enum_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (enum_modified)); + } + } + else if (type == G_TYPE_PARAM_FLAGS) + { + { + GFlagsClass *fclass; + gint j; + + prop_edit = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + fclass = G_FLAGS_CLASS (g_type_class_ref (spec->value_type)); + + for (j = 0; j < fclass->n_values; j++) + { + GtkWidget *b; + + b = gtk_check_button_new_with_label (fclass->values[j].value_name); + g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j)); + gtk_widget_show (b); + gtk_box_pack_start (GTK_BOX (prop_edit), b, FALSE, FALSE, 0); + if (can_modify) + connect_controller (G_OBJECT (b), "toggled", + object, spec, G_CALLBACK (flags_modified)); + } + + g_type_class_unref (fclass); + + g_object_connect_property (object, spec, + G_CALLBACK (flags_changed), + prop_edit, G_OBJECT (prop_edit)); + } + } + else if (type == G_TYPE_PARAM_UNICHAR) + { + prop_edit = gtk_entry_new (); + gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1); + + g_object_connect_property (object, spec, + G_CALLBACK (unichar_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "changed", + object, spec, G_CALLBACK (unichar_modified)); + } + else if (type == G_TYPE_PARAM_POINTER) + { + prop_edit = gtk_label_new (""); + + g_object_connect_property (object, spec, + G_CALLBACK (pointer_changed), + prop_edit, G_OBJECT (prop_edit)); + } + else if (type == G_TYPE_PARAM_OBJECT) + { + GtkWidget *label, *button; + + prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + + label = gtk_label_new (""); + button = gtk_button_new_with_label ("Properties"); + g_object_set_data (G_OBJECT (button), "property-name", (gpointer) spec->name); + g_signal_connect (button, "clicked", + G_CALLBACK (object_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + g_object_connect_property (object, spec, + G_CALLBACK (object_changed), + prop_edit, G_OBJECT (label)); + + /* The Properties button is not really modifying, anyway */ + can_modify = TRUE; + } + else if (type == G_TYPE_PARAM_BOXED && + G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_RGBA) + { + prop_edit = gtk_color_button_new (); + gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (prop_edit), TRUE); + + g_object_connect_property (object, spec, + G_CALLBACK (rgba_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "color-set", + object, spec, G_CALLBACK (rgba_modified)); + } + else if (type == G_TYPE_PARAM_BOXED && + G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_COLOR) + { + prop_edit = gtk_color_button_new (); + + g_object_connect_property (object, spec, + G_CALLBACK (color_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "color-set", + object, spec, G_CALLBACK (color_modified)); + } + else if (type == G_TYPE_PARAM_BOXED && + G_PARAM_SPEC_VALUE_TYPE (spec) == PANGO_TYPE_FONT_DESCRIPTION) + { + prop_edit = gtk_font_button_new (); + + g_object_connect_property (object, spec, + G_CALLBACK (font_changed), + prop_edit, G_OBJECT (prop_edit)); + + if (can_modify) + connect_controller (G_OBJECT (prop_edit), "notify::font-desc", + object, spec, G_CALLBACK (font_modified)); + } + else + { + msg = g_strdup_printf ("uneditable property type: %s", + g_type_name (G_PARAM_SPEC_TYPE (spec))); + prop_edit = gtk_label_new (msg); + g_free (msg); + gtk_widget_set_halign (prop_edit, GTK_ALIGN_START); + gtk_widget_set_valign (prop_edit, GTK_ALIGN_CENTER); + } + + if (!can_modify) + gtk_widget_set_sensitive (prop_edit, FALSE); + + if (g_param_spec_get_blurb (spec)) + gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec)); + + return prop_edit; +} + +static GtkWidget * +properties_from_type (GObject *object, + GType type) +{ + GtkWidget *prop_edit; + GtkWidget *label; + GtkWidget *sw; + GtkWidget *vbox; + GtkWidget *grid; + GParamSpec **specs; + guint n_specs; + int i; + + if (G_TYPE_IS_INTERFACE (type)) + { + gpointer vtable = g_type_default_interface_peek (type); + specs = g_object_interface_list_properties (vtable, &n_specs); + } + else + { + GObjectClass *class = G_OBJECT_CLASS (g_type_class_peek (type)); + specs = g_object_class_list_properties (class, &n_specs); + } + + if (n_specs == 0) { + g_free (specs); + return NULL; + } + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 10); + gtk_grid_set_row_spacing (GTK_GRID (grid), 3); + + 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; + } + + if (spec->owner_type != type) + { + /* we're only interested in params of type */ + ++i; + continue; + } + + label = gtk_label_new (g_param_spec_get_nick (spec)); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1); + + prop_edit = property_widget (object, spec, can_modify); + gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1); + + /* set initial value */ + g_object_notify (object, spec->name); + + ++i; + } + + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0); 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 (type, &specs, &n_specs); - + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (sw), vbox); + + g_free (specs); + + return sw; +} + +static GtkWidget * +child_properties_from_object (GObject *object) +{ + GtkWidget *prop_edit; + GtkWidget *label; + GtkWidget *sw; + GtkWidget *vbox; + GtkWidget *grid; + GtkWidget *parent; + GParamSpec **specs; + guint n_specs; + gint i; + + if (!GTK_IS_WIDGET (object)) + return NULL; + + parent = gtk_widget_get_parent (GTK_WIDGET (object)); + + if (!parent) + return NULL; + + specs = gtk_container_class_list_child_properties (G_OBJECT_GET_CLASS (parent), &n_specs); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 10); + gtk_grid_set_row_spacing (GTK_GRID (grid), 3); + 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)) + + label = gtk_label_new (g_param_spec_get_nick (spec)); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1); + + mark_child_property (spec); + prop_edit = property_widget (object, spec, can_modify); + gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1); + + /* set initial value */ + gtk_widget_child_notify (GTK_WIDGET (object), spec->name); + + ++i; + } + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (sw), vbox); + + g_free (specs); + + return sw; +} + +static void +child_properties (GtkWidget *button, + GObject *object) +{ + create_prop_editor (object, 0); +} + +static GtkWidget * +children_from_object (GObject *object) +{ + GList *children, *c; + GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw; + gchar *str; + gint i; + + if (!GTK_IS_CONTAINER (object)) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (object)); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 10); + gtk_grid_set_row_spacing (GTK_GRID (grid), 3); + + for (c = children, i = 0; c; c = c->next, i++) + { + object = c->data; + + label = gtk_label_new ("Child"); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1); + + prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + + str = object_label (object, NULL); + label = gtk_label_new (str); + g_free (str); + button = gtk_button_new_with_label ("Properties"); + g_signal_connect (button, "clicked", + G_CALLBACK (child_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1); + } + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (sw), vbox); + + g_list_free (children); + + return sw; +} + +static GtkWidget * +cells_from_object (GObject *object) +{ + GList *cells, *c; + GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw; + gchar *str; + gint i; + + if (!GTK_IS_CELL_LAYOUT (object)) + return NULL; + + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object)); + + grid = gtk_grid_new (); + gtk_grid_set_column_spacing (GTK_GRID (grid), 10); + gtk_grid_set_row_spacing (GTK_GRID (grid), 3); + + for (c = cells, i = 0; c; c = c->next, i++) + { + object = c->data; + + label = gtk_label_new ("Cell"); + gtk_widget_set_halign (label, GTK_ALIGN_START); + gtk_widget_set_valign (label, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1); + + prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); + + str = object_label (object, NULL); + label = gtk_label_new (str); + g_free (str); + button = gtk_button_new_with_label ("Properties"); + g_signal_connect (button, "clicked", + G_CALLBACK (child_properties), + object); + + gtk_container_add (GTK_CONTAINER (prop_edit), label); + gtk_container_add (GTK_CONTAINER (prop_edit), button); + + gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1); + } + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0); + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_container_add (GTK_CONTAINER (sw), vbox); + + g_list_free (cells); + + return sw; +} + +static void +open_parent_widget (GtkWidget *button, + GObject *object) +{ + GtkWidget *parent; + + parent = gtk_widget_get_parent (GTK_WIDGET (object)); + if (parent != NULL) + create_prop_editor (G_OBJECT (parent), 0); +} + + +/* Pass zero for type if you want all properties */ +GtkWidget* +create_prop_editor (GObject *object, + GType type) +{ + GtkWidget *win, *parent; + GtkWidget *notebook; + GtkWidget *properties; + GtkWidget *label; + GtkWidget *button; + GtkWidget *vbox; + gchar *title; + GType *ifaces; + guint n_ifaces; + + if ((win = g_object_get_data (G_OBJECT (object), "prop-editor-win"))) + { + gtk_window_present (GTK_WINDOW (win)); + return win; + } + + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + if (GTK_IS_WIDGET (object)) + gtk_window_set_screen (GTK_WINDOW (win), + gtk_widget_get_screen (GTK_WIDGET (object))); + + /* 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_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add (GTK_CONTAINER (win), vbox); + + if (type == 0) + { + notebook = gtk_notebook_new (); + gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT); + + gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0); + + type = G_TYPE_FROM_INSTANCE (object); + + title = g_strdup_printf ("Properties of %s widget", g_type_name (type)); + gtk_window_set_title (GTK_WINDOW (win), title); + g_free (title); + + while (type) { - 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); - } + properties = properties_from_type (object, type); + if (properties) + { + label = gtk_label_new (g_type_name (type)); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + type = g_type_parent (type); } - if (prop_edit) + ifaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), &n_ifaces); + while (n_ifaces--) { - 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); + properties = properties_from_type (object, ifaces[n_ifaces]); + if (properties) + { + label = gtk_label_new (g_type_name (ifaces[n_ifaces])); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } } - - ++i; + + g_free (ifaces); + + properties = child_properties_from_object (object); + if (properties) + { + label = gtk_label_new ("Child properties"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + properties = children_from_object (object); + if (properties) + { + label = gtk_label_new ("Children"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + properties = cells_from_object (object); + if (properties) + { + label = gtk_label_new ("Cell renderers"); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + properties, label); + } + + if (GTK_IS_WIDGET (object)) + { + parent = gtk_widget_get_parent (GTK_WIDGET (object)); + if (parent != NULL) + { + button = gtk_button_new_with_label ("Parent widget"); + gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); + g_signal_connect (button, "clicked", + G_CALLBACK (open_parent_widget), + object); + } + } } + else + { + properties = properties_from_type (object, type); + gtk_box_pack_start (GTK_BOX (vbox), properties, TRUE, TRUE, 0); + title = g_strdup_printf ("Properties of %s", g_type_name (type)); + gtk_window_set_title (GTK_WINDOW (win), title); + g_free (title); + } + + gtk_window_set_default_size (GTK_WINDOW (win), -1, 400); - gtk_window_set_default_size (GTK_WINDOW (win), 300, 500); - gtk_widget_show_all (win); return win;