]> Pileus Git - ~andy/gtk/blob - tests/prop-editor.c
prop-editor: don't use GtkColorButton deprecated API
[~andy/gtk] / tests / prop-editor.c
1 /* prop-editor.c
2  * Copyright (C) 2000  Red Hat, Inc.
3  *
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.
8  *
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.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21
22 #include <gtk/gtk.h>
23
24 #include "prop-editor.h"
25
26
27 typedef struct
28 {
29   gpointer instance;
30   GObject *alive_object;
31   gulong id;
32 } DisconnectData;
33
34 static void
35 disconnect_func (gpointer data)
36 {
37   DisconnectData *dd = data;
38
39   g_signal_handler_disconnect (dd->instance, dd->id);
40 }
41
42 static void
43 signal_removed (gpointer  data,
44                 GClosure *closure)
45 {
46   DisconnectData *dd = data;
47
48   g_object_steal_data (dd->alive_object, "alive-object-data");
49   g_free (dd);
50 }
51
52 static gboolean
53 is_child_property (GParamSpec *pspec)
54 {
55   return g_param_spec_get_qdata (pspec, g_quark_from_string ("is-child-prop")) != NULL;
56 }
57
58 static void
59 mark_child_property (GParamSpec *pspec)
60 {
61   g_param_spec_set_qdata (pspec, g_quark_from_string ("is-child-prop"),
62                           GINT_TO_POINTER (TRUE));
63 }
64
65 static void
66 g_object_connect_property (GObject     *object,
67                            GParamSpec  *spec,
68                            GCallback    func,
69                            gpointer     data,
70                            GObject     *alive_object)
71 {
72   GClosure *closure;
73   gchar *with_detail;
74   DisconnectData *dd;
75
76   if (is_child_property (spec))
77     with_detail = g_strconcat ("child-notify::", spec->name, NULL);
78   else
79     with_detail = g_strconcat ("notify::", spec->name, NULL);
80
81   dd = g_new (DisconnectData, 1);
82
83   closure = g_cclosure_new (func, data, NULL);
84
85   g_closure_add_invalidate_notifier (closure, dd, signal_removed);
86
87   dd->id = g_signal_connect_closure (object, with_detail,
88                                      closure, FALSE);
89
90   dd->instance = object;
91   dd->alive_object = alive_object;
92
93   g_object_set_data_full (G_OBJECT (alive_object),
94                           "alive-object-data",
95                           dd,
96                           disconnect_func);
97
98   g_free (with_detail);
99 }
100
101 typedef struct
102 {
103   GObject *obj;
104   GParamSpec *spec;
105   gulong modified_id;
106 } ObjectProperty;
107
108 static void
109 free_object_property (ObjectProperty *p)
110 {
111   g_free (p);
112 }
113
114 static void
115 connect_controller (GObject     *controller,
116                     const gchar *signal,
117                     GObject     *model,
118                     GParamSpec  *spec,
119                     GCallback    func)
120 {
121   ObjectProperty *p;
122
123   p = g_new (ObjectProperty, 1);
124   p->obj = model;
125   p->spec = spec;
126
127   p->modified_id = g_signal_connect_data (controller, signal, func, p,
128                                           (GClosureNotify)free_object_property,
129                                           0);
130   g_object_set_data (controller, "object-property", p);
131 }
132
133 static void
134 block_controller (GObject *controller)
135 {
136   ObjectProperty *p = g_object_get_data (controller, "object-property");
137
138   if (p)
139     g_signal_handler_block (controller, p->modified_id);
140 }
141
142 static void
143 unblock_controller (GObject *controller)
144 {
145   ObjectProperty *p = g_object_get_data (controller, "object-property");
146
147   if (p)
148     g_signal_handler_unblock (controller, p->modified_id);
149 }
150
151 static void
152 int_modified (GtkAdjustment *adj, gpointer data)
153 {
154   ObjectProperty *p = data;
155
156   if (is_child_property (p->spec))
157     {
158       GtkWidget *widget = GTK_WIDGET (p->obj);
159       GtkWidget *parent = gtk_widget_get_parent (widget);
160
161       gtk_container_child_set (GTK_CONTAINER (parent),
162                                widget, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL);
163     }
164   else
165     g_object_set (p->obj, p->spec->name, (int) gtk_adjustment_get_value (adj), NULL);
166 }
167
168 static void
169 get_property_value (GObject *object, GParamSpec *pspec, GValue *value)
170 {
171   if (is_child_property (pspec))
172     {
173       GtkWidget *widget = GTK_WIDGET (object);
174       GtkWidget *parent = gtk_widget_get_parent (widget);
175
176       gtk_container_child_get_property (GTK_CONTAINER (parent),
177                                         widget, pspec->name, value);
178     }
179   else
180     g_object_get_property (object, pspec->name, value);
181 }
182
183 static void
184 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
185 {
186   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
187   GValue val = G_VALUE_INIT;
188
189   g_value_init (&val, G_TYPE_INT);
190
191   get_property_value (object, pspec, &val);
192
193   if (g_value_get_int (&val) != (int)gtk_adjustment_get_value (adj))
194     {
195       block_controller (G_OBJECT (adj));
196       gtk_adjustment_set_value (adj, g_value_get_int (&val));
197       unblock_controller (G_OBJECT (adj));
198     }
199
200   g_value_unset (&val);
201 }
202
203 static void
204 uint_modified (GtkAdjustment *adj, gpointer data)
205 {
206   ObjectProperty *p = data;
207
208   if (is_child_property (p->spec))
209     {
210       GtkWidget *widget = GTK_WIDGET (p->obj);
211       GtkWidget *parent = gtk_widget_get_parent (widget);
212
213       gtk_container_child_set (GTK_CONTAINER (parent),
214                                widget, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL);
215     }
216   else
217     g_object_set (p->obj, p->spec->name, (guint) gtk_adjustment_get_value (adj), NULL);
218 }
219
220 static void
221 uint_changed (GObject *object, GParamSpec *pspec, gpointer data)
222 {
223   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
224   GValue val = G_VALUE_INIT;
225
226   g_value_init (&val, G_TYPE_UINT);
227   get_property_value (object, pspec, &val);
228
229   if (g_value_get_uint (&val) != (guint)gtk_adjustment_get_value (adj))
230     {
231       block_controller (G_OBJECT (adj));
232       gtk_adjustment_set_value (adj, g_value_get_uint (&val));
233       unblock_controller (G_OBJECT (adj));
234     }
235
236   g_value_unset (&val);
237 }
238
239 static void
240 float_modified (GtkAdjustment *adj, gpointer data)
241 {
242   ObjectProperty *p = data;
243
244   if (is_child_property (p->spec))
245     {
246       GtkWidget *widget = GTK_WIDGET (p->obj);
247       GtkWidget *parent = gtk_widget_get_parent (widget);
248
249       gtk_container_child_set (GTK_CONTAINER (parent),
250                                widget, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL);
251     }
252   else
253     g_object_set (p->obj, p->spec->name, (float) gtk_adjustment_get_value (adj), NULL);
254 }
255
256 static void
257 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
258 {
259   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
260   GValue val = G_VALUE_INIT;
261
262   g_value_init (&val, G_TYPE_FLOAT);
263   get_property_value (object, pspec, &val);
264
265   if (g_value_get_float (&val) != (float) gtk_adjustment_get_value (adj))
266     {
267       block_controller (G_OBJECT (adj));
268       gtk_adjustment_set_value (adj, g_value_get_float (&val));
269       unblock_controller (G_OBJECT (adj));
270     }
271
272   g_value_unset (&val);
273 }
274
275 static void
276 double_modified (GtkAdjustment *adj, gpointer data)
277 {
278   ObjectProperty *p = data;
279
280   if (is_child_property (p->spec))
281     {
282       GtkWidget *widget = GTK_WIDGET (p->obj);
283       GtkWidget *parent = gtk_widget_get_parent (widget);
284
285       gtk_container_child_set (GTK_CONTAINER (parent),
286                                widget, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL);
287     }
288   else
289     g_object_set (p->obj, p->spec->name, (double) gtk_adjustment_get_value (adj), NULL);
290 }
291
292 static void
293 double_changed (GObject *object, GParamSpec *pspec, gpointer data)
294 {
295   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
296   GValue val = G_VALUE_INIT;
297
298   g_value_init (&val, G_TYPE_DOUBLE);
299   get_property_value (object, pspec, &val);
300
301   if (g_value_get_double (&val) != gtk_adjustment_get_value (adj))
302     {
303       block_controller (G_OBJECT (adj));
304       gtk_adjustment_set_value (adj, g_value_get_double (&val));
305       unblock_controller (G_OBJECT (adj));
306     }
307
308   g_value_unset (&val);
309 }
310
311 static void
312 string_modified (GtkEntry *entry, gpointer data)
313 {
314   ObjectProperty *p = data;
315   const gchar *text;
316
317   text = gtk_entry_get_text (entry);
318
319   if (is_child_property (p->spec))
320     {
321       GtkWidget *widget = GTK_WIDGET (p->obj);
322       GtkWidget *parent = gtk_widget_get_parent (widget);
323
324       gtk_container_child_set (GTK_CONTAINER (parent),
325                                widget, p->spec->name, text, NULL);
326     }
327   else
328     g_object_set (p->obj, p->spec->name, text, NULL);
329 }
330
331 static void
332 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
333 {
334   GtkEntry *entry = GTK_ENTRY (data);
335   GValue val = G_VALUE_INIT;
336   const gchar *str;
337   const gchar *text;
338
339   g_value_init (&val, G_TYPE_STRING);
340   get_property_value (object, pspec, &val);
341
342   str = g_value_get_string (&val);
343   if (str == NULL)
344     str = "";
345   text = gtk_entry_get_text (entry);
346
347   if (strcmp (str, text) != 0)
348     {
349       block_controller (G_OBJECT (entry));
350       gtk_entry_set_text (entry, str);
351       unblock_controller (G_OBJECT (entry));
352     }
353
354   g_value_unset (&val);
355 }
356
357 static void
358 bool_modified (GtkToggleButton *tb, gpointer data)
359 {
360   ObjectProperty *p = data;
361
362   if (is_child_property (p->spec))
363     {
364       GtkWidget *widget = GTK_WIDGET (p->obj);
365       GtkWidget *parent = gtk_widget_get_parent (widget);
366
367       gtk_container_child_set (GTK_CONTAINER (parent), widget,
368                                p->spec->name, (int) gtk_toggle_button_get_active (tb),
369                                NULL);
370     }
371   else
372     g_object_set (p->obj, p->spec->name, (int) gtk_toggle_button_get_active (tb), NULL);
373 }
374
375 static void
376 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
377 {
378   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
379   GtkWidget *child;
380   GValue val = G_VALUE_INIT;
381
382   g_value_init (&val, G_TYPE_BOOLEAN);
383   get_property_value (object, pspec, &val);
384
385   if (g_value_get_boolean (&val) != gtk_toggle_button_get_active (tb))
386     {
387       block_controller (G_OBJECT (tb));
388       gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
389       unblock_controller (G_OBJECT (tb));
390     }
391
392   child = gtk_bin_get_child (GTK_BIN (tb));
393   gtk_label_set_text (GTK_LABEL (child),
394                       g_value_get_boolean (&val) ? "TRUE" : "FALSE");
395
396   g_value_unset (&val);
397 }
398
399
400 static void
401 enum_modified (GtkComboBox *cb, gpointer data)
402 {
403   ObjectProperty *p = data;
404   gint i;
405   GEnumClass *eclass;
406
407   eclass = G_ENUM_CLASS (g_type_class_peek (p->spec->value_type));
408
409   i = gtk_combo_box_get_active (cb);
410
411
412   if (is_child_property (p->spec))
413     {
414       GtkWidget *widget = GTK_WIDGET (p->obj);
415       GtkWidget *parent = gtk_widget_get_parent (widget);
416
417       gtk_container_child_set (GTK_CONTAINER (parent),
418                                widget, p->spec->name, eclass->values[i].value, NULL);
419     }
420   else
421     g_object_set (p->obj, p->spec->name, eclass->values[i].value, NULL);
422 }
423
424 static void
425 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
426 {
427   GtkComboBox *cb = GTK_COMBO_BOX (data);
428   GValue val = G_VALUE_INIT;
429   GEnumClass *eclass;
430   gint i;
431
432   eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
433
434   g_value_init (&val, pspec->value_type);
435   get_property_value (object, pspec, &val);
436
437   i = 0;
438   while (i < eclass->n_values)
439     {
440       if (eclass->values[i].value == g_value_get_enum (&val))
441         break;
442       ++i;
443     }
444
445   if (gtk_combo_box_get_active (cb) != i)
446     {
447       block_controller (G_OBJECT (cb));
448       gtk_combo_box_set_active (cb, i);
449       unblock_controller (G_OBJECT (cb));
450     }
451
452   g_value_unset (&val);
453
454 }
455
456 static void
457 flags_modified (GtkCheckButton *button, gpointer data)
458 {
459   ObjectProperty *p = data;
460   gboolean active;
461   GFlagsClass *fclass;
462   guint flags;
463   gint i;
464
465   fclass = G_FLAGS_CLASS (g_type_class_peek (p->spec->value_type));
466
467   active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
468   i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index"));
469
470   if (is_child_property (p->spec))
471     {
472       GtkWidget *widget = GTK_WIDGET (p->obj);
473       GtkWidget *parent = gtk_widget_get_parent (widget);
474
475       gtk_container_child_get (GTK_CONTAINER (parent),
476                                widget, p->spec->name, &flags, NULL);
477       if (active)
478         flags |= fclass->values[i].value;
479       else
480         flags &= ~fclass->values[i].value;
481
482       gtk_container_child_set (GTK_CONTAINER (parent),
483                                widget, p->spec->name, flags, NULL);
484     }
485   else
486     {
487       g_object_get (p->obj, p->spec->name, &flags, NULL);
488
489       if (active)
490         flags |= fclass->values[i].value;
491       else
492         flags &= ~fclass->values[i].value;
493
494       g_object_set (p->obj, p->spec->name, flags, NULL);
495     }
496 }
497
498 static void
499 flags_changed (GObject *object, GParamSpec *pspec, gpointer data)
500 {
501   GList *children, *c;
502   GValue val = G_VALUE_INIT;
503   GFlagsClass *fclass;
504   guint flags;
505   gint i;
506
507   fclass = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
508
509   g_value_init (&val, pspec->value_type);
510   get_property_value (object, pspec, &val);
511   flags = g_value_get_flags (&val);
512   g_value_unset (&val);
513
514   children = gtk_container_get_children (GTK_CONTAINER (data));
515
516   for (c = children, i = 0; c; c = c->next, i++)
517     {
518       block_controller (G_OBJECT (c->data));
519       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data),
520                                     (fclass->values[i].value & flags) != 0);
521       unblock_controller (G_OBJECT (c->data));
522     }
523
524   g_list_free (children);
525 }
526
527 static gunichar
528 unichar_get_value (GtkEntry *entry)
529 {
530   const gchar *text = gtk_entry_get_text (entry);
531
532   if (text[0])
533     return g_utf8_get_char (text);
534   else
535     return 0;
536 }
537
538 static void
539 unichar_modified (GtkEntry *entry, gpointer data)
540 {
541   ObjectProperty *p = data;
542   gunichar val = unichar_get_value (entry);
543
544   if (is_child_property (p->spec))
545     {
546       GtkWidget *widget = GTK_WIDGET (p->obj);
547       GtkWidget *parent = gtk_widget_get_parent (widget);
548
549       gtk_container_child_set (GTK_CONTAINER (parent),
550                                widget, p->spec->name, val, NULL);
551     }
552   else
553     g_object_set (p->obj, p->spec->name, val, NULL);
554 }
555
556 static void
557 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
558 {
559   GtkEntry *entry = GTK_ENTRY (data);
560   gunichar new_val;
561   gunichar old_val = unichar_get_value (entry);
562   GValue val = G_VALUE_INIT;
563   gchar buf[7];
564   gint len;
565
566   g_value_init (&val, pspec->value_type);
567   get_property_value (object, pspec, &val);
568   new_val = (gunichar)g_value_get_uint (&val);
569
570   if (new_val != old_val)
571     {
572       if (!new_val)
573         len = 0;
574       else
575         len = g_unichar_to_utf8 (new_val, buf);
576
577       buf[len] = '\0';
578
579       block_controller (G_OBJECT (entry));
580       gtk_entry_set_text (entry, buf);
581       unblock_controller (G_OBJECT (entry));
582     }
583 }
584
585 static void
586 pointer_changed (GObject *object, GParamSpec *pspec, gpointer data)
587 {
588   GtkLabel *label = GTK_LABEL (data);
589   gchar *str;
590   gpointer ptr;
591
592   g_object_get (object, pspec->name, &ptr, NULL);
593
594   str = g_strdup_printf ("Pointer: %p", ptr);
595   gtk_label_set_text (label, str);
596   g_free (str);
597 }
598
599 static gchar *
600 object_label (GObject *obj, GParamSpec *pspec)
601 {
602   const gchar *name;
603
604   if (obj)
605     name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
606   else if (pspec)
607     name = g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec));
608   else
609     name = "unknown";
610   return g_strdup_printf ("Object: %p (%s)", obj, name);
611 }
612
613 static void
614 object_changed (GObject *object, GParamSpec *pspec, gpointer data)
615 {
616   GtkWidget *label, *button;
617   gchar *str;
618   GObject *obj;
619
620   GList *children = gtk_container_get_children (GTK_CONTAINER (data));
621   label = GTK_WIDGET (children->data);
622   button = GTK_WIDGET (children->next->data);
623   g_object_get (object, pspec->name, &obj, NULL);
624   g_list_free (children);
625
626   str = object_label (obj, pspec);
627
628   gtk_label_set_text (GTK_LABEL (label), str);
629   gtk_widget_set_sensitive (button, G_IS_OBJECT (obj));
630
631   if (obj)
632     g_object_unref (obj);
633
634   g_free (str);
635 }
636
637 static void
638 model_destroy (gpointer data)
639 {
640   g_object_steal_data (data, "model-object");
641   gtk_widget_destroy (data);
642 }
643
644 static void
645 window_destroy (gpointer data)
646 {
647   g_object_steal_data (data, "prop-editor-win");
648 }
649
650 static void
651 object_properties (GtkWidget *button,
652                    GObject   *object)
653 {
654   gchar *name;
655   GObject *obj;
656
657   name = (gchar *) g_object_get_data (G_OBJECT (button), "property-name");
658   g_object_get (object, name, &obj, NULL);
659   if (G_IS_OBJECT (obj))
660     create_prop_editor (obj, 0);
661 }
662
663 static void
664 rgba_modified (GtkColorButton *cb, gpointer data)
665 {
666   ObjectProperty *p = data;
667   GdkRGBA color;
668
669   gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &color);
670
671   if (is_child_property (p->spec))
672     {
673       GtkWidget *widget = GTK_WIDGET (p->obj);
674       GtkWidget *parent = gtk_widget_get_parent (widget);
675
676       gtk_container_child_set (GTK_CONTAINER (parent),
677                                widget, p->spec->name, &color, NULL);
678     }
679   else
680     g_object_set (p->obj, p->spec->name, &color, NULL);
681 }
682
683 static void
684 rgba_changed (GObject *object, GParamSpec *pspec, gpointer data)
685 {
686   GtkColorButton *cb = GTK_COLOR_BUTTON (data);
687   GValue val = G_VALUE_INIT;
688   GdkRGBA *color;
689   GdkRGBA cb_color;
690
691   g_value_init (&val, GDK_TYPE_RGBA);
692   get_property_value (object, pspec, &val);
693
694   color = g_value_get_boxed (&val);
695   gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &cb_color);
696
697   if (color != NULL && !gdk_rgba_equal (color, &cb_color))
698     {
699       block_controller (G_OBJECT (cb));
700       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cb), color);
701       unblock_controller (G_OBJECT (cb));
702     }
703
704   g_value_unset (&val);
705 }
706
707 static void
708 color_modified (GtkColorButton *cb, gpointer data)
709 {
710   ObjectProperty *p = data;
711   GValue val = G_VALUE_INIT;
712
713   g_value_init (&val, GDK_TYPE_COLOR);
714   g_object_get_property (G_OBJECT (cb), "color", &val);
715
716   if (is_child_property (p->spec))
717     {
718       GtkWidget *widget = GTK_WIDGET (p->obj);
719       GtkWidget *parent = gtk_widget_get_parent (widget);
720
721       gtk_container_child_set_property (GTK_CONTAINER (parent),
722                                         widget, p->spec->name, &val);
723     }
724   else
725     g_object_set_property (p->obj, p->spec->name, &val);
726
727   g_value_unset (&val);
728 }
729
730 static void
731 color_changed (GObject *object, GParamSpec *pspec, gpointer data)
732 {
733   GtkColorButton *cb = GTK_COLOR_BUTTON (data);
734   GValue val = G_VALUE_INIT;
735
736   g_value_init (&val, GDK_TYPE_COLOR);
737   get_property_value (object, pspec, &val);
738
739   if (g_value_get_boxed (&val))
740     {
741       block_controller (G_OBJECT (cb));
742       g_object_set_property (G_OBJECT (cb), "color", &val);
743       unblock_controller (G_OBJECT (cb));
744     }
745
746   g_value_unset (&val);
747 }
748
749 static void
750 font_modified (GtkFontChooser *fb, GParamSpec *pspec, gpointer data)
751 {
752   ObjectProperty *p = data;
753   PangoFontDescription *font_desc;
754
755   font_desc = gtk_font_chooser_get_font_desc (fb);
756
757   if (is_child_property (p->spec))
758     {
759       GtkWidget *widget = GTK_WIDGET (p->obj);
760       GtkWidget *parent = gtk_widget_get_parent (widget);
761
762       gtk_container_child_set (GTK_CONTAINER (parent),
763                                widget, p->spec->name, font_desc, NULL);
764     }
765   else
766     g_object_set (p->obj, p->spec->name, font_desc, NULL);
767
768   pango_font_description_free (font_desc);
769 }
770
771 static void
772 font_changed (GObject *object, GParamSpec *pspec, gpointer data)
773 {
774   GtkFontChooser *fb = GTK_FONT_CHOOSER (data);
775   GValue val = G_VALUE_INIT;
776   const PangoFontDescription *font_desc;
777   PangoFontDescription *fb_font_desc;
778
779   g_value_init (&val, PANGO_TYPE_FONT_DESCRIPTION);
780   get_property_value (object, pspec, &val);
781
782   font_desc = g_value_get_boxed (&val);
783   fb_font_desc = gtk_font_chooser_get_font_desc (fb);
784
785   if (font_desc == NULL ||
786       (fb_font_desc != NULL &&
787        !pango_font_description_equal (fb_font_desc, font_desc)))
788     {
789       block_controller (G_OBJECT (fb));
790       gtk_font_chooser_set_font_desc (fb, font_desc);
791       unblock_controller (G_OBJECT (fb));
792     }
793
794   g_value_unset (&val);
795   pango_font_description_free (fb_font_desc);
796 }
797
798
799 static GtkWidget *
800 property_widget (GObject    *object,
801                  GParamSpec *spec,
802                  gboolean    can_modify)
803 {
804   GtkWidget *prop_edit;
805   GtkAdjustment *adj;
806   gchar *msg;
807   GType type = G_PARAM_SPEC_TYPE (spec);
808
809   if (type == G_TYPE_PARAM_INT)
810     {
811       adj = gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
812                                 G_PARAM_SPEC_INT (spec)->minimum,
813                                 G_PARAM_SPEC_INT (spec)->maximum,
814                                 1,
815                                 MAX ((G_PARAM_SPEC_INT (spec)->maximum - G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
816                                 0.0);
817
818       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
819
820       g_object_connect_property (object, spec,
821                                  G_CALLBACK (int_changed),
822                                  adj, G_OBJECT (adj));
823
824       if (can_modify)
825         connect_controller (G_OBJECT (adj), "value_changed",
826                             object, spec, G_CALLBACK (int_modified));
827     }
828   else if (type == G_TYPE_PARAM_UINT)
829     {
830       adj = gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value,
831                                 G_PARAM_SPEC_UINT (spec)->minimum,
832                                 G_PARAM_SPEC_UINT (spec)->maximum,
833                                 1,
834                                 MAX ((G_PARAM_SPEC_UINT (spec)->maximum - G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1),
835                                 0.0);
836
837       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
838
839       g_object_connect_property (object, spec,
840                                  G_CALLBACK (uint_changed),
841                                  adj, G_OBJECT (adj));
842
843       if (can_modify)
844         connect_controller (G_OBJECT (adj), "value_changed",
845                             object, spec, G_CALLBACK (uint_modified));
846     }
847   else if (type == G_TYPE_PARAM_FLOAT)
848     {
849       adj = gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
850                                 G_PARAM_SPEC_FLOAT (spec)->minimum,
851                                 G_PARAM_SPEC_FLOAT (spec)->maximum,
852                                 0.1,
853                                 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum - G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
854                                 0.0);
855
856       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
857
858       g_object_connect_property (object, spec,
859                                  G_CALLBACK (float_changed),
860                                  adj, G_OBJECT (adj));
861
862       if (can_modify)
863         connect_controller (G_OBJECT (adj), "value_changed",
864                             object, spec, G_CALLBACK (float_modified));
865     }
866   else if (type == G_TYPE_PARAM_DOUBLE)
867     {
868       adj = gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value,
869                                 G_PARAM_SPEC_DOUBLE (spec)->minimum,
870                                 G_PARAM_SPEC_DOUBLE (spec)->maximum,
871                                 0.1,
872                                 MAX ((G_PARAM_SPEC_DOUBLE (spec)->maximum - G_PARAM_SPEC_DOUBLE (spec)->minimum) / 10, 0.1),
873                                 0.0);
874
875       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
876
877       g_object_connect_property (object, spec,
878                                  G_CALLBACK (double_changed),
879                                  adj, G_OBJECT (adj));
880
881       if (can_modify)
882         connect_controller (G_OBJECT (adj), "value_changed",
883                             object, spec, G_CALLBACK (double_modified));
884     }
885   else if (type == G_TYPE_PARAM_STRING)
886     {
887       prop_edit = gtk_entry_new ();
888
889       g_object_connect_property (object, spec,
890                                  G_CALLBACK (string_changed),
891                                  prop_edit, G_OBJECT (prop_edit));
892
893       if (can_modify)
894         connect_controller (G_OBJECT (prop_edit), "changed",
895                             object, spec, G_CALLBACK (string_modified));
896     }
897   else if (type == G_TYPE_PARAM_BOOLEAN)
898     {
899       prop_edit = gtk_toggle_button_new_with_label ("");
900
901       g_object_connect_property (object, spec,
902                                  G_CALLBACK (bool_changed),
903                                  prop_edit, G_OBJECT (prop_edit));
904
905       if (can_modify)
906         connect_controller (G_OBJECT (prop_edit), "toggled",
907                             object, spec, G_CALLBACK (bool_modified));
908     }
909   else if (type == G_TYPE_PARAM_ENUM)
910     {
911       {
912         GEnumClass *eclass;
913         gint j;
914
915         prop_edit = gtk_combo_box_text_new ();
916
917         eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
918
919         j = 0;
920         while (j < eclass->n_values)
921           {
922             gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (prop_edit),
923                                             eclass->values[j].value_name);
924             ++j;
925           }
926
927         g_type_class_unref (eclass);
928
929         g_object_connect_property (object, spec,
930                                    G_CALLBACK (enum_changed),
931                                    prop_edit, G_OBJECT (prop_edit));
932
933         if (can_modify)
934           connect_controller (G_OBJECT (prop_edit), "changed",
935                               object, spec, G_CALLBACK (enum_modified));
936       }
937     }
938   else if (type == G_TYPE_PARAM_FLAGS)
939     {
940       {
941         GFlagsClass *fclass;
942         gint j;
943
944         prop_edit = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
945
946         fclass = G_FLAGS_CLASS (g_type_class_ref (spec->value_type));
947
948         for (j = 0; j < fclass->n_values; j++)
949           {
950             GtkWidget *b;
951
952             b = gtk_check_button_new_with_label (fclass->values[j].value_name);
953             g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j));
954             gtk_widget_show (b);
955             gtk_box_pack_start (GTK_BOX (prop_edit), b, FALSE, FALSE, 0);
956             if (can_modify)
957               connect_controller (G_OBJECT (b), "toggled",
958                                   object, spec, G_CALLBACK (flags_modified));
959           }
960
961         g_type_class_unref (fclass);
962
963         g_object_connect_property (object, spec,
964                                    G_CALLBACK (flags_changed),
965                                    prop_edit, G_OBJECT (prop_edit));
966       }
967     }
968   else if (type == G_TYPE_PARAM_UNICHAR)
969     {
970       prop_edit = gtk_entry_new ();
971       gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
972
973       g_object_connect_property (object, spec,
974                                  G_CALLBACK (unichar_changed),
975                                  prop_edit, G_OBJECT (prop_edit));
976
977       if (can_modify)
978         connect_controller (G_OBJECT (prop_edit), "changed",
979                             object, spec, G_CALLBACK (unichar_modified));
980     }
981   else if (type == G_TYPE_PARAM_POINTER)
982     {
983       prop_edit = gtk_label_new ("");
984
985       g_object_connect_property (object, spec,
986                                  G_CALLBACK (pointer_changed),
987                                  prop_edit, G_OBJECT (prop_edit));
988     }
989   else if (type == G_TYPE_PARAM_OBJECT)
990     {
991       GtkWidget *label, *button;
992
993       prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
994
995       label = gtk_label_new ("");
996       button = gtk_button_new_with_label ("Properties");
997       g_object_set_data (G_OBJECT (button), "property-name", (gpointer) spec->name);
998       g_signal_connect (button, "clicked",
999                         G_CALLBACK (object_properties),
1000                         object);
1001
1002       gtk_container_add (GTK_CONTAINER (prop_edit), label);
1003       gtk_container_add (GTK_CONTAINER (prop_edit), button);
1004
1005       g_object_connect_property (object, spec,
1006                                  G_CALLBACK (object_changed),
1007                                  prop_edit, G_OBJECT (label));
1008
1009       /* The Properties button is not really modifying, anyway */
1010       can_modify = TRUE;
1011     }
1012   else if (type == G_TYPE_PARAM_BOXED &&
1013            G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_RGBA)
1014     {
1015       prop_edit = gtk_color_button_new ();
1016       gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (prop_edit), TRUE);
1017
1018       g_object_connect_property (object, spec,
1019                                  G_CALLBACK (rgba_changed),
1020                                  prop_edit, G_OBJECT (prop_edit));
1021
1022       if (can_modify)
1023         connect_controller (G_OBJECT (prop_edit), "color-set",
1024                             object, spec, G_CALLBACK (rgba_modified));
1025     }
1026   else if (type == G_TYPE_PARAM_BOXED &&
1027            G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_COLOR)
1028     {
1029       prop_edit = gtk_color_button_new ();
1030
1031       g_object_connect_property (object, spec,
1032                                  G_CALLBACK (color_changed),
1033                                  prop_edit, G_OBJECT (prop_edit));
1034
1035       if (can_modify)
1036         connect_controller (G_OBJECT (prop_edit), "color-set",
1037                             object, spec, G_CALLBACK (color_modified));
1038     }
1039   else if (type == G_TYPE_PARAM_BOXED &&
1040            G_PARAM_SPEC_VALUE_TYPE (spec) == PANGO_TYPE_FONT_DESCRIPTION)
1041     {
1042       prop_edit = gtk_font_button_new ();
1043
1044       g_object_connect_property (object, spec,
1045                                  G_CALLBACK (font_changed),
1046                                  prop_edit, G_OBJECT (prop_edit));
1047
1048       if (can_modify)
1049         connect_controller (G_OBJECT (prop_edit), "notify::font-desc",
1050                             object, spec, G_CALLBACK (font_modified));
1051     }
1052   else
1053     {
1054       msg = g_strdup_printf ("uneditable property type: %s",
1055                              g_type_name (G_PARAM_SPEC_TYPE (spec)));
1056       prop_edit = gtk_label_new (msg);
1057       g_free (msg);
1058       gtk_widget_set_halign (prop_edit, GTK_ALIGN_START);
1059       gtk_widget_set_valign (prop_edit, GTK_ALIGN_CENTER);
1060     }
1061
1062   if (!can_modify)
1063     gtk_widget_set_sensitive (prop_edit, FALSE);
1064
1065   if (g_param_spec_get_blurb (spec))
1066     gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec));
1067
1068   return prop_edit;
1069 }
1070
1071 static GtkWidget *
1072 properties_from_type (GObject *object,
1073                       GType    type)
1074 {
1075   GtkWidget *prop_edit;
1076   GtkWidget *label;
1077   GtkWidget *sw;
1078   GtkWidget *vbox;
1079   GtkWidget *grid;
1080   GParamSpec **specs;
1081   guint n_specs;
1082   int i;
1083
1084   if (G_TYPE_IS_INTERFACE (type))
1085     {
1086       gpointer vtable = g_type_default_interface_peek (type);
1087       specs = g_object_interface_list_properties (vtable, &n_specs);
1088     }
1089   else
1090     {
1091       GObjectClass *class = G_OBJECT_CLASS (g_type_class_peek (type));
1092       specs = g_object_class_list_properties (class, &n_specs);
1093     }
1094
1095   if (n_specs == 0) {
1096     g_free (specs);
1097     return NULL;
1098   }
1099
1100   grid = gtk_grid_new ();
1101   gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1102   gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1103
1104   i = 0;
1105   while (i < n_specs)
1106     {
1107       GParamSpec *spec = specs[i];
1108       gboolean can_modify;
1109
1110       prop_edit = NULL;
1111
1112       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1113                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1114
1115       if ((spec->flags & G_PARAM_READABLE) == 0)
1116         {
1117           /* can't display unreadable properties */
1118           ++i;
1119           continue;
1120         }
1121
1122       if (spec->owner_type != type)
1123         {
1124           /* we're only interested in params of type */
1125           ++i;
1126           continue;
1127         }
1128
1129       label = gtk_label_new (g_param_spec_get_nick (spec));
1130       gtk_widget_set_halign (label, GTK_ALIGN_START);
1131       gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1132       gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1133
1134       prop_edit = property_widget (object, spec, can_modify);
1135       gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1136
1137       /* set initial value */
1138       g_object_notify (object, spec->name);
1139
1140       ++i;
1141     }
1142
1143
1144   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1145   gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1146
1147   sw = gtk_scrolled_window_new (NULL, NULL);
1148   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1149                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1150
1151   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1152
1153   g_free (specs);
1154
1155   return sw;
1156 }
1157
1158 static GtkWidget *
1159 child_properties_from_object (GObject *object)
1160 {
1161   GtkWidget *prop_edit;
1162   GtkWidget *label;
1163   GtkWidget *sw;
1164   GtkWidget *vbox;
1165   GtkWidget *grid;
1166   GtkWidget *parent;
1167   GParamSpec **specs;
1168   guint n_specs;
1169   gint i;
1170
1171   if (!GTK_IS_WIDGET (object))
1172     return NULL;
1173
1174   parent = gtk_widget_get_parent (GTK_WIDGET (object));
1175
1176   if (!parent)
1177     return NULL;
1178
1179   specs = gtk_container_class_list_child_properties (G_OBJECT_GET_CLASS (parent), &n_specs);
1180
1181   grid = gtk_grid_new ();
1182   gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1183   gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1184
1185   i = 0;
1186   while (i < n_specs)
1187     {
1188       GParamSpec *spec = specs[i];
1189       gboolean can_modify;
1190
1191       prop_edit = NULL;
1192
1193       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1194                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1195
1196       if ((spec->flags & G_PARAM_READABLE) == 0)
1197         {
1198           /* can't display unreadable properties */
1199           ++i;
1200           continue;
1201         }
1202
1203       label = gtk_label_new (g_param_spec_get_nick (spec));
1204       gtk_widget_set_halign (label, GTK_ALIGN_START);
1205       gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1206       gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1207
1208       mark_child_property (spec);
1209       prop_edit = property_widget (object, spec, can_modify);
1210       gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1211
1212       /* set initial value */
1213       gtk_widget_child_notify (GTK_WIDGET (object), spec->name);
1214
1215       ++i;
1216     }
1217
1218   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1219   gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1220
1221   sw = gtk_scrolled_window_new (NULL, NULL);
1222   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1223                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1224
1225   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1226
1227   g_free (specs);
1228
1229   return sw;
1230 }
1231
1232 static void
1233 child_properties (GtkWidget *button,
1234                   GObject   *object)
1235 {
1236   create_prop_editor (object, 0);
1237 }
1238
1239 static GtkWidget *
1240 children_from_object (GObject *object)
1241 {
1242   GList *children, *c;
1243   GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw;
1244   gchar *str;
1245   gint i;
1246
1247   if (!GTK_IS_CONTAINER (object))
1248     return NULL;
1249
1250   children = gtk_container_get_children (GTK_CONTAINER (object));
1251
1252   grid = gtk_grid_new ();
1253   gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1254   gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1255
1256   for (c = children, i = 0; c; c = c->next, i++)
1257     {
1258       object = c->data;
1259
1260       label = gtk_label_new ("Child");
1261       gtk_widget_set_halign (label, GTK_ALIGN_START);
1262       gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1263       gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1264
1265       prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1266
1267       str = object_label (object, NULL);
1268       label = gtk_label_new (str);
1269       g_free (str);
1270       button = gtk_button_new_with_label ("Properties");
1271       g_signal_connect (button, "clicked",
1272                         G_CALLBACK (child_properties),
1273                         object);
1274
1275       gtk_container_add (GTK_CONTAINER (prop_edit), label);
1276       gtk_container_add (GTK_CONTAINER (prop_edit), button);
1277
1278       gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1279     }
1280
1281   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1282   gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1283
1284   sw = gtk_scrolled_window_new (NULL, NULL);
1285   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1286                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1287
1288   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1289
1290   g_list_free (children);
1291
1292   return sw;
1293 }
1294
1295 static GtkWidget *
1296 cells_from_object (GObject *object)
1297 {
1298   GList *cells, *c;
1299   GtkWidget *grid, *label, *prop_edit, *button, *vbox, *sw;
1300   gchar *str;
1301   gint i;
1302
1303   if (!GTK_IS_CELL_LAYOUT (object))
1304     return NULL;
1305
1306   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (object));
1307
1308   grid = gtk_grid_new ();
1309   gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
1310   gtk_grid_set_row_spacing (GTK_GRID (grid), 3);
1311
1312   for (c = cells, i = 0; c; c = c->next, i++)
1313     {
1314       object = c->data;
1315
1316       label = gtk_label_new ("Cell");
1317       gtk_widget_set_halign (label, GTK_ALIGN_START);
1318       gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
1319       gtk_grid_attach (GTK_GRID (grid), label, 0, i, 1, 1);
1320
1321       prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1322
1323       str = object_label (object, NULL);
1324       label = gtk_label_new (str);
1325       g_free (str);
1326       button = gtk_button_new_with_label ("Properties");
1327       g_signal_connect (button, "clicked",
1328                         G_CALLBACK (child_properties),
1329                         object);
1330
1331       gtk_container_add (GTK_CONTAINER (prop_edit), label);
1332       gtk_container_add (GTK_CONTAINER (prop_edit), button);
1333
1334       gtk_grid_attach (GTK_GRID (grid), prop_edit, 1, i, 1, 1);
1335     }
1336
1337   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1338   gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
1339
1340   sw = gtk_scrolled_window_new (NULL, NULL);
1341   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1342                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1343
1344   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1345
1346   g_list_free (cells);
1347
1348   return sw;
1349 }
1350
1351 /* Pass zero for type if you want all properties */
1352 GtkWidget*
1353 create_prop_editor (GObject   *object,
1354                     GType      type)
1355 {
1356   GtkWidget *win;
1357   GtkWidget *notebook;
1358   GtkWidget *properties;
1359   GtkWidget *label;
1360   gchar *title;
1361   GType *ifaces;
1362   guint n_ifaces;
1363
1364   if ((win = g_object_get_data (G_OBJECT (object), "prop-editor-win")))
1365     {
1366       gtk_window_present (GTK_WINDOW (win));
1367       return win;
1368     }
1369
1370   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1371   if (GTK_IS_WIDGET (object))
1372     gtk_window_set_screen (GTK_WINDOW (win),
1373                            gtk_widget_get_screen (GTK_WIDGET (object)));
1374
1375   /* hold a weak ref to the object we're editing */
1376   g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy);
1377   g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy);
1378
1379   if (type == 0)
1380     {
1381       notebook = gtk_notebook_new ();
1382       gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);
1383
1384       gtk_container_add (GTK_CONTAINER (win), notebook);
1385
1386       type = G_TYPE_FROM_INSTANCE (object);
1387
1388       title = g_strdup_printf ("Properties of %s widget", g_type_name (type));
1389       gtk_window_set_title (GTK_WINDOW (win), title);
1390       g_free (title);
1391
1392       while (type)
1393         {
1394           properties = properties_from_type (object, type);
1395           if (properties)
1396             {
1397               label = gtk_label_new (g_type_name (type));
1398               gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1399                                         properties, label);
1400             }
1401
1402           type = g_type_parent (type);
1403         }
1404
1405       ifaces = g_type_interfaces (G_TYPE_FROM_INSTANCE (object), &n_ifaces);
1406       while (n_ifaces--)
1407         {
1408           properties = properties_from_type (object, ifaces[n_ifaces]);
1409           if (properties)
1410             {
1411               label = gtk_label_new (g_type_name (ifaces[n_ifaces]));
1412               gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1413                                         properties, label);
1414             }
1415         }
1416
1417       g_free (ifaces);
1418
1419       properties = child_properties_from_object (object);
1420       if (properties)
1421         {
1422           label = gtk_label_new ("Child properties");
1423           gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1424                                     properties, label);
1425         }
1426
1427       properties = children_from_object (object);
1428       if (properties)
1429         {
1430           label = gtk_label_new ("Children");
1431           gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1432                                     properties, label);
1433         }
1434
1435       properties = cells_from_object (object);
1436       if (properties)
1437         {
1438           label = gtk_label_new ("Cell renderers");
1439           gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
1440                                     properties, label);
1441         }
1442     }
1443   else
1444     {
1445       properties = properties_from_type (object, type);
1446       gtk_container_add (GTK_CONTAINER (win), properties);
1447       title = g_strdup_printf ("Properties of %s", g_type_name (type));
1448       gtk_window_set_title (GTK_WINDOW (win), title);
1449       g_free (title);
1450     }
1451
1452   gtk_window_set_default_size (GTK_WINDOW (win), -1, 400);
1453
1454   gtk_widget_show_all (win);
1455
1456   return win;
1457 }
1458