From 5e5517211f51345ea0cd62fe6953e108d0f74aed Mon Sep 17 00:00:00 2001 From: Christopher Li Date: Tue, 30 Mar 2010 15:48:38 -0700 Subject: inspect: add custom ast treeview model It is custom gtk treeview model to wrap the C data structure use by sparse. The ast model does not have any sparse specific stuff in it. Instead, it provide a simplier call back based API to allow user construct tree view object very easily. Signed-Off-By: Christopher Li --- ast-model.c | 468 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ast-model.h | 88 ++++++++++++ ast-view.c | 48 +++++++ ast-view.h | 7 + 4 files changed, 611 insertions(+) create mode 100644 ast-model.c create mode 100644 ast-model.h create mode 100644 ast-view.c create mode 100644 ast-view.h diff --git a/ast-model.c b/ast-model.c new file mode 100644 index 0000000..f516340 --- /dev/null +++ b/ast-model.c @@ -0,0 +1,468 @@ +/* + * ast-model.c + * + * A custom tree model to simplify viewing of AST objects. + * Modify from the Gtk+ tree view tutorial, custom-list.c + * by Tim-Philipp Mueller < tim at centricular dot net > + * + * Copyright (C) 2010 Christopher Li + */ + + +#include "ast-model.h" +#include "stdint.h" + +/* boring declarations of local functions */ + +static void ast_init(AstNode *pkg_tree); +static void ast_class_init(AstNodeClass *klass); +static void ast_tree_model_init(GtkTreeModelIface *iface); +static void ast_finalize(GObject *object); +static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model); +static gint ast_get_n_columns(GtkTreeModel *tree_model); +static GType ast_get_column_type(GtkTreeModel *tree_model, gint index); +static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter, + GtkTreePath *path); +static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter); +static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, + gint column, GValue *value); +static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter); +static gboolean ast_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent); +static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter); +static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter); +static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter, + GtkTreeIter *parent, gint n); +static gboolean ast_iter_parent(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child); + +static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */ + +static inline +void inspect_child_node(AstNode *node) +{ + if (node->inspect) { + node->inspect(node); + node->inspect = NULL; + } +} + + +static inline +AstNode* ast_nth_child(AstNode *node, int n) +{ + if (!node) + return NULL; + + inspect_child_node(node); + + if (n >= node->childnodes->len) + return FALSE; + return g_array_index(node->childnodes, AstNode *, n); +} + + +static inline +gboolean ast_set_iter(GtkTreeIter *iter, AstNode *node) +{ + iter->user_data = node; + iter->user_data2 = iter->user_data3 = NULL; + return node != NULL; +} + + +/***************************************************************************** + * + * ast_get_type: here we register our new type and its interfaces + * with the type system. If you want to implement + * additional interfaces like GtkTreeSortable, you + * will need to do it here. + * + *****************************************************************************/ + +GType +ast_get_type (void) +{ + static GType ast_type = 0; + static const GTypeInfo ast_info = { + sizeof (AstNodeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) ast_class_init, + NULL, /* class finalize */ + NULL, /* class_data */ + sizeof (AstNode), + 0, /* n_preallocs */ + (GInstanceInitFunc) ast_init + }; + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) ast_tree_model_init, + NULL, + NULL + }; + + + + if (ast_type) + return ast_type; + + /* Some boilerplate type registration stuff */ + ast_type = g_type_register_static(G_TYPE_OBJECT, "AstNode", + &ast_info, (GTypeFlags)0); + + /* Here we register our GtkTreeModel interface with the type system */ + g_type_add_interface_static(ast_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + + return ast_type; +} + + +/***************************************************************************** + * + * ast_class_init: more boilerplate GObject/GType stuff. + * Init callback for the type system, + * called once when our new class is created. + * + *****************************************************************************/ + +static void +ast_class_init (AstNodeClass *klass) +{ + GObjectClass *object_class; + + parent_class = (GObjectClass*) g_type_class_peek_parent (klass); + object_class = (GObjectClass*) klass; + + object_class->finalize = ast_finalize; +} + +/***************************************************************************** + * + * ast_tree_model_init: init callback for the interface registration + * in ast_get_type. Here we override + * the GtkTreeModel interface functions that + * we implement. + * + *****************************************************************************/ + +static void +ast_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = ast_get_flags; + iface->get_n_columns = ast_get_n_columns; + iface->get_column_type = ast_get_column_type; + iface->get_iter = ast_get_iter; + iface->get_path = ast_get_path; + iface->get_value = ast_get_value; + iface->iter_next = ast_iter_next; + iface->iter_children = ast_iter_children; + iface->iter_has_child = ast_iter_has_child; + iface->iter_n_children = ast_iter_n_children; + iface->iter_nth_child = ast_iter_nth_child; + iface->iter_parent = ast_iter_parent; +} + + +/***************************************************************************** + * + * ast_init: this is called everytime a new ast node object + * instance is created (we do that in ast_new). + * Initialise the list structure's fields here. + * + *****************************************************************************/ + +static void +ast_init (AstNode *node) +{ + node->childnodes = g_array_new(FALSE, TRUE, sizeof(AstNode *)); + node->stamp = g_random_int(); /* Random int to check whether iters belong to out model */ +} + + +/***************************************************************************** + * + * ast_finalize: this is called just before an ast node is + * destroyed. Free dynamically allocated memory here. + * + *****************************************************************************/ + +static void +ast_finalize (GObject *object) +{ + /* AstNode *node = AST_NODE(object); */ + + /* FIXME: free all node memory */ + + /* must chain up - finalize parent */ + (* parent_class->finalize) (object); +} + + +/***************************************************************************** + * + * ast_get_flags: tells the rest of the world whether our tree model + * has any special characteristics. In our case, + * we have a list model (instead of a tree), and each + * tree iter is valid as long as the row in question + * exists, as it only contains a pointer to our struct. + * + *****************************************************************************/ + +static GtkTreeModelFlags +ast_get_flags(GtkTreeModel *tree_model) +{ + return (GTK_TREE_MODEL_ITERS_PERSIST); +} + + +/***************************************************************************** + * + * ast_get_n_columns: tells the rest of the world how many data + * columns we export via the tree model interface + * + *****************************************************************************/ + +static gint +ast_get_n_columns(GtkTreeModel *tree_model) +{ + return 1; +} + + +/***************************************************************************** + * + * ast_get_column_type: tells the rest of the world which type of + * data an exported model column contains + * + *****************************************************************************/ + +static GType +ast_get_column_type(GtkTreeModel *tree_model, + gint index) +{ + return G_TYPE_STRING; +} + + +/***************************************************************************** + * + * ast_get_iter: converts a tree path (physical position) into a + * tree iter structure (the content of the iter + * fields will only be used internally by our model). + * We simply store a pointer to our AstNodeItem + * structure that represents that row in the tree iter. + * + *****************************************************************************/ + +static gboolean +ast_get_iter(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + AstNode *node; + gint *indices, depth; + int i; + + node = AST_NODE(tree_model); + indices = gtk_tree_path_get_indices(path); + depth = gtk_tree_path_get_depth(path); + + for (i = 0; i < depth; i++) + node = ast_nth_child(node, indices[i]); + + return ast_set_iter(iter, node); +} + + +/***************************************************************************** + * + * ast_get_path: converts a tree iter into a tree path (ie. the + * physical position of that row in the list). + * + *****************************************************************************/ + +static GtkTreePath * +ast_get_path(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + GtkTreePath *path; + AstNode *root = AST_NODE(tree_model); + AstNode *node = AST_NODE(iter->user_data); + + path = gtk_tree_path_new(); + while (node != root) { + gtk_tree_path_prepend_index(path, node->index); + node = node->parent; + } + return path; +} + + +/***************************************************************************** + * + * ast_get_value: Returns a row's exported data columns + * (_get_value is what gtk_tree_model_get uses) + * + *****************************************************************************/ + +static void +ast_get_value(GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + AstNode *node = iter->user_data; + + g_assert(AST_IS_NODE(tree_model)); + if (column != 1) + return; + + inspect_child_node(node); + + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, node->text); + return; +} + + +/***************************************************************************** + * + * ast_iter_next: Takes an iter structure and sets it to point + * to the next row. + * + *****************************************************************************/ + +static gboolean +ast_iter_next(GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + AstNode *node = iter->user_data; + + g_assert(AST_IS_NODE (tree_model)); + + node = ast_nth_child(node->parent, node->index + 1); + return ast_set_iter(iter, node); +} + + +/***************************************************************************** + * + * ast_iter_children: Returns TRUE or FALSE depending on whether + * the row specified by 'parent' has any children. + * If it has children, then 'iter' is set to + * point to the first child. Special case: if + * 'parent' is NULL, then the first top-level + * row should be returned if it exists. + * + *****************************************************************************/ + +static gboolean +ast_iter_children(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return ast_iter_nth_child(tree_model, iter, parent, 0); +} + + +/***************************************************************************** + * + * ast_iter_has_child: Returns TRUE or FALSE depending on whether + * the row specified by 'iter' has any children. + * We only have a list and thus no children. + * + *****************************************************************************/ + +static gboolean +ast_iter_has_child (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + AstNode *node = iter->user_data; + inspect_child_node(node); + return node->childnodes->len > 0; +} + + +/***************************************************************************** + * + * ast_iter_n_children: Returns the number of children the row + * specified by 'iter' has. This is usually 0, + * as we only have a list and thus do not have + * any children to any rows. A special case is + * when 'iter' is NULL, in which case we need + * to return the number of top-level node, + * ie. the number of rows in our list. + * + *****************************************************************************/ + +static gint +ast_iter_n_children (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + AstNode *node = iter->user_data; + + inspect_child_node(node); + return node->childnodes->len; +} + + +/***************************************************************************** + * + * ast_iter_nth_child: If the row specified by 'parent' has any + * children, set 'iter' to the n-th child and + * return TRUE if it exists, otherwise FALSE. + * A special case is when 'parent' is NULL, in + * which case we need to set 'iter' to the n-th + * row if it exists. + * + *****************************************************************************/ + +static gboolean +ast_iter_nth_child(GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + AstNode *node = parent ? parent->user_data : (AstNode*) tree_model; + GArray *array = node->childnodes; + if (n >= array->len) + return FALSE; + iter->user_data = g_array_index(array, AstNode *, n); + return TRUE; +} + + +/***************************************************************************** + * + * ast_iter_parent: Point 'iter' to the parent node of 'child'. As + * we have a list and thus no children and no + * parents of children, we can just return FALSE. + * + *****************************************************************************/ + +static gboolean +ast_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + AstNode *node = (AstNode *) child->user_data; + iter->user_data = node->parent; + return node->parent != NULL; +} + + +AstNode * +ast_new (AstNode *parent, int index, const char *text, void *ptr, void (*inspect)(AstNode*)) +{ + AstNode *node = (AstNode*) g_object_new (AST_TYPE_NODE, NULL); + g_assert(node != NULL); + node->parent = parent; + node->index = index; + node->text = text; + node->inspect = inspect; + node->ptr = ptr; + return node; +} + diff --git a/ast-model.h b/ast-model.h new file mode 100644 index 0000000..2eea056 --- /dev/null +++ b/ast-model.h @@ -0,0 +1,88 @@ + +/* + * ast-model.h + * + * Copyright (C) 2010 Christopher Li. + * + */ + +#ifndef _ast_model_h_ +#define _ast_model_h_ + +#include +#include +#include "lib.h" + +#define AST_TYPE_NODE (ast_get_type ()) +#define AST_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), AST_TYPE_NODE, AstNode)) +#define AST_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AST_TYPE_NODE, AstNodeClass)) +#define AST_IS_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AST_TYPE_NODE)) +#define AST_IS_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AST_TYPE_NODE)) +#define AST_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), AST_TYPE_NODE, AstNodeClass)) + +enum +{ + AST_COL_RECORD = 0, + AST_COL_NAME, + AST_N_COLUMNS, +} ; + + +typedef struct AstNode AstNode; +typedef struct AstNodeClass AstNodeClass; + + + +/* AstNode: this structure contains everything we need for our + * model implementation. You can add extra fields to + * this structure, e.g. hashtables to quickly lookup + * rows or whatever else you might need, but it is + * crucial that 'parent' is the first member of the + * structure. */ + +struct AstNode +{ + GObject base; /* this MUST be the first member */ + + AstNode *parent; + int index; + const gchar *text; + void (*inspect)(struct AstNode* node); + void *ptr; + GArray *childnodes; + gint stamp; +}; + + + +/* AstNodeClass: more boilerplate GObject stuff */ + +struct AstNodeClass +{ + GObjectClass base_class; +}; + + +GType ast_get_type(void); +AstNode* ast_new(AstNode *parent, int index, const char *prefix, void *ptr, void (*expand)(AstNode*)); + + +static inline +void ast_append_child(AstNode *parent, const char *text, + void *ptr, void (*inspect)(AstNode*)) +{ + if (ptr) { + AstNode *child = ast_new(parent, parent->childnodes->len, + text, ptr, inspect); + g_array_append_val(parent->childnodes, child); + } +} + +static inline +void ast_append_attribute(AstNode *parent, const char *text) +{ + AstNode *child = ast_new(parent, parent->childnodes->len, text, NULL, NULL); + g_array_append_val(parent->childnodes, child); +} + +#endif /* _ast_h_*/ diff --git a/ast-view.c b/ast-view.c new file mode 100644 index 0000000..40bf060 --- /dev/null +++ b/ast-view.c @@ -0,0 +1,48 @@ + +#include +#include "ast-model.h" +#include "ast-inspect.h" + +GtkWidget * +create_view_and_model (void *ptr) +{ + GtkTreeViewColumn *text; + GtkCellRenderer *renderer; + AstNode *root; + GtkWidget *view; + + root = ast_new(NULL, 0, "", ptr, inspect_symbol_list); + + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(root)); + + g_object_unref(root); /* destroy store automatically with view */ + + renderer = gtk_cell_renderer_text_new(); + text = gtk_tree_view_column_new_with_attributes("Node", renderer, + "text", AST_COL_NAME, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), text); + + return view; +} + +void +treeview_main (struct symbol_list *syms) +{ + GtkWidget *window, *view, *scrollwin; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW(window), 400, 600); + g_signal_connect(window, "delete_event", gtk_main_quit, NULL); + + scrollwin = gtk_scrolled_window_new(NULL,NULL); + + view = create_view_and_model(syms); + + gtk_container_add(GTK_CONTAINER(scrollwin), view); + gtk_container_add(GTK_CONTAINER(window), scrollwin); + + gtk_widget_show_all(window); + + gtk_main(); +} diff --git a/ast-view.h b/ast-view.h new file mode 100644 index 0000000..da8f5f5 --- /dev/null +++ b/ast-view.h @@ -0,0 +1,7 @@ + +#include +#include "lib.h" + +extern void treeview_main(struct symbol_list *syms); + + -- cgit v1.2.3-65-gdbad