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
17static void ast_init(AstNode *pkg_tree);
18static void ast_class_init(AstNodeClass *klass);
19static void ast_tree_model_init(GtkTreeModelIface *iface);
20static void ast_finalize(GObject *object);
21static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model);
22static gint ast_get_n_columns(GtkTreeModel *tree_model);
23static GType ast_get_column_type(GtkTreeModel *tree_model, gint index);
24static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
25				  GtkTreePath *path);
26static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter);
27static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
28			       gint column, GValue *value);
29static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
30static gboolean ast_iter_children(GtkTreeModel *tree_model,
31                                       GtkTreeIter *iter,
32                                       GtkTreeIter *parent);
33static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
34static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
35static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
36                                        GtkTreeIter *parent, gint n);
37static gboolean ast_iter_parent(GtkTreeModel *tree_model,
38                                     GtkTreeIter *iter,
39                                     GtkTreeIter *child);
40
41static GObjectClass *parent_class = NULL;  /* GObject stuff - nothing to worry about */
42
43static inline
44void inspect_child_node(AstNode *node)
45{
46	if (node->inspect) {
47		node->inspect(node);
48		node->inspect = NULL;
49	}
50}
51
52
53static inline
54AstNode* 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
67static inline
68gboolean 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
85GType
86ast_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
130static void
131ast_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
150static void
151ast_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
176static void
177ast_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
191static void
192ast_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
213static GtkTreeModelFlags
214ast_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
227static gint
228ast_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
241static GType
242ast_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
259static gboolean
260ast_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
286static GtkTreePath *
287ast_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
310static void
311ast_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
337static gboolean
338ast_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
361static gboolean
362ast_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
378static gboolean
379ast_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
400static gint
401ast_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
423static gboolean
424ast_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
446static gboolean
447ast_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
457AstNode *
458ast_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