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