]> Pileus Git - ~andy/gtk/blob - tests/testtreeview.c
0f3bdffb58724cd07eb5bf3ab74b86881f95af4f
[~andy/gtk] / tests / testtreeview.c
1 /* testtreeview.c
2  * Copyright (C) 2001 Red Hat, Inc
3  * Author: Jonathan Blandford
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <string.h>
20 #include "prop-editor.h"
21 #include <gtk/gtk.h>
22 #include <stdlib.h>
23
24 /* Don't copy this bad example; inline RGB data is always a better
25  * idea than inline XPMs.
26  */
27 static char  *book_closed_xpm[] = {
28 "16 16 6 1",
29 "       c None s None",
30 ".      c black",
31 "X      c red",
32 "o      c yellow",
33 "O      c #808080",
34 "#      c white",
35 "                ",
36 "       ..       ",
37 "     ..XX.      ",
38 "   ..XXXXX.     ",
39 " ..XXXXXXXX.    ",
40 ".ooXXXXXXXXX.   ",
41 "..ooXXXXXXXXX.  ",
42 ".X.ooXXXXXXXXX. ",
43 ".XX.ooXXXXXX..  ",
44 " .XX.ooXXX..#O  ",
45 "  .XX.oo..##OO. ",
46 "   .XX..##OO..  ",
47 "    .X.#OO..    ",
48 "     ..O..      ",
49 "      ..        ",
50 "                "
51 };
52
53 static void run_automated_tests (void);
54
55 /* This custom model is to test custom model use. */
56
57 #define GTK_TYPE_MODEL_TYPES                            (gtk_tree_model_types_get_type ())
58 #define GTK_TREE_MODEL_TYPES(obj)                       (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypes))
59 #define GTK_TREE_MODEL_TYPES_CLASS(klass)               (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypesClass))
60 #define GTK_IS_TREE_MODEL_TYPES(obj)                    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_MODEL_TYPES))
61 #define GTK_IS_TREE_MODEL_TYPES_GET_CLASS(klass)        (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MODEL_TYPES))
62
63 typedef struct _GtkTreeModelTypes       GtkTreeModelTypes;
64 typedef struct _GtkTreeModelTypesClass  GtkTreeModelTypesClass;
65
66 struct _GtkTreeModelTypes
67 {
68   GObject parent;
69
70   gint stamp;
71 };
72
73 struct _GtkTreeModelTypesClass
74 {
75   GObjectClass parent_class;
76
77   guint        (* get_flags)       (GtkTreeModel *tree_model);   
78   gint         (* get_n_columns)   (GtkTreeModel *tree_model);
79   GType        (* get_column_type) (GtkTreeModel *tree_model,
80                                     gint          index);
81   gboolean     (* get_iter)        (GtkTreeModel *tree_model,
82                                     GtkTreeIter  *iter,
83                                     GtkTreePath  *path);
84   GtkTreePath *(* get_path)        (GtkTreeModel *tree_model,
85                                     GtkTreeIter  *iter);
86   void         (* get_value)       (GtkTreeModel *tree_model,
87                                     GtkTreeIter  *iter,
88                                     gint          column,
89                                     GValue       *value);
90   gboolean     (* iter_next)       (GtkTreeModel *tree_model,
91                                     GtkTreeIter  *iter);
92   gboolean     (* iter_children)   (GtkTreeModel *tree_model,
93                                     GtkTreeIter  *iter,
94                                     GtkTreeIter  *parent);
95   gboolean     (* iter_has_child)  (GtkTreeModel *tree_model,
96                                     GtkTreeIter  *iter);
97   gint         (* iter_n_children) (GtkTreeModel *tree_model,
98                                     GtkTreeIter  *iter);
99   gboolean     (* iter_nth_child)  (GtkTreeModel *tree_model,
100                                     GtkTreeIter  *iter,
101                                     GtkTreeIter  *parent,
102                                     gint          n);
103   gboolean     (* iter_parent)     (GtkTreeModel *tree_model,
104                                     GtkTreeIter  *iter,
105                                     GtkTreeIter  *child);
106   void         (* ref_iter)        (GtkTreeModel *tree_model,
107                                     GtkTreeIter  *iter);
108   void         (* unref_iter)      (GtkTreeModel *tree_model,
109                                     GtkTreeIter  *iter);
110
111   /* These will be moved into the GtkTreeModelIface eventually */
112   void         (* changed)         (GtkTreeModel *tree_model,
113                                     GtkTreePath  *path,
114                                     GtkTreeIter  *iter);
115   void         (* inserted)        (GtkTreeModel *tree_model,
116                                     GtkTreePath  *path,
117                                     GtkTreeIter  *iter);
118   void         (* child_toggled)   (GtkTreeModel *tree_model,
119                                     GtkTreePath  *path,
120                                     GtkTreeIter  *iter);
121   void         (* deleted)         (GtkTreeModel *tree_model,
122                                     GtkTreePath  *path);
123 };
124
125 GType              gtk_tree_model_types_get_type      (void) G_GNUC_CONST;
126 GtkTreeModelTypes *gtk_tree_model_types_new           (void);
127
128 typedef enum
129 {
130   COLUMNS_NONE,
131   COLUMNS_ONE,
132   COLUMNS_LOTS,
133   COLUMNS_LAST
134 } ColumnsType;
135
136 static gchar *column_type_names[] = {
137   "No columns",
138   "One column",
139   "Many columns"
140 };
141
142 #define N_COLUMNS 9
143
144 static GType*
145 get_model_types (void)
146 {
147   static GType column_types[N_COLUMNS] = { 0 };
148   
149   if (column_types[0] == 0)
150     {
151       column_types[0] = G_TYPE_STRING;
152       column_types[1] = G_TYPE_STRING;
153       column_types[2] = GDK_TYPE_PIXBUF;
154       column_types[3] = G_TYPE_FLOAT;
155       column_types[4] = G_TYPE_UINT;
156       column_types[5] = G_TYPE_UCHAR;
157       column_types[6] = G_TYPE_CHAR;
158 #define BOOL_COLUMN 7
159       column_types[BOOL_COLUMN] = G_TYPE_BOOLEAN;
160       column_types[8] = G_TYPE_INT;
161     }
162
163   return column_types;
164 }
165
166 static void
167 col_clicked_cb (GtkTreeViewColumn *col, gpointer data)
168 {
169   GtkWindow *win;
170
171   win = GTK_WINDOW (create_prop_editor (G_OBJECT (col), GTK_TYPE_TREE_VIEW_COLUMN));
172
173   gtk_window_set_title (win, gtk_tree_view_column_get_title (col));
174 }
175
176 static void
177 setup_column (GtkTreeViewColumn *col)
178 {
179   gtk_tree_view_column_set_clickable (col, TRUE);
180   g_signal_connect (col,
181                     "clicked",
182                     G_CALLBACK (col_clicked_cb),
183                     NULL);
184 }
185
186 static void
187 toggled_callback (GtkCellRendererToggle *celltoggle,
188                   gchar                 *path_string,
189                   GtkTreeView           *tree_view)
190 {
191   GtkTreeModel *model = NULL;
192   GtkTreeModelSort *sort_model = NULL;
193   GtkTreePath *path;
194   GtkTreeIter iter;
195   gboolean active = FALSE;
196   
197   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
198
199   model = gtk_tree_view_get_model (tree_view);
200   
201   if (GTK_IS_TREE_MODEL_SORT (model))
202     {
203       sort_model = GTK_TREE_MODEL_SORT (model);
204       model = gtk_tree_model_sort_get_model (sort_model);
205     }
206
207   if (model == NULL)
208     return;
209
210   if (sort_model)
211     {
212       g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter");
213       return;
214     }
215       
216   path = gtk_tree_path_new_from_string (path_string);
217   if (!gtk_tree_model_get_iter (model,
218                                 &iter, path))
219     {
220       g_warning ("%s: bad path?", G_STRLOC);
221       return;
222     }
223   gtk_tree_path_free (path);
224   
225   if (GTK_IS_LIST_STORE (model))
226     {
227       gtk_tree_model_get (GTK_TREE_MODEL (model),
228                           &iter,
229                           BOOL_COLUMN,
230                           &active,
231                           -1);
232       
233       gtk_list_store_set (GTK_LIST_STORE (model),
234                           &iter,
235                           BOOL_COLUMN,
236                           !active,
237                           -1);
238     }
239   else if (GTK_IS_TREE_STORE (model))
240     {
241       gtk_tree_model_get (GTK_TREE_MODEL (model),
242                           &iter,
243                           BOOL_COLUMN,
244                           &active,
245                           -1);
246             
247       gtk_tree_store_set (GTK_TREE_STORE (model),
248                           &iter,
249                           BOOL_COLUMN,
250                           !active,
251                           -1);
252     }
253   else
254     g_warning ("don't know how to actually toggle value for model type %s",
255                g_type_name (G_TYPE_FROM_INSTANCE (model)));
256 }
257
258 static void
259 edited_callback (GtkCellRendererText *renderer,
260                  const gchar   *path_string,
261                  const gchar   *new_text,
262                  GtkTreeView  *tree_view)
263 {
264   GtkTreeModel *model = NULL;
265   GtkTreeModelSort *sort_model = NULL;
266   GtkTreePath *path;
267   GtkTreeIter iter;
268   guint value = atoi (new_text);
269   
270   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
271
272   model = gtk_tree_view_get_model (tree_view);
273   
274   if (GTK_IS_TREE_MODEL_SORT (model))
275     {
276       sort_model = GTK_TREE_MODEL_SORT (model);
277       model = gtk_tree_model_sort_get_model (sort_model);
278     }
279
280   if (model == NULL)
281     return;
282
283   if (sort_model)
284     {
285       g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter");
286       return;
287     }
288       
289   path = gtk_tree_path_new_from_string (path_string);
290   if (!gtk_tree_model_get_iter (model,
291                                 &iter, path))
292     {
293       g_warning ("%s: bad path?", G_STRLOC);
294       return;
295     }
296   gtk_tree_path_free (path);
297
298   if (GTK_IS_LIST_STORE (model))
299     {
300       gtk_list_store_set (GTK_LIST_STORE (model),
301                           &iter,
302                           4,
303                           value,
304                           -1);
305     }
306   else if (GTK_IS_TREE_STORE (model))
307     {
308       gtk_tree_store_set (GTK_TREE_STORE (model),
309                           &iter,
310                           4,
311                           value,
312                           -1);
313     }
314   else
315     g_warning ("don't know how to actually toggle value for model type %s",
316                g_type_name (G_TYPE_FROM_INSTANCE (model)));
317 }
318
319 static ColumnsType current_column_type = COLUMNS_LOTS;
320
321 static void
322 set_columns_type (GtkTreeView *tree_view, ColumnsType type)
323 {
324   GtkTreeViewColumn *col;
325   GtkCellRenderer *rend;
326   GdkPixbuf *pixbuf;
327   GtkWidget *image;
328   GtkAdjustment *adjustment;
329
330   current_column_type = type;
331   
332   col = gtk_tree_view_get_column (tree_view, 0);
333   while (col)
334     {
335       gtk_tree_view_remove_column (tree_view, col);
336
337       col = gtk_tree_view_get_column (tree_view, 0);
338     }
339
340   gtk_tree_view_set_rules_hint (tree_view, FALSE);
341   
342   switch (type)
343     {
344     case COLUMNS_NONE:
345       break;
346
347     case COLUMNS_LOTS:
348       /* with lots of columns we need to turn on rules */
349       gtk_tree_view_set_rules_hint (tree_view, TRUE);
350       
351       rend = gtk_cell_renderer_text_new ();
352
353       col = gtk_tree_view_column_new_with_attributes ("Column 1",
354                                                       rend,
355                                                       "text", 1,
356                                                       NULL);
357       setup_column (col);
358       
359       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
360       
361       col = gtk_tree_view_column_new();
362       gtk_tree_view_column_set_title (col, "Column 2");
363       
364       rend = gtk_cell_renderer_pixbuf_new ();
365       gtk_tree_view_column_pack_start (col, rend, FALSE);
366       gtk_tree_view_column_add_attribute (col, rend, "pixbuf", 2);
367       rend = gtk_cell_renderer_text_new ();
368       gtk_tree_view_column_pack_start (col, rend, TRUE);
369       gtk_tree_view_column_add_attribute (col, rend, "text", 0);
370
371       setup_column (col);
372       
373       
374       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
375       gtk_tree_view_set_expander_column (tree_view, col);
376       
377       rend = gtk_cell_renderer_toggle_new ();
378
379       g_signal_connect (rend, "toggled",
380                         G_CALLBACK (toggled_callback), tree_view);
381       
382       col = gtk_tree_view_column_new_with_attributes ("Column 3",
383                                                       rend,
384                                                       "active", BOOL_COLUMN,
385                                                       NULL);
386
387       setup_column (col);
388       
389       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
390
391       pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)book_closed_xpm);
392
393       image = gtk_image_new_from_pixbuf (pixbuf);
394
395       g_object_unref (pixbuf);
396       
397       gtk_widget_show (image);
398       
399       gtk_tree_view_column_set_widget (col, image);
400       
401       rend = gtk_cell_renderer_toggle_new ();
402
403       /* you could also set this per-row by tying it to a column
404        * in the model of course.
405        */
406       g_object_set (rend, "radio", TRUE, NULL);
407       
408       g_signal_connect (rend, "toggled",
409                         G_CALLBACK (toggled_callback), tree_view);
410       
411       col = gtk_tree_view_column_new_with_attributes ("Column 4",
412                                                       rend,
413                                                       "active", BOOL_COLUMN,
414                                                       NULL);
415
416       setup_column (col);
417       
418       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
419
420       rend = gtk_cell_renderer_spin_new ();
421
422       adjustment = gtk_adjustment_new (0, 0, 10000, 100, 100, 100);
423       g_object_set (rend, "editable", TRUE, NULL);
424       g_object_set (rend, "adjustment", adjustment, NULL);
425
426       g_signal_connect (rend, "edited",
427                         G_CALLBACK (edited_callback), tree_view);
428
429       col = gtk_tree_view_column_new_with_attributes ("Column 5",
430                                                       rend,
431                                                       "text", 4,
432                                                       NULL);
433
434       setup_column (col);
435       
436       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
437 #if 0
438       
439       rend = gtk_cell_renderer_text_new ();
440       
441       col = gtk_tree_view_column_new_with_attributes ("Column 6",
442                                                       rend,
443                                                       "text", 4,
444                                                       NULL);
445
446       setup_column (col);
447       
448       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
449       
450       rend = gtk_cell_renderer_text_new ();
451       
452       col = gtk_tree_view_column_new_with_attributes ("Column 7",
453                                                       rend,
454                                                       "text", 5,
455                                                       NULL);
456
457       setup_column (col);
458       
459       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
460       
461       rend = gtk_cell_renderer_text_new ();
462       
463       col = gtk_tree_view_column_new_with_attributes ("Column 8",
464                                                       rend,
465                                                       "text", 6,
466                                                       NULL);
467
468       setup_column (col);
469       
470       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
471       
472       rend = gtk_cell_renderer_text_new ();
473       
474       col = gtk_tree_view_column_new_with_attributes ("Column 9",
475                                                       rend,
476                                                       "text", 7,
477                                                       NULL);
478
479       setup_column (col);
480       
481       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
482       
483       rend = gtk_cell_renderer_text_new ();
484       
485       col = gtk_tree_view_column_new_with_attributes ("Column 10",
486                                                       rend,
487                                                       "text", 8,
488                                                       NULL);
489
490       setup_column (col);
491       
492       gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col);
493       
494 #endif
495       
496       /* FALL THRU */
497       
498     case COLUMNS_ONE:
499       rend = gtk_cell_renderer_text_new ();
500       
501       col = gtk_tree_view_column_new_with_attributes ("Column 0",
502                                                       rend,
503                                                       "text", 0,
504                                                       NULL);
505
506       setup_column (col);
507       
508       gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), col, 0);
509     default:
510       break;
511     }
512 }
513
514 static ColumnsType
515 get_columns_type (void)
516 {
517   return current_column_type;
518 }
519
520 static GdkPixbuf *our_pixbuf;
521   
522 typedef enum
523 {
524   /*   MODEL_TYPES, */
525   MODEL_TREE,
526   MODEL_LIST,
527   MODEL_SORTED_TREE,
528   MODEL_SORTED_LIST,
529   MODEL_EMPTY_LIST,
530   MODEL_EMPTY_TREE,
531   MODEL_NULL,
532   MODEL_LAST
533 } ModelType;
534
535 /* FIXME add a custom model to test */
536 static GtkTreeModel *models[MODEL_LAST];
537 static const char *model_names[MODEL_LAST] = {
538   "GtkTreeStore",
539   "GtkListStore",
540   "GtkTreeModelSort wrapping GtkTreeStore",
541   "GtkTreeModelSort wrapping GtkListStore",
542   "Empty GtkListStore",
543   "Empty GtkTreeStore",
544   "NULL (no model)"
545 };
546
547 static GtkTreeModel*
548 create_list_model (void)
549 {
550   GtkListStore *store;
551   GtkTreeIter iter;
552   gint i;
553   GType *t;
554
555   t = get_model_types ();
556   
557   store = gtk_list_store_new (N_COLUMNS,
558                               t[0], t[1], t[2],
559                               t[3], t[4], t[5],
560                               t[6], t[7], t[8]);
561
562   i = 0;
563   while (i < 200)
564     {
565       char *msg;
566       
567       gtk_list_store_append (store, &iter);
568
569       msg = g_strdup_printf ("%d", i);
570       
571       gtk_list_store_set (store, &iter, 0, msg, 1, "Foo! Foo! Foo!",
572                           2, our_pixbuf,
573                           3, 7.0, 4, (guint) 9000,
574                           5, 'f', 6, 'g',
575                           7, TRUE, 8, 23245454,
576                           -1);
577
578       g_free (msg);
579       
580       ++i;
581     }
582
583   return GTK_TREE_MODEL (store);
584 }
585
586 static void
587 typesystem_recurse (GType        type,
588                     GtkTreeIter *parent_iter,
589                     GtkTreeStore *store)
590 {
591   GType* children;
592   guint n_children = 0;
593   gint i;
594   GtkTreeIter iter;
595   gchar *str;
596   
597   gtk_tree_store_append (store, &iter, parent_iter);
598
599   str = g_strdup_printf ("%ld", (glong)type);
600   gtk_tree_store_set (store, &iter, 0, str, 1, g_type_name (type),
601                       2, our_pixbuf,
602                       3, 7.0, 4, (guint) 9000,
603                       5, 'f', 6, 'g',
604                       7, TRUE, 8, 23245454,
605                       -1);
606   g_free (str);
607   
608   children = g_type_children (type, &n_children);
609
610   i = 0;
611   while (i < n_children)
612     {
613       typesystem_recurse (children[i], &iter, store);
614
615       ++i;
616     }
617   
618   g_free (children);
619 }
620
621 static GtkTreeModel*
622 create_tree_model (void)
623 {
624   GtkTreeStore *store;
625   gint i;
626   GType *t;
627   
628   /* Make the tree more interesting */
629   /* - we need this magic here so we are sure the type ends up being
630    * registered and gcc doesn't optimize away the code */
631   g_type_class_unref (g_type_class_ref (gtk_scrolled_window_get_type ()));
632   g_type_class_unref (g_type_class_ref (gtk_label_get_type ()));
633   g_type_class_unref (g_type_class_ref (gtk_scrollbar_get_type ()));
634   g_type_class_unref (g_type_class_ref (pango_layout_get_type ()));
635
636   t = get_model_types ();
637   
638   store = gtk_tree_store_new (N_COLUMNS,
639                               t[0], t[1], t[2],
640                               t[3], t[4], t[5],
641                               t[6], t[7], t[8]);
642
643   i = 0;
644   while (i < G_TYPE_FUNDAMENTAL_MAX)
645     {
646       typesystem_recurse (i, NULL, store);
647       
648       ++i;
649     }
650
651   return GTK_TREE_MODEL (store);
652 }
653
654 static void
655 model_selected (GtkComboBox *combo_box, gpointer data)
656 {
657   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
658   gint hist;
659
660   hist = gtk_combo_box_get_active (combo_box);
661
662   if (models[hist] != gtk_tree_view_get_model (tree_view))
663     {
664       gtk_tree_view_set_model (tree_view, models[hist]);
665     }
666 }
667
668 static void
669 columns_selected (GtkComboBox *combo_box, gpointer data)
670 {
671   GtkTreeView *tree_view = GTK_TREE_VIEW (data);
672   gint hist;
673
674   hist = gtk_combo_box_get_active (combo_box);
675
676   if (hist != get_columns_type ())
677     {
678       set_columns_type (tree_view, hist);
679     }
680 }
681
682
683 enum
684 {
685   TARGET_GTK_TREE_MODEL_ROW
686 };
687
688 static GtkTargetEntry row_targets[] = {
689   { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP,
690     TARGET_GTK_TREE_MODEL_ROW }
691 };
692
693 int
694 main (int    argc,
695       char **argv)
696 {
697   GtkWidget *window;
698   GtkWidget *sw;
699   GtkWidget *tv;
700   GtkWidget *box;
701   GtkWidget *combo_box;
702   GtkTreeModel *model;
703   gint i;
704   
705   gtk_init (&argc, &argv);
706
707   if (g_getenv ("RTL"))
708     gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
709
710   our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm);  
711   
712 #if 0
713   models[MODEL_TYPES] = GTK_TREE_MODEL (gtk_tree_model_types_new ());
714 #endif
715   models[MODEL_LIST] = create_list_model ();
716   models[MODEL_TREE] = create_tree_model ();
717
718   model = create_list_model ();
719   models[MODEL_SORTED_LIST] = gtk_tree_model_sort_new_with_model (model);
720   g_object_unref (model);
721
722   model = create_tree_model ();
723   models[MODEL_SORTED_TREE] = gtk_tree_model_sort_new_with_model (model);
724   g_object_unref (model);
725
726   models[MODEL_EMPTY_LIST] = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_INT));
727   models[MODEL_EMPTY_TREE] = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT));
728   
729   models[MODEL_NULL] = NULL;
730
731   run_automated_tests ();
732   
733   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
734   g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
735   gtk_window_set_default_size (GTK_WINDOW (window), 430, 400);
736
737   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
738
739   gtk_container_add (GTK_CONTAINER (window), box);
740
741   tv = gtk_tree_view_new_with_model (models[0]);
742   
743   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tv),
744                                           GDK_BUTTON1_MASK,
745                                           row_targets,
746                                           G_N_ELEMENTS (row_targets),
747                                           GDK_ACTION_MOVE | GDK_ACTION_COPY);
748
749   gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tv),
750                                         row_targets,
751                                         G_N_ELEMENTS (row_targets),
752                                         GDK_ACTION_MOVE | GDK_ACTION_COPY);
753   
754   /* Model menu */
755   combo_box = gtk_combo_box_text_new ();
756   gtk_widget_set_halign (combo_box, GTK_ALIGN_CENTER);
757   for (i = 0; i < MODEL_LAST; i++)
758       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), model_names[i]);
759
760   gtk_container_add (GTK_CONTAINER (box), combo_box);
761   g_signal_connect (combo_box,
762                     "changed",
763                     G_CALLBACK (model_selected),
764                     tv);
765   
766   /* Columns menu */
767   combo_box = gtk_combo_box_text_new ();
768   gtk_widget_set_halign (combo_box, GTK_ALIGN_CENTER);
769   for (i = 0; i < COLUMNS_LAST; i++)
770       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), column_type_names[i]);
771
772   gtk_container_add (GTK_CONTAINER (box), combo_box);
773
774   set_columns_type (GTK_TREE_VIEW (tv), COLUMNS_LOTS);
775   gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), COLUMNS_LOTS);
776
777   g_signal_connect (combo_box,
778                     "changed",
779                     G_CALLBACK (columns_selected),
780                     tv);
781   
782   sw = gtk_scrolled_window_new (NULL, NULL);
783   gtk_widget_set_hexpand (sw, TRUE);
784   gtk_widget_set_vexpand (sw, TRUE);
785   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
786                                   GTK_POLICY_AUTOMATIC,
787                                   GTK_POLICY_AUTOMATIC);
788   
789   gtk_container_add (GTK_CONTAINER (box), sw);
790   
791   gtk_container_add (GTK_CONTAINER (sw), tv);
792   
793   create_prop_editor (G_OBJECT (tv), GTK_TYPE_TREE_VIEW);
794   create_prop_editor (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (tv))), GTK_TYPE_TREE_SELECTION);
795
796   gtk_widget_show_all (window);
797   
798   gtk_main ();
799
800   return 0;
801 }
802
803 /*
804  * GtkTreeModelTypes
805  */
806
807 static void         gtk_tree_model_types_init                 (GtkTreeModelTypes      *model_types);
808 static void         gtk_tree_model_types_tree_model_init      (GtkTreeModelIface   *iface);
809 static gint         gtk_real_model_types_get_n_columns   (GtkTreeModel        *tree_model);
810 static GType        gtk_real_model_types_get_column_type (GtkTreeModel        *tree_model,
811                                                            gint                 index);
812 static GtkTreePath *gtk_real_model_types_get_path        (GtkTreeModel        *tree_model,
813                                                            GtkTreeIter         *iter);
814 static void         gtk_real_model_types_get_value       (GtkTreeModel        *tree_model,
815                                                            GtkTreeIter         *iter,
816                                                            gint                 column,
817                                                            GValue              *value);
818 static gboolean     gtk_real_model_types_iter_next       (GtkTreeModel        *tree_model,
819                                                            GtkTreeIter         *iter);
820 static gboolean     gtk_real_model_types_iter_children   (GtkTreeModel        *tree_model,
821                                                            GtkTreeIter         *iter,
822                                                            GtkTreeIter         *parent);
823 static gboolean     gtk_real_model_types_iter_has_child  (GtkTreeModel        *tree_model,
824                                                            GtkTreeIter         *iter);
825 static gint         gtk_real_model_types_iter_n_children (GtkTreeModel        *tree_model,
826                                                            GtkTreeIter         *iter);
827 static gboolean     gtk_real_model_types_iter_nth_child  (GtkTreeModel        *tree_model,
828                                                            GtkTreeIter         *iter,
829                                                            GtkTreeIter         *parent,
830                                                            gint                 n);
831 static gboolean     gtk_real_model_types_iter_parent     (GtkTreeModel        *tree_model,
832                                                            GtkTreeIter         *iter,
833                                                            GtkTreeIter         *child);
834
835
836 GType
837 gtk_tree_model_types_get_type (void)
838 {
839   static GType model_types_type = 0;
840
841   if (!model_types_type)
842     {
843       const GTypeInfo model_types_info =
844       {
845         sizeof (GtkTreeModelTypesClass),
846         NULL,           /* base_init */
847         NULL,           /* base_finalize */
848         NULL,           /* class_init */
849         NULL,           /* class_finalize */
850         NULL,           /* class_data */
851         sizeof (GtkTreeModelTypes),
852         0,
853         (GInstanceInitFunc) gtk_tree_model_types_init
854       };
855
856       const GInterfaceInfo tree_model_info =
857       {
858         (GInterfaceInitFunc) gtk_tree_model_types_tree_model_init,
859         NULL,
860         NULL
861       };
862
863       model_types_type = g_type_register_static (G_TYPE_OBJECT,
864                                                  "GtkTreeModelTypes",
865                                                  &model_types_info, 0);
866       g_type_add_interface_static (model_types_type,
867                                    GTK_TYPE_TREE_MODEL,
868                                    &tree_model_info);
869     }
870
871   return model_types_type;
872 }
873
874 GtkTreeModelTypes *
875 gtk_tree_model_types_new (void)
876 {
877   GtkTreeModelTypes *retval;
878
879   retval = g_object_new (GTK_TYPE_MODEL_TYPES, NULL);
880
881   return retval;
882 }
883
884 static void
885 gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface)
886 {
887   iface->get_n_columns = gtk_real_model_types_get_n_columns;
888   iface->get_column_type = gtk_real_model_types_get_column_type;
889   iface->get_path = gtk_real_model_types_get_path;
890   iface->get_value = gtk_real_model_types_get_value;
891   iface->iter_next = gtk_real_model_types_iter_next;
892   iface->iter_children = gtk_real_model_types_iter_children;
893   iface->iter_has_child = gtk_real_model_types_iter_has_child;
894   iface->iter_n_children = gtk_real_model_types_iter_n_children;
895   iface->iter_nth_child = gtk_real_model_types_iter_nth_child;
896   iface->iter_parent = gtk_real_model_types_iter_parent;
897 }
898
899 static void
900 gtk_tree_model_types_init (GtkTreeModelTypes *model_types)
901 {
902   model_types->stamp = g_random_int ();
903 }
904
905 static GType column_types[] = {
906   G_TYPE_STRING, /* GType */
907   G_TYPE_STRING  /* type name */
908 };
909   
910 static gint
911 gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model)
912 {
913   return G_N_ELEMENTS (column_types);
914 }
915
916 static GType
917 gtk_real_model_types_get_column_type (GtkTreeModel *tree_model,
918                                       gint          index)
919 {
920   g_return_val_if_fail (index < G_N_ELEMENTS (column_types), G_TYPE_INVALID);
921   
922   return column_types[index];
923 }
924
925 #if 0
926 /* Use default implementation of this */
927 static gboolean
928 gtk_real_model_types_get_iter (GtkTreeModel *tree_model,
929                                GtkTreeIter  *iter,
930                                GtkTreePath  *path)
931 {
932   
933 }
934 #endif
935
936 /* The toplevel nodes of the tree are the reserved types, G_TYPE_NONE through
937  * G_TYPE_RESERVED_FUNDAMENTAL.
938  */
939
940 static GtkTreePath *
941 gtk_real_model_types_get_path (GtkTreeModel *tree_model,
942                                GtkTreeIter  *iter)
943 {
944   GtkTreePath *retval;
945   GType type;
946   GType parent;
947   
948   g_return_val_if_fail (GTK_IS_TREE_MODEL_TYPES (tree_model), NULL);
949   g_return_val_if_fail (iter != NULL, NULL);
950
951   type = GPOINTER_TO_INT (iter->user_data);
952   
953   retval = gtk_tree_path_new ();
954   
955   parent = g_type_parent (type);
956   while (parent != G_TYPE_INVALID)
957     {
958       GType* children = g_type_children (parent, NULL);
959       gint i = 0;
960
961       if (!children || children[0] == G_TYPE_INVALID)
962         {
963           g_warning ("bad iterator?");
964           return NULL;
965         }
966       
967       while (children[i] != type)
968         ++i;
969
970       gtk_tree_path_prepend_index (retval, i);
971
972       g_free (children);
973       
974       type = parent;
975       parent = g_type_parent (parent);
976     }
977
978   /* The fundamental type itself is the index on the toplevel */
979   gtk_tree_path_prepend_index (retval, type);
980
981   return retval;
982 }
983
984 static void
985 gtk_real_model_types_get_value (GtkTreeModel *tree_model,
986                                 GtkTreeIter  *iter,
987                                 gint          column,
988                                 GValue       *value)
989 {
990   GType type;
991
992   type = GPOINTER_TO_INT (iter->user_data);
993
994   switch (column)
995     {
996     case 0:
997       {
998         gchar *str;
999         
1000         g_value_init (value, G_TYPE_STRING);
1001
1002         str = g_strdup_printf ("%ld", (long int) type);
1003         g_value_set_string (value, str);
1004         g_free (str);
1005       }
1006       break;
1007
1008     case 1:
1009       g_value_init (value, G_TYPE_STRING);
1010       g_value_set_string (value, g_type_name (type));
1011       break;
1012
1013     default:
1014       g_warning ("Bad column %d requested", column);
1015     }
1016 }
1017
1018 static gboolean
1019 gtk_real_model_types_iter_next (GtkTreeModel  *tree_model,
1020                                 GtkTreeIter   *iter)
1021 {
1022   
1023   GType parent;
1024   GType type;
1025
1026   type = GPOINTER_TO_INT (iter->user_data);
1027
1028   parent = g_type_parent (type);
1029   
1030   if (parent == G_TYPE_INVALID)
1031     {
1032       /* find next _valid_ fundamental type */
1033       do
1034         type++;
1035       while (!g_type_name (type) && type <= G_TYPE_FUNDAMENTAL_MAX);
1036       if (type <= G_TYPE_FUNDAMENTAL_MAX)
1037         {
1038           /* found one */
1039           iter->user_data = GINT_TO_POINTER (type);
1040           return TRUE;
1041         }
1042       else
1043         return FALSE;
1044     }
1045   else
1046     {
1047       GType* children = g_type_children (parent, NULL);
1048       gint i = 0;
1049
1050       g_assert (children != NULL);
1051       
1052       while (children[i] != type)
1053         ++i;
1054   
1055       ++i;
1056
1057       if (children[i] != G_TYPE_INVALID)
1058         {
1059           g_free (children);
1060           iter->user_data = GINT_TO_POINTER (children[i]);
1061           return TRUE;
1062         }
1063       else
1064         {
1065           g_free (children);
1066           return FALSE;
1067         }
1068     }
1069 }
1070
1071 static gboolean
1072 gtk_real_model_types_iter_children (GtkTreeModel *tree_model,
1073                                     GtkTreeIter  *iter,
1074                                     GtkTreeIter  *parent)
1075 {
1076   GType type;
1077   GType* children;
1078   
1079   type = GPOINTER_TO_INT (parent->user_data);
1080
1081   children = g_type_children (type, NULL);
1082
1083   if (!children || children[0] == G_TYPE_INVALID)
1084     {
1085       g_free (children);
1086       return FALSE;
1087     }
1088   else
1089     {
1090       iter->user_data = GINT_TO_POINTER (children[0]);
1091       g_free (children);
1092       return TRUE;
1093     }
1094 }
1095
1096 static gboolean
1097 gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model,
1098                                      GtkTreeIter  *iter)
1099 {
1100   GType type;
1101   GType* children;
1102   
1103   type = GPOINTER_TO_INT (iter->user_data);
1104   
1105   children = g_type_children (type, NULL);
1106
1107   if (!children || children[0] == G_TYPE_INVALID)
1108     {
1109       g_free (children);
1110       return FALSE;
1111     }
1112   else
1113     {
1114       g_free (children);
1115       return TRUE;
1116     }
1117 }
1118
1119 static gint
1120 gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model,
1121                                       GtkTreeIter  *iter)
1122 {
1123   if (iter == NULL)
1124     {
1125       return G_TYPE_FUNDAMENTAL_MAX;
1126     }
1127   else
1128     {
1129       GType type;
1130       GType* children;
1131       guint n_children = 0;
1132
1133       type = GPOINTER_TO_INT (iter->user_data);
1134       
1135       children = g_type_children (type, &n_children);
1136       
1137       g_free (children);
1138       
1139       return n_children;
1140     }
1141 }
1142
1143 static gboolean
1144 gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model,
1145                                      GtkTreeIter  *iter,
1146                                      GtkTreeIter  *parent,
1147                                      gint          n)
1148 {  
1149   if (parent == NULL)
1150     {
1151       /* fundamental type */
1152       if (n < G_TYPE_FUNDAMENTAL_MAX)
1153         {
1154           iter->user_data = GINT_TO_POINTER (n);
1155           return TRUE;
1156         }
1157       else
1158         return FALSE;
1159     }
1160   else
1161     {
1162       GType type = GPOINTER_TO_INT (parent->user_data);      
1163       guint n_children = 0;
1164       GType* children = g_type_children (type, &n_children);
1165
1166       if (n_children == 0)
1167         {
1168           g_free (children);
1169           return FALSE;
1170         }
1171       else if (n >= n_children)
1172         {
1173           g_free (children);
1174           return FALSE;
1175         }
1176       else
1177         {
1178           iter->user_data = GINT_TO_POINTER (children[n]);
1179           g_free (children);
1180
1181           return TRUE;
1182         }
1183     }
1184 }
1185
1186 static gboolean
1187 gtk_real_model_types_iter_parent (GtkTreeModel *tree_model,
1188                                   GtkTreeIter  *iter,
1189                                   GtkTreeIter  *child)
1190 {
1191   GType type;
1192   GType parent;
1193   
1194   type = GPOINTER_TO_INT (child->user_data);
1195   
1196   parent = g_type_parent (type);
1197   
1198   if (parent == G_TYPE_INVALID)
1199     {
1200       if (type > G_TYPE_FUNDAMENTAL_MAX)
1201         g_warning ("no parent for %ld %s\n",
1202                    (long int) type,
1203                    g_type_name (type));
1204       return FALSE;
1205     }
1206   else
1207     {
1208       iter->user_data = GINT_TO_POINTER (parent);
1209       
1210       return TRUE;
1211     }
1212 }
1213
1214 /*
1215  * Automated testing
1216  */
1217
1218 #if 0
1219
1220 static void
1221 treestore_torture_recurse (GtkTreeStore *store,
1222                            GtkTreeIter  *root,
1223                            gint          depth)
1224 {
1225   GtkTreeModel *model;
1226   gint i;
1227   GtkTreeIter iter;  
1228   
1229   model = GTK_TREE_MODEL (store);    
1230
1231   if (depth > 2)
1232     return;
1233
1234   ++depth;
1235
1236   gtk_tree_store_append (store, &iter, root);
1237   
1238   gtk_tree_model_iter_children (model, &iter, root);
1239   
1240   i = 0;
1241   while (i < 100)
1242     {
1243       gtk_tree_store_append (store, &iter, root);
1244       ++i;
1245     }
1246
1247   while (gtk_tree_model_iter_children (model, &iter, root))
1248     gtk_tree_store_remove (store, &iter);
1249
1250   gtk_tree_store_append (store, &iter, root);
1251
1252   /* inserts before last node in tree */
1253   i = 0;
1254   while (i < 100)
1255     {
1256       gtk_tree_store_insert_before (store, &iter, root, &iter);
1257       ++i;
1258     }
1259
1260   /* inserts after the node before the last node */
1261   i = 0;
1262   while (i < 100)
1263     {
1264       gtk_tree_store_insert_after (store, &iter, root, &iter);
1265       ++i;
1266     }
1267
1268   /* inserts after the last node */
1269   gtk_tree_store_append (store, &iter, root);
1270     
1271   i = 0;
1272   while (i < 100)
1273     {
1274       gtk_tree_store_insert_after (store, &iter, root, &iter);
1275       ++i;
1276     }
1277
1278   /* remove everything again */
1279   while (gtk_tree_model_iter_children (model, &iter, root))
1280     gtk_tree_store_remove (store, &iter);
1281
1282
1283     /* Prepends */
1284   gtk_tree_store_prepend (store, &iter, root);
1285     
1286   i = 0;
1287   while (i < 100)
1288     {
1289       gtk_tree_store_prepend (store, &iter, root);
1290       ++i;
1291     }
1292
1293   /* remove everything again */
1294   while (gtk_tree_model_iter_children (model, &iter, root))
1295     gtk_tree_store_remove (store, &iter);
1296
1297   gtk_tree_store_append (store, &iter, root);
1298   gtk_tree_store_append (store, &iter, root);
1299   gtk_tree_store_append (store, &iter, root);
1300   gtk_tree_store_append (store, &iter, root);
1301
1302   while (gtk_tree_model_iter_children (model, &iter, root))
1303     {
1304       treestore_torture_recurse (store, &iter, depth);
1305       gtk_tree_store_remove (store, &iter);
1306     }
1307 }
1308
1309 #endif
1310
1311 static void
1312 run_automated_tests (void)
1313 {
1314   g_print ("Running automated tests...\n");
1315   
1316   /* FIXME TreePath basic verification */
1317
1318   /* FIXME generic consistency checks on the models */
1319
1320   {
1321     /* Make sure list store mutations don't crash anything */
1322     GtkListStore *store;
1323     GtkTreeModel *model;
1324     gint i;
1325     GtkTreeIter iter;
1326     
1327     store = gtk_list_store_new (1, G_TYPE_INT);
1328
1329     model = GTK_TREE_MODEL (store);
1330     
1331     i = 0;
1332     while (i < 100)
1333       {
1334         gtk_list_store_append (store, &iter);
1335         ++i;
1336       }
1337
1338     while (gtk_tree_model_get_iter_first (model, &iter))
1339       gtk_list_store_remove (store, &iter);
1340
1341     gtk_list_store_append (store, &iter);
1342
1343     /* inserts before last node in list */
1344     i = 0;
1345     while (i < 100)
1346       {
1347         gtk_list_store_insert_before (store, &iter, &iter);
1348         ++i;
1349       }
1350
1351     /* inserts after the node before the last node */
1352     i = 0;
1353     while (i < 100)
1354       {
1355         gtk_list_store_insert_after (store, &iter, &iter);
1356         ++i;
1357       }
1358
1359     /* inserts after the last node */
1360     gtk_list_store_append (store, &iter);
1361     
1362     i = 0;
1363     while (i < 100)
1364       {
1365         gtk_list_store_insert_after (store, &iter, &iter);
1366         ++i;
1367       }
1368
1369     /* remove everything again */
1370     while (gtk_tree_model_get_iter_first (model, &iter))
1371       gtk_list_store_remove (store, &iter);
1372
1373
1374     /* Prepends */
1375     gtk_list_store_prepend (store, &iter);
1376     
1377     i = 0;
1378     while (i < 100)
1379       {
1380         gtk_list_store_prepend (store, &iter);
1381         ++i;
1382       }
1383
1384     /* remove everything again */
1385     while (gtk_tree_model_get_iter_first (model, &iter))
1386       gtk_list_store_remove (store, &iter);
1387     
1388     g_object_unref (store);
1389   }
1390
1391   {
1392     /* Make sure tree store mutations don't crash anything */
1393     GtkTreeStore *store;
1394     GtkTreeIter root;
1395
1396     store = gtk_tree_store_new (1, G_TYPE_INT);
1397     gtk_tree_store_append (GTK_TREE_STORE (store), &root, NULL);
1398     /* Remove test until it is rewritten to work */
1399     /*    treestore_torture_recurse (store, &root, 0);*/
1400     
1401     g_object_unref (store);
1402   }
1403
1404   g_print ("Passed.\n");
1405 }