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