From c7ed2c21307efe26f2259bd28ef076bdff0a2b78 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 26 Aug 2015 15:28:22 +0200 Subject: [PATCH] inspector: Refactor object tree construction Instead of having lots of if statements, create a vtable. This will be useful for the following patches. --- gtk/inspector/object-tree.c | 697 +++++++++++++++++++++++++----------- 1 file changed, 480 insertions(+), 217 deletions(-) diff --git a/gtk/inspector/object-tree.c b/gtk/inspector/object-tree.c index 55fc7e3f53..4af3868e06 100644 --- a/gtk/inspector/object-tree.c +++ b/gtk/inspector/object-tree.c @@ -83,10 +83,477 @@ struct _GtkInspectorObjectTreePrivate gint search_length; }; +typedef struct _ObjectTreeClassFuncs ObjectTreeClassFuncs; +typedef void (* ObjectTreeForallFunc) (GObject *object, + const char *name, + gpointer data); + +struct _ObjectTreeClassFuncs { + GType (* get_type) (void); + GObject * (* get_parent) (GObject *object); + void (* forall) (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data); + gboolean (* get_sensitive) (GObject *object); +}; + static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorObjectTree, gtk_inspector_object_tree, GTK_TYPE_BOX) +static GObject * +object_tree_get_parent_default (GObject *object) +{ + return NULL; +} + +static void +object_tree_forall_default (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ +} + +static gboolean +object_tree_get_sensitive_default (GObject *object) +{ + return TRUE; +} + +static GObject * +object_tree_widget_get_parent (GObject *object) +{ + return G_OBJECT (gtk_widget_get_parent (GTK_WIDGET (object))); +} + +static void +object_tree_widget_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + struct { + GtkPropagationPhase phase; + const gchar *name; + } phases[] = { + { GTK_PHASE_CAPTURE, "capture" }, + { GTK_PHASE_TARGET, "target" }, + { GTK_PHASE_BUBBLE, "bubble" }, + { GTK_PHASE_NONE, "" } + }; + gint i; + + for (i = 0; i < G_N_ELEMENTS (phases); i++) + { + GList *list, *l; + + list = _gtk_widget_list_controllers (GTK_WIDGET (object), phases[i].phase); + for (l = list; l; l = l->next) + { + GObject *controller = l->data; + forall_func (controller, phases[i].name, forall_data); + } + g_list_free (list); + } + + if (gtk_widget_is_toplevel (GTK_WIDGET (object))) + { + GObject *clock; + + clock = G_OBJECT (gtk_widget_get_frame_clock (GTK_WIDGET (object))); + if (clock) + forall_func (clock, "frame-clock", forall_data); + } +} + +static gboolean +object_tree_widget_get_sensitive (GObject *object) +{ + return gtk_widget_get_mapped (GTK_WIDGET (object)); +} + +typedef struct { + ObjectTreeForallFunc forall_func; + gpointer forall_data; +} ForallData; + +static void +container_children_callback (GtkWidget *widget, + gpointer client_data) +{ + ForallData *forall_data = client_data; + + forall_data->forall_func (G_OBJECT (widget), NULL, forall_data->forall_data); +} + +static void +object_tree_container_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + ForallData data = { + forall_func, + forall_data + }; + + gtk_container_forall (GTK_CONTAINER (object), + container_children_callback, + &data); +} + +static void +object_tree_tree_model_sort_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GObject *child = G_OBJECT (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (object))); + + if (child) + forall_func (child, "model", forall_data); +} + +static void +object_tree_tree_model_filter_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GObject *child = G_OBJECT (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (object))); + + if (child) + forall_func (child, "model", forall_data); +} + +static void +object_tree_menu_item_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GtkWidget *submenu; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (object)); + if (submenu) + forall_func (G_OBJECT (submenu), "submenu", forall_data); +} + +static void +object_tree_combo_box_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GtkWidget *popup; + GObject *child; + + popup = gtk_combo_box_get_popup (GTK_COMBO_BOX (object)); + if (popup) + forall_func (G_OBJECT (popup), "popup", forall_data); + + child = G_OBJECT (gtk_combo_box_get_model (GTK_COMBO_BOX (object))); + if (child) + forall_func (child, "model", forall_data); +} + +static void +object_tree_tree_view_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + gint n_columns, i; + GObject *child; + + child = G_OBJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (object))); + if (child) + forall_func (child, "model", forall_data); + + child = G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (object))); + if (child) + forall_func (child, "selection", forall_data); + + n_columns = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (object)); + for (i = 0; i < n_columns; i++) + { + child = G_OBJECT (gtk_tree_view_get_column (GTK_TREE_VIEW (object), i)); + forall_func (child, NULL, forall_data); + } +} + +static void +object_tree_icon_view_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GObject *child; + + child = G_OBJECT (gtk_icon_view_get_model (GTK_ICON_VIEW (object))); + if (child) + forall_func (child, "mnodel", forall_data); +} + +typedef struct { + ObjectTreeForallFunc forall_func; + gpointer forall_data; + GObject *parent; +} ParentForallData; + +static gboolean +cell_callback (GtkCellRenderer *renderer, + gpointer data) +{ + ParentForallData *d = data; + gpointer cell_layout; + + cell_layout = g_object_get_data (d->parent, "gtk-inspector-cell-layout"); + g_object_set_data (G_OBJECT (renderer), "gtk-inspector-cell-layout", cell_layout); + d->forall_func (G_OBJECT (renderer), NULL, d->forall_data); + + return FALSE; +} + +static void +object_tree_cell_area_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + ParentForallData data = { + forall_func, + forall_data, + object + }; + + gtk_cell_area_foreach (GTK_CELL_AREA (object), cell_callback, &data); +} + +static void +object_tree_cell_layout_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GtkCellArea *area; + + /* cell areas handle their own stuff */ + if (GTK_IS_CELL_AREA (object)) + return; + + area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (object)); + g_object_set_data (G_OBJECT (area), "gtk-inspector-cell-layout", object); + forall_func (G_OBJECT (area), "cell-area", forall_data); +} + +static void +object_tree_text_view_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GtkTextBuffer *buffer; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (object)); + forall_func (G_OBJECT (buffer), "buffer", forall_data); +} + +static void +object_tree_text_buffer_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GtkTextTagTable *tags; + + tags = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (object)); + forall_func (G_OBJECT (tags), "tag-table", forall_data); +} + +static void +tag_callback (GtkTextTag *tag, + gpointer data) +{ + ForallData *d = data; + gchar *name; + + g_object_get (tag, "name", &name, NULL); + d->forall_func (G_OBJECT (tag), name, d->forall_data); + g_free (name); +} + +static void +object_tree_text_tag_table_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + ForallData data = { + forall_func, + forall_data + }; + + gtk_text_tag_table_foreach (GTK_TEXT_TAG_TABLE (object), tag_callback, &data); +} + +static void +object_tree_application_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GObject *menu; + + menu = (GObject *)gtk_application_get_app_menu (GTK_APPLICATION (object)); + if (menu) + forall_func (menu, "app-menu", forall_data); + + menu = (GObject *)gtk_application_get_menubar (GTK_APPLICATION (object)); + if (menu) + forall_func (menu, "menubar", forall_data); +} + +/* Note: + * This tree must be sorted with the most specific types first. + * We iterate over it top to bottom and return the first match + * using g_type_is_a () + */ +static const ObjectTreeClassFuncs object_tree_class_funcs[] = { + { + gtk_application_get_type, + object_tree_get_parent_default, + object_tree_application_forall, + object_tree_get_sensitive_default + }, + { + gtk_text_tag_table_get_type, + object_tree_get_parent_default, + object_tree_text_tag_table_forall, + object_tree_get_sensitive_default + }, + { + gtk_text_buffer_get_type, + object_tree_get_parent_default, + object_tree_text_buffer_forall, + object_tree_get_sensitive_default + }, + { + gtk_text_view_get_type, + object_tree_widget_get_parent, + object_tree_text_view_forall, + object_tree_widget_get_sensitive + }, + { + gtk_icon_view_get_type, + object_tree_widget_get_parent, + object_tree_icon_view_forall, + object_tree_widget_get_sensitive + }, + { + gtk_tree_view_get_type, + object_tree_widget_get_parent, + object_tree_tree_view_forall, + object_tree_widget_get_sensitive + }, + { + gtk_combo_box_get_type, + object_tree_widget_get_parent, + object_tree_combo_box_forall, + object_tree_widget_get_sensitive + }, + { + gtk_menu_item_get_type, + object_tree_widget_get_parent, + object_tree_menu_item_forall, + object_tree_widget_get_sensitive + }, + { + gtk_container_get_type, + object_tree_widget_get_parent, + object_tree_container_forall, + object_tree_widget_get_sensitive + }, + { + gtk_widget_get_type, + object_tree_widget_get_parent, + object_tree_widget_forall, + object_tree_widget_get_sensitive + }, + { + gtk_tree_model_filter_get_type, + object_tree_get_parent_default, + object_tree_tree_model_filter_forall, + object_tree_get_sensitive_default + }, + { + gtk_tree_model_sort_get_type, + object_tree_get_parent_default, + object_tree_tree_model_sort_forall, + object_tree_get_sensitive_default + }, + { + gtk_cell_area_get_type, + object_tree_get_parent_default, + object_tree_cell_area_forall, + object_tree_get_sensitive_default + }, + { + gtk_cell_layout_get_type, + object_tree_get_parent_default, + object_tree_cell_layout_forall, + object_tree_get_sensitive_default + }, + { + g_object_get_type, + object_tree_get_parent_default, + object_tree_forall_default, + object_tree_get_sensitive_default + }, +}; + +static const ObjectTreeClassFuncs * +find_class_funcs (GObject *object) +{ + GType object_type; + guint i; + + object_type = G_OBJECT_TYPE (object); + + for (i = 0; i < G_N_ELEMENTS (object_tree_class_funcs); i++) + { + if (g_type_is_a (object_type, object_tree_class_funcs[i].get_type ())) + return &object_tree_class_funcs[i]; + } + + g_assert_not_reached (); + + return NULL; +} + +static G_GNUC_UNUSED GObject * +object_get_parent (GObject *object) +{ + const ObjectTreeClassFuncs *funcs; + + funcs = find_class_funcs (object); + + return funcs->get_parent (object); +} + +static void +object_forall (GObject *object, + ObjectTreeForallFunc forall_func, + gpointer forall_data) +{ + GType object_type; + guint i; + + object_type = G_OBJECT_TYPE (object); + + for (i = 0; i < G_N_ELEMENTS (object_tree_class_funcs); i++) + { + if (g_type_is_a (object_type, object_tree_class_funcs[i].get_type ())) + object_tree_class_funcs[i].forall (object, forall_func, forall_data); + } +} + +static gboolean +object_get_sensitive (GObject *object) +{ + const ObjectTreeClassFuncs *funcs; + + funcs = find_class_funcs (object); + + return funcs->get_sensitive (object); +} + static void on_row_activated (GtkTreeView *tree, GtkTreePath *path, @@ -511,38 +978,13 @@ typedef struct } FindAllData; static void -child_callback (GtkWidget *widget, - gpointer data) +child_callback (GObject *object, + const char *name, + gpointer data) { FindAllData *d = data; - gtk_inspector_object_tree_append_object (d->wt, G_OBJECT (widget), d->iter, NULL); -} - -static gboolean -cell_callback (GtkCellRenderer *renderer, - gpointer data) -{ - FindAllData *d = data; - gpointer cell_layout; - - cell_layout = g_object_get_data (d->parent, "gtk-inspector-cell-layout"); - g_object_set_data (G_OBJECT (renderer), "gtk-inspector-cell-layout", cell_layout); - gtk_inspector_object_tree_append_object (d->wt, G_OBJECT (renderer), d->iter, NULL); - - return FALSE; -} - -static void -tag_callback (GtkTextTag *tag, - gpointer data) -{ - FindAllData *d = data; - gchar *name; - - g_object_get (tag, "name", &name, NULL); - gtk_inspector_object_tree_append_object (d->wt, G_OBJECT (tag), d->iter, name); - g_free (name); + gtk_inspector_object_tree_append_object (d->wt, object, d->iter, NULL); } void @@ -555,19 +997,11 @@ gtk_inspector_object_tree_append_object (GtkInspectorObjectTree *wt, GtkTreePath *path; const gchar *class_name; gchar *classes; - gboolean mapped; ObjectData *od; const gchar *label; + FindAllData data; - if (GTK_IS_WIDGET (object)) - mapped = gtk_widget_get_mapped (GTK_WIDGET (object)); - else - mapped = TRUE; - - if (G_OBJECT (object)) - class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)); - else - class_name = ""; + class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)); if (GTK_IS_WIDGET (object)) { @@ -624,7 +1058,7 @@ gtk_inspector_object_tree_append_object (GtkInspectorObjectTree *wt, OBJECT_NAME, name, OBJECT_LABEL, label, OBJECT_CLASSES, classes, - SENSITIVE, mapped, + SENSITIVE, object_get_sensitive (object), -1); if (name && *name) @@ -649,183 +1083,12 @@ gtk_inspector_object_tree_append_object (GtkInspectorObjectTree *wt, g_hash_table_insert (wt->priv->iters, object, od); g_object_weak_ref (object, gtk_object_tree_remove_dead_object, od); + + data.wt = wt; + data.iter = &iter; + data.parent = object; - if (GTK_IS_CONTAINER (object)) - { - FindAllData data; - - data.wt = wt; - data.iter = &iter; - data.parent = object; - - gtk_container_forall (GTK_CONTAINER (object), child_callback, &data); - } - - /* Below are special cases for dependent objects which are not - * children in the GtkContainer sense, but which we still want - * to show in the tree right away. - */ - if (GTK_IS_TREE_MODEL_SORT (object)) - { - GObject *child = G_OBJECT (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "model"); - } - - if (GTK_IS_TREE_MODEL_FILTER (object)) - { - GObject *child = G_OBJECT (gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "model"); - } - - if (GTK_IS_MENU_ITEM (object)) - { - GtkWidget *submenu; - - submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (object)); - if (submenu) - gtk_inspector_object_tree_append_object (wt, G_OBJECT (submenu), &iter, "submenu"); - } - - if (GTK_IS_COMBO_BOX (object)) - { - GtkWidget *popup; - GObject *child; - - popup = gtk_combo_box_get_popup (GTK_COMBO_BOX (object)); - if (popup) - gtk_inspector_object_tree_append_object (wt, G_OBJECT (popup), &iter, "popup"); - - child = G_OBJECT (gtk_combo_box_get_model (GTK_COMBO_BOX (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "model"); - } - - if (GTK_IS_TREE_VIEW (object)) - { - gint n_columns, i; - GObject *child; - - child = G_OBJECT (gtk_tree_view_get_model (GTK_TREE_VIEW (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "model"); - - child = G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "selection"); - - n_columns = gtk_tree_view_get_n_columns (GTK_TREE_VIEW (object)); - for (i = 0; i < n_columns; i++) - { - child = G_OBJECT (gtk_tree_view_get_column (GTK_TREE_VIEW (object), i)); - gtk_inspector_object_tree_append_object (wt, child, &iter, NULL); - } - } - - if (GTK_IS_ICON_VIEW (object)) - { - GObject *child; - - child = G_OBJECT (gtk_icon_view_get_model (GTK_ICON_VIEW (object))); - if (child) - gtk_inspector_object_tree_append_object (wt, child, &iter, "model"); - } - - if (GTK_IS_CELL_AREA (object)) - { - FindAllData data; - - data.wt = wt; - data.iter = &iter; - data.parent = object; - - gtk_cell_area_foreach (GTK_CELL_AREA (object), cell_callback, &data); - } - else if (GTK_IS_CELL_LAYOUT (object)) - { - GtkCellArea *area; - - area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (object)); - g_object_set_data (G_OBJECT (area), "gtk-inspector-cell-layout", object); - gtk_inspector_object_tree_append_object (wt, G_OBJECT (area), &iter, "cell-area"); - } - - if (GTK_IS_TEXT_VIEW (object)) - { - GtkTextBuffer *buffer; - - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (object)); - gtk_inspector_object_tree_append_object (wt, G_OBJECT (buffer), &iter, "buffer"); - } - - if (GTK_IS_TEXT_BUFFER (object)) - { - GtkTextTagTable *tags; - - tags = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (object)); - gtk_inspector_object_tree_append_object (wt, G_OBJECT (tags), &iter, "tag-table"); - } - - if (GTK_IS_TEXT_TAG_TABLE (object)) - { - FindAllData data; - - data.wt = wt; - data.iter = &iter; - data.parent = object; - - gtk_text_tag_table_foreach (GTK_TEXT_TAG_TABLE (object), tag_callback, &data); - } - - if (GTK_IS_WIDGET (object)) - { - struct { - GtkPropagationPhase phase; - const gchar *name; - } phases[] = { - { GTK_PHASE_CAPTURE, "capture" }, - { GTK_PHASE_TARGET, "target" }, - { GTK_PHASE_BUBBLE, "bubble" }, - { GTK_PHASE_NONE, "" } - }; - gint i; - - for (i = 0; i < G_N_ELEMENTS (phases); i++) - { - GList *list, *l; - - list = _gtk_widget_list_controllers (GTK_WIDGET (object), phases[i].phase); - for (l = list; l; l = l->next) - { - GObject *controller = l->data; - gtk_inspector_object_tree_append_object (wt, controller, &iter, phases[i].name); - } - g_list_free (list); - } - - if (gtk_widget_is_toplevel (GTK_WIDGET (object))) - { - GObject *clock; - - clock = (GObject *)gtk_widget_get_frame_clock (GTK_WIDGET (object)); - if (clock) - gtk_inspector_object_tree_append_object (wt, clock, &iter, "frame-clock"); - } - } - - if (GTK_IS_APPLICATION (object)) - { - GObject *menu; - - menu = (GObject *)gtk_application_get_app_menu (GTK_APPLICATION (object)); - if (menu) - gtk_inspector_object_tree_append_object (wt, menu, &iter, "app-menu"); - - menu = (GObject *)gtk_application_get_menubar (GTK_APPLICATION (object)); - if (menu) - gtk_inspector_object_tree_append_object (wt, menu, &iter, "menubar"); - } + object_forall (object, child_callback, &data); } gboolean @@ -878,8 +1141,8 @@ gtk_inspector_object_tree_scan (GtkInspectorObjectTree *wt, selected = gtk_inspector_object_tree_get_selected (wt); - gtk_tree_store_clear (wt->priv->model); g_hash_table_remove_all (wt->priv->iters); + gtk_tree_store_clear (wt->priv->model); gtk_inspector_object_tree_append_object (wt, G_OBJECT (gtk_settings_get_default ()), NULL, NULL); if (g_application_get_default ()) gtk_inspector_object_tree_append_object (wt, G_OBJECT (g_application_get_default ()), NULL, NULL);