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