2 * Copyright (C) 2000 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 #include "prop-editor.h"
28 GObject *alive_object;
33 disconnect_func (gpointer data)
35 DisconnectData *dd = data;
37 g_signal_handler_disconnect (dd->instance, dd->id);
41 signal_removed (gpointer data,
44 DisconnectData *dd = data;
46 g_object_steal_data (dd->alive_object, "alive-object-data");
51 is_child_property (GParamSpec *pspec)
53 return g_param_spec_get_qdata (pspec, g_quark_from_string ("is-child-prop")) != NULL;
57 mark_child_property (GParamSpec *pspec)
59 g_param_spec_set_qdata (pspec, g_quark_from_string ("is-child-prop"),
60 GINT_TO_POINTER (TRUE));
64 g_object_connect_property (GObject *object,
68 GObject *alive_object)
74 if (is_child_property (spec))
75 with_detail = g_strconcat ("child-notify::", spec->name, NULL);
77 with_detail = g_strconcat ("notify::", spec->name, NULL);
79 dd = g_new (DisconnectData, 1);
81 closure = g_cclosure_new (func, data, NULL);
83 g_closure_add_invalidate_notifier (closure, dd, signal_removed);
85 dd->id = g_signal_connect_closure (object, with_detail,
88 dd->instance = object;
89 dd->alive_object = alive_object;
91 g_object_set_data_full (G_OBJECT (alive_object),
107 free_object_property (ObjectProperty *p)
113 connect_controller (GObject *controller,
121 p = g_new (ObjectProperty, 1);
125 p->modified_id = g_signal_connect_data (controller, signal, func, p,
126 (GClosureNotify)free_object_property,
128 g_object_set_data (controller, "object-property", p);
132 block_controller (GObject *controller)
134 ObjectProperty *p = g_object_get_data (controller, "object-property");
137 g_signal_handler_block (controller, p->modified_id);
141 unblock_controller (GObject *controller)
143 ObjectProperty *p = g_object_get_data (controller, "object-property");
146 g_signal_handler_unblock (controller, p->modified_id);
150 int_modified (GtkAdjustment *adj, gpointer data)
152 ObjectProperty *p = data;
154 if (is_child_property (p->spec))
156 GtkWidget *widget = GTK_WIDGET (p->obj);
157 GtkWidget *parent = gtk_widget_get_parent (widget);
159 gtk_container_child_set (GTK_CONTAINER (parent),
160 widget, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL);
163 g_object_set (p->obj, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL);
167 get_property_value (GObject *object, GParamSpec *pspec, GValue *value)
169 if (is_child_property (pspec))
171 GtkWidget *widget = GTK_WIDGET (object);
172 GtkWidget *parent = gtk_widget_get_parent (widget);
174 gtk_container_child_get_property (GTK_CONTAINER (parent),
175 widget, pspec->name, value);
178 g_object_get_property (object, pspec->name, value);
182 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
184 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
185 GValue val = G_VALUE_INIT;
187 g_value_init (&val, G_TYPE_INT);
189 get_property_value (object, pspec, &val);
191 if (g_value_get_int (&val) != (int)gtk_adjustment_get_value (adj))
193 block_controller (G_OBJECT (adj));
194 gtk_adjustment_set_value (adj, g_value_get_int (&val));
195 unblock_controller (G_OBJECT (adj));
198 g_value_unset (&val);
202 uint_modified (GtkAdjustment *adj, gpointer data)
204 ObjectProperty *p = data;
206 if (is_child_property (p->spec))
208 GtkWidget *widget = GTK_WIDGET (p->obj);
209 GtkWidget *parent = gtk_widget_get_parent (widget);
211 gtk_container_child_set (GTK_CONTAINER (parent),
212 widget, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL);
215 g_object_set (p->obj, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL);
219 uint_changed (GObject *object, GParamSpec *pspec, gpointer data)
221 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
222 GValue val = G_VALUE_INIT;
224 g_value_init (&val, G_TYPE_UINT);
225 get_property_value (object, pspec, &val);
227 if (g_value_get_uint (&val) != (guint)gtk_adjustment_get_value (adj))
229 block_controller (G_OBJECT (adj));
230 gtk_adjustment_set_value (adj, g_value_get_uint (&val));
231 unblock_controller (G_OBJECT (adj));
234 g_value_unset (&val);
238 float_modified (GtkAdjustment *adj, gpointer data)
240 ObjectProperty *p = data;
242 if (is_child_property (p->spec))
244 GtkWidget *widget = GTK_WIDGET (p->obj);
245 GtkWidget *parent = gtk_widget_get_parent (widget);
247 gtk_container_child_set (GTK_CONTAINER (parent),
248 widget, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL);
251 g_object_set (p->obj, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL);
255 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
257 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
258 GValue val = G_VALUE_INIT;
260 g_value_init (&val, G_TYPE_FLOAT);
261 get_property_value (object, pspec, &val);
263 if (g_value_get_float (&val) != (float) gtk_adjustment_get_value (adj))
265 block_controller (G_OBJECT (adj));
266 gtk_adjustment_set_value (adj, g_value_get_float (&val));
267 unblock_controller (G_OBJECT (adj));
270 g_value_unset (&val);
274 double_modified (GtkAdjustment *adj, gpointer data)
276 ObjectProperty *p = data;
278 if (is_child_property (p->spec))
280 GtkWidget *widget = GTK_WIDGET (p->obj);
281 GtkWidget *parent = gtk_widget_get_parent (widget);
283 gtk_container_child_set (GTK_CONTAINER (parent),
284 widget, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL);
287 g_object_set (p->obj, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL);
291 double_changed (GObject *object, GParamSpec *pspec, gpointer data)
293 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
294 GValue val = G_VALUE_INIT;
296 g_value_init (&val, G_TYPE_DOUBLE);
297 get_property_value (object, pspec, &val);
299 if (g_value_get_double (&val) != gtk_adjustment_get_value (adj))
301 block_controller (G_OBJECT (adj));
302 gtk_adjustment_set_value (adj, g_value_get_double (&val));
303 unblock_controller (G_OBJECT (adj));
306 g_value_unset (&val);
310 string_modified (GtkEntry *entry, gpointer data)
312 ObjectProperty *p = data;
315 text = gtk_entry_get_text (entry);
317 if (is_child_property (p->spec))
319 GtkWidget *widget = GTK_WIDGET (p->obj);
320 GtkWidget *parent = gtk_widget_get_parent (widget);
322 gtk_container_child_set (GTK_CONTAINER (parent),
323 widget, p->spec->name, text, NULL);
326 g_object_set (p->obj, p->spec->name, text, NULL);
330 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
332 GtkEntry *entry = GTK_ENTRY (data);
333 GValue val = G_VALUE_INIT;
337 g_value_init (&val, G_TYPE_STRING);
338 get_property_value (object, pspec, &val);
340 str = g_value_get_string (&val);
343 text = gtk_entry_get_text (entry);
345 if (strcmp (str, text) != 0)
347 block_controller (G_OBJECT (entry));
348 gtk_entry_set_text (entry, str);
349 unblock_controller (G_OBJECT (entry));
352 g_value_unset (&val);
356 bool_modified (GtkToggleButton *tb, gpointer data)
358 ObjectProperty *p = data;
360 if (is_child_property (p->spec))
362 GtkWidget *widget = GTK_WIDGET (p->obj);
363 GtkWidget *parent = gtk_widget_get_parent (widget);
365 gtk_container_child_set (GTK_CONTAINER (parent), widget,
366 p->spec->name, (int) gtk_toggle_button_get_active (tb),
370 g_object_set (p->obj, p->spec->name, (int) gtk_toggle_button_get_active (tb), NULL);
374 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
376 GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
378 GValue val = G_VALUE_INIT;
380 g_value_init (&val, G_TYPE_BOOLEAN);
381 get_property_value (object, pspec, &val);
383 if (g_value_get_boolean (&val) != gtk_toggle_button_get_active (tb))
385 block_controller (G_OBJECT (tb));
386 gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
387 unblock_controller (G_OBJECT (tb));
390 child = gtk_bin_get_child (GTK_BIN (tb));
391 gtk_label_set_text (GTK_LABEL (child),
392 g_value_get_boolean (&val) ? "TRUE" : "FALSE");
394 g_value_unset (&val);
399 enum_modified (GtkComboBox *cb, gpointer data)
401 ObjectProperty *p = data;
405 eclass = G_ENUM_CLASS (g_type_class_peek (p->spec->value_type));
407 i = gtk_combo_box_get_active (cb);
410 if (is_child_property (p->spec))
412 GtkWidget *widget = GTK_WIDGET (p->obj);
413 GtkWidget *parent = gtk_widget_get_parent (widget);
415 gtk_container_child_set (GTK_CONTAINER (parent),
416 widget, p->spec->name, eclass->values[i].value, NULL);
419 g_object_set (p->obj, p->spec->name, eclass->values[i].value, NULL);
423 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
425 GtkComboBox *cb = GTK_COMBO_BOX (data);
426 GValue val = G_VALUE_INIT;
430 eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
432 g_value_init (&val, pspec->value_type);
433 get_property_value (object, pspec, &val);
436 while (i < eclass->n_values)
438 if (eclass->values[i].value == g_value_get_enum (&val))
443 if (gtk_combo_box_get_active (cb) != i)
445 block_controller (G_OBJECT (cb));
446 gtk_combo_box_set_active (cb, i);
447 unblock_controller (G_OBJECT (cb));
450 g_value_unset (&val);
455 flags_modified (GtkCheckButton *button, gpointer data)
457 ObjectProperty *p = data;
463 fclass = G_FLAGS_CLASS (g_type_class_peek (p->spec->value_type));
465 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
466 i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index"));
468 if (is_child_property (p->spec))
470 GtkWidget *widget = GTK_WIDGET (p->obj);
471 GtkWidget *parent = gtk_widget_get_parent (widget);
473 gtk_container_child_get (GTK_CONTAINER (parent),
474 widget, p->spec->name, &flags, NULL);
476 flags |= fclass->values[i].value;
478 flags &= ~fclass->values[i].value;
480 gtk_container_child_set (GTK_CONTAINER (parent),
481 widget, p->spec->name, flags, NULL);
485 g_object_get (p->obj, p->spec->name, &flags, NULL);
488 flags |= fclass->values[i].value;
490 flags &= ~fclass->values[i].value;
492 g_object_set (p->obj, p->spec->name, flags, NULL);
497 flags_changed (GObject *object, GParamSpec *pspec, gpointer data)
500 GValue val = G_VALUE_INIT;
505 fclass = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
507 g_value_init (&val, pspec->value_type);
508 get_property_value (object, pspec, &val);
509 flags = g_value_get_flags (&val);
510 g_value_unset (&val);
512 children = gtk_container_get_children (GTK_CONTAINER (data));
514 for (c = children, i = 0; c; c = c->next, i++)
516 block_controller (G_OBJECT (c->data));
517 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data),
518 (fclass->values[i].value & flags) != 0);
519 unblock_controller (G_OBJECT (c->data));
522 g_list_free (children);
526 unichar_get_value (GtkEntry *entry)
528 const gchar *text = gtk_entry_get_text (entry);
531 return g_utf8_get_char (text);
537 unichar_modified (GtkEntry *entry, gpointer data)
539 ObjectProperty *p = data;
540 gunichar val = unichar_get_value (entry);
542 if (is_child_property (p->spec))
544 GtkWidget *widget = GTK_WIDGET (p->obj);
545 GtkWidget *parent = gtk_widget_get_parent (widget);
547 gtk_container_child_set (GTK_CONTAINER (parent),
548 widget, p->spec->name, val, NULL);
551 g_object_set (p->obj, p->spec->name, val, NULL);
555 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
557 GtkEntry *entry = GTK_ENTRY (data);
559 gunichar old_val = unichar_get_value (entry);
560 GValue val = G_VALUE_INIT;
564 g_value_init (&val, pspec->value_type);
565 get_property_value (object, pspec, &val);
566 new_val = (gunichar)g_value_get_uint (&val);
568 if (new_val != old_val)
573 len = g_unichar_to_utf8 (new_val, buf);
577 block_controller (G_OBJECT (entry));
578 gtk_entry_set_text (entry, buf);
579 unblock_controller (G_OBJECT (entry));
584 pointer_changed (GObject *object, GParamSpec *pspec, gpointer data)
586 GtkLabel *label = GTK_LABEL (data);
590 g_object_get (object, pspec->name, &ptr, NULL);
592 str = g_strdup_printf ("Pointer: %p", ptr);
593 gtk_label_set_text (label, str);
598 object_label (GObject *obj, GParamSpec *pspec)
603 name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
605 name = g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec));
608 return g_strdup_printf ("Object: %p (%s)", obj, name);
612 object_changed (GObject *object, GParamSpec *pspec, gpointer data)
614 GtkWidget *label, *button;
618 GList *children = gtk_container_get_children (GTK_CONTAINER (data));
619 label = GTK_WIDGET (children->data);
620 button = GTK_WIDGET (children->next->data);
621 g_object_get (object, pspec->name, &obj, NULL);
622 g_list_free (children);
624 str = object_label (obj, pspec);
626 gtk_label_set_text (GTK_LABEL (label), str);
627 gtk_widget_set_sensitive (button, G_IS_OBJECT (obj));
630 g_object_unref (obj);
636 model_destroy (gpointer data)
638 g_object_steal_data (data, "model-object");
639 gtk_widget_destroy (data);
643 window_destroy (gpointer data)
645 g_object_steal_data (data, "prop-editor-win");
649 object_properties (GtkWidget *button,
655 name = (gchar *) g_object_get_data (G_OBJECT (button), "property-name");
656 g_object_get (object, name, &obj, NULL);
657 if (G_IS_OBJECT (obj))
658 create_prop_editor (obj, 0);
662 rgba_modified (GtkColorButton *cb, gpointer data)
664 ObjectProperty *p = data;
667 gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &color);
669 if (is_child_property (p->spec))
671 GtkWidget *widget = GTK_WIDGET (p->obj);
672 GtkWidget *parent = gtk_widget_get_parent (widget);
674 gtk_container_child_set (GTK_CONTAINER (parent),
675 widget, p->spec->name, &color, NULL);
678 g_object_set (p->obj, p->spec->name, &color, NULL);
682 rgba_changed (GObject *object, GParamSpec *pspec, gpointer data)
684 GtkColorButton *cb = GTK_COLOR_BUTTON (data);
685 GValue val = G_VALUE_INIT;
689 g_value_init (&val, GDK_TYPE_RGBA);
690 get_property_value (object, pspec, &val);
692 color = g_value_get_boxed (&val);
693 gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &cb_color);
695 if (color != NULL && !gdk_rgba_equal (color, &cb_color))
697 block_controller (G_OBJECT (cb));
698 gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cb), color);
699 unblock_controller (G_OBJECT (cb));
702 g_value_unset (&val);
706 color_modified (GtkColorButton *cb, gpointer data)
708 ObjectProperty *p = data;
709 GValue val = G_VALUE_INIT;
711 g_value_init (&val, GDK_TYPE_COLOR);
712 g_object_get_property (G_OBJECT (cb), "color", &val);
714 if (is_child_property (p->spec))
716 GtkWidget *widget = GTK_WIDGET (p->obj);
717 GtkWidget *parent = gtk_widget_get_parent (widget);
719 gtk_container_child_set_property (GTK_CONTAINER (parent),
720 widget, p->spec->name, &val);
723 g_object_set_property (p->obj, p->spec->name, &val);
725 g_value_unset (&val);
729 color_changed (GObject *object, GParamSpec *pspec, gpointer data)
731 GtkColorButton *cb = GTK_COLOR_BUTTON (data);
732 GValue val = G_VALUE_INIT;
734 g_value_init (&val, GDK_TYPE_COLOR);
735 get_property_value (object, pspec, &val);
737 if (g_value_get_boxed (&val))
739 block_controller (G_OBJECT (cb));
740 g_object_set_property (G_OBJECT (cb), "color", &val);
741 unblock_controller (G_OBJECT (cb));
744 g_value_unset (&val);
748 font_modified (GtkFontChooser *fb, GParamSpec *pspec, gpointer data)
750 ObjectProperty *p = data;
751 PangoFontDescription *font_desc;
753 font_desc = gtk_font_chooser_get_font_desc (fb);
755 if (is_child_property (p->spec))
757 GtkWidget *widget = GTK_WIDGET (p->obj);
758 GtkWidget *parent = gtk_widget_get_parent (widget);
760 gtk_container_child_set (GTK_CONTAINER (parent),
761 widget, p->spec->name, font_desc, NULL);
764 g_object_set (p->obj, p->spec->name, font_desc, NULL);
766 pango_font_description_free (font_desc);
770 font_changed (GObject *object, GParamSpec *pspec, gpointer data)
772 GtkFontChooser *fb = GTK_FONT_CHOOSER (data);
773 GValue val = G_VALUE_INIT;
774 const PangoFontDescription *font_desc;
775 PangoFontDescription *fb_font_desc;
777 g_value_init (&val, PANGO_TYPE_FONT_DESCRIPTION);
778 get_property_value (object, pspec, &val);
780 font_desc = g_value_get_boxed (&val);
781 fb_font_desc = gtk_font_chooser_get_font_desc (fb);
783 if (font_desc == NULL ||
784 (fb_font_desc != NULL &&
785 !pango_font_description_equal (fb_font_desc, font_desc)))
787 block_controller (G_OBJECT (fb));
788 gtk_font_chooser_set_font_desc (fb, font_desc);
789 unblock_controller (G_OBJECT (fb));
792 g_value_unset (&val);
793 pango_font_description_free (fb_font_desc);
798 property_widget (GObject *object,
802 GtkWidget *prop_edit;
805 GType type = G_PARAM_SPEC_TYPE (spec);
807 if (type == G_TYPE_PARAM_INT)
809 adj = gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
810 G_PARAM_SPEC_INT (spec)->minimum,
811 G_PARAM_SPEC_INT (spec)->maximum,
813 MAX ((G_PARAM_SPEC_INT (spec)->maximum - G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
816 prop_edit = gtk_spin_button_new (adj, 1.0, 0);
818 g_object_connect_property (object, spec,
819 G_CALLBACK (int_changed),
820 adj, G_OBJECT (adj));
823 connect_controller (G_OBJECT (adj), "value_changed",
824 object, spec, G_CALLBACK (int_modified));
826 else if (type == G_TYPE_PARAM_UINT)
828 adj = gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value,
829 G_PARAM_SPEC_UINT (spec)->minimum,
830 G_PARAM_SPEC_UINT (spec)->maximum,
832 MAX ((G_PARAM_SPEC_UINT (spec)->maximum - G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1),
835 prop_edit = gtk_spin_button_new (adj, 1.0, 0);
837 g_object_connect_property (object, spec,
838 G_CALLBACK (uint_changed),
839 adj, G_OBJECT (adj));
842 connect_controller (G_OBJECT (adj), "value_changed",
843 object, spec, G_CALLBACK (uint_modified));
845 else if (type == G_TYPE_PARAM_FLOAT)
847 adj = gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
848 G_PARAM_SPEC_FLOAT (spec)->minimum,
849 G_PARAM_SPEC_FLOAT (spec)->maximum,
851 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum - G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
854 prop_edit = gtk_spin_button_new (adj, 0.1, 2);
856 g_object_connect_property (object, spec,
857 G_CALLBACK (float_changed),
858 adj, G_OBJECT (adj));
861 connect_controller (G_OBJECT (adj), "value_changed",
862 object, spec, G_CALLBACK (float_modified));
864 else if (type == G_TYPE_PARAM_DOUBLE)
866 adj = gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value,
867 G_PARAM_SPEC_DOUBLE (spec)->minimum,
868 G_PARAM_SPEC_DOUBLE (spec)->maximum,
870 MAX ((G_PARAM_SPEC_DOUBLE (spec)->maximum - G_PARAM_SPEC_DOUBLE (spec)->minimum) / 10, 0.1),
873 prop_edit = gtk_spin_button_new (adj, 0.1, 2);
875 g_object_connect_property (object, spec,
876 G_CALLBACK (double_changed),
877 adj, G_OBJECT (adj));
880 connect_controller (G_OBJECT (adj), "value_changed",
881 object, spec, G_CALLBACK (double_modified));
883 else if (type == G_TYPE_PARAM_STRING)
885 prop_edit = gtk_entry_new ();
887 g_object_connect_property (object, spec,
888 G_CALLBACK (string_changed),
889 prop_edit, G_OBJECT (prop_edit));
892 connect_controller (G_OBJECT (prop_edit), "changed",
893 object, spec, G_CALLBACK (string_modified));
895 else if (type == G_TYPE_PARAM_BOOLEAN)
897 prop_edit = gtk_toggle_button_new_with_label ("");
899 g_object_connect_property (object, spec,
900 G_CALLBACK (bool_changed),
901 prop_edit, G_OBJECT (prop_edit));
904 connect_controller (G_OBJECT (prop_edit), "toggled",
905 object, spec, G_CALLBACK (bool_modified));
907 else if (type == G_TYPE_PARAM_ENUM)
913 prop_edit = gtk_combo_box_text_new ();
915 eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
918 while (j < eclass->n_values)
920 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (prop_edit),
921 eclass->values[j].value_name);
925 g_type_class_unref (eclass);
927 g_object_connect_property (object, spec,
928 G_CALLBACK (enum_changed),
929 prop_edit, G_OBJECT (prop_edit));
932 connect_controller (G_OBJECT (prop_edit), "changed",
933 object, spec, G_CALLBACK (enum_modified));
936 else if (type == G_TYPE_PARAM_FLAGS)
942 prop_edit = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
944 fclass = G_FLAGS_CLASS (g_type_class_ref (spec->value_type));
946 for (j = 0; j < fclass->n_values; j++)
950 b = gtk_check_button_new_with_label (fclass->values[j].value_name);
951 g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j));
953 gtk_box_pack_start (GTK_BOX (prop_edit), b, FALSE, FALSE, 0);
955 connect_controller (G_OBJECT (b), "toggled",
956 object, spec, G_CALLBACK (flags_modified));
959 g_type_class_unref (fclass);
961 g_object_connect_property (object, spec,
962 G_CALLBACK (flags_changed),
963 prop_edit, G_OBJECT (prop_edit));
966 else if (type == G_TYPE_PARAM_UNICHAR)
968 prop_edit = gtk_entry_new ();
969 gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
971 g_object_connect_property (object, spec,
972 G_CALLBACK (unichar_changed),
973 prop_edit, G_OBJECT (prop_edit));
976 connect_controller (G_OBJECT (prop_edit), "changed",
977 object, spec, G_CALLBACK (unichar_modified));
979 else if (type == G_TYPE_PARAM_POINTER)
981 prop_edit = gtk_label_new ("");
983 g_object_connect_property (object, spec,
984 G_CALLBACK (pointer_changed),
985 prop_edit, G_OBJECT (prop_edit));
987 else if (type == G_TYPE_PARAM_OBJECT)
989 GtkWidget *label, *button;
991 prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
993 label = gtk_label_new ("");
994 button = gtk_button_new_with_label ("Properties");
995 g_object_set_data (G_OBJECT (button), "property-name", (gpointer) spec->name);
996 g_signal_connect (button, "clicked",
997 G_CALLBACK (object_properties),
1000 gtk_container_add (GTK_CONTAINER (prop_edit), label);
1001 gtk_container_add (GTK_CONTAINER (prop_edit), button);
1003 g_object_connect_property (object, spec,
1004 G_CALLBACK (object_changed),
1005 prop_edit, G_OBJECT (label));
1007 /* The Properties button is not really modifying, anyway */
1010 else if (type == G_TYPE_PARAM_BOXED &&
1011 G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_RGBA)
1013 prop_edit = gtk_color_button_new ();
1014 gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (prop_edit), TRUE);
1016 g_object_connect_property (object, spec,
1017 G_CALLBACK (rgba_changed),
1018 prop_edit, G_OBJECT (prop_edit));
1021 connect_controller (G_OBJECT (prop_edit), "color-set",
1022 object, spec, G_CALLBACK (rgba_modified));
1024 else if (type == G_TYPE_PARAM_BOXED &&
1025 G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_COLOR)
1027 prop_edit = gtk_color_button_new ();
1029 g_object_connect_property (object, spec,
1030 G_CALLBACK (color_changed),
1031 prop_edit, G_OBJECT (prop_edit));
1034 connect_controller (G_OBJECT (prop_edit), "color-set",
1035 object, spec, G_CALLBACK (color_modified));
1037 else if (type == G_TYPE_PARAM_BOXED &&
1038 G_PARAM_SPEC_VALUE_TYPE (spec) == PANGO_TYPE_FONT_DESCRIPTION)
1040 prop_edit = gtk_font_button_new ();
1042 g_object_connect_property (object, spec,
1043 G_CALLBACK (font_changed),
1044 prop_edit, G_OBJECT (prop_edit));
1047 connect_controller (G_OBJECT (prop_edit), "notify::font-desc",
1048 object, spec, G_CALLBACK (font_modified));
1052 msg = g_strdup_printf ("uneditable property type: %s",
1053 g_type_name (G_PARAM_SPEC_TYPE (spec)));
1054 prop_edit = gtk_label_new (msg);
1056 gtk_widget_set_halign (prop_edit, GTK_ALIGN_START);
1057 gtk_widget_set_valign (prop_edit, GTK_ALIGN_CENTER);
1061 gtk_widget_set_sensitive (prop_edit, FALSE);
1063 if (g_param_spec_get_blurb (spec))
1064 gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec));
1070 properties_from_type (GObject *object,
1073 GtkWidget *prop_edit;
1082 if (G_TYPE_IS_INTERFACE (type))
1084 gpointer vtable = g_type_default_interface_peek (type);
1085 specs = g_object_interface_list_properties (vtable, &n_specs);
1089 GObjectClass *class = G_OBJECT_CLASS (g_type_class_peek (type));
1090 specs = g_object_class_list_properties (class, &n_specs);
1098 grid = gtk_grid_new ();
1099 gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1100 gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1105 GParamSpec *spec = specs[i];
1106 gboolean can_modify;
1110 can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1111 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1113 if ((spec->flags & G_PARAM_READABLE) == 0)
1115 /* can't display unreadable properties */
1120 if (spec->owner_type != type)
1122 /* we're only interested in params of type */
1127 label = gtk_label_new (g_param_spec_get_nick (spec));
1128 gtk_widget_set_halign (label, GTK_ALIGN_START);
1129 gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1130 gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1132 prop_edit = property_widget (object, spec, can_modify);
1133 gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1135 /* set initial value */
1136 g_object_notify (object, spec->name);
1142 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1143 gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1145 sw = gtk_scrolled_window_new (NULL, NULL);
1146 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1147 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1149 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1157 child_properties_from_object (GObject *object)
1159 GtkWidget *prop_edit;
1169 if (!GTK_IS_WIDGET (object))
1172 parent = gtk_widget_get_parent (GTK_WIDGET (object));
1177 specs = gtk_container_class_list_child_properties (G_OBJECT_GET_CLASS (parent), &n_specs);
1179 grid = gtk_grid_new ();
1180 gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1181 gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1186 GParamSpec *spec = specs[i];
1187 gboolean can_modify;
1191 can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1192 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1194 if ((spec->flags & G_PARAM_READABLE) == 0)
1196 /* can't display unreadable properties */
1201 label = gtk_label_new (g_param_spec_get_nick (spec));
1202 gtk_widget_set_halign (label, GTK_ALIGN_START);
1203 gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1204 gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1206 mark_child_property (spec);
1207 prop_edit = property_widget (object, spec, can_modify);
1208 gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1210 /* set initial value */
1211 gtk_widget_child_notify (GTK_WIDGET (object), spec->name);
1216 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1217 gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1219 sw = gtk_scrolled_window_new (NULL, NULL);
1220 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1221 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1223 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1231 child_properties (GtkWidget *button,
1234 create_prop_editor (object, 0);
1238 children_from_object (GObject *object)
1240 GList *children, *c;
1241 GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw;
1245 if (!GTK_IS_CONTAINER (object))
1248 children = gtk_container_get_children (GTK_CONTAINER (object));
1250 grid = gtk_grid_new ();
1251 gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1252 gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1254 for (c = children, i = 0; c; c = c->next, i++)
1258 label = gtk_label_new ("Child");
1259 gtk_widget_set_halign (label, GTK_ALIGN_START);
1260 gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1261 gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1263 prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1265 str = object_label (object, NULL);
1266 label = gtk_label_new (str);
1268 button = gtk_button_new_with_label ("Properties");
1269 g_signal_connect (button, "clicked",
1270 G_CALLBACK (child_properties),
1273 gtk_container_add (GTK_CONTAINER (prop_edit), label);
1274 gtk_container_add (GTK_CONTAINER (prop_edit), button);
1276 gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1279 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1280 gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1282 sw = gtk_scrolled_window_new (NULL, NULL);
1283 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1284 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1286 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1288 g_list_free (children);
1294 cells_from_object (GObject *object)
1297 GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw;
1301 if (!GTK_IS_CELL_LAYOUT (object))
1304 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object));
1306 grid = gtk_grid_new ();
1307 gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1308 gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1310 for (c = cells, i = 0; c; c = c->next, i++)
1314 label = gtk_label_new ("Cell");
1315 gtk_widget_set_halign (label, GTK_ALIGN_START);
1316 gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1317 gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1319 prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1321 str = object_label (object, NULL);
1322 label = gtk_label_new (str);
1324 button = gtk_button_new_with_label ("Properties");
1325 g_signal_connect (button, "clicked",
1326 G_CALLBACK (child_properties),
1329 gtk_container_add (GTK_CONTAINER (prop_edit), label);
1330 gtk_container_add (GTK_CONTAINER (prop_edit), button);
1332 gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1335 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1336 gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1338 sw = gtk_scrolled_window_new (NULL, NULL);
1339 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1340 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1342 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1344 g_list_free (cells);
1349 /* Pass zero for type if you want all properties */
1351 create_prop_editor (GObject *object,
1355 GtkWidget *notebook;
1356 GtkWidget *properties;
1362 if ((win = g_object_get_data (G_OBJECT (object), "prop-editor-win")))
1364 gtk_window_present (GTK_WINDOW (win));
1368 win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1369 if (GTK_IS_WIDGET (object))
1370 gtk_window_set_screen (GTK_WINDOW (win),
1371 gtk_widget_get_screen (GTK_WIDGET (object)));
1373 /* hold a weak ref to the object we're editing */
1374 g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy);
1375 g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy);
1379 notebook = gtk_notebook_new ();
1380 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);
1382 gtk_container_add (GTK_CONTAINER (win), notebook);
1384 type = G_TYPE_FROM_INSTANCE (object);
1386 title = g_strdup_printf ("Properties of %s widget", g_type_name (type));
1387 gtk_window_set_title (GTK_WINDOW (win), title);
1392 properties = properties_from_type (object, type);
1395 label = gtk_label_new (g_type_name (type));
1396 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1400 type = g_type_parent (type);
1403 ifaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), &n_ifaces);
1406 properties = properties_from_type (object, ifaces[n_ifaces]);
1409 label = gtk_label_new (g_type_name (ifaces[n_ifaces]));
1410 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1417 properties = child_properties_from_object (object);
1420 label = gtk_label_new ("Child properties");
1421 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1425 properties = children_from_object (object);
1428 label = gtk_label_new ("Children");
1429 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1433 properties = cells_from_object (object);
1436 label = gtk_label_new ("Cell renderers");
1437 gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1443 properties = properties_from_type (object, type);
1444 gtk_container_add (GTK_CONTAINER (win), properties);
1445 title = g_strdup_printf ("Properties of %s", g_type_name (type));
1446 gtk_window_set_title (GTK_WINDOW (win), title);
1450 gtk_window_set_default_size (GTK_WINDOW (win), -1, 400);
1452 gtk_widget_show_all (win);