aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Li <sparse@chrisli.org>2010-03-30 15:48:38 -0700
committerChristopher Li <sparse@chrisli.org>2010-07-13 19:00:14 +0000
commit5e5517211f51345ea0cd62fe6953e108d0f74aed (patch)
treec2077b3c389f2a8880cc53c6b34df89a66ebe7c0
parent8832df8bc4edf263ad1753b82922d72e1c72e1bd (diff)
downloadsparse-5e5517211f51345ea0cd62fe6953e108d0f74aed.tar.gz
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 <sparse@chrisli.org>
-rw-r--r--ast-model.c468
-rw-r--r--ast-model.h88
-rw-r--r--ast-view.c48
-rw-r--r--ast-view.h7
4 files changed, 611 insertions, 0 deletions
diff --git a/ast-model.c b/ast-model.c
new file mode 100644
index 00000000..f5163408
--- /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 00000000..2eea056c
--- /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 <stdint.h>
+#include <gtk/gtk.h>
+#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 00000000..40bf060b
--- /dev/null
+++ b/ast-view.c
@@ -0,0 +1,48 @@
+
+#include <stdlib.h>
+#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 00000000..da8f5f50
--- /dev/null
+++ b/ast-view.h
@@ -0,0 +1,7 @@
+
+#include <gtk/gtk.h>
+#include "lib.h"
+
+extern void treeview_main(struct symbol_list *syms);
+
+