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