1 /*
2  *
3  *   Copyright (C) 2012-2018 by C.H. Huang
4  *   plushuang.tw@gmail.com
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  *  ---
21  *
22  *  In addition, as a special exception, the copyright holders give
23  *  permission to link the code of portions of this program with the
24  *  OpenSSL library under certain conditions as described in each
25  *  individual source file, and distribute linked combinations
26  *  including the two.
27  *  You must obey the GNU Lesser General Public License in all respects
28  *  for all of the code used other than OpenSSL.  If you modify
29  *  file(s) with this exception, you may extend this exception to your
30  *  version of the file(s), but you are not obligated to do so.  If you
31  *  do not wish to do so, delete this exception statement from your
32  *  version.  If you delete this exception statement from all source
33  *  files in the program, then also delete it here.
34  *
35  */
36 
37 /*
38  * This file base on GTK+ 2.0 Tree View Tutorial - custom-list.c
39  */
40 
41 #include <UgtkNodeList.h>
42 #define NODE_LIST_N_COLUMNS    1
43 
44 /* boring declarations of local functions */
45 
46 static void      init (UgtkNodeList* ugtree);
47 
48 static void      class_init (UgtkNodeListClass *klass);
49 
50 static void      tree_model_init (GtkTreeModelIface *iface);
51 
52 static void      finalize (GObject* object);
53 
54 static GtkTreeModelFlags  get_flags (GtkTreeModel* tree_model);
55 
56 static gint      get_n_columns (GtkTreeModel* tree_model);
57 
58 static GType     get_column_type (GtkTreeModel* tree_model, gint index);
59 
60 static gboolean  get_iter (GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreePath* path);
61 
62 static GtkTreePath*  get_path (GtkTreeModel* tree_model, GtkTreeIter* iter);
63 
64 static void      get_value (GtkTreeModel* tree_model, GtkTreeIter* iter, gint column, GValue* value);
65 
66 static gboolean  iter_next (GtkTreeModel* tree_model, GtkTreeIter* iter);
67 
68 static gboolean  iter_children (GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreeIter* parent);
69 
70 static gboolean  iter_has_child (GtkTreeModel* tree_model, GtkTreeIter* iter);
71 
72 static gint      iter_n_children (GtkTreeModel* tree_model, GtkTreeIter* iter);
73 
74 static gboolean  iter_nth_child (GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreeIter* parent, gint n);
75 
76 static gboolean  iter_parent (GtkTreeModel* tree_model, GtkTreeIter* iter, GtkTreeIter* child);
77 
78 
79 
80 static GObjectClass* parent_class = NULL;  /* GObject stuff - nothing to worry about */
81 
82 
83 /*****************************************************************************
84  *
85  *  class_init: more boilerplate GObject/GType stuff.
86  *              Init callback for the type system,
87  *              called once when our new class is created.
88  *
89  *****************************************************************************/
90 
class_init(UgtkNodeListClass * klass)91 static void  class_init (UgtkNodeListClass* klass)
92 {
93 	GObjectClass *object_class;
94 
95 	parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
96 	object_class = (GObjectClass*) klass;
97 
98 	object_class->finalize = finalize;
99 }
100 
101 /*****************************************************************************
102  *
103  *  tree_model_init: init callback for the interface registration
104  *                   in ugtk_node_list_get_type. Here we override
105  *                   the GtkTreeModel interface functions that
106  *                   we implement.
107  *
108  *****************************************************************************/
109 
tree_model_init(GtkTreeModelIface * iface)110 static void  tree_model_init (GtkTreeModelIface* iface)
111 {
112 	iface->get_flags       = get_flags;
113 	iface->get_n_columns   = get_n_columns;
114 	iface->get_column_type = get_column_type;
115 	iface->get_iter        = get_iter;
116 	iface->get_path        = get_path;
117 	iface->get_value       = get_value;
118 	iface->iter_next       = iter_next;
119 	iface->iter_children   = iter_children;
120 	iface->iter_has_child  = iter_has_child;
121 	iface->iter_n_children = iter_n_children;
122 	iface->iter_nth_child  = iter_nth_child;
123 	iface->iter_parent     = iter_parent;
124 }
125 
126 
127 /*****************************************************************************
128  *
129  *  init: this is called everytime a new object object
130  *        instance is created (we do that in g_object_new).
131  *        Initialise the list structure's fields here.
132  *
133  *****************************************************************************/
134 
init(UgtkNodeList * ulist)135 static void  init (UgtkNodeList* ulist)
136 {
137 	ulist->stamp = g_random_int();  /* Random int to check whether an iter belongs to our model */
138 }
139 
140 
141 /*****************************************************************************
142  *
143  *  finalize: this is called just before a object is
144  *            destroyed. Free dynamically allocated memory here.
145  *
146  *****************************************************************************/
147 
finalize(GObject * object)148 static void  finalize (GObject* object)
149 {
150 //	UgtkNodeList *ulist = UGTK_NODE_LIST(object);
151 
152 	// must chain up - finalize parent
153 	(*parent_class->finalize) (object);
154 }
155 
156 
157 /*****************************************************************************
158  *
159  *  get_flags: tells the rest of the world whether our tree model
160  *             has any special characteristics. In our case,
161  *             we have a list model (instead of a tree), and each
162  *             tree iter is valid as long as the row in question
163  *             exists, as it only contains a pointer to our struct.
164  *
165  *****************************************************************************/
166 
get_flags(GtkTreeModel * tree_model)167 static GtkTreeModelFlags  get_flags (GtkTreeModel* tree_model)
168 {
169 	g_return_val_if_fail (UGTK_IS_NODE_LIST(tree_model), (GtkTreeModelFlags)0);
170 
171 	return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
172 }
173 
174 
175 /*****************************************************************************
176  *
177  *  get_n_columns: tells the rest of the world how many data
178  *                 columns we export via the tree model interface
179  *
180  *****************************************************************************/
181 
get_n_columns(GtkTreeModel * tree_model)182 static gint  get_n_columns (GtkTreeModel* tree_model)
183 {
184 	g_return_val_if_fail (UGTK_IS_NODE_LIST(tree_model), 0);
185 
186 	return NODE_LIST_N_COLUMNS;
187 }
188 
189 
190 /*****************************************************************************
191  *
192  *  get_column_type: tells the rest of the world which type of
193  *                   data an exported model column contains
194  *
195  *****************************************************************************/
196 
get_column_type(GtkTreeModel * tree_model,gint index)197 static GType  get_column_type (GtkTreeModel* tree_model,
198                                gint          index)
199 {
200 	g_return_val_if_fail (UGTK_IS_NODE_LIST(tree_model), G_TYPE_INVALID);
201 	g_return_val_if_fail (index < NODE_LIST_N_COLUMNS && index >= 0, G_TYPE_INVALID);
202 
203 	return G_TYPE_POINTER;
204 }
205 
206 
207 /*****************************************************************************
208  *
209  *  get_iter: converts a tree path (physical position) into a
210  *            tree iter structure (the content of the iter
211  *            fields will only be used internally by our model).
212  *            We simply store a pointer to our CustomRecord
213  *            structure that represents that row in the tree iter.
214  *
215  *****************************************************************************/
216 
get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)217 static gboolean  get_iter (GtkTreeModel* tree_model,
218                            GtkTreeIter*  iter,
219                            GtkTreePath*  path)
220 {
221 	UgtkNodeList*  ulist;
222 	UgetNode*      node;
223 	gint*          indices;
224 	gint           depth;
225 
226 	g_assert (UGTK_IS_NODE_LIST(tree_model));
227 	g_assert (path != NULL);
228 
229 	ulist = UGTK_NODE_LIST(tree_model);
230 	if (ulist->root == NULL)
231 		return FALSE;
232 
233 	indices = gtk_tree_path_get_indices(path);
234 	depth   = gtk_tree_path_get_depth(path);
235 
236 	g_assert (depth == 1);
237 
238 	if (ulist->root_visible) {
239 		if (indices[0] == 0)
240 			node = ulist->root;
241 		else if (indices[0] <= ulist->n_fake)
242 			node = uget_node_nth_fake (ulist->root, indices[0] - 1);
243 		else
244 			return FALSE;
245 	}
246 	else {
247 		if (indices[0] < ulist->n_fake)
248 			node = uget_node_nth_fake (ulist->root, indices[0]);
249 		else
250 			return FALSE;
251 	}
252 
253 	if (node == NULL)
254 		return FALSE;
255 	else {
256 		// We store a pointer to UgNode in the iter
257 		iter->stamp      = ulist->stamp;
258 		iter->user_data  = node;
259 		iter->user_data2 = NULL;   /* unused */
260 		iter->user_data3 = NULL;   /* unused */
261 		return TRUE;
262 	}
263 }
264 
265 
266 /*****************************************************************************
267  *
268  *  get_path: converts a tree iter into a tree path (ie. the
269  *            physical position of that row in the list).
270  *
271  *****************************************************************************/
272 
get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)273 static GtkTreePath*  get_path (GtkTreeModel* tree_model,
274                                GtkTreeIter*  iter)
275 {
276 	GtkTreePath*  path;
277 	UgtkNodeList* ulist;
278 	UgetNode*     node;
279 	gint          n;
280 
281 	g_return_val_if_fail (UGTK_IS_NODE_LIST(tree_model), NULL);
282 	g_return_val_if_fail (iter != NULL,             NULL);
283 	g_return_val_if_fail (iter->user_data != NULL,  NULL);
284 
285 	node = iter->user_data;
286 	path = gtk_tree_path_new();
287 	ulist = UGTK_NODE_LIST (tree_model);
288 
289 	if (ulist->root_visible == FALSE)
290 		n = uget_node_fake_position (node->real, node);
291 	else {
292 		if (ulist->root == node)
293 			n = 0;
294 		else
295 			n = uget_node_fake_position (node->real, node) + 1;
296 	}
297 
298 	gtk_tree_path_prepend_index (path, n);
299 	return path;
300 }
301 
302 
303 /*****************************************************************************
304  *
305  *  get_value: Returns a row's exported data columns
306  *             (_get_value is what gtk_tree_model_get uses)
307  *
308  *****************************************************************************/
309 
get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)310 static void  get_value (GtkTreeModel* tree_model,
311                         GtkTreeIter*  iter,
312                         gint          column,
313                         GValue*       value)
314 {
315 //	UgtkNodeList*  ulist;
316 
317 	g_return_if_fail (UGTK_IS_NODE_LIST (tree_model));
318 	g_return_if_fail (iter != NULL);
319 	g_return_if_fail (column < NODE_LIST_N_COLUMNS);
320 
321 	g_value_init (value, G_TYPE_POINTER);
322 
323 //	ulist = UGTK_NODE_LIST (tree_model);
324 	g_value_set_pointer (value, iter->user_data);
325 }
326 
327 
328 /*****************************************************************************
329  *
330  *  iter_next: Takes an iter structure and sets it to point
331  *             to the next row.
332  *
333  *****************************************************************************/
334 
iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)335 static gboolean  iter_next (GtkTreeModel* tree_model,
336                             GtkTreeIter*  iter)
337 {
338 	UgtkNodeList* ulist;
339 	UgetNode*     node;
340 
341 	g_return_val_if_fail (UGTK_IS_NODE_LIST (tree_model), FALSE);
342 
343 	ulist = UGTK_NODE_LIST(tree_model);
344 	if (ulist->root == NULL)
345 		return FALSE;
346 
347 	node = iter->user_data;
348 	if (ulist->root_visible && ulist->root == node)
349 		node = node->fake;
350 	else
351 		node = node->peer;
352 
353 	if (node == NULL || uget_node_fake_position (node->real, node) >= ulist->n_fake)
354 		return FALSE;
355 	else {
356 		iter->stamp     = ulist->stamp;
357 		iter->user_data = node;
358 		return TRUE;
359 	}
360 }
361 
362 
363 /*****************************************************************************
364  *
365  *  iter_children: Returns TRUE or FALSE depending on whether
366  *                 the row specified by 'parent' has any children.
367  *                 If it has children, then 'iter' is set to
368  *                 point to the first child. Special case: if
369  *                 'parent' is NULL, then the first top-level
370  *                 row should be returned if it exists.
371  *
372  *****************************************************************************/
373 
iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)374 static gboolean  iter_children (GtkTreeModel* tree_model,
375                                 GtkTreeIter*  iter,
376                                 GtkTreeIter*  parent)
377 {
378 	UgtkNodeList*  ulist;
379 	UgetNode*      node;
380 
381 	g_return_val_if_fail (UGTK_IS_NODE_LIST (tree_model), FALSE);
382 	g_return_val_if_fail (parent == NULL, FALSE);
383 
384 	ulist = UGTK_NODE_LIST (tree_model);
385 	if (ulist->root == NULL)
386 		return FALSE;
387 
388 	if (ulist->root_visible)
389 		node = ulist->root;
390 	else
391 		node = ulist->root->fake;
392 
393 	// Set iter to first child item.
394 	if (node == NULL)
395 		return FALSE;
396 	else {
397 		iter->stamp     = ulist->stamp;
398 		iter->user_data = node;
399 		return TRUE;
400 	}
401 }
402 
403 
404 /*****************************************************************************
405  *
406  *  iter_has_child: Returns TRUE or FALSE depending on whether
407  *                  the row specified by 'iter' has any children.
408  *                  We only have a list and thus no children.
409  *
410  *****************************************************************************/
411 
iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)412 static gboolean  iter_has_child (GtkTreeModel* tree_model,
413                                  GtkTreeIter*  iter)
414 {
415 	return FALSE;
416 }
417 
418 
419 /*****************************************************************************
420  *
421  *  iter_n_children: Returns the number of children the row
422  *                   specified by 'iter' has. This is usually 0,
423  *                   as we only have a list and thus do not have
424  *                   any children to any rows. A special case is
425  *                   when 'iter' is NULL, in which case we need
426  *                   to return the number of top-level nodes,
427  *                   ie. the number of rows in our list.
428  *
429  *****************************************************************************/
430 
iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)431 static gint  iter_n_children (GtkTreeModel* tree_model,
432                               GtkTreeIter*  iter)
433 {
434 	UgtkNodeList*  ulist;
435 	UgetNode*      node;
436 	gint           n = 0;
437 
438 	g_return_val_if_fail (UGTK_IS_NODE_LIST (tree_model), -1);
439 	g_return_val_if_fail (iter == NULL, -1);
440 
441 	ulist = UGTK_NODE_LIST (tree_model);
442 	if (ulist->root == NULL)
443 		return 0;
444 
445 	node = iter->user_data;
446 	for (n = 0, node = node->fake;  node;  node = node->peer)
447 		n++;
448 
449 	if (n > ulist->n_fake)
450 		n = ulist->n_fake;
451 	if (ulist->root_visible)
452 		n++;
453 
454 	return n;
455 }
456 
457 
458 /*****************************************************************************
459  *
460  *  iter_nth_child: If the row specified by 'parent' has any
461  *                  children, set 'iter' to the n-th child and
462  *                  return TRUE if it exists, otherwise FALSE.
463  *                  A special case is when 'parent' is NULL, in
464  *                  which case we need to set 'iter' to the n-th
465  *                  row if it exists.
466  *
467  *****************************************************************************/
468 
iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)469 static gboolean  iter_nth_child (GtkTreeModel* tree_model,
470                                  GtkTreeIter*  iter,
471                                  GtkTreeIter*  parent,
472                                  gint          n)
473 {
474 	UgtkNodeList*  ulist;
475 	UgetNode*      node;
476 
477 	g_return_val_if_fail (UGTK_IS_NODE_LIST (tree_model), FALSE);
478 	g_return_val_if_fail (parent == NULL, FALSE);
479 
480 	ulist = UGTK_NODE_LIST (tree_model);
481 	if (ulist->root == NULL)
482 		return FALSE;
483 
484 	if (ulist->root_visible) {
485 		if (n == 0)
486 			node = ulist->root;
487 		else if (n <= ulist->n_fake)
488 			node = uget_node_nth_fake (ulist->root, n - 1);
489 		else
490 			return FALSE;
491 	}
492 	else {
493 		if (n < ulist->n_fake)
494 			node = uget_node_nth_fake (ulist->root, n);
495 		else
496 			return FALSE;
497 	}
498 
499 	if (node == NULL)
500 		return FALSE;
501 	else {
502 		iter->stamp = ulist->stamp;
503 		iter->user_data = node;
504 		return TRUE;
505 	}
506 }
507 
508 
509 /*****************************************************************************
510  *
511  *  iter_parent: Point 'iter' to the parent node of 'child'. As
512  *               we have a list and thus no children and no
513  *               parents of children, we can just return FALSE.
514  *
515  *****************************************************************************/
516 
iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)517 static gboolean  iter_parent (GtkTreeModel* tree_model,
518                               GtkTreeIter*  iter,
519                               GtkTreeIter*  child)
520 {
521 	return FALSE;
522 }
523 
524 
525 /*****************************************************************************
526  *
527  *  ugtk_node_list_get_type: here we register our new type and its interfaces
528  *                      with the type system. If you want to implement
529  *                      additional interfaces like GtkTreeSortable, you
530  *                      will need to do it here.
531  *
532  *****************************************************************************/
533 
ugtk_node_list_get_type(void)534 GType  ugtk_node_list_get_type (void)
535 {
536 	static GType ugtk_node_list_type = 0;
537 
538 	/* Some boilerplate type registration stuff */
539 	if (ugtk_node_list_type == 0) {
540 		static const GTypeInfo ugtk_node_list_info =
541 		{
542 			sizeof (UgtkNodeListClass),
543 			NULL,                                         /* base_init */
544 			NULL,                                         /* base_finalize */
545 			(GClassInitFunc) class_init,
546 			NULL,                                         /* class finalize */
547 			NULL,                                         /* class_data */
548 			sizeof (UgtkNodeList),
549 			0,                                           /* n_preallocs */
550 			(GInstanceInitFunc) init
551 		};
552 
553 		static const GInterfaceInfo tree_model_info =
554 		{
555 			(GInterfaceInitFunc) tree_model_init,
556 			NULL,
557 			NULL
558 		};
559 
560 		/* First register the new derived type with the GObject type system */
561 		ugtk_node_list_type = g_type_register_static (G_TYPE_OBJECT, "UgtkNodeList",
562 				&ugtk_node_list_info, (GTypeFlags)0);
563 
564 		/* Now register our GtkTreeModel interface with the type system */
565 		g_type_add_interface_static (ugtk_node_list_type,
566 				GTK_TYPE_TREE_MODEL, &tree_model_info);
567 	}
568 
569 	return ugtk_node_list_type;
570 }
571 
572 /*****************************************************************************
573  *
574  *  ugtk_node_list_new:  This is what you use in your own code to create a
575  *                  new tree model for you to use.
576  *
577  *****************************************************************************/
578 
ugtk_node_list_new(UgetNode * root,gint n_fake,gboolean root_visible)579 UgtkNodeList*  ugtk_node_list_new (UgetNode* root, gint n_fake, gboolean root_visible)
580 {
581 	UgtkNodeList* ulist;
582 
583 	ulist = (UgtkNodeList*) g_object_new (UGTK_TYPE_NODE_LIST, NULL);
584 	ulist->root = root;
585 	ulist->n_fake = n_fake;
586 	ulist->root_visible = root_visible;
587 
588 	g_assert( ulist != NULL );
589 	return ulist;
590 }
591