1 /*
2  *   ast-model.c
3  *
4  *   A custom tree model to simplify viewing of AST objects.
5  *   Modify from the Gtk+ tree view tutorial, custom-list.c
6  *   by Tim-Philipp Mueller < tim at centricular dot net >
7  *
8  *   Copyright (C) 2010 Christopher Li
9  */
10 
11 
12 #include "ast-model.h"
13 #include "stdint.h"
14 
15 /* boring declarations of local functions */
16 
17 static void ast_init(AstNode *pkg_tree);
18 static void ast_class_init(AstNodeClass *klass);
19 static void ast_tree_model_init(GtkTreeModelIface *iface);
20 static void ast_finalize(GObject *object);
21 static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model);
22 static gint ast_get_n_columns(GtkTreeModel *tree_model);
23 static GType ast_get_column_type(GtkTreeModel *tree_model, gint index);
24 static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
25 				  GtkTreePath *path);
26 static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter);
27 static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
28 			       gint column, GValue *value);
29 static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
30 static gboolean ast_iter_children(GtkTreeModel *tree_model,
31                                        GtkTreeIter *iter,
32                                        GtkTreeIter *parent);
33 static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
34 static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
35 static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
36                                         GtkTreeIter *parent, gint n);
37 static gboolean ast_iter_parent(GtkTreeModel *tree_model,
38                                      GtkTreeIter *iter,
39                                      GtkTreeIter *child);
40 
41 static GObjectClass *parent_class = NULL;  /* GObject stuff - nothing to worry about */
42 
43 static inline
inspect_child_node(AstNode * node)44 void inspect_child_node(AstNode *node)
45 {
46 	if (node->inspect) {
47 		node->inspect(node);
48 		node->inspect = NULL;
49 	}
50 }
51 
52 
53 static inline
ast_nth_child(AstNode * node,int n)54 AstNode* ast_nth_child(AstNode *node, int n)
55 {
56 	if (!node)
57 		return NULL;
58 
59 	inspect_child_node(node);
60 
61 	if (n >= node->childnodes->len)
62 		return NULL;
63 	return g_array_index(node->childnodes, AstNode *, n);
64 }
65 
66 
67 static inline
ast_set_iter(GtkTreeIter * iter,AstNode * node)68 gboolean ast_set_iter(GtkTreeIter *iter, AstNode *node)
69 {
70 	iter->user_data = node;
71 	iter->user_data2 = iter->user_data3 = NULL;
72 	return node != NULL;
73 }
74 
75 
76 /*****************************************************************************
77  *
78  *  ast_get_type: here we register our new type and its interfaces
79  *                with the type system. If you want to implement
80  *                additional interfaces like GtkTreeSortable, you
81  *                will need to do it here.
82  *
83  *****************************************************************************/
84 
85 GType
ast_get_type(void)86 ast_get_type (void)
87 {
88 	static GType ast_type = 0;
89 	static const GTypeInfo ast_info = {
90 		sizeof (AstNodeClass),
91 		NULL,                                         /* base_init */
92 		NULL,                                         /* base_finalize */
93 		(GClassInitFunc) ast_class_init,
94 		NULL,                                         /* class finalize */
95 		NULL,                                         /* class_data */
96 		sizeof (AstNode),
97 		0,                                           /* n_preallocs */
98 		(GInstanceInitFunc) ast_init
99 	};
100 	static const GInterfaceInfo tree_model_info = {
101 		(GInterfaceInitFunc) ast_tree_model_init,
102 		NULL,
103 		NULL
104 	};
105 
106 
107 
108 	if (ast_type)
109 		return ast_type;
110 
111 	/* Some boilerplate type registration stuff */
112 	ast_type = g_type_register_static(G_TYPE_OBJECT, "AstNode",
113 						&ast_info, (GTypeFlags)0);
114 
115 	/* Here we register our GtkTreeModel interface with the type system */
116 	g_type_add_interface_static(ast_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
117 
118 	return ast_type;
119 }
120 
121 
122 /*****************************************************************************
123  *
124  *  ast_class_init: more boilerplate GObject/GType stuff.
125  *                  Init callback for the type system,
126  *                  called once when our new class is created.
127  *
128  *****************************************************************************/
129 
130 static void
ast_class_init(AstNodeClass * klass)131 ast_class_init (AstNodeClass *klass)
132 {
133 	GObjectClass *object_class;
134 
135 	parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
136 	object_class = (GObjectClass*) klass;
137 
138 	object_class->finalize = ast_finalize;
139 }
140 
141 /*****************************************************************************
142  *
143  *  ast_tree_model_init: init callback for the interface registration
144  *                       in ast_get_type. Here we override
145  *                       the GtkTreeModel interface functions that
146  *                       we implement.
147  *
148  *****************************************************************************/
149 
150 static void
ast_tree_model_init(GtkTreeModelIface * iface)151 ast_tree_model_init (GtkTreeModelIface *iface)
152 {
153 	iface->get_flags       = ast_get_flags;
154 	iface->get_n_columns   = ast_get_n_columns;
155 	iface->get_column_type = ast_get_column_type;
156 	iface->get_iter        = ast_get_iter;
157 	iface->get_path        = ast_get_path;
158 	iface->get_value       = ast_get_value;
159 	iface->iter_next       = ast_iter_next;
160 	iface->iter_children   = ast_iter_children;
161 	iface->iter_has_child  = ast_iter_has_child;
162 	iface->iter_n_children = ast_iter_n_children;
163 	iface->iter_nth_child  = ast_iter_nth_child;
164 	iface->iter_parent     = ast_iter_parent;
165 }
166 
167 
168 /*****************************************************************************
169  *
170  *  ast_init: this is called every time a new ast node object
171  *            instance is created (we do that in ast_new).
172  *            Initialise the list structure's fields here.
173  *
174  *****************************************************************************/
175 
176 static void
ast_init(AstNode * node)177 ast_init (AstNode *node)
178 {
179 	node->childnodes = g_array_new(FALSE, TRUE, sizeof(AstNode *));
180 	node->stamp    = g_random_int(); /* Random int to check whether iters belong to out model */
181 }
182 
183 
184 /*****************************************************************************
185  *
186  *  ast_finalize: this is called just before an ast node is
187  *                destroyed. Free dynamically allocated memory here.
188  *
189  *****************************************************************************/
190 
191 static void
ast_finalize(GObject * object)192 ast_finalize (GObject *object)
193 {
194 	/*  AstNode *node = AST_NODE(object); */
195 
196 	/* FIXME: free all node memory */
197 
198 	/* must chain up - finalize parent */
199 	(* parent_class->finalize) (object);
200 }
201 
202 
203 /*****************************************************************************
204  *
205  *  ast_get_flags: tells the rest of the world whether our tree model
206  *                 has any special characteristics. In our case,
207  *                 we have a list model (instead of a tree), and each
208  *                 tree iter is valid as long as the row in question
209  *                 exists, as it only contains a pointer to our struct.
210  *
211  *****************************************************************************/
212 
213 static GtkTreeModelFlags
ast_get_flags(GtkTreeModel * tree_model)214 ast_get_flags(GtkTreeModel *tree_model)
215 {
216 	return (GTK_TREE_MODEL_ITERS_PERSIST);
217 }
218 
219 
220 /*****************************************************************************
221  *
222  *  ast_get_n_columns: tells the rest of the world how many data
223  *                          columns we export via the tree model interface
224  *
225  *****************************************************************************/
226 
227 static gint
ast_get_n_columns(GtkTreeModel * tree_model)228 ast_get_n_columns(GtkTreeModel *tree_model)
229 {
230 	return 1;
231 }
232 
233 
234 /*****************************************************************************
235  *
236  *  ast_get_column_type: tells the rest of the world which type of
237  *                       data an exported model column contains
238  *
239  *****************************************************************************/
240 
241 static GType
ast_get_column_type(GtkTreeModel * tree_model,gint index)242 ast_get_column_type(GtkTreeModel *tree_model,
243                          gint index)
244 {
245 	return G_TYPE_STRING;
246 }
247 
248 
249 /*****************************************************************************
250  *
251  *  ast_get_iter: converts a tree path (physical position) into a
252  *                tree iter structure (the content of the iter
253  *                fields will only be used internally by our model).
254  *                We simply store a pointer to our AstNodeItem
255  *                structure that represents that row in the tree iter.
256  *
257  *****************************************************************************/
258 
259 static gboolean
ast_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)260 ast_get_iter(GtkTreeModel *tree_model,
261                   GtkTreeIter  *iter,
262                   GtkTreePath  *path)
263 {
264 	AstNode    *node;
265 	gint          *indices, depth;
266 	int i;
267 
268 	node = AST_NODE(tree_model);
269 	indices = gtk_tree_path_get_indices(path);
270 	depth   = gtk_tree_path_get_depth(path);
271 
272 	for (i = 0; i < depth; i++)
273 		node = ast_nth_child(node, indices[i]);
274 
275 	return ast_set_iter(iter, node);
276 }
277 
278 
279 /*****************************************************************************
280  *
281  *  ast_get_path: converts a tree iter into a tree path (ie. the
282  *                physical position of that row in the list).
283  *
284  *****************************************************************************/
285 
286 static GtkTreePath *
ast_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)287 ast_get_path(GtkTreeModel *tree_model,
288                   GtkTreeIter  *iter)
289 {
290 	GtkTreePath  *path;
291 	AstNode   *root = AST_NODE(tree_model);
292 	AstNode   *node = AST_NODE(iter->user_data);
293 
294 	path = gtk_tree_path_new();
295 	while (node != root) {
296 		gtk_tree_path_prepend_index(path, node->index);
297 		node = node->parent;
298 	}
299 	return path;
300 }
301 
302 
303 /*****************************************************************************
304  *
305  *  ast_get_value: Returns a row's exported data columns
306  *                 (_get_value is what gtk_tree_model_get uses)
307  *
308  *****************************************************************************/
309 
310 static void
ast_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)311 ast_get_value(GtkTreeModel *tree_model,
312                    GtkTreeIter  *iter,
313                    gint          column,
314                    GValue       *value)
315 {
316 	AstNode    *node = iter->user_data;
317 
318 	g_assert(AST_IS_NODE(tree_model));
319 	if (column != 1)
320 		return;
321 
322 	inspect_child_node(node);
323 
324 	g_value_init(value, G_TYPE_STRING);
325 	g_value_set_string(value, node->text);
326 	return;
327 }
328 
329 
330 /*****************************************************************************
331  *
332  *  ast_iter_next: Takes an iter structure and sets it to point
333  *                 to the next row.
334  *
335  *****************************************************************************/
336 
337 static gboolean
ast_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)338 ast_iter_next(GtkTreeModel  *tree_model,
339                    GtkTreeIter   *iter)
340 {
341 	AstNode    *node = iter->user_data;
342 
343 	g_assert(AST_IS_NODE (tree_model));
344 
345 	node = ast_nth_child(node->parent, node->index + 1);
346 	return ast_set_iter(iter, node);
347 }
348 
349 
350 /*****************************************************************************
351  *
352  *  ast_iter_children: Returns TRUE or FALSE depending on whether
353  *                     the row specified by 'parent' has any children.
354  *                     If it has children, then 'iter' is set to
355  *                     point to the first child. Special case: if
356  *                     'parent' is NULL, then the first top-level
357  *                     row should be returned if it exists.
358  *
359  *****************************************************************************/
360 
361 static gboolean
ast_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)362 ast_iter_children(GtkTreeModel *tree_model,
363                        GtkTreeIter  *iter,
364                        GtkTreeIter  *parent)
365 {
366 	return ast_iter_nth_child(tree_model, iter, parent, 0);
367 }
368 
369 
370 /*****************************************************************************
371  *
372  *  ast_iter_has_child: Returns TRUE or FALSE depending on whether
373  *                      the row specified by 'iter' has any children.
374  *                      We only have a list and thus no children.
375  *
376  *****************************************************************************/
377 
378 static gboolean
ast_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)379 ast_iter_has_child (GtkTreeModel *tree_model,
380                          GtkTreeIter  *iter)
381 {
382 	AstNode    *node = iter->user_data;
383 	inspect_child_node(node);
384 	return node->childnodes->len > 0;
385 }
386 
387 
388 /*****************************************************************************
389  *
390  *  ast_iter_n_children: Returns the number of children the row
391  *                       specified by 'iter' has. This is usually 0,
392  *                       as we only have a list and thus do not have
393  *                       any children to any rows. A special case is
394  *                       when 'iter' is NULL, in which case we need
395  *                       to return the number of top-level node,
396  *                       ie. the number of rows in our list.
397  *
398  *****************************************************************************/
399 
400 static gint
ast_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)401 ast_iter_n_children (GtkTreeModel *tree_model,
402                           GtkTreeIter  *iter)
403 {
404 	AstNode  *node = iter ? iter->user_data
405 				: AST_NODE(tree_model);
406 
407 	inspect_child_node(node);
408 	return node->childnodes->len;
409 }
410 
411 
412 /*****************************************************************************
413  *
414  *  ast_iter_nth_child: If the row specified by 'parent' has any
415  *                      children, set 'iter' to the n-th child and
416  *                      return TRUE if it exists, otherwise FALSE.
417  *                      A special case is when 'parent' is NULL, in
418  *                      which case we need to set 'iter' to the n-th
419  *                      row if it exists.
420  *
421  *****************************************************************************/
422 
423 static gboolean
ast_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)424 ast_iter_nth_child(GtkTreeModel *tree_model,
425                         GtkTreeIter  *iter,
426                         GtkTreeIter  *parent,
427                         gint          n)
428 {
429 	AstNode    *node = parent ? parent->user_data : (AstNode*) tree_model;
430 	GArray *array = node->childnodes;
431 	if (n >= array->len)
432 		return FALSE;
433 	iter->user_data = g_array_index(array, AstNode *, n);
434 	return TRUE;
435 }
436 
437 
438 /*****************************************************************************
439  *
440  *  ast_iter_parent: Point 'iter' to the parent node of 'child'. As
441  *                   we have a list and thus no children and no
442  *                   parents of children, we can just return FALSE.
443  *
444  *****************************************************************************/
445 
446 static gboolean
ast_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)447 ast_iter_parent (GtkTreeModel *tree_model,
448                       GtkTreeIter  *iter,
449                       GtkTreeIter  *child)
450 {
451 	AstNode *node = (AstNode *) child->user_data;
452 	iter->user_data = node->parent;
453 	return node->parent != NULL;
454 }
455 
456 
457 AstNode *
ast_new(AstNode * parent,int index,const char * text,void * ptr,void (* inspect)(AstNode *))458 ast_new (AstNode *parent, int index, const char *text, void *ptr, void (*inspect)(AstNode*))
459 {
460 	AstNode *node = (AstNode*) g_object_new (AST_TYPE_NODE, NULL);
461 	g_assert(node != NULL);
462 	node->parent = parent;
463 	node->index = index;
464 	node->text = text;
465 	node->inspect = inspect;
466 	node->ptr = ptr;
467 	return node;
468 }
469 
470