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