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