]> Pileus Git - ~andy/gtk/blob - tests/prop-editor.c
up version to 1.3.7, interface age 0, binary age 0. depend on glib 1.3.7.
[~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 static void
27 get_param_specs (GType         type,
28                  GParamSpec ***specs,
29                  gint         *n_specs)
30 {
31   GObjectClass *class = g_type_class_peek (type);
32
33   /* We count on the fact we have an instance, or else we'd have
34    * to use g_type_class_ref ();
35    */
36
37   /* Use private interface for now, fix later */
38   *specs = NULL; /* class->property_specs; */
39   *n_specs = 0; /* class->n_property_specs; */
40 }
41
42 typedef struct
43 {
44   gpointer instance;
45   GObject *alive_object;
46   guint id;
47 } DisconnectData;
48
49 static void
50 disconnect_func (gpointer data)
51 {
52   DisconnectData *dd = data;
53   
54   g_signal_handler_disconnect (dd->instance, dd->id);
55 }
56
57 static void
58 signal_removed (gpointer  data,
59                 GClosure *closure)
60 {
61   DisconnectData *dd = data;
62
63   g_object_steal_data (dd->alive_object, "alive-object-data");
64   g_free (dd);
65 }
66
67 static void
68 g_object_connect_property (GObject *object,
69                            const gchar *prop_name,
70                            GtkSignalFunc func,
71                            gpointer data,
72                            GObject *alive_object)
73 {
74   GClosure *closure;
75   gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
76   DisconnectData *dd;
77
78   dd = g_new (DisconnectData, 1);
79
80   closure = g_cclosure_new (func, data, NULL);
81
82   g_closure_add_invalidate_notifier (closure, dd, signal_removed);
83
84   dd->id = g_signal_connect_closure (object, with_detail,
85                                      closure, FALSE);
86
87   dd->instance = object;
88   dd->alive_object = alive_object;
89   
90   g_object_set_data_full (G_OBJECT (alive_object),
91                           "alive-object-data",
92                           dd,
93                           disconnect_func);
94   
95   g_free (with_detail);
96 }
97
98 typedef struct 
99 {
100   GObject *obj;
101   gchar *prop;
102   gint modified_id;
103 } ObjectProperty;
104
105 static void
106 free_object_property (ObjectProperty *p)
107 {
108   g_free (p->prop);
109   g_free (p);
110 }
111
112 static void
113 connect_controller (GObject *controller,
114                     const gchar *signal,
115                     GObject *model,
116                     const gchar *prop_name,
117                     GtkSignalFunc func)
118 {
119   ObjectProperty *p;
120
121   p = g_new (ObjectProperty, 1);
122   p->obj = model;
123   p->prop = g_strdup (prop_name);
124
125   p->modified_id = g_signal_connect_data (controller, signal, func, p,
126                                           (GClosureNotify)free_object_property,
127                                           0);
128   g_object_set_data (controller, "object-property", p);
129 }
130
131 static void
132 block_controller (GObject *controller)
133 {
134   ObjectProperty *p = g_object_get_data (controller, "object-property");
135
136   if (p)
137     g_signal_handler_block (controller, p->modified_id);
138 }
139
140 static void
141 unblock_controller (GObject *controller)
142 {
143   ObjectProperty *p = g_object_get_data (controller, "object-property");
144
145   if (p)
146     g_signal_handler_unblock (controller, p->modified_id);
147 }
148
149 static void
150 int_modified (GtkAdjustment *adj, gpointer data)
151 {
152   ObjectProperty *p = data;
153
154   g_object_set (p->obj, p->prop, (int) adj->value, NULL);
155 }
156
157 static void
158 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
159 {
160   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
161   GValue val = { 0, };  
162
163   g_value_init (&val, G_TYPE_INT);
164   g_object_get_property (object, pspec->name, &val);
165
166   if (g_value_get_int (&val) != (int)adj->value)
167     {
168       block_controller (G_OBJECT (adj));
169       gtk_adjustment_set_value (adj, g_value_get_int (&val));
170       unblock_controller (G_OBJECT (adj));
171     }
172
173   g_value_unset (&val);
174 }
175
176 static void
177 uint_modified (GtkAdjustment *adj, gpointer data)
178 {
179   ObjectProperty *p = data;
180
181   g_object_set (p->obj, p->prop, (guint) adj->value, NULL);
182 }
183
184 static void
185 uint_changed (GObject *object, GParamSpec *pspec, gpointer data)
186 {
187   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
188   GValue val = { 0, };  
189
190   g_value_init (&val, G_TYPE_UINT);
191   g_object_get_property (object, pspec->name, &val);
192
193   if (g_value_get_uint (&val) != (guint)adj->value)
194     {
195       block_controller (G_OBJECT (adj));
196       gtk_adjustment_set_value (adj, g_value_get_uint (&val));
197       unblock_controller (G_OBJECT (adj));
198     }
199
200   g_value_unset (&val);
201 }
202
203 static void
204 float_modified (GtkAdjustment *adj, gpointer data)
205 {
206   ObjectProperty *p = data;
207
208   g_object_set (p->obj, p->prop, (float) adj->value, NULL);
209 }
210
211 static void
212 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
213 {
214   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
215   GValue val = { 0, };  
216
217   g_value_init (&val, G_TYPE_FLOAT);
218   g_object_get_property (object, pspec->name, &val);
219
220   if (g_value_get_float (&val) != (float) adj->value)
221     {
222       block_controller (G_OBJECT (adj));
223       gtk_adjustment_set_value (adj, g_value_get_float (&val));
224       unblock_controller (G_OBJECT (adj));
225     }
226
227   g_value_unset (&val);
228 }
229
230 static void
231 double_modified (GtkAdjustment *adj, gpointer data)
232 {
233   ObjectProperty *p = data;
234
235   g_object_set (p->obj, p->prop, (double) adj->value, NULL);
236 }
237
238 static void
239 double_changed (GObject *object, GParamSpec *pspec, gpointer data)
240 {
241   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
242   GValue val = { 0, };  
243
244   g_value_init (&val, G_TYPE_DOUBLE);
245   g_object_get_property (object, pspec->name, &val);
246
247   if (g_value_get_double (&val) != adj->value)
248     {
249       block_controller (G_OBJECT (adj));
250       gtk_adjustment_set_value (adj, g_value_get_double (&val));
251       unblock_controller (G_OBJECT (adj));
252     }
253
254   g_value_unset (&val);
255 }
256
257 static void
258 string_modified (GtkEntry *entry, gpointer data)
259 {
260   ObjectProperty *p = data;
261   const gchar *text;
262
263   text = gtk_entry_get_text (entry);
264
265   g_object_set (p->obj, p->prop, text, NULL);
266 }
267
268 static void
269 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
270 {
271   GtkEntry *entry = GTK_ENTRY (data);
272   GValue val = { 0, };  
273   const gchar *str;
274   const gchar *text;
275   
276   g_value_init (&val, G_TYPE_STRING);
277   g_object_get_property (object, pspec->name, &val);
278
279   str = g_value_get_string (&val);
280   if (str == NULL)
281     str = "";
282   text = gtk_entry_get_text (entry);
283
284   if (strcmp (str, text) != 0)
285     {
286       block_controller (G_OBJECT (entry));
287       gtk_entry_set_text (entry, str);
288       unblock_controller (G_OBJECT (entry));
289     }
290   
291   g_value_unset (&val);
292 }
293
294 static void
295 bool_modified (GtkToggleButton *tb, gpointer data)
296 {
297   ObjectProperty *p = data;
298
299   g_object_set (p->obj, p->prop, (int) tb->active, NULL);
300 }
301
302 static void
303 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
304 {
305   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
306   GValue val = { 0, };  
307   
308   g_value_init (&val, G_TYPE_BOOLEAN);
309   g_object_get_property (object, pspec->name, &val);
310
311   if (g_value_get_boolean (&val) != tb->active)
312     {
313       block_controller (G_OBJECT (tb));
314       gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
315       unblock_controller (G_OBJECT (tb));
316     }
317
318   gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
319                       "TRUE" : "FALSE");
320   
321   g_value_unset (&val);
322 }
323
324
325 static void
326 enum_modified (GtkOptionMenu *om, gpointer data)
327 {
328   ObjectProperty *p = data;
329   gint i;
330   GParamSpec *spec;
331   GEnumClass *eclass;
332   
333   spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
334                                        p->prop);
335
336   eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
337   
338   i = gtk_option_menu_get_history (om);
339
340   g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
341 }
342
343 static void
344 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
345 {
346   GtkOptionMenu *om = GTK_OPTION_MENU (data);
347   GValue val = { 0, };  
348   GEnumClass *eclass;
349   gint i;
350
351   eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
352   
353   g_value_init (&val, pspec->value_type);
354   g_object_get_property (object, pspec->name, &val);
355
356   i = 0;
357   while (i < eclass->n_values)
358     {
359       if (eclass->values[i].value == g_value_get_enum (&val))
360         break;
361       ++i;
362     }
363   
364   if (gtk_option_menu_get_history (om) != i)
365     {
366       block_controller (G_OBJECT (om));
367       gtk_option_menu_set_history (om, i);
368       unblock_controller (G_OBJECT (om));
369     }
370   
371   g_value_unset (&val);
372
373 }
374
375 static gunichar
376 unichar_get_value (GtkEntry *entry)
377 {
378   const gchar *text = gtk_entry_get_text (entry);
379   
380   if (text[0])
381     return g_utf8_get_char (text);
382   else
383     return 0;
384 }
385
386 static void
387 unichar_modified (GtkEntry *entry, gpointer data)
388 {
389   ObjectProperty *p = data;
390   gunichar val = unichar_get_value (entry);
391
392   g_object_set (p->obj, p->prop, val, NULL);
393 }
394
395 static void
396 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
397 {
398   GtkEntry *entry = GTK_ENTRY (data);
399   gunichar new_val;
400   gunichar old_val = unichar_get_value (entry);
401   gchar buf[7];
402   gint len;
403
404   g_object_get (object, pspec->name, &new_val, NULL);
405
406   if (new_val != old_val)
407     {
408       if (!new_val)
409         len = 0;
410       else
411         len = g_unichar_to_utf8 (new_val, buf);
412       
413       buf[len] = '\0';
414       
415       block_controller (G_OBJECT (entry));
416       gtk_entry_set_text (entry, buf);
417       unblock_controller (G_OBJECT (entry));
418     }
419 }
420
421 static void
422 pointer_changed (GObject *object, GParamSpec *pspec, gpointer data)
423 {
424   GtkLabel *label = GTK_LABEL (data);
425   gchar *str;
426   gpointer ptr;
427   
428   g_object_get (object, pspec->name, &ptr, NULL);
429
430   str = g_strdup_printf ("Pointer: %p", ptr);
431   gtk_label_set_text (label, str);
432   g_free (str);
433 }
434
435 static void
436 object_changed (GObject *object, GParamSpec *pspec, gpointer data)
437 {
438   GtkLabel *label = GTK_LABEL (data);
439   gchar *str;
440   GObject *obj;
441   const gchar *name;
442   
443   g_object_get (object, pspec->name, &obj, NULL);
444
445   if (obj)
446     name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
447   else
448     name = "unknown";
449   str = g_strdup_printf ("Objetct: %p (%s)", obj, name);
450   
451   gtk_label_set_text (label, str);
452   g_free (str);
453 }
454
455 void
456 model_destroy (gpointer data)
457 {
458   g_object_steal_data (data, "model-object");
459   gtk_widget_destroy (data);
460 }
461
462 void
463 window_destroy (gpointer data)
464 {
465   g_object_steal_data (data, "prop-editor-win");
466 }
467
468 static GtkWidget *
469 property_widget (GObject *object, GParamSpec *spec, gboolean can_modify)
470 {
471   GtkWidget *prop_edit;
472   GtkAdjustment *adj;
473   gchar *msg;
474   
475   switch (G_PARAM_SPEC_TYPE (spec))
476     {
477     case G_TYPE_PARAM_INT:
478       adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
479                                                 G_PARAM_SPEC_INT (spec)->minimum,
480                                                 G_PARAM_SPEC_INT (spec)->maximum,
481                                                 1,
482                                                 MAX ((G_PARAM_SPEC_INT (spec)->maximum -
483                                                       G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
484                                                 0.0));
485       
486       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
487       
488       g_object_connect_property (object, spec->name,
489                                  GTK_SIGNAL_FUNC (int_changed),
490                                  adj, G_OBJECT (adj));
491       
492       if (can_modify)
493         connect_controller (G_OBJECT (adj), "value_changed",
494                             object, spec->name, (GtkSignalFunc) int_modified);
495       break;
496       
497     case G_TYPE_PARAM_UINT:
498       adj = GTK_ADJUSTMENT (
499              gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value,
500                                  G_PARAM_SPEC_UINT (spec)->minimum,
501                                  G_PARAM_SPEC_UINT (spec)->maximum,
502                                  1,
503                                  MAX ((G_PARAM_SPEC_UINT (spec)->maximum -
504                                        G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1),
505                                  0.0));
506       
507       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
508       
509       g_object_connect_property (object, spec->name,
510                                  GTK_SIGNAL_FUNC (uint_changed),
511                                  adj, G_OBJECT (adj));
512       
513       if (can_modify)
514         connect_controller (G_OBJECT (adj), "value_changed",
515                             object, spec->name, (GtkSignalFunc) uint_modified);
516       break;
517       
518     case G_TYPE_PARAM_FLOAT:
519       adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
520                                                 G_PARAM_SPEC_FLOAT (spec)->minimum,
521                                                 G_PARAM_SPEC_FLOAT (spec)->maximum,
522                                                 0.1,
523                                                 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum -
524                                                       G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
525                                                 0.0));
526       
527       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
528       
529       g_object_connect_property (object, spec->name,
530                                  GTK_SIGNAL_FUNC (float_changed),
531                                  adj, G_OBJECT (adj));
532       
533       if (can_modify)
534         connect_controller (G_OBJECT (adj), "value_changed",
535                             object, spec->name, (GtkSignalFunc) float_modified);
536       break;
537       
538     case G_TYPE_PARAM_DOUBLE:
539       adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value,
540                                                 G_PARAM_SPEC_DOUBLE (spec)->minimum,
541                                                 G_PARAM_SPEC_DOUBLE (spec)->maximum,
542                                                 0.1,
543                                                 MAX ((G_PARAM_SPEC_DOUBLE (spec)->maximum -
544                                                       G_PARAM_SPEC_DOUBLE (spec)->minimum) / 10, 0.1),
545                                                 0.0));
546       
547       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
548       
549       g_object_connect_property (object, spec->name,
550                                  GTK_SIGNAL_FUNC (double_changed),
551                                  adj, G_OBJECT (adj));
552       
553       if (can_modify)
554         connect_controller (G_OBJECT (adj), "value_changed",
555                             object, spec->name, (GtkSignalFunc) double_modified);
556       break;
557       
558     case G_TYPE_PARAM_STRING:
559       prop_edit = gtk_entry_new ();
560       
561       g_object_connect_property (object, spec->name,
562                                  GTK_SIGNAL_FUNC (string_changed),
563                                  prop_edit, G_OBJECT (prop_edit));
564       
565       if (can_modify)
566         connect_controller (G_OBJECT (prop_edit), "changed",
567                             object, spec->name, (GtkSignalFunc) string_modified);
568       break;
569       
570     case G_TYPE_PARAM_BOOLEAN:
571       prop_edit = gtk_toggle_button_new_with_label ("");
572       
573       g_object_connect_property (object, spec->name,
574                                  GTK_SIGNAL_FUNC (bool_changed),
575                                  prop_edit, G_OBJECT (prop_edit));
576       
577       if (can_modify)
578         connect_controller (G_OBJECT (prop_edit), "toggled",
579                             object, spec->name, (GtkSignalFunc) bool_modified);
580       break;
581       
582     case G_TYPE_PARAM_ENUM:
583       {
584         GtkWidget *menu;
585         GEnumClass *eclass;
586         gint j;
587         
588         prop_edit = gtk_option_menu_new ();
589         
590         menu = gtk_menu_new ();
591         
592         eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
593         
594         j = 0;
595         while (j < eclass->n_values)
596           {
597             GtkWidget *mi;
598             
599             mi = gtk_menu_item_new_with_label (eclass->values[j].value_name);
600             
601             gtk_widget_show (mi);
602             
603             gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
604             
605             ++j;
606           }
607         
608         g_type_class_unref (eclass);
609         
610         gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
611         
612         g_object_connect_property (object, spec->name,
613                                    GTK_SIGNAL_FUNC (enum_changed),
614                                    prop_edit, G_OBJECT (prop_edit));
615         
616         if (can_modify)
617           connect_controller (G_OBJECT (prop_edit), "changed",
618                               object, spec->name, (GtkSignalFunc) enum_modified);
619       }
620       break;
621       
622     case G_TYPE_PARAM_UNICHAR:
623       prop_edit = gtk_entry_new ();
624       gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
625       
626       g_object_connect_property (object, spec->name,
627                                  GTK_SIGNAL_FUNC (unichar_changed),
628                                  prop_edit, G_OBJECT (prop_edit));
629       
630       if (can_modify)
631         connect_controller (G_OBJECT (prop_edit), "changed",
632                             object, spec->name, (GtkSignalFunc) unichar_modified);
633       break;
634       
635     case G_TYPE_PARAM_POINTER:
636       prop_edit = gtk_label_new ("");
637       
638       g_object_connect_property (object, spec->name,
639                                  GTK_SIGNAL_FUNC (pointer_changed),
640                                  prop_edit, G_OBJECT (prop_edit));
641       break;
642       
643     case G_TYPE_PARAM_OBJECT:
644       prop_edit = gtk_label_new ("");
645       
646       g_object_connect_property (object, spec->name,
647                                  GTK_SIGNAL_FUNC (object_changed),
648                                  prop_edit, G_OBJECT (prop_edit));
649       break;
650       
651       
652     default:
653       msg = g_strdup_printf ("uneditable property type: %s",
654                              g_type_name (G_PARAM_SPEC_TYPE (spec)));
655       prop_edit = gtk_label_new (msg);            
656       g_free (msg);
657       gtk_misc_set_alignment (GTK_MISC (prop_edit), 0.0, 0.5);
658     }
659   
660   return prop_edit;
661 }
662
663 static GtkWidget *
664 properties_from_type (GObject     *object,
665                       GType        type,
666                       GtkTooltips *tips)
667 {
668   GtkWidget *prop_edit;
669   GtkWidget *label;
670   GtkWidget *sw;
671   GtkWidget *vbox;
672   GtkWidget *table;
673   int i;
674   gint n_specs = 0;
675   GParamSpec **specs = NULL;
676
677   get_param_specs (type, &specs, &n_specs);
678
679   if (n_specs == 0)
680     return NULL;
681   
682   table = gtk_table_new (n_specs, 2, FALSE);
683   gtk_table_set_col_spacing (GTK_TABLE (table), 0, 10);
684   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
685
686   i = 0;
687   while (i < n_specs)
688     {
689       GParamSpec *spec = specs[i];
690       gboolean can_modify;
691       
692       prop_edit = NULL;
693
694       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
695                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
696       
697       if ((spec->flags & G_PARAM_READABLE) == 0)
698         {
699           /* can't display unreadable properties */
700           ++i;
701           continue;
702         }
703       
704       label = gtk_label_new (spec->nick);
705       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
706       gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, i, i + 1);
707       
708       prop_edit = property_widget (object, spec, can_modify);
709       gtk_table_attach_defaults (GTK_TABLE (table), prop_edit, 1, 2, i, i + 1);
710
711       if (prop_edit)
712         {
713           if (!can_modify)
714             gtk_widget_set_sensitive (prop_edit, FALSE);
715
716           if (spec->blurb)
717             gtk_tooltips_set_tip (tips, prop_edit, spec->blurb, NULL);
718           
719           /* set initial value */
720           g_object_notify (object, spec->name);
721         }
722       
723       ++i;
724     }
725
726
727   vbox = gtk_vbox_new (FALSE, 0);
728   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
729
730   sw = gtk_scrolled_window_new (NULL, NULL);
731   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
732                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
733   
734   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
735   return sw;
736 }
737
738
739 /* Pass zero for type if you want all properties */
740 GtkWidget*
741 create_prop_editor (GObject *object, GType type)
742 {
743   GtkWidget *win;
744   GtkWidget *notebook;
745   GtkTooltips *tips;
746   GtkWidget *properties;
747   GtkWidget *label;
748   gchar *title;
749
750   if ((win = g_object_get_data (G_OBJECT (object), "prop-editor-win")))
751     {
752       gtk_window_present (GTK_WINDOW (win));
753       return win;
754     }
755
756   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
757
758   tips = gtk_tooltips_new ();
759   gtk_signal_connect_object (GTK_OBJECT (win), "destroy",
760                              GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (tips));
761
762   /* hold a weak ref to the object we're editing */
763   g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy);
764   g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy);
765
766   if (type == 0)
767     {
768       notebook = gtk_notebook_new ();
769       gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);
770       
771       gtk_container_add (GTK_CONTAINER (win), notebook);
772       
773       type = G_TYPE_FROM_INSTANCE (object);
774       
775       title = g_strdup_printf ("Properties of %s widget", g_type_name (type));
776       gtk_window_set_title (GTK_WINDOW (win), title);
777       g_free (title);
778       
779       while (type)
780         {
781           properties = properties_from_type (object, type, tips);
782           if (properties)
783             {
784               label = gtk_label_new (g_type_name (type));
785               gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
786                                         properties, label);
787             }
788           
789           type = g_type_parent (type);
790         }
791     }
792   else
793     {
794       properties = properties_from_type (object, type, tips);
795       gtk_container_add (GTK_CONTAINER (win), properties);
796       title = g_strdup_printf ("Properties of %s", g_type_name (type));
797       gtk_window_set_title (GTK_WINDOW (win), title);
798     }
799   
800   gtk_window_set_default_size (GTK_WINDOW (win), -1, 400);
801   
802   gtk_widget_show_all (win);
803
804   return win;
805 }
806