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