/* testtreeview.c * Copyright (C) 2001 Red Hat, Inc * Author: Jonathan Blandford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #undef GTK_DISABLE_DEPRECATED #include #include "prop-editor.h" #include #include /* Don't copy this bad example; inline RGB data is always a better * idea than inline XPMs. */ static char *book_closed_xpm[] = { "16 16 6 1", " c None s None", ". c black", "X c red", "o c yellow", "O c #808080", "# c white", " ", " .. ", " ..XX. ", " ..XXXXX. ", " ..XXXXXXXX. ", ".ooXXXXXXXXX. ", "..ooXXXXXXXXX. ", ".X.ooXXXXXXXXX. ", ".XX.ooXXXXXX.. ", " .XX.ooXXX..#O ", " .XX.oo..##OO. ", " .XX..##OO.. ", " .X.#OO.. ", " ..O.. ", " .. ", " " }; static void run_automated_tests (void); /* This custom model is to test custom model use. */ #define GTK_TYPE_MODEL_TYPES (gtk_tree_model_types_get_type ()) #define GTK_TREE_MODEL_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypes)) #define GTK_TREE_MODEL_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MODEL_TYPES, GtkTreeModelTypesClass)) #define GTK_IS_TREE_MODEL_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_MODEL_TYPES)) #define GTK_IS_TREE_MODEL_TYPES_GET_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MODEL_TYPES)) typedef struct _GtkTreeModelTypes GtkTreeModelTypes; typedef struct _GtkTreeModelTypesClass GtkTreeModelTypesClass; struct _GtkTreeModelTypes { GObject parent; gint stamp; }; struct _GtkTreeModelTypesClass { GObjectClass parent_class; guint (* get_flags) (GtkTreeModel *tree_model); gint (* get_n_columns) (GtkTreeModel *tree_model); GType (* get_column_type) (GtkTreeModel *tree_model, gint index); gboolean (* get_iter) (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path); GtkTreePath *(* get_path) (GtkTreeModel *tree_model, GtkTreeIter *iter); void (* get_value) (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value); gboolean (* iter_next) (GtkTreeModel *tree_model, GtkTreeIter *iter); gboolean (* iter_children) (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent); gboolean (* iter_has_child) (GtkTreeModel *tree_model, GtkTreeIter *iter); gint (* iter_n_children) (GtkTreeModel *tree_model, GtkTreeIter *iter); gboolean (* iter_nth_child) (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n); gboolean (* iter_parent) (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child); void (* ref_iter) (GtkTreeModel *tree_model, GtkTreeIter *iter); void (* unref_iter) (GtkTreeModel *tree_model, GtkTreeIter *iter); /* These will be moved into the GtkTreeModelIface eventually */ void (* changed) (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter); void (* inserted) (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter); void (* child_toggled) (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter); void (* deleted) (GtkTreeModel *tree_model, GtkTreePath *path); }; GType gtk_tree_model_types_get_type (void) G_GNUC_CONST; GtkTreeModelTypes *gtk_tree_model_types_new (void); typedef enum { COLUMNS_NONE, COLUMNS_ONE, COLUMNS_LOTS, COLUMNS_LAST } ColumnsType; static gchar *column_type_names[] = { "No columns", "One column", "Many columns" }; #define N_COLUMNS 9 static GType* get_model_types (void) { static GType column_types[N_COLUMNS] = { 0 }; if (column_types[0] == 0) { column_types[0] = G_TYPE_STRING; column_types[1] = G_TYPE_STRING; column_types[2] = GDK_TYPE_PIXBUF; column_types[3] = G_TYPE_FLOAT; column_types[4] = G_TYPE_UINT; column_types[5] = G_TYPE_UCHAR; column_types[6] = G_TYPE_CHAR; #define BOOL_COLUMN 7 column_types[BOOL_COLUMN] = G_TYPE_BOOLEAN; column_types[8] = G_TYPE_INT; } return column_types; } static void col_clicked_cb (GtkTreeViewColumn *col, gpointer data) { GtkWindow *win; win = GTK_WINDOW (create_prop_editor (G_OBJECT (col), GTK_TYPE_TREE_VIEW_COLUMN)); gtk_window_set_title (win, gtk_tree_view_column_get_title (col)); } static void setup_column (GtkTreeViewColumn *col) { gtk_tree_view_column_set_clickable (col, TRUE); g_signal_connect (col, "clicked", G_CALLBACK (col_clicked_cb), NULL); } static void toggled_callback (GtkCellRendererToggle *celltoggle, gchar *path_string, GtkTreeView *tree_view) { GtkTreeModel *model = NULL; GtkTreeModelSort *sort_model = NULL; GtkTreePath *path; GtkTreeIter iter; gboolean active = FALSE; g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); model = gtk_tree_view_get_model (tree_view); if (GTK_IS_TREE_MODEL_SORT (model)) { sort_model = GTK_TREE_MODEL_SORT (model); model = gtk_tree_model_sort_get_model (sort_model); } if (model == NULL) return; if (sort_model) { g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter"); return; } path = gtk_tree_path_new_from_string (path_string); if (!gtk_tree_model_get_iter (model, &iter, path)) { g_warning ("%s: bad path?", G_STRLOC); return; } gtk_tree_path_free (path); if (GTK_IS_LIST_STORE (model)) { gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, BOOL_COLUMN, &active, -1); gtk_list_store_set (GTK_LIST_STORE (model), &iter, BOOL_COLUMN, !active, -1); } else if (GTK_IS_TREE_STORE (model)) { gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, BOOL_COLUMN, &active, -1); gtk_tree_store_set (GTK_TREE_STORE (model), &iter, BOOL_COLUMN, !active, -1); } else g_warning ("don't know how to actually toggle value for model type %s", g_type_name (G_TYPE_FROM_INSTANCE (model))); } static void edited_callback (GtkCellRendererText *renderer, const gchar *path_string, const gchar *new_text, GtkTreeView *tree_view) { GtkTreeModel *model = NULL; GtkTreeModelSort *sort_model = NULL; GtkTreePath *path; GtkTreeIter iter; guint value = atoi (new_text); g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); model = gtk_tree_view_get_model (tree_view); if (GTK_IS_TREE_MODEL_SORT (model)) { sort_model = GTK_TREE_MODEL_SORT (model); model = gtk_tree_model_sort_get_model (sort_model); } if (model == NULL) return; if (sort_model) { g_warning ("FIXME implement conversion from TreeModelSort iter to child model iter"); return; } path = gtk_tree_path_new_from_string (path_string); if (!gtk_tree_model_get_iter (model, &iter, path)) { g_warning ("%s: bad path?", G_STRLOC); return; } gtk_tree_path_free (path); if (GTK_IS_LIST_STORE (model)) { gtk_list_store_set (GTK_LIST_STORE (model), &iter, 4, value, -1); } else if (GTK_IS_TREE_STORE (model)) { gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 4, value, -1); } else g_warning ("don't know how to actually toggle value for model type %s", g_type_name (G_TYPE_FROM_INSTANCE (model))); } static ColumnsType current_column_type = COLUMNS_LOTS; static void set_columns_type (GtkTreeView *tree_view, ColumnsType type) { GtkTreeViewColumn *col; GtkCellRenderer *rend; GdkPixbuf *pixbuf; GtkWidget *image; GtkObject *adjustment; current_column_type = type; col = gtk_tree_view_get_column (tree_view, 0); while (col) { gtk_tree_view_remove_column (tree_view, col); col = gtk_tree_view_get_column (tree_view, 0); } gtk_tree_view_set_rules_hint (tree_view, FALSE); switch (type) { case COLUMNS_NONE: break; case COLUMNS_LOTS: /* with lots of columns we need to turn on rules */ gtk_tree_view_set_rules_hint (tree_view, TRUE); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 1", rend, "text", 1, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title (col, "Column 2"); rend = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (col, rend, FALSE); gtk_tree_view_column_add_attribute (col, rend, "pixbuf", 2); rend = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (col, rend, TRUE); gtk_tree_view_column_add_attribute (col, rend, "text", 0); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); gtk_tree_view_set_expander_column (tree_view, col); rend = gtk_cell_renderer_toggle_new (); g_signal_connect (rend, "toggled", G_CALLBACK (toggled_callback), tree_view); col = gtk_tree_view_column_new_with_attributes ("Column 3", rend, "active", BOOL_COLUMN, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)book_closed_xpm); image = gtk_image_new_from_pixbuf (pixbuf); g_object_unref (pixbuf); gtk_widget_show (image); gtk_tree_view_column_set_widget (col, image); rend = gtk_cell_renderer_toggle_new (); /* you could also set this per-row by tying it to a column * in the model of course. */ g_object_set (rend, "radio", TRUE, NULL); g_signal_connect (rend, "toggled", G_CALLBACK (toggled_callback), tree_view); col = gtk_tree_view_column_new_with_attributes ("Column 4", rend, "active", BOOL_COLUMN, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); rend = gtk_cell_renderer_spin_new (); adjustment = gtk_adjustment_new (0, 0, 10000, 100, 100, 100); g_object_set (rend, "editable", TRUE, NULL); g_object_set (rend, "adjustment", adjustment, NULL); g_signal_connect (rend, "edited", G_CALLBACK (edited_callback), tree_view); col = gtk_tree_view_column_new_with_attributes ("Column 5", rend, "text", 4, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); #if 0 rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 6", rend, "text", 4, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 7", rend, "text", 5, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 8", rend, "text", 6, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 9", rend, "text", 7, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 10", rend, "text", 8, NULL); setup_column (col); gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), col); #endif /* FALL THRU */ case COLUMNS_ONE: rend = gtk_cell_renderer_text_new (); col = gtk_tree_view_column_new_with_attributes ("Column 0", rend, "text", 0, NULL); setup_column (col); gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), col, 0); default: break; } } static ColumnsType get_columns_type (void) { return current_column_type; } static GdkPixbuf *our_pixbuf; typedef enum { /* MODEL_TYPES, */ MODEL_TREE, MODEL_LIST, MODEL_SORTED_TREE, MODEL_SORTED_LIST, MODEL_EMPTY_LIST, MODEL_EMPTY_TREE, MODEL_NULL, MODEL_LAST } ModelType; /* FIXME add a custom model to test */ static GtkTreeModel *models[MODEL_LAST]; static const char *model_names[MODEL_LAST] = { "GtkTreeStore", "GtkListStore", "GtkTreeModelSort wrapping GtkTreeStore", "GtkTreeModelSort wrapping GtkListStore", "Empty GtkListStore", "Empty GtkTreeStore", "NULL (no model)" }; static GtkTreeModel* create_list_model (void) { GtkListStore *store; GtkTreeIter iter; gint i; GType *t; t = get_model_types (); store = gtk_list_store_new (N_COLUMNS, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8]); i = 0; while (i < 200) { char *msg; gtk_list_store_append (store, &iter); msg = g_strdup_printf ("%d", i); gtk_list_store_set (store, &iter, 0, msg, 1, "Foo! Foo! Foo!", 2, our_pixbuf, 3, 7.0, 4, (guint) 9000, 5, 'f', 6, 'g', 7, TRUE, 8, 23245454, -1); g_free (msg); ++i; } return GTK_TREE_MODEL (store); } static void typesystem_recurse (GType type, GtkTreeIter *parent_iter, GtkTreeStore *store) { GType* children; guint n_children = 0; gint i; GtkTreeIter iter; gchar *str; gtk_tree_store_append (store, &iter, parent_iter); str = g_strdup_printf ("%ld", (glong)type); gtk_tree_store_set (store, &iter, 0, str, 1, g_type_name (type), 2, our_pixbuf, 3, 7.0, 4, (guint) 9000, 5, 'f', 6, 'g', 7, TRUE, 8, 23245454, -1); g_free (str); children = g_type_children (type, &n_children); i = 0; while (i < n_children) { typesystem_recurse (children[i], &iter, store); ++i; } g_free (children); } static GtkTreeModel* create_tree_model (void) { GtkTreeStore *store; gint i; GType *t; volatile GType dummy; /* G_GNUC_CONST makes the optimizer remove * get_type calls if you don't do something * like this */ /* Make the tree more interesting */ dummy = gtk_scrolled_window_get_type (); dummy = gtk_label_get_type (); dummy = gtk_hscrollbar_get_type (); dummy = gtk_vscrollbar_get_type (); dummy = pango_layout_get_type (); t = get_model_types (); store = gtk_tree_store_new (N_COLUMNS, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8]); i = 0; while (i < G_TYPE_FUNDAMENTAL_MAX) { typesystem_recurse (i, NULL, store); ++i; } return GTK_TREE_MODEL (store); } static void model_selected (GtkOptionMenu *om, gpointer data) { GtkTreeView *tree_view = GTK_TREE_VIEW (data); gint hist; hist = gtk_option_menu_get_history (om); if (models[hist] != gtk_tree_view_get_model (tree_view)) { gtk_tree_view_set_model (tree_view, models[hist]); } } static void columns_selected (GtkOptionMenu *om, gpointer data) { GtkTreeView *tree_view = GTK_TREE_VIEW (data); gint hist; hist = gtk_option_menu_get_history (om); if (hist != get_columns_type ()) { set_columns_type (tree_view, hist); } } enum { TARGET_GTK_TREE_MODEL_ROW }; static GtkTargetEntry row_targets[] = { { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW } }; int main (int argc, char **argv) { GtkWidget *window; GtkWidget *sw; GtkWidget *tv; GtkWidget *table; GtkWidget *om; GtkWidget *menu; GtkTreeModel *model; gint i; gtk_init (&argc, &argv); our_pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **) book_closed_xpm); #if 0 models[MODEL_TYPES] = GTK_TREE_MODEL (gtk_tree_model_types_new ()); #endif models[MODEL_LIST] = create_list_model (); models[MODEL_TREE] = create_tree_model (); model = create_list_model (); models[MODEL_SORTED_LIST] = gtk_tree_model_sort_new_with_model (model); g_object_unref (model); model = create_tree_model (); models[MODEL_SORTED_TREE] = gtk_tree_model_sort_new_with_model (model); g_object_unref (model); models[MODEL_EMPTY_LIST] = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_INT)); models[MODEL_EMPTY_TREE] = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT)); models[MODEL_NULL] = NULL; run_automated_tests (); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); gtk_window_set_default_size (GTK_WINDOW (window), 430, 400); table = gtk_table_new (3, 1, FALSE); gtk_container_add (GTK_CONTAINER (window), table); tv = gtk_tree_view_new_with_model (models[0]); gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tv), GDK_BUTTON1_MASK, row_targets, G_N_ELEMENTS (row_targets), GDK_ACTION_MOVE | GDK_ACTION_COPY); gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tv), row_targets, G_N_ELEMENTS (row_targets), GDK_ACTION_MOVE | GDK_ACTION_COPY); gtk_tree_view_set_grid_lines(GTK_TREE_VIEW (tv),GTK_TREE_VIEW_GRID_LINES_BOTH); /* Model menu */ menu = gtk_menu_new (); i = 0; while (i < MODEL_LAST) { GtkWidget *mi; const char *name; name = model_names[i]; mi = gtk_menu_item_new_with_label (name); gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi); #if 0 window = create_prop_editor (G_OBJECT (models[i])); gtk_window_set_title (GTK_WINDOW (window), name); #endif ++i; } gtk_widget_show_all (menu); om = gtk_option_menu_new (); gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu); gtk_table_attach (GTK_TABLE (table), om, 0, 1, 0, 1, 0, 0, 0, 0); g_signal_connect (om, "changed", G_CALLBACK (model_selected), tv); /* Columns menu */ menu = gtk_menu_new (); i = 0; while (i < COLUMNS_LAST) { GtkWidget *mi; const char *name; name = column_type_names[i]; mi = gtk_menu_item_new_with_label (name); gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi); ++i; } gtk_widget_show_all (menu); om = gtk_option_menu_new (); gtk_option_menu_set_menu (GTK_OPTION_MENU (om), menu); gtk_table_attach (GTK_TABLE (table), om, 0, 1, 1, 2, 0, 0, 0, 0); set_columns_type (GTK_TREE_VIEW (tv), COLUMNS_LOTS); gtk_option_menu_set_history (GTK_OPTION_MENU (om), COLUMNS_LOTS); g_signal_connect (om, "changed", G_CALLBACK (columns_selected), tv); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_table_attach (GTK_TABLE (table), sw, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_container_add (GTK_CONTAINER (sw), tv); gtk_widget_show_all (window); gtk_main (); return 0; } /* * GtkTreeModelTypes */ static void gtk_tree_model_types_init (GtkTreeModelTypes *model_types); static void gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface); static gint gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model); static GType gtk_real_model_types_get_column_type (GtkTreeModel *tree_model, gint index); static GtkTreePath *gtk_real_model_types_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter); static void gtk_real_model_types_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value); static gboolean gtk_real_model_types_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter); static gboolean gtk_real_model_types_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent); static gboolean gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter); static gint gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter); static gboolean gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n); static gboolean gtk_real_model_types_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child); GType gtk_tree_model_types_get_type (void) { static GType model_types_type = 0; if (!model_types_type) { static const GTypeInfo model_types_info = { sizeof (GtkTreeModelTypesClass), NULL, /* base_init */ NULL, /* base_finalize */ NULL, /* class_init */ NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkTreeModelTypes), 0, (GInstanceInitFunc) gtk_tree_model_types_init }; static const GInterfaceInfo tree_model_info = { (GInterfaceInitFunc) gtk_tree_model_types_tree_model_init, NULL, NULL }; model_types_type = g_type_register_static (G_TYPE_OBJECT, "GtkTreeModelTypes", &model_types_info, 0); g_type_add_interface_static (model_types_type, GTK_TYPE_TREE_MODEL, &tree_model_info); } return model_types_type; } GtkTreeModelTypes * gtk_tree_model_types_new (void) { GtkTreeModelTypes *retval; retval = g_object_new (GTK_TYPE_MODEL_TYPES, NULL); return retval; } static void gtk_tree_model_types_tree_model_init (GtkTreeModelIface *iface) { iface->get_n_columns = gtk_real_model_types_get_n_columns; iface->get_column_type = gtk_real_model_types_get_column_type; iface->get_path = gtk_real_model_types_get_path; iface->get_value = gtk_real_model_types_get_value; iface->iter_next = gtk_real_model_types_iter_next; iface->iter_children = gtk_real_model_types_iter_children; iface->iter_has_child = gtk_real_model_types_iter_has_child; iface->iter_n_children = gtk_real_model_types_iter_n_children; iface->iter_nth_child = gtk_real_model_types_iter_nth_child; iface->iter_parent = gtk_real_model_types_iter_parent; } static void gtk_tree_model_types_init (GtkTreeModelTypes *model_types) { model_types->stamp = g_random_int (); } static GType column_types[] = { G_TYPE_STRING, /* GType */ G_TYPE_STRING /* type name */ }; static gint gtk_real_model_types_get_n_columns (GtkTreeModel *tree_model) { return G_N_ELEMENTS (column_types); } static GType gtk_real_model_types_get_column_type (GtkTreeModel *tree_model, gint index) { g_return_val_if_fail (index < G_N_ELEMENTS (column_types), G_TYPE_INVALID); return column_types[index]; } #if 0 /* Use default implementation of this */ static gboolean gtk_real_model_types_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { } #endif /* The toplevel nodes of the tree are the reserved types, G_TYPE_NONE through * G_TYPE_RESERVED_FUNDAMENTAL. */ static GtkTreePath * gtk_real_model_types_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkTreePath *retval; GType type; GType parent; g_return_val_if_fail (GTK_IS_TREE_MODEL_TYPES (tree_model), NULL); g_return_val_if_fail (iter != NULL, NULL); type = GPOINTER_TO_INT (iter->user_data); retval = gtk_tree_path_new (); parent = g_type_parent (type); while (parent != G_TYPE_INVALID) { GType* children = g_type_children (parent, NULL); gint i = 0; if (!children || children[0] == G_TYPE_INVALID) { g_warning ("bad iterator?"); return NULL; } while (children[i] != type) ++i; gtk_tree_path_prepend_index (retval, i); g_free (children); type = parent; parent = g_type_parent (parent); } /* The fundamental type itself is the index on the toplevel */ gtk_tree_path_prepend_index (retval, type); return retval; } static void gtk_real_model_types_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, gint column, GValue *value) { GType type; type = GPOINTER_TO_INT (iter->user_data); switch (column) { case 0: { gchar *str; g_value_init (value, G_TYPE_STRING); str = g_strdup_printf ("%ld", type); g_value_set_string (value, str); g_free (str); } break; case 1: g_value_init (value, G_TYPE_STRING); g_value_set_string (value, g_type_name (type)); break; default: g_warning ("Bad column %d requested", column); } } static gboolean gtk_real_model_types_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { GType parent; GType type; type = GPOINTER_TO_INT (iter->user_data); parent = g_type_parent (type); if (parent == G_TYPE_INVALID) { /* find next _valid_ fundamental type */ do type++; while (!g_type_name (type) && type <= G_TYPE_FUNDAMENTAL_MAX); if (type <= G_TYPE_FUNDAMENTAL_MAX) { /* found one */ iter->user_data = GINT_TO_POINTER (type); return TRUE; } else return FALSE; } else { GType* children = g_type_children (parent, NULL); gint i = 0; g_assert (children != NULL); while (children[i] != type) ++i; ++i; if (children[i] != G_TYPE_INVALID) { g_free (children); iter->user_data = GINT_TO_POINTER (children[i]); return TRUE; } else { g_free (children); return FALSE; } } } static gboolean gtk_real_model_types_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { GType type; GType* children; type = GPOINTER_TO_INT (parent->user_data); children = g_type_children (type, NULL); if (!children || children[0] == G_TYPE_INVALID) { g_free (children); return FALSE; } else { iter->user_data = GINT_TO_POINTER (children[0]); g_free (children); return TRUE; } } static gboolean gtk_real_model_types_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) { GType type; GType* children; type = GPOINTER_TO_INT (iter->user_data); children = g_type_children (type, NULL); if (!children || children[0] == G_TYPE_INVALID) { g_free (children); return FALSE; } else { g_free (children); return TRUE; } } static gint gtk_real_model_types_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { if (iter == NULL) { return G_TYPE_FUNDAMENTAL_MAX; } else { GType type; GType* children; guint n_children = 0; type = GPOINTER_TO_INT (iter->user_data); children = g_type_children (type, &n_children); g_free (children); return n_children; } } static gboolean gtk_real_model_types_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent, gint n) { if (parent == NULL) { /* fundamental type */ if (n < G_TYPE_FUNDAMENTAL_MAX) { iter->user_data = GINT_TO_POINTER (n); return TRUE; } else return FALSE; } else { GType type = GPOINTER_TO_INT (parent->user_data); guint n_children = 0; GType* children = g_type_children (type, &n_children); if (n_children == 0) { g_free (children); return FALSE; } else if (n >= n_children) { g_free (children); return FALSE; } else { iter->user_data = GINT_TO_POINTER (children[n]); g_free (children); return TRUE; } } } static gboolean gtk_real_model_types_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { GType type; GType parent; type = GPOINTER_TO_INT (child->user_data); parent = g_type_parent (type); if (parent == G_TYPE_INVALID) { if (type > G_TYPE_FUNDAMENTAL_MAX) g_warning ("no parent for %ld %s\n", type, g_type_name (type)); return FALSE; } else { iter->user_data = GINT_TO_POINTER (parent); return TRUE; } } /* * Automated testing */ static void treestore_torture_recurse (GtkTreeStore *store, GtkTreeIter *root, gint depth) { GtkTreeModel *model; gint i; GtkTreeIter iter; model = GTK_TREE_MODEL (store); if (depth > 2) return; ++depth; gtk_tree_store_append (store, &iter, root); gtk_tree_model_iter_children (model, &iter, root); i = 0; while (i < 100) { gtk_tree_store_append (store, &iter, root); ++i; } while (gtk_tree_model_iter_children (model, &iter, root)) gtk_tree_store_remove (store, &iter); gtk_tree_store_append (store, &iter, root); /* inserts before last node in tree */ i = 0; while (i < 100) { gtk_tree_store_insert_before (store, &iter, root, &iter); ++i; } /* inserts after the node before the last node */ i = 0; while (i < 100) { gtk_tree_store_insert_after (store, &iter, root, &iter); ++i; } /* inserts after the last node */ gtk_tree_store_append (store, &iter, root); i = 0; while (i < 100) { gtk_tree_store_insert_after (store, &iter, root, &iter); ++i; } /* remove everything again */ while (gtk_tree_model_iter_children (model, &iter, root)) gtk_tree_store_remove (store, &iter); /* Prepends */ gtk_tree_store_prepend (store, &iter, root); i = 0; while (i < 100) { gtk_tree_store_prepend (store, &iter, root); ++i; } /* remove everything again */ while (gtk_tree_model_iter_children (model, &iter, root)) gtk_tree_store_remove (store, &iter); gtk_tree_store_append (store, &iter, root); gtk_tree_store_append (store, &iter, root); gtk_tree_store_append (store, &iter, root); gtk_tree_store_append (store, &iter, root); while (gtk_tree_model_iter_children (model, &iter, root)) { treestore_torture_recurse (store, &iter, depth); gtk_tree_store_remove (store, &iter); } } static void run_automated_tests (void) { g_print ("Running automated tests...\n"); /* FIXME TreePath basic verification */ /* FIXME generic consistency checks on the models */ { /* Make sure list store mutations don't crash anything */ GtkListStore *store; GtkTreeModel *model; gint i; GtkTreeIter iter; store = gtk_list_store_new (1, G_TYPE_INT); model = GTK_TREE_MODEL (store); i = 0; while (i < 100) { gtk_list_store_append (store, &iter); ++i; } while (gtk_tree_model_get_iter_first (model, &iter)) gtk_list_store_remove (store, &iter); gtk_list_store_append (store, &iter); /* inserts before last node in list */ i = 0; while (i < 100) { gtk_list_store_insert_before (store, &iter, &iter); ++i; } /* inserts after the node before the last node */ i = 0; while (i < 100) { gtk_list_store_insert_after (store, &iter, &iter); ++i; } /* inserts after the last node */ gtk_list_store_append (store, &iter); i = 0; while (i < 100) { gtk_list_store_insert_after (store, &iter, &iter); ++i; } /* remove everything again */ while (gtk_tree_model_get_iter_first (model, &iter)) gtk_list_store_remove (store, &iter); /* Prepends */ gtk_list_store_prepend (store, &iter); i = 0; while (i < 100) { gtk_list_store_prepend (store, &iter); ++i; } /* remove everything again */ while (gtk_tree_model_get_iter_first (model, &iter)) gtk_list_store_remove (store, &iter); g_object_unref (store); } { /* Make sure tree store mutations don't crash anything */ GtkTreeStore *store; GtkTreeIter root; store = gtk_tree_store_new (1, G_TYPE_INT); gtk_tree_store_append (GTK_TREE_STORE (store), &root, NULL); /* Remove test until it is rewritten to work */ /* treestore_torture_recurse (store, &root, 0);*/ g_object_unref (store); } g_print ("Passed.\n"); }