]> Pileus Git - ~andy/gtk/blob - tests/testtreeview.c
adapt to handle PangoColor
[~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 static gchar *column_type_names[] = {
120   "No columns",
121   "One column",
122   "Many columns"
123 };
124
125 #define N_COLUMNS 9
126
127 static GType*
128 get_model_types (void)
129 {
130   static GType column_types[N_COLUMNS] = { 0 };
131   
132   if (column_types[0] == 0)
133     {
134       column_types[0] = G_TYPE_STRING;
135       column_types[1] = G_TYPE_STRING;
136       column_types[2] = GDK_TYPE_PIXBUF;
137       column_types[3] = G_TYPE_FLOAT;
138       column_types[4] = G_TYPE_UINT;
139       column_types[5] = G_TYPE_UCHAR;
140       column_types[6] = G_TYPE_CHAR;
141 #define BOOL_COLUMN 7
142       column_types[BOOL_COLUMN] = G_TYPE_BOOLEAN;
143       column_types[8] = G_TYPE_INT;
144     }
145
146   return column_types;
147 }
148
149 static void
150 col_clicked_cb (GtkTreeViewColumn *col, gpointer data)
151 {
152   GtkWindow *win;
153
154   win = GTK_WINDOW (create_prop_editor (G_OBJECT (col)));
155
156   gtk_window_set_title (win, gtk_tree_view_column_get_title (col));
157 }
158
159 static void
160 setup_column (GtkTreeViewColumn *col)
161 {
162   g_signal_connect_data (G_OBJECT (col),
163                          "clicked",
164                          (GCallback) col_clicked_cb,
165                          NULL,
166                          NULL,
167                          FALSE,
168                          FALSE);
169 }
170
171 static void
172 toggled_callback (GtkCellRendererToggle *celltoggle,
173                   gchar                 *path_string,
174                   GtkTreeView           *tree_view)
175 {
176   GtkTreeModel *model = NULL;
177   GtkTreeModelSort *sort_model = NULL;
178   GtkTreePath *path;
179   GtkTreeIter iter;
180   gboolean active = FALSE;
181   
182   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
183
184   model = gtk_tree_view_get_model (tree_view);
185   
186   if (GTK_IS_TREE_MODEL_SORT (model))
187     {
188       sort_model = GTK_TREE_MODEL_SORT (model);
189       model = gtk_tree_model_sort_get_model (sort_model);
190     }
191
192   if (model == NULL)
193     return;
194
195   if (sort_model)
196     {
197       g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter");
198       return;
199     }
200       
201   path = gtk_tree_path_new_from_string (path_string);
202   if (!gtk_tree_model_get_iter (model,
203                                 &iter, path))
204     {
205       g_warning ("%s: bad path?", G_STRLOC);
206       return;
207     }
208   gtk_tree_path_free (path);
209   
210   if (GTK_IS_LIST_STORE (model))
211     {
212       gtk_list_store_get (GTK_LIST_STORE (model),
213                           &iter,
214                           BOOL_COLUMN,
215                           &active,
216                           -1);
217       
218       gtk_list_store_set (GTK_LIST_STORE (model),
219                           &iter,
220                           BOOL_COLUMN,
221                           !active,
222                           -1);
223     }
224   else if (GTK_IS_TREE_STORE (model))
225     {
226       gtk_tree_store_get (GTK_TREE_STORE (model),
227                           &iter,
228                           BOOL_COLUMN,
229                           &active,
230                           -1);
231             
232       gtk_tree_store_set (GTK_TREE_STORE (model),
233                           &iter,
234                           BOOL_COLUMN,
235                           !active,
236                           -1);
237     }
238   else
239     g_warning ("don't know how to actually toggle value for model type %s",
240                g_type_name (G_TYPE_FROM_INSTANCE (model)));
241 }
242
243
244 static ColumnsType current_column_type = COLUMNS_LOTS;
245
246 static void
247 set_columns_type (GtkTreeView *tree_view, ColumnsType type)
248 {
249   GtkTreeViewColumn *col;
250   GtkCellRenderer *rend;
251   GdkPixbuf *pixbuf;
252   GtkWidget *image;
253   
254   current_column_type = type;
255   
256   col = gtk_tree_view_get_column (tree_view, 0);
257   while (col)
258     {
259       gtk_tree_view_remove_column (tree_view, col);
260
261       col = gtk_tree_view_get_column (tree_view, 0);
262     }
263
264   switch (type)
265     {
266     case COLUMNS_NONE:
267       break;
268
269     case COLUMNS_LOTS:      
270       rend = gtk_cell_renderer_text_new ();
271       
272       col = gtk_tree_view_column_new_with_attributes ("Column 1",
273                                                       rend,
274                                                       "text", 1,
275                                                       NULL);
276       setup_column (col);
277       
278       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
279       
280       g_object_unref (G_OBJECT (rend));
281       g_object_unref (G_OBJECT (col));
282
283       rend = gtk_cell_renderer_text_pixbuf_new ();
284       
285       col = gtk_tree_view_column_new_with_attributes ("Column 2",
286                                                       rend,
287                                                       "text", 0,
288                                                       "pixbuf", 2,
289                                                       NULL);
290
291       setup_column (col);
292       
293       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
294       
295       g_object_unref (G_OBJECT (rend));
296       g_object_unref (G_OBJECT (col));
297
298       rend = gtk_cell_renderer_toggle_new ();
299
300       g_signal_connect_data (G_OBJECT (rend), "toggled",
301                              toggled_callback, tree_view,
302                              NULL, FALSE, FALSE);
303       
304       col = gtk_tree_view_column_new_with_attributes ("Column 3",
305                                                       rend,
306                                                       "active", BOOL_COLUMN,
307                                                       NULL);
308
309       setup_column (col);
310       
311       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
312
313       pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
314
315       image = gtk_image_new_from_pixbuf (pixbuf);
316
317       g_object_unref (G_OBJECT (pixbuf));
318       
319       gtk_widget_show (image);
320       
321       gtk_tree_view_column_set_widget (col, image);
322       
323       g_object_unref (G_OBJECT (rend));
324       g_object_unref (G_OBJECT (col));
325
326       rend = gtk_cell_renderer_toggle_new ();
327
328       /* you could also set this per-row by tying it to a column
329        * in the model of course.
330        */
331       g_object_set (G_OBJECT (rend), "radio", TRUE, NULL);
332       
333       g_signal_connect_data (G_OBJECT (rend), "toggled",
334                              toggled_callback, tree_view,
335                              NULL, FALSE, FALSE);
336       
337       col = gtk_tree_view_column_new_with_attributes ("Column 4",
338                                                       rend,
339                                                       "active", BOOL_COLUMN,
340                                                       NULL);
341
342       setup_column (col);
343       
344       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
345       
346       g_object_unref (G_OBJECT (rend));
347       g_object_unref (G_OBJECT (col));
348
349 #if 0
350
351       rend = gtk_cell_renderer_text_new ();
352       
353       col = gtk_tree_view_column_new_with_attributes ("Column 5",
354                                                       rend,
355                                                       "text", 3,
356                                                       NULL);
357
358       setup_column (col);
359       
360       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
361       
362       g_object_unref (G_OBJECT (rend));
363       g_object_unref (G_OBJECT (col));
364
365
366       rend = gtk_cell_renderer_text_new ();
367       
368       col = gtk_tree_view_column_new_with_attributes ("Column 6",
369                                                       rend,
370                                                       "text", 4,
371                                                       NULL);
372
373       setup_column (col);
374       
375       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
376       
377       g_object_unref (G_OBJECT (rend));
378       g_object_unref (G_OBJECT (col));
379
380
381       rend = gtk_cell_renderer_text_new ();
382       
383       col = gtk_tree_view_column_new_with_attributes ("Column 7",
384                                                       rend,
385                                                       "text", 5,
386                                                       NULL);
387
388       setup_column (col);
389       
390       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
391       
392       g_object_unref (G_OBJECT (rend));
393       g_object_unref (G_OBJECT (col));
394
395       rend = gtk_cell_renderer_text_new ();
396       
397       col = gtk_tree_view_column_new_with_attributes ("Column 8",
398                                                       rend,
399                                                       "text", 6,
400                                                       NULL);
401
402       setup_column (col);
403       
404       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
405       
406       g_object_unref (G_OBJECT (rend));
407       g_object_unref (G_OBJECT (col));
408
409
410       rend = gtk_cell_renderer_text_new ();
411       
412       col = gtk_tree_view_column_new_with_attributes ("Column 9",
413                                                       rend,
414                                                       "text", 7,
415                                                       NULL);
416
417       setup_column (col);
418       
419       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
420       
421       g_object_unref (G_OBJECT (rend));
422       g_object_unref (G_OBJECT (col));
423       
424
425       rend = gtk_cell_renderer_text_new ();
426       
427       col = gtk_tree_view_column_new_with_attributes ("Column 10",
428                                                       rend,
429                                                       "text", 8,
430                                                       NULL);
431
432       setup_column (col);
433       
434       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
435       
436       g_object_unref (G_OBJECT (rend));
437       g_object_unref (G_OBJECT (col));
438
439 #endif
440       
441       gtk_tree_view_set_expander_column (tree_view, 1);
442       
443       /* FALL THRU */
444       
445     case COLUMNS_ONE:
446       rend = gtk_cell_renderer_text_new ();
447       
448       col = gtk_tree_view_column_new_with_attributes ("Column 0",
449                                                       rend,
450                                                       "text", 0,
451                                                       NULL);
452
453       setup_column (col);
454       
455       gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), col, 0);
456       
457       g_object_unref (G_OBJECT (rend));
458       g_object_unref (G_OBJECT (col));
459       
460     default:
461       break;
462     }
463 }
464
465 static ColumnsType
466 get_columns_type (void)
467 {
468   return current_column_type;
469 }
470
471 static GdkPixbuf *our_pixbuf;
472   
473 typedef enum
474 {
475   /*   MODEL_TYPES, */
476   MODEL_TREE,
477   MODEL_LIST,
478   MODEL_SORTED_TREE,
479   MODEL_SORTED_LIST,
480   MODEL_EMPTY_LIST,
481   MODEL_EMPTY_TREE,
482   MODEL_NULL,
483   MODEL_LAST
484 } ModelType;
485
486 /* FIXME add a custom model to test */
487 static GtkTreeModel *models[MODEL_LAST];
488 static const char *model_names[MODEL_LAST] = {
489   "GtkTreeStore",
490   "GtkListStore",
491   "GtkTreeModelSort wrapping GtkTreeStore",
492   "GtkTreeModelSort wrapping GtkListStore",
493   "Empty GtkListStore",
494   "Empty GtkTreeStore",
495   "NULL (no model)"
496 };
497
498 static GtkTreeModel*
499 create_list_model (void)
500 {
501   GtkListStore *store;
502   GtkTreeIter iter;
503   gint i;
504   GType *t;
505
506   t = get_model_types ();
507   
508   store = gtk_list_store_new_with_types (N_COLUMNS,
509                                          t[0], t[1], t[2],
510                                          t[3], t[4], t[5],
511                                          t[6], t[7], t[8]);
512
513   i = 0;
514   while (i < 200)
515     {
516       char *msg;
517       
518       gtk_list_store_append (store, &iter);
519
520       msg = g_strdup_printf ("%d", i);
521       
522       gtk_list_store_set (store, &iter, 0, msg, 1, "Foo! Foo! Foo!",
523                           2, our_pixbuf,
524                           3, 7.0, 4, (guint) 9000,
525                           5, 'f', 6, 'g',
526                           7, TRUE, 8, 23245454,
527                           -1);
528
529       g_free (msg);
530       
531       ++i;
532     }
533
534   return GTK_TREE_MODEL (store);
535 }
536
537 static void
538 typesystem_recurse (GType        type,
539                     GtkTreeIter *parent_iter,
540                     GtkTreeStore *store)
541 {
542   GType* children;
543   guint n_children = 0;
544   gint i;
545   GtkTreeIter iter;
546   gchar *str;
547   
548   gtk_tree_store_append (store, &iter, parent_iter);
549
550   str = g_strdup_printf ("%d", type);
551   gtk_tree_store_set (store, &iter, 0, str, 1, g_type_name (type),
552                       2, our_pixbuf,
553                       3, 7.0, 4, (guint) 9000,
554                       5, 'f', 6, 'g',
555                       7, TRUE, 8, 23245454,
556                       -1);
557   g_free (str);
558   
559   children = g_type_children (type, &n_children);
560
561   i = 0;
562   while (i < n_children)
563     {
564       typesystem_recurse (children[i], &iter, store);
565
566       ++i;
567     }
568   
569   g_free (children);
570 }
571
572 static GtkTreeModel*
573 create_tree_model (void)
574 {
575   GtkTreeStore *store;
576   gint i;
577   GType *t;
578   volatile GType dummy; /* G_GNUC_CONST makes the optimizer remove
579                          * get_type calls if you don't do something
580                          * like this
581                          */
582   
583   /* Make the tree more interesting */
584   dummy = gtk_scrolled_window_get_type ();
585   dummy = gtk_label_get_type ();
586   dummy = gtk_hscrollbar_get_type ();
587   dummy = gtk_vscrollbar_get_type ();
588   dummy = pango_layout_get_type ();
589
590   t = get_model_types ();
591   
592   store = gtk_tree_store_new_with_types (N_COLUMNS,
593                                          t[0], t[1], t[2],
594                                          t[3], t[4], t[5],
595                                          t[6], t[7], t[8]);
596
597   i = 0;
598   while (i < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
599     {
600       typesystem_recurse (i, NULL, store);
601       
602       ++i;
603     }
604
605   return GTK_TREE_MODEL (store);
606 }
607
608 static void
609 model_selected (GtkOptionMenu *om, gpointer data)
610 {
611   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
612   gint hist;
613
614   hist = gtk_option_menu_get_history (om);
615
616   if (models[hist] != gtk_tree_view_get_model (tree_view))
617     {
618       gtk_tree_view_set_model (tree_view, models[hist]);
619     }
620 }
621
622 static void
623 columns_selected (GtkOptionMenu *om, gpointer data)
624 {
625   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
626   gint hist;
627
628   hist = gtk_option_menu_get_history (om);
629
630   if (hist != get_columns_type ())
631     {
632       set_columns_type (tree_view, hist);
633     }
634 }
635
636
637 enum
638 {
639   TARGET_GTK_TREE_MODEL_ROW
640 };
641
642 static GtkTargetEntry row_targets[] = {
643   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,
644     TARGET_GTK_TREE_MODEL_ROW }
645 };
646
647 int
648 main (int    argc,
649       char **argv)
650 {
651   GtkWidget *window;
652   GtkWidget *sw;
653   GtkWidget *tv;
654   GtkWidget *table;
655   GtkWidget *om;
656   GtkWidget *menu;
657   GtkTreeModel *model;
658   gint i;
659   
660   gtk_init (&argc, &argv);
661
662   our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm);  
663   
664 #if 0
665   models[MODEL_TYPES] = GTK_TREE_MODEL (gtk_tree_model_types_new ());
666 #endif
667   models[MODEL_LIST] = create_list_model ();
668   models[MODEL_TREE] = create_tree_model ();
669
670   model = create_list_model ();
671   models[MODEL_SORTED_LIST] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
672   g_object_unref (G_OBJECT (model));
673
674   model = create_tree_model ();
675   models[MODEL_SORTED_TREE] = gtk_tree_model_sort_new_with_model (model, NULL, 0);
676   g_object_unref (G_OBJECT (model));
677
678   models[MODEL_EMPTY_LIST] = GTK_TREE_MODEL (gtk_list_store_new ());
679   models[MODEL_EMPTY_TREE] = GTK_TREE_MODEL (gtk_tree_store_new ());
680   
681   models[MODEL_NULL] = NULL;
682
683   run_automated_tests ();
684   
685   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
686
687   gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
688
689   table = gtk_table_new (3, 1, FALSE);
690
691   gtk_container_add (GTK_CONTAINER (window), table);
692
693   tv = gtk_tree_view_new_with_model (models[0]);
694
695   gtk_tree_view_set_rows_drag_source (GTK_TREE_VIEW (tv),
696                                       GDK_BUTTON1_MASK,
697                                       row_targets,
698                                       G_N_ELEMENTS (row_targets),
699                                       GDK_ACTION_MOVE | GDK_ACTION_COPY,
700                                       NULL, NULL);
701
702   gtk_tree_view_set_rows_drag_dest (GTK_TREE_VIEW (tv),
703                                     row_targets,
704                                     G_N_ELEMENTS (row_targets),
705                                     GDK_ACTION_MOVE | GDK_ACTION_COPY,
706                                     NULL, NULL);
707   
708   /* Model menu */
709
710   menu = gtk_menu_new ();
711   
712   i = 0;
713   while (i < MODEL_LAST)
714     {
715       GtkWidget *mi;
716       const char *name;
717
718       name = model_names[i];
719       
720       mi = gtk_menu_item_new_with_label (name);
721
722       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
723
724 #if 0
725       window = create_prop_editor (G_OBJECT (models[i]));
726
727       gtk_window_set_title (GTK_WINDOW (window),                            
728                             name);
729 #endif
730
731       ++i;
732     }
733   gtk_widget_show_all (menu);
734   
735   om = gtk_option_menu_new ();
736   gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu);
737   
738   gtk_table_attach (GTK_TABLE (table), om,
739                     0, 1, 0, 1,
740                     0, 0, 
741                     0, 0);
742
743   gtk_signal_connect (GTK_OBJECT (om),
744                       "changed",
745                       GTK_SIGNAL_FUNC (model_selected),
746                       tv);
747   
748   /* Columns menu */
749
750   menu = gtk_menu_new ();
751   
752   i = 0;
753   while (i < COLUMNS_LAST)
754     {
755       GtkWidget *mi;
756       const char *name;
757
758       name = column_type_names[i];
759       
760       mi = gtk_menu_item_new_with_label (name);
761
762       gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
763
764       ++i;
765     }
766   gtk_widget_show_all (menu);
767   
768   om = gtk_option_menu_new ();
769   gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu);
770   
771   gtk_table_attach (GTK_TABLE (table), om,
772                     0, 1, 1, 2,
773                     0, 0, 
774                     0, 0);
775
776   set_columns_type (GTK_TREE_VIEW (tv), COLUMNS_LOTS);
777   gtk_option_menu_set_history (GTK_OPTION_MENU (om), COLUMNS_LOTS);
778   
779   gtk_signal_connect (GTK_OBJECT (om),
780                       "changed",
781                       GTK_SIGNAL_FUNC (columns_selected),
782                       tv);
783   
784   sw = gtk_scrolled_window_new (NULL, NULL);
785   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
786                                   GTK_POLICY_AUTOMATIC,
787                                   GTK_POLICY_AUTOMATIC);
788   
789   gtk_table_attach (GTK_TABLE (table), sw,
790                     0, 1, 2, 3,
791                     GTK_EXPAND | GTK_FILL,
792                     GTK_EXPAND | GTK_FILL,
793                     0, 0);
794   
795   gtk_container_add (GTK_CONTAINER (sw), tv);
796   
797   gtk_widget_show_all (window);
798   
799   gtk_main ();
800
801   return 0;
802 }
803
804 /*
805  * GtkTreeModelTypes
806  */
807
808 enum {
809   CHANGED,
810   INSERTED,
811   CHILD_TOGGLED,
812   DELETED,
813
814   LAST_SIGNAL
815 };
816
817 static void         gtk_tree_model_types_init                 (GtkTreeModelTypes      *model_types);
818 static void         gtk_tree_model_types_class_init           (GtkTreeModelTypesClass *class);
819 static void         gtk_tree_model_types_tree_model_init      (GtkTreeModelIface   *iface);
820 static gint         gtk_real_model_types_get_n_columns   (GtkTreeModel        *tree_model);
821 static GType        gtk_real_model_types_get_column_type (GtkTreeModel        *tree_model,
822                                                            gint                 index);
823 static GtkTreePath *gtk_real_model_types_get_path        (GtkTreeModel        *tree_model,
824                                                            GtkTreeIter         *iter);
825 static void         gtk_real_model_types_get_value       (GtkTreeModel        *tree_model,
826                                                            GtkTreeIter         *iter,
827                                                            gint                 column,
828                                                            GValue              *value);
829 static gboolean     gtk_real_model_types_iter_next       (GtkTreeModel        *tree_model,
830                                                            GtkTreeIter         *iter);
831 static gboolean     gtk_real_model_types_iter_children   (GtkTreeModel        *tree_model,
832                                                            GtkTreeIter         *iter,
833                                                            GtkTreeIter         *parent);
834 static gboolean     gtk_real_model_types_iter_has_child  (GtkTreeModel        *tree_model,
835                                                            GtkTreeIter         *iter);
836 static gint         gtk_real_model_types_iter_n_children (GtkTreeModel        *tree_model,
837                                                            GtkTreeIter         *iter);
838 static gboolean     gtk_real_model_types_iter_nth_child  (GtkTreeModel        *tree_model,
839                                                            GtkTreeIter         *iter,
840                                                            GtkTreeIter         *parent,
841                                                            gint                 n);
842 static gboolean     gtk_real_model_types_iter_parent     (GtkTreeModel        *tree_model,
843                                                            GtkTreeIter         *iter,
844                                                            GtkTreeIter         *child);
845
846
847 static guint model_types_signals[LAST_SIGNAL] = { 0 };
848
849
850 GtkType
851 gtk_tree_model_types_get_type (void)
852 {
853   static GtkType model_types_type = 0;
854
855   if (!model_types_type)
856     {
857       static const GTypeInfo model_types_info =
858       {
859         sizeof (GtkTreeModelTypesClass),
860         NULL,           /* base_init */
861         NULL,           /* base_finalize */
862         (GClassInitFunc) gtk_tree_model_types_class_init,
863         NULL,           /* class_finalize */
864         NULL,           /* class_data */
865         sizeof (GtkTreeModelTypes),
866         0,
867         (GInstanceInitFunc) gtk_tree_model_types_init
868       };
869
870       static const GInterfaceInfo tree_model_info =
871       {
872         (GInterfaceInitFunc) gtk_tree_model_types_tree_model_init,
873         NULL,
874         NULL
875       };
876
877       model_types_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeModelTypes", &model_types_info, 0);
878       g_type_add_interface_static (model_types_type,
879                                    GTK_TYPE_TREE_MODEL,
880                                    &tree_model_info);
881     }
882
883   return model_types_type;
884 }
885
886 GtkTreeModelTypes *
887 gtk_tree_model_types_new (void)
888 {
889   GtkTreeModelTypes *retval;
890
891   retval = GTK_TREE_MODEL_TYPES (g_object_new (GTK_TYPE_MODEL_TYPES, NULL));
892
893   return retval;
894 }
895
896 static void
897 gtk_tree_model_types_class_init (GtkTreeModelTypesClass *class)
898 {
899   GObjectClass *object_class;
900
901   object_class = (GObjectClass*) class;
902
903   model_types_signals[CHANGED] =
904     g_signal_newc ("changed",
905                    GTK_CLASS_TYPE (object_class),
906                    G_SIGNAL_RUN_FIRST,
907                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, changed),
908                    NULL,
909                    gtk_marshal_VOID__BOXED_BOXED,
910                    G_TYPE_NONE, 2,
911                    G_TYPE_POINTER,
912                    G_TYPE_POINTER);
913   model_types_signals[INSERTED] =
914     g_signal_newc ("inserted",
915                    GTK_CLASS_TYPE (object_class),
916                    G_SIGNAL_RUN_FIRST,
917                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, inserted),
918                    NULL,
919                    gtk_marshal_VOID__BOXED_BOXED,
920                    G_TYPE_NONE, 2,
921                    G_TYPE_POINTER,
922                    G_TYPE_POINTER);
923   model_types_signals[CHILD_TOGGLED] =
924     g_signal_newc ("child_toggled",
925                    GTK_CLASS_TYPE (object_class),
926                    G_SIGNAL_RUN_FIRST,
927                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, child_toggled),
928                    NULL,
929                    gtk_marshal_VOID__BOXED_BOXED,
930                    G_TYPE_NONE, 2,
931                    G_TYPE_POINTER,
932                    G_TYPE_POINTER);
933   model_types_signals[DELETED] =
934     g_signal_newc ("deleted",
935                    GTK_CLASS_TYPE (object_class),
936                    G_SIGNAL_RUN_FIRST,
937                    GTK_SIGNAL_OFFSET (GtkTreeModelTypesClass, deleted),
938                    NULL,
939                    gtk_marshal_VOID__BOXED,
940                    G_TYPE_NONE, 1,
941                    G_TYPE_POINTER);
942 }
943
944 static void
945 gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface)
946 {
947   iface->get_n_columns = gtk_real_model_types_get_n_columns;
948   iface->get_column_type = gtk_real_model_types_get_column_type;
949   iface->get_path = gtk_real_model_types_get_path;
950   iface->get_value = gtk_real_model_types_get_value;
951   iface->iter_next = gtk_real_model_types_iter_next;
952   iface->iter_children = gtk_real_model_types_iter_children;
953   iface->iter_has_child = gtk_real_model_types_iter_has_child;
954   iface->iter_n_children = gtk_real_model_types_iter_n_children;
955   iface->iter_nth_child = gtk_real_model_types_iter_nth_child;
956   iface->iter_parent = gtk_real_model_types_iter_parent;
957 }
958
959 static void
960 gtk_tree_model_types_init (GtkTreeModelTypes *model_types)
961 {
962   model_types->stamp = g_random_int ();
963 }
964
965 static GType column_types[] = {
966   G_TYPE_STRING, /* GType */
967   G_TYPE_STRING  /* type name */
968 };
969   
970 static gint
971 gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model)
972 {
973   return G_N_ELEMENTS (column_types);
974 }
975
976 static GType
977 gtk_real_model_types_get_column_type (GtkTreeModel *tree_model,
978                                       gint          index)
979 {
980   g_return_val_if_fail (index < G_N_ELEMENTS (column_types), G_TYPE_INVALID);
981   
982   return column_types[index];
983 }
984
985 #if 0
986 /* Use default implementation of this */
987 static gboolean
988 gtk_real_model_types_get_iter (GtkTreeModel *tree_model,
989                                GtkTreeIter  *iter,
990                                GtkTreePath  *path)
991 {
992   
993 }
994 #endif
995
996 /* The toplevel nodes of the tree are the reserved types, G_TYPE_NONE through
997  * G_TYPE_RESERVED_FUNDAMENTAL.
998  */
999
1000 static GtkTreePath *
1001 gtk_real_model_types_get_path (GtkTreeModel *tree_model,
1002                                GtkTreeIter  *iter)
1003 {
1004   GtkTreePath *retval;
1005   GType type;
1006   GType parent;
1007   
1008   g_return_val_if_fail (GTK_IS_TREE_MODEL_TYPES (tree_model), NULL);
1009   g_return_val_if_fail (iter != NULL, NULL);
1010
1011   type = GPOINTER_TO_INT (iter->user_data);
1012   
1013   retval = gtk_tree_path_new ();
1014   
1015   parent = g_type_parent (type);
1016   while (parent != G_TYPE_INVALID)
1017     {
1018       GType* children = g_type_children (parent, NULL);
1019       gint i = 0;
1020
1021       if (!children || children[0] == G_TYPE_INVALID)
1022         {
1023           g_warning ("bad iterator?");
1024           return NULL;
1025         }
1026       
1027       while (children[i] != type)
1028         ++i;
1029
1030       gtk_tree_path_prepend_index (retval, i);
1031
1032       g_free (children);
1033       
1034       type = parent;
1035       parent = g_type_parent (parent);
1036     }
1037
1038   /* The fundamental type itself is the index on the toplevel */
1039   gtk_tree_path_prepend_index (retval, type);
1040
1041   return retval;
1042 }
1043
1044 static void
1045 gtk_real_model_types_get_value (GtkTreeModel *tree_model,
1046                                 GtkTreeIter  *iter,
1047                                 gint          column,
1048                                 GValue       *value)
1049 {
1050   GType type;
1051
1052   type = GPOINTER_TO_INT (iter->user_data);
1053
1054   switch (column)
1055     {
1056     case 0:
1057       {
1058         gchar *str;
1059         
1060         g_value_init (value, G_TYPE_STRING);
1061
1062         str = g_strdup_printf ("%d", type);
1063         g_value_set_string (value, str);
1064         g_free (str);
1065       }
1066       break;
1067
1068     case 1:
1069       g_value_init (value, G_TYPE_STRING);
1070       g_value_set_string (value, g_type_name (type));
1071       break;
1072
1073     default:
1074       g_warning ("Bad column %d requested", column);
1075     }
1076 }
1077
1078 static gboolean
1079 gtk_real_model_types_iter_next (GtkTreeModel  *tree_model,
1080                                 GtkTreeIter   *iter)
1081 {
1082   
1083   GType parent;
1084   GType type;
1085
1086   type = GPOINTER_TO_INT (iter->user_data);
1087
1088   parent = g_type_parent (type);
1089
1090   if (parent == G_TYPE_INVALID)
1091     {
1092       /* fundamental type, add 1 */
1093       if ((type + 1) < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
1094         {
1095           iter->user_data = GINT_TO_POINTER (type + 1);
1096           return TRUE;
1097         }
1098       else
1099         return FALSE;
1100     }
1101   else
1102     {
1103       GType* children = g_type_children (parent, NULL);
1104       gint i = 0;
1105
1106       g_assert (children != NULL);
1107       
1108       while (children[i] != type)
1109         ++i;
1110   
1111       ++i;
1112
1113       if (children[i] != G_TYPE_INVALID)
1114         {
1115           g_free (children);
1116           iter->user_data = GINT_TO_POINTER (children[i]);
1117           return TRUE;
1118         }
1119       else
1120         {
1121           g_free (children);
1122           return FALSE;
1123         }
1124     }
1125 }
1126
1127 static gboolean
1128 gtk_real_model_types_iter_children (GtkTreeModel *tree_model,
1129                                     GtkTreeIter  *iter,
1130                                     GtkTreeIter  *parent)
1131 {
1132   GType type;
1133   GType* children;
1134   
1135   type = GPOINTER_TO_INT (parent->user_data);
1136
1137   children = g_type_children (type, NULL);
1138
1139   if (!children || children[0] == G_TYPE_INVALID)
1140     {
1141       g_free (children);
1142       return FALSE;
1143     }
1144   else
1145     {
1146       iter->user_data = GINT_TO_POINTER (children[0]);
1147       g_free (children);
1148       return TRUE;
1149     }
1150 }
1151
1152 static gboolean
1153 gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model,
1154                                      GtkTreeIter  *iter)
1155 {
1156   GType type;
1157   GType* children;
1158   
1159   type = GPOINTER_TO_INT (iter->user_data);
1160   
1161   children = g_type_children (type, NULL);
1162
1163   if (!children || children[0] == G_TYPE_INVALID)
1164     {
1165       g_free (children);
1166       return FALSE;
1167     }
1168   else
1169     {
1170       g_free (children);
1171       return TRUE;
1172     }
1173 }
1174
1175 static gint
1176 gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model,
1177                                       GtkTreeIter  *iter)
1178 {
1179   if (iter == NULL)
1180     {
1181       return G_TYPE_LAST_RESERVED_FUNDAMENTAL - 1;
1182     }
1183   else
1184     {
1185       GType type;
1186       GType* children;
1187       guint n_children = 0;
1188
1189       type = GPOINTER_TO_INT (iter->user_data);
1190       
1191       children = g_type_children (type, &n_children);
1192       
1193       g_free (children);
1194       
1195       return n_children;
1196     }
1197 }
1198
1199 static gboolean
1200 gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model,
1201                                      GtkTreeIter  *iter,
1202                                      GtkTreeIter  *parent,
1203                                      gint          n)
1204 {  
1205   if (parent == NULL)
1206     {
1207       /* fundamental type */
1208       if (n < G_TYPE_LAST_RESERVED_FUNDAMENTAL)
1209         {
1210           iter->user_data = GINT_TO_POINTER (n);
1211           return TRUE;
1212         }
1213       else
1214         return FALSE;
1215     }
1216   else
1217     {
1218       GType type = GPOINTER_TO_INT (parent->user_data);      
1219       guint n_children = 0;
1220       GType* children = g_type_children (type, &n_children);
1221
1222       if (n_children == 0)
1223         {
1224           g_free (children);
1225           return FALSE;
1226         }
1227       else if (n >= n_children)
1228         {
1229           g_free (children);
1230           return FALSE;
1231         }
1232       else
1233         {
1234           iter->user_data = GINT_TO_POINTER (children[n]);
1235           g_free (children);
1236
1237           return TRUE;
1238         }
1239     }
1240 }
1241
1242 static gboolean
1243 gtk_real_model_types_iter_parent (GtkTreeModel *tree_model,
1244                                   GtkTreeIter  *iter,
1245                                   GtkTreeIter  *child)
1246 {
1247   GType type;
1248   GType parent;
1249   
1250   type = GPOINTER_TO_INT (child->user_data);
1251   
1252   parent = g_type_parent (type);
1253   
1254   if (parent == G_TYPE_INVALID)
1255     {
1256       if (type >= G_TYPE_LAST_RESERVED_FUNDAMENTAL)
1257         g_warning ("no parent for %d %s\n", type, g_type_name (type));
1258       return FALSE;
1259     }
1260   else
1261     {
1262       iter->user_data = GINT_TO_POINTER (parent);
1263       
1264       return TRUE;
1265     }
1266 }
1267
1268 /*
1269  * Property editor thingy
1270  */
1271
1272 static void
1273 get_param_specs (GObject *object,
1274                  GParamSpec ***specs,
1275                  gint         *n_specs)
1276 {
1277   /* Use private interface for now, fix later */
1278   *specs = G_OBJECT_GET_CLASS (object)->property_specs;
1279   *n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs;
1280 }
1281
1282 typedef struct
1283 {
1284   gpointer instance;
1285   guint id;
1286 } DisconnectData;
1287
1288 static void
1289 disconnect_func (gpointer data)
1290 {
1291   DisconnectData *dd = data;
1292   
1293   g_signal_handler_disconnect (dd->instance, dd->id);
1294   g_free (dd);
1295 }
1296
1297 static void
1298 g_object_connect_property (GObject *object,
1299                            const gchar *prop_name,
1300                            GtkSignalFunc func,
1301                            gpointer data,
1302                            GObject *alive_object)
1303 {
1304   gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
1305   DisconnectData *dd;
1306
1307   dd = g_new (DisconnectData, 1);
1308   
1309   dd->id = g_signal_connect_data (object, with_detail,
1310                                   func, data,
1311                                   NULL, FALSE, FALSE);
1312
1313   dd->instance = object;
1314   
1315   g_object_set_data_full (G_OBJECT (alive_object),
1316                           "alive-object",
1317                           dd,
1318                           disconnect_func);
1319   
1320   g_free (with_detail);
1321 }
1322
1323 typedef struct 
1324 {
1325   GObject *obj;
1326   gchar *prop;
1327 } ObjectProperty;
1328
1329 static void
1330 free_object_property (ObjectProperty *p)
1331 {
1332   g_free (p->prop);
1333   g_free (p);
1334 }
1335
1336 static void
1337 connect_controller (GObject *controller,
1338                     const gchar *signal,
1339                     GObject *model,
1340                     const gchar *prop_name,
1341                     GtkSignalFunc func)
1342 {
1343   ObjectProperty *p;
1344
1345   p = g_new (ObjectProperty, 1);
1346   p->obj = model;
1347   p->prop = g_strdup (prop_name);
1348
1349   g_signal_connect_data (controller, signal, func, p,
1350                          (GClosureNotify)free_object_property,
1351                          FALSE, FALSE);
1352 }
1353
1354 static void
1355 int_modified (GtkAdjustment *adj, gpointer data)
1356 {
1357   ObjectProperty *p = data;
1358
1359   g_object_set (p->obj, p->prop, (int) adj->value, NULL);
1360 }
1361
1362 static void
1363 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
1364 {
1365   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
1366   GValue val = { 0, };  
1367
1368   g_value_init (&val, G_TYPE_INT);
1369   g_object_get_property (object, pspec->name, &val);
1370
1371   if (g_value_get_int (&val) != (int)adj->value)
1372     gtk_adjustment_set_value (adj, g_value_get_int (&val));
1373
1374   g_value_unset (&val);
1375 }
1376
1377
1378 static void
1379 string_modified (GtkEntry *entry, gpointer data)
1380 {
1381   ObjectProperty *p = data;
1382   gchar *text;
1383
1384   text = gtk_entry_get_text (entry);
1385
1386   g_object_set (p->obj, p->prop, text, NULL);
1387 }
1388
1389 static void
1390 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
1391 {
1392   GtkEntry *entry = GTK_ENTRY (data);
1393   GValue val = { 0, };  
1394   gchar *str;
1395   gchar *text;
1396   
1397   g_value_init (&val, G_TYPE_STRING);
1398   g_object_get_property (object, pspec->name, &val);
1399
1400   str = g_value_get_string (&val);
1401   if (str == NULL)
1402     str = "";
1403   text = gtk_entry_get_text (entry);
1404
1405   if (strcmp (str, text) != 0)
1406     gtk_entry_set_text (entry, str);
1407   
1408   g_value_unset (&val);
1409 }
1410
1411 static void
1412 bool_modified (GtkToggleButton *tb, gpointer data)
1413 {
1414   ObjectProperty *p = data;
1415
1416   g_object_set (p->obj, p->prop, (int) tb->active, NULL);
1417 }
1418
1419 static void
1420 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
1421 {
1422   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
1423   GValue val = { 0, };  
1424   
1425   g_value_init (&val, G_TYPE_BOOLEAN);
1426   g_object_get_property (object, pspec->name, &val);
1427
1428   if (g_value_get_boolean (&val) != tb->active)
1429     gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
1430
1431   gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
1432                       "TRUE" : "FALSE");
1433   
1434   g_value_unset (&val);
1435 }
1436
1437
1438 static void
1439 enum_modified (GtkOptionMenu *om, gpointer data)
1440 {
1441   ObjectProperty *p = data;
1442   gint i;
1443   GParamSpec *spec;
1444   GEnumClass *eclass;
1445   
1446   spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
1447                                        p->prop);
1448
1449   eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
1450   
1451   i = gtk_option_menu_get_history (om);
1452
1453   g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
1454 }
1455
1456 static void
1457 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
1458 {
1459   GtkOptionMenu *om = GTK_OPTION_MENU (data);
1460   GValue val = { 0, };  
1461   GEnumClass *eclass;
1462   gint i;
1463
1464   eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
1465   
1466   g_value_init (&val, pspec->value_type);
1467   g_object_get_property (object, pspec->name, &val);
1468
1469   i = 0;
1470   while (i < eclass->n_values)
1471     {
1472       if (eclass->values[i].value == g_value_get_enum (&val))
1473         break;
1474       ++i;
1475     }
1476   
1477   if (gtk_option_menu_get_history (om) != i)
1478     gtk_option_menu_set_history (om, i);
1479   
1480   g_value_unset (&val);
1481 }
1482
1483 static GtkWidget*
1484 create_prop_editor (GObject *object)
1485 {
1486   GtkWidget *win;
1487   GtkWidget *vbox;
1488   GtkWidget *hbox;
1489   GtkWidget *label;
1490   GtkWidget *prop_edit;
1491   GtkWidget *sw;
1492   gint n_specs = 0;
1493   GParamSpec **specs = NULL;
1494   gint i;
1495   GtkAdjustment *adj;
1496   
1497   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1498
1499   /* hold a strong ref to the object we're editing */
1500   g_object_ref (G_OBJECT (object));
1501   g_object_set_data_full (G_OBJECT (win), "model-object",
1502                           object, (GDestroyNotify)g_object_unref);
1503   
1504   vbox = gtk_vbox_new (TRUE, 2);
1505
1506   sw = gtk_scrolled_window_new (NULL, NULL);
1507   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1508                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1509   
1510   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
1511   gtk_container_add (GTK_CONTAINER (win), sw);
1512   
1513   get_param_specs (object, &specs, &n_specs);
1514   
1515   i = 0;
1516   while (i < n_specs)
1517     {
1518       GParamSpec *spec = specs[i];
1519       gboolean can_modify;
1520       
1521       prop_edit = NULL;
1522
1523       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1524                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1525       
1526       if ((spec->flags & G_PARAM_READABLE) == 0)
1527         {
1528           /* can't display unreadable properties */
1529           ++i;
1530           continue;
1531         }
1532       
1533       switch (spec->value_type)
1534         {
1535         case G_TYPE_INT:
1536           hbox = gtk_hbox_new (FALSE, 10);
1537           label = gtk_label_new (spec->nick);
1538           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1539           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1540           adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
1541                                                     G_PARAM_SPEC_INT (spec)->minimum,
1542                                                     G_PARAM_SPEC_INT (spec)->maximum,
1543                                                     1,
1544                                                     MAX ((G_PARAM_SPEC_INT (spec)->maximum -
1545                                                           G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
1546                                                     0.0));
1547
1548           prop_edit = gtk_spin_button_new (adj, 1.0, 0);
1549           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1550           
1551           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1552
1553           g_object_connect_property (object, spec->name,
1554                                      GTK_SIGNAL_FUNC (int_changed),
1555                                      adj, G_OBJECT (adj));
1556
1557           if (can_modify)
1558             connect_controller (G_OBJECT (adj), "value_changed",
1559                                 object, spec->name, (GtkSignalFunc) int_modified);
1560           break;
1561
1562         case G_TYPE_STRING:
1563           hbox = gtk_hbox_new (FALSE, 10);
1564           label = gtk_label_new (spec->nick);
1565           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1566           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1567
1568           prop_edit = gtk_entry_new ();
1569           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1570           
1571           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1572
1573           g_object_connect_property (object, spec->name,
1574                                      GTK_SIGNAL_FUNC (string_changed),
1575                                      prop_edit, G_OBJECT (prop_edit));
1576
1577           if (can_modify)
1578             connect_controller (G_OBJECT (prop_edit), "changed",
1579                                 object, spec->name, (GtkSignalFunc) string_modified);
1580           break;
1581
1582         case G_TYPE_BOOLEAN:
1583           hbox = gtk_hbox_new (FALSE, 10);
1584           label = gtk_label_new (spec->nick);
1585           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1586           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1587
1588           prop_edit = gtk_toggle_button_new_with_label ("");
1589           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1590           
1591           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1592
1593           g_object_connect_property (object, spec->name,
1594                                      GTK_SIGNAL_FUNC (bool_changed),
1595                                      prop_edit, G_OBJECT (prop_edit));
1596
1597           if (can_modify)
1598             connect_controller (G_OBJECT (prop_edit), "toggled",
1599                                 object, spec->name, (GtkSignalFunc) bool_modified);
1600           break;
1601           
1602         default:
1603           if (g_type_is_a (spec->value_type, G_TYPE_ENUM))
1604             {
1605               GtkWidget *menu;
1606               GEnumClass *eclass;
1607               gint i;
1608             
1609               hbox = gtk_hbox_new (FALSE, 10);
1610               label = gtk_label_new (spec->nick);
1611               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1612               gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1613             
1614               prop_edit = gtk_option_menu_new ();
1615
1616               menu = gtk_menu_new ();
1617
1618               eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
1619
1620               i = 0;
1621               while (i < eclass->n_values)
1622                 {
1623                   GtkWidget *mi;
1624                 
1625                   mi = gtk_menu_item_new_with_label (eclass->values[i].value_name);
1626
1627                   gtk_widget_show (mi);
1628                 
1629                   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1630                 
1631                   ++i;
1632                 }
1633
1634               gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
1635               
1636               gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
1637             
1638               gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
1639             
1640               g_object_connect_property (object, spec->name,
1641                                          GTK_SIGNAL_FUNC (enum_changed),
1642                                          prop_edit, G_OBJECT (prop_edit));
1643             
1644               if (can_modify)
1645                 connect_controller (G_OBJECT (prop_edit), "changed",
1646                                     object, spec->name, (GtkSignalFunc) enum_modified);
1647             }
1648           else
1649             {
1650               gchar *msg = g_strdup_printf ("%s: don't know how to edit type %s",
1651                                             spec->nick, g_type_name (spec->value_type));
1652               hbox = gtk_hbox_new (FALSE, 10);
1653               label = gtk_label_new (msg);            
1654               g_free (msg);
1655               gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1656               gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1657               gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1658             }
1659           break;
1660         }
1661
1662       if (prop_edit)
1663         {
1664           if (!can_modify)
1665             gtk_widget_set_sensitive (prop_edit, FALSE);
1666           
1667           /* set initial value */
1668           g_object_notify (object, spec->name);
1669         }
1670       
1671       ++i;
1672     }
1673
1674   gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
1675   
1676   gtk_widget_show_all (win);
1677
1678   return win;
1679 }
1680
1681 /*
1682  * Automated testing
1683  */
1684
1685 static void
1686 treestore_torture_recurse (GtkTreeStore *store,
1687                            GtkTreeIter  *root,
1688                            gint          depth)
1689 {
1690   GtkTreeModel *model;
1691   gint i;
1692   GtkTreeIter iter;  
1693   
1694   model = GTK_TREE_MODEL (store);    
1695
1696   if (depth > 2)
1697     return;
1698
1699   ++depth;
1700
1701   gtk_tree_store_append (store, &iter, root);
1702   
1703   gtk_tree_model_iter_children (model, &iter, root);
1704   
1705   i = 0;
1706   while (i < 100)
1707     {
1708       gtk_tree_store_append (store, &iter, root);
1709       ++i;
1710     }
1711
1712   while (gtk_tree_model_iter_children (model, &iter, root))
1713     gtk_tree_store_remove (store, &iter);
1714
1715   gtk_tree_store_append (store, &iter, root);
1716
1717   /* inserts before last node in tree */
1718   i = 0;
1719   while (i < 100)
1720     {
1721       gtk_tree_store_insert_before (store, &iter, root, &iter);
1722       ++i;
1723     }
1724
1725   /* inserts after the node before the last node */
1726   i = 0;
1727   while (i < 100)
1728     {
1729       gtk_tree_store_insert_after (store, &iter, root, &iter);
1730       ++i;
1731     }
1732
1733   /* inserts after the last node */
1734   gtk_tree_store_append (store, &iter, root);
1735     
1736   i = 0;
1737   while (i < 100)
1738     {
1739       gtk_tree_store_insert_after (store, &iter, root, &iter);
1740       ++i;
1741     }
1742
1743   /* remove everything again */
1744   while (gtk_tree_model_iter_children (model, &iter, root))
1745     gtk_tree_store_remove (store, &iter);
1746
1747
1748     /* Prepends */
1749   gtk_tree_store_prepend (store, &iter, root);
1750     
1751   i = 0;
1752   while (i < 100)
1753     {
1754       gtk_tree_store_prepend (store, &iter, root);
1755       ++i;
1756     }
1757
1758   /* remove everything again */
1759   while (gtk_tree_model_iter_children (model, &iter, root))
1760     gtk_tree_store_remove (store, &iter);
1761
1762   gtk_tree_store_append (store, &iter, root);
1763   gtk_tree_store_append (store, &iter, root);
1764   gtk_tree_store_append (store, &iter, root);
1765   gtk_tree_store_append (store, &iter, root);
1766
1767   while (gtk_tree_model_iter_children (model, &iter, root))
1768     {
1769       treestore_torture_recurse (store, &iter, depth);
1770       gtk_tree_store_remove (store, &iter);
1771     }
1772 }
1773
1774 static void
1775 run_automated_tests (void)
1776 {
1777   g_print ("Running automated tests...\n");
1778   
1779   /* FIXME TreePath basic verification */
1780
1781   /* FIXME generic consistency checks on the models */
1782
1783   {
1784     /* Make sure list store mutations don't crash anything */
1785     GtkListStore *store;
1786     GtkTreeModel *model;
1787     gint i;
1788     GtkTreeIter iter;
1789     
1790     store = gtk_list_store_new_with_types (1, G_TYPE_INT);
1791
1792     model = GTK_TREE_MODEL (store);
1793     
1794     i = 0;
1795     while (i < 100)
1796       {
1797         gtk_list_store_append (store, &iter);
1798         ++i;
1799       }
1800
1801     while (gtk_tree_model_get_first (model, &iter))
1802       gtk_list_store_remove (store, &iter);
1803
1804     gtk_list_store_append (store, &iter);
1805
1806     /* inserts before last node in list */
1807     i = 0;
1808     while (i < 100)
1809       {
1810         gtk_list_store_insert_before (store, &iter, &iter);
1811         ++i;
1812       }
1813
1814     /* inserts after the node before the last node */
1815     i = 0;
1816     while (i < 100)
1817       {
1818         gtk_list_store_insert_after (store, &iter, &iter);
1819         ++i;
1820       }
1821
1822     /* inserts after the last node */
1823     gtk_list_store_append (store, &iter);
1824     
1825     i = 0;
1826     while (i < 100)
1827       {
1828         gtk_list_store_insert_after (store, &iter, &iter);
1829         ++i;
1830       }
1831
1832     /* remove everything again */
1833     while (gtk_tree_model_get_first (model, &iter))
1834       gtk_list_store_remove (store, &iter);
1835
1836
1837     /* Prepends */
1838     gtk_list_store_prepend (store, &iter);
1839     
1840     i = 0;
1841     while (i < 100)
1842       {
1843         gtk_list_store_prepend (store, &iter);
1844         ++i;
1845       }
1846
1847     /* remove everything again */
1848     while (gtk_tree_model_get_first (model, &iter))
1849       gtk_list_store_remove (store, &iter);
1850     
1851     g_object_unref (G_OBJECT (store));
1852   }
1853
1854   {
1855     /* Make sure tree store mutations don't crash anything */
1856     GtkTreeStore *store;
1857     
1858     store = gtk_tree_store_new_with_types (1, G_TYPE_INT);
1859     
1860     treestore_torture_recurse (store, NULL, 0);
1861     
1862     g_object_unref (G_OBJECT (store));
1863   }
1864
1865   g_print ("Passed.\n");
1866 }