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