]> Pileus Git - ~andy/gtk/blob - tests/testtreeview.c
don't draw if label->layout is NULL due to a pending resize
[~andy/gtk] / tests / testtreeview.c
1
2 #include <gtk/gtk.h>
3 #include <string.h>
4
5
6 /* Don't copy this bad example; inline RGB data is always a better
7  * idea than inline XPMs.
8  */
9 static char  *book_closed_xpm[] = {
10 "16 16 6 1",
11 "       c None s None",
12 ".      c black",
13 "X      c red",
14 "o      c yellow",
15 "O      c #808080",
16 "#      c white",
17 "                ",
18 "       ..       ",
19 "     ..XX.      ",
20 "   ..XXXXX.     ",
21 " ..XXXXXXXX.    ",
22 ".ooXXXXXXXXX.   ",
23 "..ooXXXXXXXXX.  ",
24 ".X.ooXXXXXXXXX. ",
25 ".XX.ooXXXXXX..  ",
26 " .XX.ooXXX..#O  ",
27 "  .XX.oo..##OO. ",
28 "   .XX..##OO..  ",
29 "    .X.#OO..    ",
30 "     ..O..      ",
31 "      ..        ",
32 "                "
33 };
34
35 static GtkWidget* create_prop_editor (GObject *object);
36 static void run_automated_tests (void);
37
38 /* This custom model is to test custom model use. */
39
40 #define GTK_TYPE_MODEL_TYPES                    (gtk_tree_model_types_get_type ())
41 #define GTK_TREE_MODEL_TYPES(obj)               (GTK_CHECK_CAST ((obj), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypes))
42 #define GTK_TREE_MODEL_TYPES_CLASS(klass)       (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypesClass))
43 #define GTK_IS_TREE_MODEL_TYPES(obj)            (GTK_CHECK_TYPE ((obj), GTK_TYPE_MODEL_TYPES))
44 #define GTK_IS_TREE_MODEL_TYPES_CLASS(klass)    (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_MODEL_TYPES))
45
46 typedef struct _GtkTreeModelTypes       GtkTreeModelTypes;
47 typedef struct _GtkTreeModelTypesClass  GtkTreeModelTypesClass;
48
49 struct _GtkTreeModelTypes
50 {
51   GtkObject parent;
52
53   gint stamp;
54 };
55
56 struct _GtkTreeModelTypesClass
57 {
58   GtkObjectClass parent_class;
59
60   guint        (* get_flags)       (GtkTreeModel *tree_model);   
61   gint         (* get_n_columns)   (GtkTreeModel *tree_model);
62   GType        (* get_column_type) (GtkTreeModel *tree_model,
63                                     gint          index);
64   gboolean     (* get_iter)        (GtkTreeModel *tree_model,
65                                     GtkTreeIter  *iter,
66                                     GtkTreePath  *path);
67   GtkTreePath *(* get_path)        (GtkTreeModel *tree_model,
68                                     GtkTreeIter  *iter);
69   void         (* get_value)       (GtkTreeModel *tree_model,
70                                     GtkTreeIter  *iter,
71                                     gint          column,
72                                     GValue       *value);
73   gboolean     (* iter_next)       (GtkTreeModel *tree_model,
74                                     GtkTreeIter  *iter);
75   gboolean     (* iter_children)   (GtkTreeModel *tree_model,
76                                     GtkTreeIter  *iter,
77                                     GtkTreeIter  *parent);
78   gboolean     (* iter_has_child)  (GtkTreeModel *tree_model,
79                                     GtkTreeIter  *iter);
80   gint         (* iter_n_children) (GtkTreeModel *tree_model,
81                                     GtkTreeIter  *iter);
82   gboolean     (* iter_nth_child)  (GtkTreeModel *tree_model,
83                                     GtkTreeIter  *iter,
84                                     GtkTreeIter  *parent,
85                                     gint          n);
86   gboolean     (* iter_parent)     (GtkTreeModel *tree_model,
87                                     GtkTreeIter  *iter,
88                                     GtkTreeIter  *child);
89   void         (* ref_iter)        (GtkTreeModel *tree_model,
90                                     GtkTreeIter  *iter);
91   void         (* unref_iter)      (GtkTreeModel *tree_model,
92                                     GtkTreeIter  *iter);
93
94   /* These will be moved into the GtkTreeModelIface eventually */
95   void         (* changed)         (GtkTreeModel *tree_model,
96                                     GtkTreePath  *path,
97                                     GtkTreeIter  *iter);
98   void         (* inserted)        (GtkTreeModel *tree_model,
99                                     GtkTreePath  *path,
100                                     GtkTreeIter  *iter);
101   void         (* child_toggled)   (GtkTreeModel *tree_model,
102                                     GtkTreePath  *path,
103                                     GtkTreeIter  *iter);
104   void         (* deleted)         (GtkTreeModel *tree_model,
105                                     GtkTreePath  *path);
106 };
107
108 GtkType             gtk_tree_model_types_get_type      (void);
109 GtkTreeModelTypes *gtk_tree_model_types_new           (void);
110
111 typedef enum
112 {
113   COLUMNS_NONE,
114   COLUMNS_ONE,
115   COLUMNS_LOTS,
116   COLUMNS_LAST
117 } ColumnsType;
118
119 #define N_COLUMNS 9
120
121 static GType*
122 get_model_types (void)
123 {
124   static GType column_types[N_COLUMNS] = { 0 };
125   
126   if (column_types[0] == 0)
127     {
128       column_types[0] = G_TYPE_STRING;
129       column_types[1] = G_TYPE_STRING;
130       column_types[2] = GDK_TYPE_PIXBUF;
131       column_types[3] = G_TYPE_FLOAT;
132       column_types[4] = G_TYPE_UINT;
133       column_types[5] = G_TYPE_UCHAR;
134       column_types[6] = G_TYPE_CHAR;
135       column_types[7] = G_TYPE_BOOLEAN;
136       column_types[8] = G_TYPE_INT;
137     }
138
139   return column_types;
140 }
141
142 static void
143 col_clicked_cb (GtkTreeViewColumn *col, gpointer data)
144 {
145   GtkWindow *win;
146
147   win = GTK_WINDOW (create_prop_editor (G_OBJECT (col)));
148
149   gtk_window_set_title (win, gtk_tree_view_column_get_title (col));
150 }
151
152 static void
153 setup_column (GtkTreeViewColumn *col)
154 {
155   g_signal_connect_data (G_OBJECT (col),
156                          "clicked",
157                          (GCallback) col_clicked_cb,
158                          NULL,
159                          NULL,
160                          FALSE,
161                          FALSE);
162 }
163
164 static void
165 set_columns_type (GtkTreeView *tree_view, ColumnsType type)
166 {
167   GtkTreeViewColumn *col;
168   GtkCellRenderer *rend;
169
170   col = gtk_tree_view_get_column (tree_view, 0);
171   while (col)
172     {
173       gtk_tree_view_remove_column (tree_view, col);
174
175       col = gtk_tree_view_get_column (tree_view, 0);
176     }
177
178   switch (type)
179     {
180     case COLUMNS_NONE:
181       break;
182
183     case COLUMNS_LOTS:      
184       rend = gtk_cell_renderer_text_new ();
185       
186       col = gtk_tree_view_column_new_with_attributes ("Column 1",
187                                                       rend,
188                                                       "text", 1,
189                                                       NULL);
190       setup_column (col);
191       
192       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
193       
194       g_object_unref (G_OBJECT (rend));
195       g_object_unref (G_OBJECT (col));
196
197       rend = gtk_cell_renderer_text_pixbuf_new ();
198       
199       col = gtk_tree_view_column_new_with_attributes ("Column 2",
200                                                       rend,
201                                                       "text", 0,
202                                                       "pixbuf", 2,
203                                                       NULL);
204
205       setup_column (col);
206       
207       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
208       
209       g_object_unref (G_OBJECT (rend));
210       g_object_unref (G_OBJECT (col));
211       
212       /* FALL THRU */
213       
214     case COLUMNS_ONE:
215       rend = gtk_cell_renderer_text_new ();
216       
217       col = gtk_tree_view_column_new_with_attributes ("Column 0",
218                                                       rend,
219                                                       "text", 0,
220                                                       NULL);
221
222       setup_column (col);
223       
224       gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), col, 0);
225       
226       g_object_unref (G_OBJECT (rend));
227       g_object_unref (G_OBJECT (col));
228       
229     default:
230       break;
231     }
232 }
233
234 static GdkPixbuf *our_pixbuf;
235   
236 typedef enum
237 {
238   /*   MODEL_TYPES, */
239   MODEL_TREE,
240   MODEL_LIST,
241   MODEL_SORTED_TREE,
242   MODEL_SORTED_LIST,
243   MODEL_EMPTY_LIST,
244   MODEL_EMPTY_TREE,
245   MODEL_NULL,
246   MODEL_LAST
247 } ModelType;
248
249 /* FIXME add a custom model to test */
250 static GtkTreeModel *models[MODEL_LAST];
251 static const char *model_names[MODEL_LAST] = {
252   "GtkTreeStore",
253   "GtkListStore",
254   "GtkTreeModelSort wrapping GtkTreeStore",
255   "GtkTreeModelSort wrapping GtkListStore",
256   "Empty GtkListStore",
257   "Empty GtkTreeStore",
258   "NULL (no model)"
259 };
260
261 static GtkTreeModel*
262 create_list_model (void)
263 {
264   GtkListStore *store;
265   GtkTreeIter iter;
266   gint i;
267   GType *t;
268
269   t = get_model_types ();
270   
271   store = gtk_list_store_new_with_types (N_COLUMNS,
272                                          t[0], t[1], t[2],
273                                          t[3], t[4], t[5],
274                                          t[6], t[7], t[8]);
275
276   i = 0;
277   while (i < 200)
278     {
279       char *msg;
280       
281       gtk_list_store_append (store, &iter);
282
283       msg = g_strdup_printf ("%d", i);
284       
285       gtk_list_store_set (store, &iter, 0, msg, 1, "Foo! Foo! Foo!",
286                           2, our_pixbuf,
287                           3, 7.0, 4, (guint) 9000,
288                           5, 'f', 6, 'g',
289                           7, TRUE, 8, 23245454,
290                           -1);
291
292       g_free (msg);
293       
294       ++i;
295     }
296
297   return GTK_TREE_MODEL (store);
298 }
299
300 static void
301 typesystem_recurse (GType        type,
302                     GtkTreeIter *parent_iter,
303                     GtkTreeStore *store)
304 {
305   GType* children;
306   guint n_children = 0;
307   gint i;
308   GtkTreeIter iter;
309   gchar *str;
310   
311   gtk_tree_store_append (store, &iter, parent_iter);
312
313   str = g_strdup_printf ("%d", type);
314   gtk_tree_store_set (store, &iter, 0, str, 1, g_type_name (type),
315                       2, our_pixbuf,
316                       3, 7.0, 4, (guint) 9000,
317                       5, 'f', 6, 'g',
318                       7, TRUE, 8, 23245454,
319                       -1);
320   g_free (str);
321   
322   children = g_type_children (type, &n_children);
323
324   i = 0;
325   while (i < n_children)
326     {
327       typesystem_recurse (children[i], &iter, store);
328
329       ++i;
330     }
331   
332   g_free (children);
333 }
334
335 static GtkTreeModel*
336 create_tree_model (void)
337 {
338   GtkTreeStore *store;
339   gint i;
340   GType *t;
341   volatile GType dummy; /* G_GNUC_CONST makes the optimizer remove
342                          * get_type calls if you don't do something
343                          * like this
344                          */
345   
346   /* Make the tree more interesting */
347   dummy = gtk_scrolled_window_get_type ();
348   dummy = gtk_label_get_type ();
349   dummy = gtk_hscrollbar_get_type ();
350   dummy = gtk_vscrollbar_get_type ();
351   dummy = pango_layout_get_type ();
352
353   t = get_model_types ();
354   
355   store = gtk_tree_store_new_with_types (N_COLUMNS,
356                                          t[0], t[1], t[2],
357                                          t[3], t[4], t[5],
358                                          t[6], t[7], t[8]);
359
360   i = 0;
361   while (i < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
362     {
363       typesystem_recurse (i, NULL, store);
364       
365       ++i;
366     }
367
368   return GTK_TREE_MODEL (store);
369 }
370
371 static void
372 model_selected (GtkOptionMenu *om, gpointer data)
373 {
374   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
375   gint hist;
376
377   hist = gtk_option_menu_get_history (om);
378
379   if (models[hist] != gtk_tree_view_get_model (tree_view))
380     {
381       gtk_tree_view_set_model (tree_view, models[hist]);
382     }
383 }
384
385 int
386 main (int    argc,
387       char **argv)
388 {
389   GtkWidget *window;
390   GtkWidget *sw;
391   GtkWidget *tv;
392   GtkWidget *table;
393   GtkWidget *om;
394   GtkWidget *menu;
395   GtkTreeModel *model;
396   gint i;
397   
398   gtk_init (&argc, &argv);
399
400   our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm);  
401   
402 #if 0
403   models[MODEL_TYPES] = GTK_TREE_MODEL (gtk_tree_model_types_new ());
404 #endif
405   models[MODEL_LIST] = create_list_model ();
406   models[MODEL_TREE] = create_tree_model ();
407
408   model = create_list_model ();
409   models[MODEL_SORTED_LIST] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
410   g_object_unref (G_OBJECT (model));
411
412   model = create_tree_model ();
413   models[MODEL_SORTED_TREE] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
414   g_object_unref (G_OBJECT (model));
415
416   models[MODEL_EMPTY_LIST] = GTK_TREE_MODEL (gtk_list_store_new ());
417   models[MODEL_EMPTY_TREE] = GTK_TREE_MODEL (gtk_tree_store_new ());
418   
419   models[MODEL_NULL] = NULL;
420
421   run_automated_tests ();
422   
423   menu = gtk_menu_new ();
424   
425   i = 0;
426   while (i < MODEL_LAST)
427     {
428       GtkWidget *mi;
429       const char *name;
430
431       name = model_names[i];
432       
433       mi = gtk_menu_item_new_with_label (name);
434
435       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
436
437 #if 0
438       window = create_prop_editor (G_OBJECT (models[i]));
439
440       gtk_window_set_title (GTK_WINDOW (window),                            
441                             name);
442 #endif
443
444       ++i;
445     }
446   gtk_widget_show_all (menu);
447   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
448
449   gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
450
451   table = gtk_table_new (2, 1, FALSE);
452
453   gtk_container_add (GTK_CONTAINER (window), table);
454
455   om = gtk_option_menu_new ();
456   gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu);
457   
458   gtk_table_attach (GTK_TABLE (table), om,
459                     0, 1, 0, 1,
460                     0, 0, 
461                     0, 0);
462   
463   sw = gtk_scrolled_window_new (NULL, NULL);
464   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
465                                   GTK_POLICY_AUTOMATIC,
466                                   GTK_POLICY_AUTOMATIC);
467
468   
469   gtk_table_attach (GTK_TABLE (table), sw,
470                     0, 1, 1, 2,
471                     GTK_EXPAND | GTK_FILL,
472                     GTK_EXPAND | GTK_FILL,
473                     0, 0);
474   
475   tv = gtk_tree_view_new_with_model (models[0]);
476
477   gtk_signal_connect (GTK_OBJECT (om),
478                       "changed",
479                       GTK_SIGNAL_FUNC (model_selected),
480                       tv);
481   
482   gtk_container_add (GTK_CONTAINER (sw), tv);
483
484   set_columns_type (GTK_TREE_VIEW (tv), COLUMNS_LOTS);
485   
486   gtk_widget_show_all (window);
487   
488   gtk_main ();
489
490   return 0;
491 }
492
493 /*
494  * GtkTreeModelTypes
495  */
496
497 enum {
498   CHANGED,
499   INSERTED,
500   CHILD_TOGGLED,
501   DELETED,
502
503   LAST_SIGNAL
504 };
505
506 static void         gtk_tree_model_types_init                 (GtkTreeModelTypes      *model_types);
507 static void         gtk_tree_model_types_class_init           (GtkTreeModelTypesClass *class);
508 static void         gtk_tree_model_types_tree_model_init      (GtkTreeModelIface   *iface);
509 static gint         gtk_real_model_types_get_n_columns   (GtkTreeModel        *tree_model);
510 static GType        gtk_real_model_types_get_column_type (GtkTreeModel        *tree_model,
511                                                            gint                 index);
512 static GtkTreePath *gtk_real_model_types_get_path        (GtkTreeModel        *tree_model,
513                                                            GtkTreeIter         *iter);
514 static void         gtk_real_model_types_get_value       (GtkTreeModel        *tree_model,
515                                                            GtkTreeIter         *iter,
516                                                            gint                 column,
517                                                            GValue              *value);
518 static gboolean     gtk_real_model_types_iter_next       (GtkTreeModel        *tree_model,
519                                                            GtkTreeIter         *iter);
520 static gboolean     gtk_real_model_types_iter_children   (GtkTreeModel        *tree_model,
521                                                            GtkTreeIter         *iter,
522                                                            GtkTreeIter         *parent);
523 static gboolean     gtk_real_model_types_iter_has_child  (GtkTreeModel        *tree_model,
524                                                            GtkTreeIter         *iter);
525 static gint         gtk_real_model_types_iter_n_children (GtkTreeModel        *tree_model,
526                                                            GtkTreeIter         *iter);
527 static gboolean     gtk_real_model_types_iter_nth_child  (GtkTreeModel        *tree_model,
528                                                            GtkTreeIter         *iter,
529                                                            GtkTreeIter         *parent,
530                                                            gint                 n);
531 static gboolean     gtk_real_model_types_iter_parent     (GtkTreeModel        *tree_model,
532                                                            GtkTreeIter         *iter,
533                                                            GtkTreeIter         *child);
534
535
536 static guint model_types_signals[LAST_SIGNAL] = { 0 };
537
538
539 GtkType
540 gtk_tree_model_types_get_type (void)
541 {
542   static GtkType model_types_type = 0;
543
544   if (!model_types_type)
545     {
546       static const GTypeInfo model_types_info =
547       {
548         sizeof (GtkTreeModelTypesClass),
549         NULL,           /* base_init */
550         NULL,           /* base_finalize */
551         (GClassInitFunc) gtk_tree_model_types_class_init,
552         NULL,           /* class_finalize */
553         NULL,           /* class_data */
554         sizeof (GtkTreeModelTypes),
555         0,
556         (GInstanceInitFunc) gtk_tree_model_types_init
557       };
558
559       static const GInterfaceInfo tree_model_info =
560       {
561         (GInterfaceInitFunc) gtk_tree_model_types_tree_model_init,
562         NULL,
563         NULL
564       };
565
566       model_types_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeModelTypes", &model_types_info, 0);
567       g_type_add_interface_static (model_types_type,
568                                    GTK_TYPE_TREE_MODEL,
569                                    &tree_model_info);
570     }
571
572   return model_types_type;
573 }
574
575 GtkTreeModelTypes *
576 gtk_tree_model_types_new (void)
577 {
578   GtkTreeModelTypes *retval;
579
580   retval = GTK_TREE_MODEL_TYPES (g_object_new (GTK_TYPE_MODEL_TYPES, NULL));
581
582   return retval;
583 }
584
585 static void
586 gtk_tree_model_types_class_init (GtkTreeModelTypesClass *class)
587 {
588   GObjectClass *object_class;
589
590   object_class = (GObjectClass*) class;
591
592   model_types_signals[CHANGED] =
593     g_signal_newc ("changed",
594                    GTK_CLASS_TYPE (object_class),
595                    G_SIGNAL_RUN_FIRST,
596                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, changed),
597                    NULL,
598                    gtk_marshal_VOID__BOXED_BOXED,
599                    G_TYPE_NONE, 2,
600                    G_TYPE_POINTER,
601                    G_TYPE_POINTER);
602   model_types_signals[INSERTED] =
603     g_signal_newc ("inserted",
604                    GTK_CLASS_TYPE (object_class),
605                    G_SIGNAL_RUN_FIRST,
606                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, inserted),
607                    NULL,
608                    gtk_marshal_VOID__BOXED_BOXED,
609                    G_TYPE_NONE, 2,
610                    G_TYPE_POINTER,
611                    G_TYPE_POINTER);
612   model_types_signals[CHILD_TOGGLED] =
613     g_signal_newc ("child_toggled",
614                    GTK_CLASS_TYPE (object_class),
615                    G_SIGNAL_RUN_FIRST,
616                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, child_toggled),
617                    NULL,
618                    gtk_marshal_VOID__BOXED_BOXED,
619                    G_TYPE_NONE, 2,
620                    G_TYPE_POINTER,
621                    G_TYPE_POINTER);
622   model_types_signals[DELETED] =
623     g_signal_newc ("deleted",
624                    GTK_CLASS_TYPE (object_class),
625                    G_SIGNAL_RUN_FIRST,
626                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, deleted),
627                    NULL,
628                    gtk_marshal_VOID__BOXED,
629                    G_TYPE_NONE, 1,
630                    G_TYPE_POINTER);
631 }
632
633 static void
634 gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface)
635 {
636   iface->get_n_columns = gtk_real_model_types_get_n_columns;
637   iface->get_column_type = gtk_real_model_types_get_column_type;
638   iface->get_path = gtk_real_model_types_get_path;
639   iface->get_value = gtk_real_model_types_get_value;
640   iface->iter_next = gtk_real_model_types_iter_next;
641   iface->iter_children = gtk_real_model_types_iter_children;
642   iface->iter_has_child = gtk_real_model_types_iter_has_child;
643   iface->iter_n_children = gtk_real_model_types_iter_n_children;
644   iface->iter_nth_child = gtk_real_model_types_iter_nth_child;
645   iface->iter_parent = gtk_real_model_types_iter_parent;
646 }
647
648 static void
649 gtk_tree_model_types_init (GtkTreeModelTypes *model_types)
650 {
651   model_types->stamp = g_random_int ();
652 }
653
654 static GType column_types[] = {
655   G_TYPE_STRING, /* GType */
656   G_TYPE_STRING  /* type name */
657 };
658   
659 static gint
660 gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model)
661 {
662   return G_N_ELEMENTS (column_types);
663 }
664
665 static GType
666 gtk_real_model_types_get_column_type (GtkTreeModel *tree_model,
667                                       gint          index)
668 {
669   g_return_val_if_fail (index < G_N_ELEMENTS (column_types), G_TYPE_INVALID);
670   
671   return column_types[index];
672 }
673
674 #if 0
675 /* Use default implementation of this */
676 static gboolean
677 gtk_real_model_types_get_iter (GtkTreeModel *tree_model,
678                                GtkTreeIter  *iter,
679                                GtkTreePath  *path)
680 {
681   
682 }
683 #endif
684
685 /* The toplevel nodes of the tree are the reserved types, G_TYPE_NONE through
686  * G_TYPE_RESERVED_FUNDAMENTAL.
687  */
688
689 static GtkTreePath *
690 gtk_real_model_types_get_path (GtkTreeModel *tree_model,
691                                GtkTreeIter  *iter)
692 {
693   GtkTreePath *retval;
694   GType type;
695   GType parent;
696   
697   g_return_val_if_fail (GTK_IS_TREE_MODEL_TYPES (tree_model), NULL);
698   g_return_val_if_fail (iter != NULL, NULL);
699
700   type = GPOINTER_TO_INT (iter->user_data);
701   
702   retval = gtk_tree_path_new ();
703   
704   parent = g_type_parent (type);
705   while (parent != G_TYPE_INVALID)
706     {
707       GType* children = g_type_children (parent, NULL);
708       gint i = 0;
709
710       if (!children || children[0] == G_TYPE_INVALID)
711         {
712           g_warning ("bad iterator?");
713           return NULL;
714         }
715       
716       while (children[i] != type)
717         ++i;
718
719       gtk_tree_path_prepend_index (retval, i);
720
721       g_free (children);
722       
723       type = parent;
724       parent = g_type_parent (parent);
725     }
726
727   /* The fundamental type itself is the index on the toplevel */
728   gtk_tree_path_prepend_index (retval, type);
729
730   return retval;
731 }
732
733 static void
734 gtk_real_model_types_get_value (GtkTreeModel *tree_model,
735                                 GtkTreeIter  *iter,
736                                 gint          column,
737                                 GValue       *value)
738 {
739   GType type;
740
741   type = GPOINTER_TO_INT (iter->user_data);
742
743   switch (column)
744     {
745     case 0:
746       {
747         gchar *str;
748         
749         g_value_init (value, G_TYPE_STRING);
750
751         str = g_strdup_printf ("%d", type);
752         g_value_set_string (value, str);
753         g_free (str);
754       }
755       break;
756
757     case 1:
758       g_value_init (value, G_TYPE_STRING);
759       g_value_set_string (value, g_type_name (type));
760       break;
761
762     default:
763       g_warning ("Bad column %d requested", column);
764     }
765 }
766
767 static gboolean
768 gtk_real_model_types_iter_next (GtkTreeModel  *tree_model,
769                                 GtkTreeIter   *iter)
770 {
771   
772   GType parent;
773   GType type;
774
775   type = GPOINTER_TO_INT (iter->user_data);
776
777   parent = g_type_parent (type);
778
779   if (parent == G_TYPE_INVALID)
780     {
781       /* fundamental type, add 1 */
782       if ((type + 1) < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
783         {
784           iter->user_data = GINT_TO_POINTER (type + 1);
785           return TRUE;
786         }
787       else
788         return FALSE;
789     }
790   else
791     {
792       GType* children = g_type_children (parent, NULL);
793       gint i = 0;
794
795       g_assert (children != NULL);
796       
797       while (children[i] != type)
798         ++i;
799   
800       ++i;
801
802       if (children[i] != G_TYPE_INVALID)
803         {
804           g_free (children);
805           iter->user_data = GINT_TO_POINTER (children[i]);
806           return TRUE;
807         }
808       else
809         {
810           g_free (children);
811           return FALSE;
812         }
813     }
814 }
815
816 static gboolean
817 gtk_real_model_types_iter_children (GtkTreeModel *tree_model,
818                                     GtkTreeIter  *iter,
819                                     GtkTreeIter  *parent)
820 {
821   GType type;
822   GType* children;
823   
824   type = GPOINTER_TO_INT (parent->user_data);
825
826   children = g_type_children (type, NULL);
827
828   if (!children || children[0] == G_TYPE_INVALID)
829     {
830       g_free (children);
831       return FALSE;
832     }
833   else
834     {
835       iter->user_data = GINT_TO_POINTER (children[0]);
836       g_free (children);
837       return TRUE;
838     }
839 }
840
841 static gboolean
842 gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model,
843                                      GtkTreeIter  *iter)
844 {
845   GType type;
846   GType* children;
847   
848   type = GPOINTER_TO_INT (iter->user_data);
849   
850   children = g_type_children (type, NULL);
851
852   if (!children || children[0] == G_TYPE_INVALID)
853     {
854       g_free (children);
855       return FALSE;
856     }
857   else
858     {
859       g_free (children);
860       return TRUE;
861     }
862 }
863
864 static gint
865 gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model,
866                                       GtkTreeIter  *iter)
867 {
868   if (iter == NULL)
869     {
870       return G_TYPE_LAST_RESERVED_FUNDAMENTAL - 1;
871     }
872   else
873     {
874       GType type;
875       GType* children;
876       guint n_children = 0;
877
878       type = GPOINTER_TO_INT (iter->user_data);
879       
880       children = g_type_children (type, &n_children);
881       
882       g_free (children);
883       
884       return n_children;
885     }
886 }
887
888 static gboolean
889 gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model,
890                                      GtkTreeIter  *iter,
891                                      GtkTreeIter  *parent,
892                                      gint          n)
893 {  
894   if (parent == NULL)
895     {
896       /* fundamental type */
897       if (n < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
898         {
899           iter->user_data = GINT_TO_POINTER (n);
900           return TRUE;
901         }
902       else
903         return FALSE;
904     }
905   else
906     {
907       GType type = GPOINTER_TO_INT (parent->user_data);      
908       guint n_children = 0;
909       GType* children = g_type_children (type, &n_children);
910
911       if (n_children == 0)
912         {
913           g_free (children);
914           return FALSE;
915         }
916       else if (n >= n_children)
917         {
918           g_free (children);
919           return FALSE;
920         }
921       else
922         {
923           iter->user_data = GINT_TO_POINTER (children[n]);
924           g_free (children);
925
926           return TRUE;
927         }
928     }
929 }
930
931 static gboolean
932 gtk_real_model_types_iter_parent (GtkTreeModel *tree_model,
933                                   GtkTreeIter  *iter,
934                                   GtkTreeIter  *child)
935 {
936   GType type;
937   GType parent;
938   
939   type = GPOINTER_TO_INT (child->user_data);
940   
941   parent = g_type_parent (type);
942   
943   if (parent == G_TYPE_INVALID)
944     {
945       if (type >= G_TYPE_LAST_RESERVED_FUNDAMENTAL)
946         g_warning ("no parent for %d %s\n", type, g_type_name (type));
947       return FALSE;
948     }
949   else
950     {
951       iter->user_data = GINT_TO_POINTER (parent);
952       
953       return TRUE;
954     }
955 }
956
957 /*
958  * Property editor thingy
959  */
960
961 static void
962 get_param_specs (GObject *object,
963                  GParamSpec ***specs,
964                  gint         *n_specs)
965 {
966   /* Use private interface for now, fix later */
967   *specs = G_OBJECT_GET_CLASS (object)->property_specs;
968   *n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs;
969 }
970
971 typedef struct
972 {
973   gpointer instance;
974   guint id;
975 } DisconnectData;
976
977 static void
978 disconnect_func (gpointer data)
979 {
980   DisconnectData *dd = data;
981   
982   g_signal_handler_disconnect (dd->instance, dd->id);
983   g_free (dd);
984 }
985
986 static void
987 g_object_connect_property (GObject *object,
988                            const gchar *prop_name,
989                            GtkSignalFunc func,
990                            gpointer data,
991                            GObject *alive_object)
992 {
993   gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
994   DisconnectData *dd;
995
996   dd = g_new (DisconnectData, 1);
997   
998   dd->id = g_signal_connect_data (object, with_detail,
999                                   func, data,
1000                                   NULL, FALSE, FALSE);
1001
1002   dd->instance = object;
1003   
1004   g_object_set_data_full (G_OBJECT (alive_object),
1005                           "alive-object",
1006                           dd,
1007                           disconnect_func);
1008   
1009   g_free (with_detail);
1010 }
1011
1012 typedef struct 
1013 {
1014   GObject *obj;
1015   gchar *prop;
1016 } ObjectProperty;
1017
1018 static void
1019 free_object_property (ObjectProperty *p)
1020 {
1021   g_free (p->prop);
1022   g_free (p);
1023 }
1024
1025 static void
1026 connect_controller (GObject *controller,
1027                     const gchar *signal,
1028                     GObject *model,
1029                     const gchar *prop_name,
1030                     GtkSignalFunc func)
1031 {
1032   ObjectProperty *p;
1033
1034   p = g_new (ObjectProperty, 1);
1035   p->obj = model;
1036   p->prop = g_strdup (prop_name);
1037
1038   g_signal_connect_data (controller, signal, func, p,
1039                          (GClosureNotify)free_object_property,
1040                          FALSE, FALSE);
1041 }
1042
1043 static void
1044 int_modified (GtkAdjustment *adj, gpointer data)
1045 {
1046   ObjectProperty *p = data;
1047
1048   g_object_set (p->obj, p->prop, (int) adj->value, NULL);
1049 }
1050
1051 static void
1052 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
1053 {
1054   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
1055   GValue val = { 0, };  
1056
1057   g_value_init (&val, G_TYPE_INT);
1058   g_object_get_property (object, pspec->name, &val);
1059
1060   if (g_value_get_int (&val) != (int)adj->value)
1061     gtk_adjustment_set_value (adj, g_value_get_int (&val));
1062
1063   g_value_unset (&val);
1064 }
1065
1066
1067 static void
1068 string_modified (GtkEntry *entry, gpointer data)
1069 {
1070   ObjectProperty *p = data;
1071   gchar *text;
1072
1073   text = gtk_entry_get_text (entry);
1074
1075   g_object_set (p->obj, p->prop, text, NULL);
1076 }
1077
1078 static void
1079 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
1080 {
1081   GtkEntry *entry = GTK_ENTRY (data);
1082   GValue val = { 0, };  
1083   gchar *str;
1084   gchar *text;
1085   
1086   g_value_init (&val, G_TYPE_STRING);
1087   g_object_get_property (object, pspec->name, &val);
1088
1089   str = g_value_get_string (&val);
1090   if (str == NULL)
1091     str = "";
1092   text = gtk_entry_get_text (entry);
1093
1094   if (strcmp (str, text) != 0)
1095     gtk_entry_set_text (entry, str);
1096   
1097   g_value_unset (&val);
1098 }
1099
1100 static void
1101 bool_modified (GtkToggleButton *tb, gpointer data)
1102 {
1103   ObjectProperty *p = data;
1104
1105   g_object_set (p->obj, p->prop, (int) tb->active, NULL);
1106 }
1107
1108 static void
1109 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
1110 {
1111   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
1112   GValue val = { 0, };  
1113   
1114   g_value_init (&val, G_TYPE_BOOLEAN);
1115   g_object_get_property (object, pspec->name, &val);
1116
1117   if (g_value_get_boolean (&val) != tb->active)
1118     gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
1119
1120   gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
1121                       "TRUE" : "FALSE");
1122   
1123   g_value_unset (&val);
1124 }
1125
1126
1127 static void
1128 enum_modified (GtkOptionMenu *om, gpointer data)
1129 {
1130   ObjectProperty *p = data;
1131   gint i;
1132   GParamSpec *spec;
1133   GEnumClass *eclass;
1134   
1135   spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
1136                                        p->prop);
1137
1138   eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
1139   
1140   i = gtk_option_menu_get_history (om);
1141
1142   g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
1143 }
1144
1145 static void
1146 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
1147 {
1148   GtkOptionMenu *om = GTK_OPTION_MENU (data);
1149   GValue val = { 0, };  
1150   GEnumClass *eclass;
1151   gint i;
1152
1153   eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
1154   
1155   g_value_init (&val, pspec->value_type);
1156   g_object_get_property (object, pspec->name, &val);
1157
1158   i = 0;
1159   while (i < eclass->n_values)
1160     {
1161       if (eclass->values[i].value == g_value_get_enum (&val))
1162         break;
1163       ++i;
1164     }
1165   
1166   if (gtk_option_menu_get_history (om) != i)
1167     gtk_option_menu_set_history (om, i);
1168   
1169   g_value_unset (&val);
1170 }
1171
1172 static GtkWidget*
1173 create_prop_editor (GObject *object)
1174 {
1175   GtkWidget *win;
1176   GtkWidget *vbox;
1177   GtkWidget *hbox;
1178   GtkWidget *label;
1179   GtkWidget *prop_edit;
1180   GtkWidget *sw;
1181   gint n_specs = 0;
1182   GParamSpec **specs = NULL;
1183   gint i;
1184   GtkAdjustment *adj;
1185   
1186   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1187
1188   /* hold a strong ref to the object we're editing */
1189   g_object_ref (G_OBJECT (object));
1190   g_object_set_data_full (G_OBJECT (win), "model-object",
1191                           object, (GDestroyNotify)g_object_unref);
1192   
1193   vbox = gtk_vbox_new (TRUE, 2);
1194
1195   sw = gtk_scrolled_window_new (NULL, NULL);
1196   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1197                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1198   
1199   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1200   gtk_container_add (GTK_CONTAINER (win), sw);
1201   
1202   get_param_specs (object, &specs, &n_specs);
1203   
1204   i = 0;
1205   while (i < n_specs)
1206     {
1207       GParamSpec *spec = specs[i];
1208       gboolean can_modify;
1209       
1210       prop_edit = NULL;
1211
1212       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1213                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1214       
1215       if ((spec->flags & G_PARAM_READABLE) == 0)
1216         {
1217           /* can't display unreadable properties */
1218           ++i;
1219           continue;
1220         }
1221       
1222       switch (spec->value_type)
1223         {
1224         case G_TYPE_INT:
1225           hbox = gtk_hbox_new (FALSE, 10);
1226           label = gtk_label_new (spec->nick);
1227           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1228           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1229           adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
1230                                                     G_PARAM_SPEC_INT (spec)->minimum,
1231                                                     G_PARAM_SPEC_INT (spec)->maximum,
1232                                                     1,
1233                                                     MAX ((G_PARAM_SPEC_INT (spec)->maximum -
1234                                                           G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
1235                                                     0.0));
1236
1237           prop_edit = gtk_spin_button_new (adj, 1.0, 0);
1238           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1239           
1240           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1241
1242           g_object_connect_property (object, spec->name,
1243                                      GTK_SIGNAL_FUNC (int_changed),
1244                                      adj, G_OBJECT (adj));
1245
1246           if (can_modify)
1247             connect_controller (G_OBJECT (adj), "value_changed",
1248                                 object, spec->name, (GtkSignalFunc) int_modified);
1249           break;
1250
1251         case G_TYPE_STRING:
1252           hbox = gtk_hbox_new (FALSE, 10);
1253           label = gtk_label_new (spec->nick);
1254           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1255           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1256
1257           prop_edit = gtk_entry_new ();
1258           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1259           
1260           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1261
1262           g_object_connect_property (object, spec->name,
1263                                      GTK_SIGNAL_FUNC (string_changed),
1264                                      prop_edit, G_OBJECT (prop_edit));
1265
1266           if (can_modify)
1267             connect_controller (G_OBJECT (prop_edit), "changed",
1268                                 object, spec->name, (GtkSignalFunc) string_modified);
1269           break;
1270
1271         case G_TYPE_BOOLEAN:
1272           hbox = gtk_hbox_new (FALSE, 10);
1273           label = gtk_label_new (spec->nick);
1274           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1275           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1276
1277           prop_edit = gtk_toggle_button_new_with_label ("");
1278           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1279           
1280           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1281
1282           g_object_connect_property (object, spec->name,
1283                                      GTK_SIGNAL_FUNC (bool_changed),
1284                                      prop_edit, G_OBJECT (prop_edit));
1285
1286           if (can_modify)
1287             connect_controller (G_OBJECT (prop_edit), "toggled",
1288                                 object, spec->name, (GtkSignalFunc) bool_modified);
1289           break;
1290           
1291         default:
1292           if (g_type_is_a (spec->value_type, G_TYPE_ENUM))
1293             {
1294               GtkWidget *menu;
1295               GEnumClass *eclass;
1296               gint i;
1297             
1298               hbox = gtk_hbox_new (FALSE, 10);
1299               label = gtk_label_new (spec->nick);
1300               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1301               gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1302             
1303               prop_edit = gtk_option_menu_new ();
1304
1305               menu = gtk_menu_new ();
1306
1307               eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
1308
1309               i = 0;
1310               while (i < eclass->n_values)
1311                 {
1312                   GtkWidget *mi;
1313                 
1314                   mi = gtk_menu_item_new_with_label (eclass->values[i].value_name);
1315
1316                   gtk_widget_show (mi);
1317                 
1318                   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1319                 
1320                   ++i;
1321                 }
1322
1323               gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
1324               
1325               gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1326             
1327               gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1328             
1329               g_object_connect_property (object, spec->name,
1330                                          GTK_SIGNAL_FUNC (enum_changed),
1331                                          prop_edit, G_OBJECT (prop_edit));
1332             
1333               if (can_modify)
1334                 connect_controller (G_OBJECT (prop_edit), "changed",
1335                                     object, spec->name, (GtkSignalFunc) enum_modified);
1336             }
1337           else
1338             {
1339               gchar *msg = g_strdup_printf ("%s: don't know how to edit type %s",
1340                                             spec->nick, g_type_name (spec->value_type));
1341               hbox = gtk_hbox_new (FALSE, 10);
1342               label = gtk_label_new (msg);            
1343               g_free (msg);
1344               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1345               gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1346               gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1347             }
1348           break;
1349         }
1350
1351       if (prop_edit)
1352         {
1353           if (!can_modify)
1354             gtk_widget_set_sensitive (prop_edit, FALSE);
1355           
1356           /* set initial value */
1357           g_object_notify (object, spec->name);
1358         }
1359       
1360       ++i;
1361     }
1362
1363   gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
1364   
1365   gtk_widget_show_all (win);
1366
1367   return win;
1368 }
1369
1370 /*
1371  * Automated testing
1372  */
1373
1374 static void
1375 run_automated_tests (void)
1376 {
1377   /* FIXME TreePath basic verification */
1378
1379   /* FIXME consistency checks on the models */
1380   
1381 }