1 /* gtktreestore.c
2 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
3 *
4 * scptreestore.c
5 * Copyright 2013 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <gtk/gtk.h>
27
28 #include "scptreedata.h"
29 #include "scptreestore.h"
30
31 #if defined(ENABLE_NLS) && defined(GETTEXT_PACKAGE)
32 #define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties", (String))
33 #else
34 #define P_(String) (String)
35 #endif
36
37 #if !GLIB_CHECK_VERSION(2, 30, 0)
38 #define G_VALUE_INIT { 0, { { 0 } } }
39 #endif
40
41 #if !GTK_CHECK_VERSION(2, 90, 7)
42 #define gtk_get_debug_flags() (gtk_debug_flags + 0)
43 #endif
44
45 #ifdef G_DISABLE_CHECKS
46 #define VALIDATE_ONLY G_GNUC_UNUSED
47 #else
48 #define VALIDATE_ONLY
49 #endif
50
51 typedef struct _AElem AElem;
52
53 struct _AElem
54 {
55 AElem *parent;
56 GPtrArray *children;
57 ScpTreeData data[1];
58 };
59
60 struct _ScpTreeStorePrivate
61 {
62 gint stamp;
63 AElem *root;
64 GPtrArray *roar;
65 guint n_columns;
66 ScpTreeDataHeader *headers;
67 gint sort_column_id;
68 GtkSortType sort_order;
69 GtkTreeIterCompareFunc sort_func; /* current */
70 gboolean sublevels;
71 guint toplevel_reserved;
72 guint sublevel_reserved;
73 gboolean sublevel_discard;
74 gboolean columns_dirty;
75 };
76
77 #define VALID_ITER(iter, store) \
78 ((iter) && (iter)->user_data && (store)->priv->stamp == (iter)->stamp)
79 #define VALID_ITER_OR_NULL(iter, store) \
80 (!(iter) || ((iter)->user_data && (store)->priv->stamp == (iter)->stamp))
81 #define SCP_TREE_MODEL(store) ((GtkTreeModel *) store)
82
83 #define ITER_ARRAY(iter) ((GPtrArray *) ((iter)->user_data))
84 #define ITER_INDEX(iter) (GPOINTER_TO_INT((iter)->user_data2))
85 #define ITER_ELEM(iter) ((AElem *) ITER_ARRAY(iter)->pdata[ITER_INDEX(iter)])
86 #define ELEM_SIZE(n_columns) (sizeof(AElem) + ((n_columns) - 1) * sizeof(ScpTreeData))
87
88 /* Store */
89
scp_ptr_array_insert_val(GPtrArray * array,guint index,gpointer data)90 static void scp_ptr_array_insert_val(GPtrArray *array, guint index, gpointer data)
91 {
92 g_ptr_array_set_size(array, array->len + 1);
93 memmove(array->pdata + index + 1, array->pdata + index, (array->len - index - 1) *
94 sizeof(gpointer));
95 array->pdata[index] = data;
96 }
97
98 static void scp_free_element(ScpTreeStore *store, AElem *elem);
99
scp_free_array(ScpTreeStore * store,GPtrArray * array)100 static void scp_free_array(ScpTreeStore *store, GPtrArray *array)
101 {
102 if (array)
103 {
104 guint i;
105
106 for (i = 0; i < array->len; i++)
107 scp_free_element(store, (AElem *) array->pdata[i]);
108 g_ptr_array_free(array, TRUE);
109 }
110 }
111
scp_free_element(ScpTreeStore * store,AElem * elem)112 static void scp_free_element(ScpTreeStore *store, AElem *elem)
113 {
114 ScpTreeStorePrivate *priv = store->priv;
115 guint i;
116
117 scp_free_array(store, elem->children);
118
119 for (i = 0; i < priv->n_columns; i++)
120 scp_tree_data_free(elem->data + i, priv->headers[i].type);
121
122 g_slice_free1(ELEM_SIZE(priv->n_columns), elem);
123 }
124
scp_tree_store_new(gboolean sublevels,gint n_columns,...)125 ScpTreeStore *scp_tree_store_new(gboolean sublevels, gint n_columns, ...)
126 {
127 GType *types;
128 va_list ap;
129 gint i;
130 ScpTreeStore *store;
131
132 g_return_val_if_fail(n_columns > 0, NULL);
133 types = g_malloc(n_columns * sizeof(GType));
134
135 va_start(ap, n_columns);
136 for (i = 0; i < n_columns; i++)
137 types[i] = va_arg(ap, GType);
138 va_end(ap);
139
140 store = scp_tree_store_newv(sublevels, n_columns, types);
141 g_free(types);
142 return store;
143 }
144
scp_tree_store_newv(gboolean sublevels,gint n_columns,GType * types)145 ScpTreeStore *scp_tree_store_newv(gboolean sublevels, gint n_columns, GType *types)
146 {
147 ScpTreeStore *store;
148
149 g_return_val_if_fail(n_columns > 0, NULL);
150 store = g_object_new(SCP_TYPE_TREE_STORE, "sublevels", sublevels != FALSE, NULL);
151
152 if (!scp_tree_store_set_column_types(store, n_columns, types))
153 {
154 g_object_unref(store);
155 store = NULL;
156 }
157
158 return store;
159 }
160
161 #define scp_tree_model_compare_func ((GtkTreeIterCompareFunc) scp_tree_store_compare_func)
162
scp_tree_store_set_column_types(ScpTreeStore * store,gint n_columns,GType * types)163 gboolean scp_tree_store_set_column_types(ScpTreeStore *store, gint n_columns, GType *types)
164 {
165 ScpTreeStorePrivate *priv = store->priv;
166
167 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
168 g_return_val_if_fail(!priv->columns_dirty, FALSE);
169
170 if (priv->headers)
171 scp_tree_data_headers_free(priv->n_columns, priv->headers);
172
173 priv->headers = scp_tree_data_headers_new(n_columns, types, scp_tree_model_compare_func);
174 priv->n_columns = n_columns;
175 return TRUE;
176 }
177
scp_emit_reordered(ScpTreeStore * store,GtkTreeIter * iter,gint * new_order)178 static void scp_emit_reordered(ScpTreeStore *store, GtkTreeIter *iter, gint *new_order)
179 {
180 ScpTreeStorePrivate *priv = store->priv;
181 GtkTreePath *path;
182 GtkTreeIter parent;
183
184 if (iter->user_data == priv->root->children)
185 {
186 path = gtk_tree_path_new();
187 parent.stamp = priv->stamp;
188 parent.user_data = store->priv->roar;
189 parent.user_data2 = GINT_TO_POINTER(0);
190 }
191 else
192 {
193 scp_tree_store_iter_parent(store, &parent, iter);
194 path = scp_tree_store_get_path(store, &parent);
195 }
196
197 gtk_tree_model_rows_reordered(SCP_TREE_MODEL(store), path, &parent, new_order);
198 gtk_tree_path_free(path);
199 }
200
scp_move_element(ScpTreeStore * store,GPtrArray * array,GtkTreeIter * iter,guint new_pos,gboolean emit_reordered)201 static void scp_move_element(ScpTreeStore *store, GPtrArray *array, GtkTreeIter *iter,
202 guint new_pos, gboolean emit_reordered)
203 {
204 guint old_pos = ITER_INDEX(iter);
205
206 if (old_pos != new_pos)
207 {
208 gpointer data = array->pdata[old_pos];
209
210 if (new_pos < old_pos)
211 {
212 memmove(array->pdata + new_pos + 1, array->pdata + new_pos,
213 (old_pos - new_pos) * sizeof(gpointer));
214 }
215 else
216 {
217 memmove(array->pdata + old_pos, array->pdata + old_pos + 1,
218 (new_pos - old_pos) * sizeof(gpointer));
219 }
220
221 array->pdata[new_pos] = data;
222 iter->user_data2 = GINT_TO_POINTER(new_pos);
223
224 if (emit_reordered)
225 {
226 gint *new_order = g_new(gint, array->len);
227 guint i;
228
229 for (i = 0; i < array->len; i++)
230 {
231 if (G_UNLIKELY(i == new_pos))
232 new_order[i] = old_pos;
233 else if (new_pos < old_pos)
234 new_order[i] = i - (i > new_pos && i <= old_pos);
235 else
236 new_order[i] = i + (i >= old_pos && i < new_pos);
237 }
238
239 scp_emit_reordered(store, iter, new_order);
240 g_free(new_order);
241 }
242 }
243 }
244
scp_fixup_compare_result(ScpTreeStore * store,gint result)245 static inline gint scp_fixup_compare_result(ScpTreeStore *store, gint result)
246 {
247 return store->priv->sort_order == GTK_SORT_ASCENDING ? result :
248 result > 0 ? -1 : result < 0;
249 }
250
251 #define scp_store_compare(store, func, a, b, data) \
252 scp_fixup_compare_result((store), (*(func))(SCP_TREE_MODEL(store), (a), (b), (data)))
253
scp_search_pos(ScpTreeStore * store,GtkTreeIterCompareFunc func,gpointer data,GtkTreeIter * iter,gint low,gint high)254 static guint scp_search_pos(ScpTreeStore *store, GtkTreeIterCompareFunc func, gpointer data,
255 GtkTreeIter *iter, gint low, gint high)
256 {
257 if (low <= high)
258 {
259 gint mid;
260 GtkTreeIter iter1;
261
262 iter1.stamp = iter->stamp;
263 iter1.user_data = iter->user_data;
264
265 while (low < high)
266 {
267 gint result;
268
269 mid = (low + high) / 2;
270 iter1.user_data2 = GINT_TO_POINTER(mid);
271 result = scp_store_compare(store, func, iter, &iter1, data);
272
273 if (result > 0)
274 low = mid + 1;
275 else if (result < 0)
276 high = mid - 1;
277 else
278 return mid;
279 }
280
281 iter1.user_data2 = GINT_TO_POINTER(low);
282 if (scp_store_compare(store, func, iter, &iter1, data) > 0)
283 low++;
284 }
285
286 return low;
287 }
288
scp_sort_element(ScpTreeStore * store,GtkTreeIter * iter,gboolean emit_reordered)289 static void scp_sort_element(ScpTreeStore *store, GtkTreeIter *iter, gboolean emit_reordered)
290 {
291 ScpTreeStorePrivate *priv = store->priv;
292 GPtrArray *array = ITER_ARRAY(iter);
293 gint index = ITER_INDEX(iter);
294 GtkTreeIter iter1;
295 gpointer data = priv->headers[priv->sort_column_id].data;
296
297 iter1.stamp = priv->stamp;
298 iter1.user_data = array;
299
300 if (index > 0)
301 {
302 iter1.user_data2 = GINT_TO_POINTER(index - 1);
303
304 if (scp_store_compare(store, priv->sort_func, iter, &iter1, data) < 0)
305 {
306 scp_move_element(store, array, iter, scp_search_pos(store, priv->sort_func,
307 data, iter, 0, index - 2), emit_reordered);
308 return;
309 }
310 }
311
312 if (index < (gint) array->len - 1)
313 {
314 iter1.user_data2 = GINT_TO_POINTER(index + 1);
315
316 if (scp_store_compare(store, priv->sort_func, iter, &iter1, data) > 0)
317 {
318 scp_move_element(store, array, iter, scp_search_pos(store, priv->sort_func,
319 data, iter, index + 2, array->len - 1) - 1, emit_reordered);
320 }
321 }
322 }
323
scp_set_value(ScpTreeStore * store,AElem * elem,gint column,GValue * value)324 static gboolean scp_set_value(ScpTreeStore *store, AElem *elem, gint column, GValue *value)
325 {
326 ScpTreeStorePrivate *priv = store->priv;
327 GType dest_type = priv->headers[column].type;
328
329 g_return_val_if_fail((guint) column < priv->n_columns, FALSE);
330
331 if (g_type_is_a(G_VALUE_TYPE(value), dest_type))
332 scp_tree_data_from_value(elem->data + column, value, TRUE);
333 else
334 {
335 GValue real_value = G_VALUE_INIT;
336 g_value_init(&real_value, dest_type);
337
338 if (!g_value_transform(value, &real_value))
339 {
340 g_warning("%s: Unable to make conversion from %s to %s\n", G_STRFUNC,
341 g_type_name(G_VALUE_TYPE(value)), g_type_name(dest_type));
342 return FALSE;
343 }
344
345 scp_tree_data_from_value(elem->data + column, &real_value, TRUE);
346 g_value_unset(&real_value);
347 }
348
349 return TRUE;
350 }
351
scp_set_vector(ScpTreeStore * store,AElem * elem,gboolean * changed,gboolean * sort_changed,gint * columns,GValue * values,gint n_values)352 static void scp_set_vector(ScpTreeStore *store, AElem *elem, gboolean *changed,
353 gboolean *sort_changed, gint *columns, GValue *values, gint n_values)
354 {
355 ScpTreeStorePrivate *priv = store->priv;
356 gint i;
357
358 if (priv->sort_func && priv->sort_func != scp_tree_model_compare_func)
359 *sort_changed = TRUE;
360
361 for (i = 0; i < n_values; i++)
362 {
363 gint column = columns[i];
364
365 if ((guint) column >= priv->n_columns)
366 {
367 g_warning("%s: Invalid column number %d added to iter (remember to end "
368 "your list of columns with a -1)", G_STRFUNC, column);
369 break;
370 }
371
372 if (scp_set_value(store, elem, column, values + i))
373 *changed = TRUE;
374
375 if (column == priv->sort_column_id)
376 *sort_changed = TRUE;
377 }
378 }
379
scp_set_valist(ScpTreeStore * store,AElem * elem,gboolean * changed,gboolean * sort_changed,va_list ap)380 static void scp_set_valist(ScpTreeStore *store, AElem *elem, gboolean *changed,
381 gboolean *sort_changed, va_list ap)
382 {
383 ScpTreeStorePrivate *priv = store->priv;
384 gint column;
385
386 if (priv->sort_func && priv->sort_func != scp_tree_model_compare_func)
387 *sort_changed = TRUE;
388
389 while ((column = va_arg(ap, int)) != -1)
390 {
391 if ((guint) column >= priv->n_columns)
392 {
393 g_warning("%s: Invalid column number %d added to iter (remember to end "
394 "your list of columns with a -1)", G_STRFUNC, column);
395 break;
396 }
397
398 scp_tree_data_from_stack(elem->data + column, priv->headers[column].type, ap, TRUE);
399 *changed = TRUE;
400
401 if (column == priv->sort_column_id)
402 *sort_changed = TRUE;
403 }
404 }
405
scp_set_values_signals(ScpTreeStore * store,GtkTreeIter * iter,gboolean changed,gboolean sort_changed)406 static void scp_set_values_signals(ScpTreeStore *store, GtkTreeIter *iter, gboolean changed,
407 gboolean sort_changed)
408 {
409 if (sort_changed)
410 scp_sort_element(store, iter, TRUE);
411
412 if (changed)
413 {
414 GtkTreePath *path;
415
416 path = scp_tree_store_get_path(store, iter);
417 gtk_tree_model_row_changed(SCP_TREE_MODEL(store), path, iter);
418 gtk_tree_path_free(path);
419 }
420 }
421
scp_tree_store_set_valuesv(ScpTreeStore * store,GtkTreeIter * iter,gint * columns,GValue * values,gint n_values)422 void scp_tree_store_set_valuesv(ScpTreeStore *store, GtkTreeIter *iter, gint *columns,
423 GValue *values, gint n_values)
424 {
425 gboolean changed = FALSE;
426 gboolean sort_changed = FALSE;
427
428 g_return_if_fail(SCP_IS_TREE_STORE(store));
429 g_return_if_fail(VALID_ITER(iter, store));
430
431 scp_set_vector(store, ITER_ELEM(iter), &changed, &sort_changed, columns, values, n_values);
432 scp_set_values_signals(store, iter, changed, sort_changed);
433 }
434
scp_tree_store_set_value(ScpTreeStore * store,GtkTreeIter * iter,gint column,GValue * value)435 void scp_tree_store_set_value(ScpTreeStore *store, GtkTreeIter *iter, gint column,
436 GValue *value)
437 {
438 scp_tree_store_set_valuesv(store, iter, &column, value, 1);
439 }
440
scp_tree_store_set_valist(ScpTreeStore * store,GtkTreeIter * iter,va_list ap)441 void scp_tree_store_set_valist(ScpTreeStore *store, GtkTreeIter *iter, va_list ap)
442 {
443 gboolean changed = FALSE;
444 gboolean sort_changed = FALSE;
445
446 g_return_if_fail(SCP_IS_TREE_STORE(store));
447 g_return_if_fail(VALID_ITER(iter, store));
448
449 scp_set_valist(store, ITER_ELEM(iter), &changed, &sort_changed, ap);
450 scp_set_values_signals(store, iter, changed, sort_changed);
451 }
452
scp_tree_store_set(ScpTreeStore * store,GtkTreeIter * iter,...)453 void scp_tree_store_set(ScpTreeStore *store, GtkTreeIter *iter, ...)
454 {
455 va_list ap;
456
457 va_start(ap, iter);
458 scp_tree_store_set_valist(store, iter, ap);
459 va_end(ap);
460 }
461
scp_tree_store_remove(ScpTreeStore * store,GtkTreeIter * iter)462 gboolean scp_tree_store_remove(ScpTreeStore *store, GtkTreeIter *iter)
463 {
464 ScpTreeStorePrivate *priv = store->priv;
465 GPtrArray *array;
466 guint index;
467 GtkTreePath *path;
468 AElem *elem, *parent;
469
470 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
471 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
472
473 array = ITER_ARRAY(iter);
474 index = ITER_INDEX(iter);
475 elem = (AElem *) array->pdata[index];
476 parent = elem->parent;
477
478 path = scp_tree_store_get_path(store, iter);
479 scp_free_element(store, elem);
480 g_ptr_array_remove_index(array, index);
481 gtk_tree_model_row_deleted(SCP_TREE_MODEL(store), path);
482
483 if (index == array->len)
484 {
485 if (array->len == 0 && parent != priv->root)
486 {
487 if (priv->sublevel_discard)
488 {
489 g_ptr_array_free(array, TRUE);
490 parent->children = NULL;
491 }
492 /* child_toggled */
493 iter->user_data = parent->parent->children;
494 gtk_tree_path_up(path);
495 iter->user_data2 = GINT_TO_POINTER(gtk_tree_path_get_indices(path)
496 [gtk_tree_path_get_depth(path) - 1]);
497 gtk_tree_model_row_has_child_toggled(SCP_TREE_MODEL(store), path, iter);
498 }
499 /* invalidate */
500 iter->stamp = 0;
501 }
502
503 gtk_tree_path_free(path);
504 return iter->stamp != 0;
505 }
506
validate_elem(AElem * parent,AElem * elem)507 static void validate_elem(AElem *parent, AElem *elem)
508 {
509 GPtrArray *array = elem->children;
510 g_assert(elem->parent == parent);
511
512 if (array)
513 {
514 guint i;
515
516 for (i = 0; i < array->len; i++)
517 validate_elem(elem, (AElem *) array->pdata[i]);
518 }
519 }
520
validate_store(ScpTreeStore * store)521 static void validate_store(ScpTreeStore *store)
522 {
523 if (gtk_get_debug_flags() & GTK_DEBUG_TREE)
524 validate_elem(NULL, (store)->priv->root);
525 }
526
scp_insert_element(ScpTreeStore * store,GtkTreeIter * iter,AElem * elem,gint position,GtkTreeIter * parent_iter)527 static gboolean scp_insert_element(ScpTreeStore *store, GtkTreeIter *iter, AElem *elem,
528 gint position, GtkTreeIter *parent_iter)
529 {
530 ScpTreeStorePrivate *priv;
531 AElem *parent;
532 GPtrArray *array;
533 GtkTreePath *path;
534
535 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
536 g_return_val_if_fail(iter != NULL, FALSE);
537 priv = store->priv;
538 g_return_val_if_fail(priv->sublevels == TRUE || parent_iter == NULL, FALSE);
539 g_return_val_if_fail(VALID_ITER_OR_NULL(parent_iter, store), FALSE);
540
541 parent = parent_iter ? ITER_ELEM(parent_iter) : priv->root;
542 array = parent->children;
543 if (array)
544 {
545 if (position == -1)
546 position = array->len;
547 else
548 g_return_val_if_fail((guint) position <= array->len, FALSE);
549 }
550 else
551 {
552 g_return_val_if_fail(position == 0 || position == -1, FALSE);
553 position = 0;
554 parent->children = array = g_ptr_array_sized_new(parent_iter ?
555 priv->sublevel_reserved : priv->toplevel_reserved);
556 }
557
558 elem->parent = parent;
559 scp_ptr_array_insert_val(array, position, elem);
560 iter->stamp = priv->stamp;
561 iter->user_data = array;
562 iter->user_data2 = GINT_TO_POINTER(position);
563
564 if (priv->sort_func)
565 scp_sort_element(store, iter, FALSE);
566
567 priv->columns_dirty = TRUE;
568 path = scp_tree_store_get_path(store, iter);
569 gtk_tree_model_row_inserted(SCP_TREE_MODEL(store), path, iter);
570
571 if (parent_iter && array->len == 1)
572 {
573 gtk_tree_path_up(path);
574 gtk_tree_model_row_has_child_toggled(SCP_TREE_MODEL(store), path, parent_iter);
575 }
576
577 gtk_tree_path_free(path);
578 validate_store(store);
579 return TRUE;
580 }
581
scp_tree_store_insert(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,gint position)582 void scp_tree_store_insert(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter *parent,
583 gint position)
584 {
585 AElem *elem = g_slice_alloc0(ELEM_SIZE(store->priv->n_columns));
586
587 if (!scp_insert_element(store, iter, elem, position, parent))
588 g_slice_free1(ELEM_SIZE(store->priv->n_columns), elem);
589 }
590
scp_tree_store_insert_with_valuesv(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,gint position,gint * columns,GValue * values,gint n_values)591 void scp_tree_store_insert_with_valuesv(ScpTreeStore *store, GtkTreeIter *iter,
592 GtkTreeIter *parent, gint position, gint *columns, GValue *values, gint n_values)
593 {
594 AElem *elem = g_slice_alloc0(ELEM_SIZE(store->priv->n_columns));
595 gboolean changed, sort_changed;
596 GtkTreeIter iter1;
597
598 scp_set_vector(store, elem, &changed, &sort_changed, columns, values, n_values);
599
600 if (!scp_insert_element(store, iter ? iter : &iter1, elem, position, parent))
601 scp_free_element(store, elem);
602 }
603
scp_tree_store_insert_with_valist(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,gint position,va_list ap)604 void scp_tree_store_insert_with_valist(ScpTreeStore *store, GtkTreeIter *iter, GtkTreeIter
605 *parent, gint position, va_list ap)
606 {
607 AElem *elem = g_slice_alloc0(ELEM_SIZE(store->priv->n_columns));
608 gboolean changed, sort_changed;
609 GtkTreeIter iter1;
610
611 scp_set_valist(store, elem, &changed, &sort_changed, ap);
612
613 if (!scp_insert_element(store, iter ? iter : &iter1, elem, position, parent))
614 scp_free_element(store, elem);
615 }
616
scp_tree_store_insert_with_values(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,gint position,...)617 void scp_tree_store_insert_with_values(ScpTreeStore *store, GtkTreeIter *iter,
618 GtkTreeIter *parent, gint position, ...)
619 {
620 va_list ap;
621
622 va_start(ap, position);
623 scp_tree_store_insert_with_valist(store, iter, parent, position, ap);
624 va_end(ap);
625 }
626
scp_tree_store_get_valist(ScpTreeStore * store,GtkTreeIter * iter,va_list ap)627 void scp_tree_store_get_valist(ScpTreeStore *store, GtkTreeIter *iter, va_list ap)
628 {
629 ScpTreeStorePrivate *priv = store->priv;
630 AElem *elem;
631 gint column;
632
633 g_return_if_fail(SCP_IS_TREE_STORE(store));
634 g_return_if_fail(VALID_ITER(iter, store));
635
636 elem = ITER_ELEM(iter);
637 while ((column = va_arg(ap, int)) != -1)
638 {
639 gpointer dest;
640
641 if ((guint) column >= priv->n_columns)
642 {
643 g_warning("%s: Invalid column number %d added to iter (remember to end "
644 "your list of columns with a -1)", G_STRFUNC, column);
645 break;
646 }
647
648 dest = va_arg(ap, gpointer);
649 scp_tree_data_to_pointer(elem->data + column, priv->headers[column].type, dest);
650 }
651 }
652
scp_tree_store_get(ScpTreeStore * store,GtkTreeIter * iter,...)653 void scp_tree_store_get(ScpTreeStore *store, GtkTreeIter *iter, ...)
654 {
655 va_list ap;
656
657 va_start(ap, iter);
658 scp_tree_store_get_valist(store, iter, ap);
659 va_end(ap);
660 }
661
scp_tree_contains(GPtrArray * array,AElem * elem)662 static gboolean scp_tree_contains(GPtrArray *array, AElem *elem)
663 {
664 if (array)
665 {
666 guint i;
667
668 for (i = 0; i < array->len; i++)
669 {
670 AElem *elem1 = (AElem *) array->pdata[i];
671 if (elem1 == elem || scp_tree_contains(elem1->children, elem))
672 return TRUE;
673 }
674 }
675
676 return FALSE;
677 }
678
scp_tree_store_is_ancestor(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * descendant)679 gboolean scp_tree_store_is_ancestor(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter,
680 GtkTreeIter *descendant)
681 {
682 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
683 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
684 g_return_val_if_fail(VALID_ITER(descendant, store), FALSE);
685
686 return scp_tree_contains(ITER_ELEM(iter)->children, ITER_ELEM(descendant));
687 }
688
scp_tree_store_iter_depth(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)689 gint scp_tree_store_iter_depth(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
690 {
691 gint depth = 0;
692 AElem *elem;
693
694 g_return_val_if_fail(SCP_IS_TREE_STORE(store), 0);
695 g_return_val_if_fail(VALID_ITER(iter, store), 0);
696
697 for (elem = ITER_ELEM(iter); elem->parent; depth++, elem = elem->parent);
698 return depth;
699 }
700
scp_clear_array(ScpTreeStore * store,GPtrArray * array,gboolean emit_subsignals)701 static void scp_clear_array(ScpTreeStore *store, GPtrArray *array, gboolean emit_subsignals)
702 {
703 if (array)
704 {
705 GtkTreeIter iter;
706 gint i;
707
708 iter.user_data = array;
709
710 for (i = (gint) array->len - 1; i >= 0; i--)
711 {
712 if (emit_subsignals)
713 scp_clear_array(store, ((AElem *) array->pdata[i])->children, TRUE);
714
715 iter.stamp = store->priv->stamp;
716 iter.user_data2 = GINT_TO_POINTER(i);
717 scp_tree_store_remove(store, &iter);
718 }
719 }
720 }
721
scp_tree_store_clear_children(ScpTreeStore * store,GtkTreeIter * parent,gboolean emit_subsignals)722 void scp_tree_store_clear_children(ScpTreeStore *store, GtkTreeIter *parent,
723 gboolean emit_subsignals)
724 {
725 g_return_if_fail(SCP_IS_TREE_STORE(store));
726 g_return_if_fail(VALID_ITER_OR_NULL(parent, store));
727
728 if (parent)
729 scp_clear_array(store, ITER_ELEM(parent)->children, emit_subsignals);
730 else
731 {
732 scp_clear_array(store, store->priv->root->children, emit_subsignals);
733 while (++store->priv->stamp == 0);
734 }
735 }
736
scp_tree_store_iter_is_valid(ScpTreeStore * store,GtkTreeIter * iter)737 gboolean scp_tree_store_iter_is_valid(ScpTreeStore *store, GtkTreeIter *iter)
738 {
739 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
740 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
741 return scp_tree_contains(store->priv->root->children, ITER_ELEM(iter));
742 }
743
scp_reorder_array(ScpTreeStore * store,GtkTreeIter * parent,GPtrArray * array,gint * new_order)744 static void scp_reorder_array(ScpTreeStore *store, GtkTreeIter *parent, GPtrArray *array,
745 gint *new_order)
746 {
747 gpointer *pdata = g_new(gpointer, array->len);
748 GtkTreePath *path;
749 guint i;
750
751 for (i = 0; i < array->len; i++)
752 pdata[i] = array->pdata[new_order[i]];
753
754 memcpy(array->pdata, pdata, array->len * sizeof(gpointer));
755 g_free(pdata);
756 /* emit signal */
757 path = parent ? scp_tree_store_get_path(store, parent) : gtk_tree_path_new();
758 gtk_tree_model_rows_reordered(SCP_TREE_MODEL(store), path, parent, new_order);
759 gtk_tree_path_free(path);
760 }
761
scp_tree_store_reorder(ScpTreeStore * store,GtkTreeIter * parent,gint * new_order)762 void scp_tree_store_reorder(ScpTreeStore *store, GtkTreeIter *parent, gint *new_order)
763 {
764 GPtrArray *array;
765
766 g_return_if_fail(SCP_IS_TREE_STORE(store));
767 g_return_if_fail(store->priv->sort_func == NULL);
768 g_return_if_fail(VALID_ITER_OR_NULL(parent, store));
769 g_return_if_fail(new_order != NULL);
770
771 if ((array = (parent ? ITER_ELEM(parent) : store->priv->root)->children) != NULL)
772 scp_reorder_array(store, parent, array, new_order);
773 }
774
scp_tree_store_swap(ScpTreeStore * store,GtkTreeIter * a,GtkTreeIter * b)775 void scp_tree_store_swap(ScpTreeStore *store, GtkTreeIter *a, GtkTreeIter *b)
776 {
777 GPtrArray *array = ITER_ARRAY(a);
778 guint index_a = ITER_INDEX(a);
779 guint index_b = ITER_INDEX(b);
780
781 g_return_if_fail(SCP_IS_TREE_STORE(store));
782 g_return_if_fail(VALID_ITER(a, store));
783 g_return_if_fail(VALID_ITER(b, store));
784
785 if (ITER_ARRAY(b) != array)
786 {
787 g_warning("%s: Given children don't have a common parent\n", G_STRFUNC);
788 return;
789 }
790
791 if (index_a != index_b)
792 {
793 AElem *swap = (AElem *) array->pdata[index_a];
794 gint *new_order = g_new(gint, array->len);
795 guint i;
796
797 array->pdata[index_a] = array->pdata[index_b];
798 array->pdata[index_b] = swap;
799
800 for (i = 0; i < array->len; i++)
801 new_order[i] = i == index_a ? index_b : i == index_b ? index_a : i;
802
803 scp_emit_reordered(store, a, new_order);
804 g_free(new_order);
805 }
806 }
807
scp_tree_store_move(ScpTreeStore * store,GtkTreeIter * iter,gint position)808 void scp_tree_store_move(ScpTreeStore *store, GtkTreeIter *iter, gint position)
809 {
810 GPtrArray *array = ITER_ARRAY(iter);
811
812 g_return_if_fail(SCP_IS_TREE_STORE(store));
813 g_return_if_fail(store->priv->sort_func == NULL);
814 g_return_if_fail(VALID_ITER(iter, store));
815
816 if (position == -1)
817 {
818 g_return_if_fail(array->len > 0);
819 position = array->len - 1;
820 }
821 else
822 g_return_if_fail((guint) position < array->len);
823
824 scp_move_element(store, array, iter, position, TRUE);
825 }
826
scp_tree_store_iter_tell(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)827 gint scp_tree_store_iter_tell(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
828 {
829 g_return_val_if_fail(SCP_IS_TREE_STORE(store), -1);
830 g_return_val_if_fail(VALID_ITER(iter, store), -1);
831 g_return_val_if_fail((guint) ITER_INDEX(iter) < ITER_ARRAY(iter)->len, -1);
832
833 return ITER_INDEX(iter);
834 }
835
836 /* Model */
837
scp_ptr_array_find(GPtrArray * array,AElem * elem)838 static gint scp_ptr_array_find(GPtrArray *array, AElem *elem)
839 {
840 guint i;
841
842 for (i = 0; i < array->len; i++)
843 if (array->pdata[i] == elem)
844 return i;
845
846 return -1;
847 }
848
scp_tree_store_get_flags(ScpTreeStore * store)849 GtkTreeModelFlags scp_tree_store_get_flags(ScpTreeStore *store)
850 {
851 return store->priv->sublevels ? 0 : GTK_TREE_MODEL_LIST_ONLY;
852 }
853
scp_tree_store_get_n_columns(ScpTreeStore * store)854 gint scp_tree_store_get_n_columns(ScpTreeStore *store)
855 {
856 store->priv->columns_dirty = TRUE;
857 return store->priv->n_columns;
858 }
859
scp_tree_store_get_column_type(ScpTreeStore * store,gint index)860 GType scp_tree_store_get_column_type(ScpTreeStore *store, gint index)
861 {
862 ScpTreeStorePrivate *priv = store->priv;
863
864 g_return_val_if_fail((guint) index < priv->n_columns, G_TYPE_INVALID);
865 priv->columns_dirty = TRUE;
866 return priv->headers[index].type;
867 }
868
scp_tree_store_get_iter(ScpTreeStore * store,GtkTreeIter * iter,GtkTreePath * path)869 gboolean scp_tree_store_get_iter(ScpTreeStore *store, GtkTreeIter *iter, GtkTreePath *path)
870 {
871 ScpTreeStorePrivate *priv = store->priv;
872 GPtrArray *array = priv->root->children;
873 const gint *indices;
874 gint depth, i;
875
876 priv->columns_dirty = TRUE;
877 indices = gtk_tree_path_get_indices(path);
878 depth = gtk_tree_path_get_depth(path);
879 g_return_val_if_fail(depth > 0, FALSE);
880
881 for (i = 0; ; i++)
882 {
883 if (!array || (guint) indices[i] >= array->len)
884 {
885 iter->stamp = 0;
886 return FALSE;
887 }
888
889 if (i == depth - 1)
890 break;
891
892 array = ((AElem *) array->pdata[indices[i]])->children;
893 }
894
895 iter->stamp = priv->stamp;
896 iter->user_data = array;
897 iter->user_data2 = GINT_TO_POINTER(indices[depth - 1]);
898 return TRUE;
899 }
900
scp_tree_store_get_path(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)901 GtkTreePath *scp_tree_store_get_path(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
902 {
903 AElem *elem;
904 GtkTreePath *path;
905
906 g_return_val_if_fail(VALID_ITER(iter, store), NULL);
907 path = gtk_tree_path_new();
908
909 elem = ITER_ELEM(iter);
910 if (elem->parent)
911 {
912 gtk_tree_path_append_index(path, ITER_INDEX(iter));
913
914 for (elem = elem->parent; elem->parent; elem = elem->parent)
915 {
916 gint index = scp_ptr_array_find(elem->parent->children, elem);
917
918 if (index == -1)
919 {
920 /* We couldn't find node, meaning it's prolly not ours */
921 /* Perhaps I should do a g_return_if_fail here. */
922 gtk_tree_path_free(path);
923 return NULL;
924 }
925
926 gtk_tree_path_prepend_index(path, index);
927 }
928 }
929
930 return path;
931 }
932
scp_tree_store_get_value(ScpTreeStore * store,GtkTreeIter * iter,gint column,GValue * value)933 void scp_tree_store_get_value(ScpTreeStore *store, GtkTreeIter *iter, gint column,
934 GValue *value)
935 {
936 ScpTreeStorePrivate *priv = store->priv;
937
938 g_return_if_fail((guint) column < priv->n_columns);
939 g_return_if_fail(VALID_ITER(iter, store));
940 scp_tree_data_to_value(ITER_ELEM(iter)->data + column, priv->headers[column].type, value);
941 }
942
scp_tree_store_iter_next(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)943 gboolean scp_tree_store_iter_next(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
944 {
945 gint index = ITER_INDEX(iter);
946
947 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
948
949 if (index < (gint) ITER_ARRAY(iter)->len - 1)
950 {
951 iter->user_data2 = GINT_TO_POINTER(index + 1);
952 return TRUE;
953 }
954
955 iter->stamp = 0;
956 return FALSE;
957 }
958
scp_tree_store_iter_previous(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)959 gboolean scp_tree_store_iter_previous(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
960 {
961 gint index = ITER_INDEX(iter);
962
963 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
964
965 if (index > 0)
966 {
967 iter->user_data2 = GINT_TO_POINTER(index - 1);
968 return TRUE;
969 }
970
971 iter->stamp = 0;
972 return FALSE;
973 }
974
scp_tree_store_iter_children(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent)975 gboolean scp_tree_store_iter_children(ScpTreeStore *store, GtkTreeIter *iter,
976 GtkTreeIter *parent)
977 {
978 ScpTreeStorePrivate *priv = store->priv;
979 GPtrArray *array;
980
981 g_return_val_if_fail(VALID_ITER_OR_NULL(parent, store), FALSE);
982 array = (parent ? ITER_ELEM(parent) : priv->root)->children;
983
984 if (array && array->len)
985 {
986 iter->stamp = priv->stamp;
987 iter->user_data = array;
988 iter->user_data2 = GINT_TO_POINTER(0);
989 return TRUE;
990 }
991
992 iter->stamp = 0;
993 return FALSE;
994 }
995
scp_tree_store_iter_has_child(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter)996 gboolean scp_tree_store_iter_has_child(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter)
997 {
998 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
999 return ITER_ELEM(iter)->children && ITER_ELEM(iter)->children->len;
1000 }
1001
scp_tree_store_iter_n_children(ScpTreeStore * store,GtkTreeIter * iter)1002 gint scp_tree_store_iter_n_children(ScpTreeStore *store, GtkTreeIter *iter)
1003 {
1004 GPtrArray *array;
1005
1006 g_return_val_if_fail(VALID_ITER_OR_NULL(iter, store), 0);
1007 array = (iter ? ITER_ELEM(iter) : store->priv->root)->children;
1008 return array ? array->len : 0;
1009 }
1010
scp_tree_store_iter_nth_child(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * parent,gint n)1011 gboolean scp_tree_store_iter_nth_child(ScpTreeStore *store, GtkTreeIter *iter,
1012 GtkTreeIter *parent, gint n)
1013 {
1014 ScpTreeStorePrivate *priv = store->priv;
1015 GPtrArray *array;
1016
1017 g_return_val_if_fail(VALID_ITER_OR_NULL(parent, store), FALSE);
1018 array = (parent ? ITER_ELEM(parent) : priv->root)->children;
1019
1020 if (array && (guint) n < array->len)
1021 {
1022 iter->stamp = priv->stamp;
1023 iter->user_data = array;
1024 iter->user_data2 = GINT_TO_POINTER(n);
1025 return TRUE;
1026 }
1027
1028 iter->stamp = 0;
1029 return FALSE;
1030 }
1031
scp_tree_store_iter_parent(ScpTreeStore * store,GtkTreeIter * iter,GtkTreeIter * child)1032 gboolean scp_tree_store_iter_parent(ScpTreeStore *store, GtkTreeIter *iter,
1033 GtkTreeIter *child)
1034 {
1035 ScpTreeStorePrivate *priv = store->priv;
1036 AElem *parent;
1037
1038 g_return_val_if_fail(iter != NULL, FALSE);
1039 g_return_val_if_fail(VALID_ITER(child, store), FALSE);
1040 parent = ITER_ELEM(child)->parent;
1041 g_assert(parent != NULL);
1042
1043 if (parent->parent)
1044 {
1045 gint index = scp_ptr_array_find(parent->parent->children, parent);
1046
1047 if (index != -1)
1048 {
1049 iter->stamp = priv->stamp;
1050 iter->user_data = parent->parent->children;
1051 iter->user_data2 = GINT_TO_POINTER(index);
1052 return TRUE;
1053 }
1054 }
1055
1056 iter->stamp = 0;
1057 return FALSE;
1058 }
1059
scp_foreach(ScpTreeStore * store,GPtrArray * array,GtkTreePath * path,GtkTreeModelForeachFunc func,gpointer gdata)1060 static gboolean scp_foreach(ScpTreeStore *store, GPtrArray *array, GtkTreePath *path,
1061 GtkTreeModelForeachFunc func, gpointer gdata)
1062 {
1063 if (array)
1064 {
1065 guint i;
1066 GtkTreeIter iter;
1067
1068 gtk_tree_path_down(path);
1069 iter.stamp = store->priv->stamp;
1070 iter.user_data = array;
1071
1072 for (i = 0; i < array->len; i++)
1073 {
1074 iter.user_data2 = GINT_TO_POINTER(i);
1075
1076 if (func((GtkTreeModel *) store, path, &iter, gdata) || scp_foreach(store,
1077 ((AElem *) array->pdata[i])->children, path, func, gdata))
1078 {
1079 return TRUE;
1080 }
1081 gtk_tree_path_next(path);
1082 }
1083
1084 gtk_tree_path_up(path);
1085 }
1086
1087 return FALSE;
1088 }
1089
scp_tree_store_foreach(ScpTreeStore * store,GtkTreeModelForeachFunc func,gpointer gdata)1090 void scp_tree_store_foreach(ScpTreeStore *store, GtkTreeModelForeachFunc func, gpointer gdata)
1091 {
1092 GtkTreePath *path;
1093
1094 g_return_if_fail(SCP_IS_TREE_STORE(store));
1095 g_return_if_fail(func != NULL);
1096
1097 path = gtk_tree_path_new();
1098 scp_foreach(store, store->priv->root->children, path, func, gdata);
1099 gtk_tree_path_free(path);
1100 }
1101
1102 /* DND */
scp_tree_store_row_draggable(G_GNUC_UNUSED ScpTreeStore * store,G_GNUC_UNUSED GtkTreePath * path)1103 gboolean scp_tree_store_row_draggable(G_GNUC_UNUSED ScpTreeStore *store,
1104 G_GNUC_UNUSED GtkTreePath *path)
1105 {
1106 return TRUE;
1107 }
1108
scp_tree_store_drag_data_delete(ScpTreeStore * store,GtkTreePath * path)1109 gboolean scp_tree_store_drag_data_delete(ScpTreeStore *store, GtkTreePath *path)
1110 {
1111 GtkTreeIter iter;
1112
1113 if (scp_tree_store_get_iter(store, &iter, path))
1114 {
1115 scp_tree_store_remove(store, &iter);
1116 return TRUE;
1117 }
1118
1119 return FALSE;
1120 }
1121
scp_tree_store_drag_data_get(ScpTreeStore * store,GtkTreePath * path,GtkSelectionData * selection_data)1122 gboolean scp_tree_store_drag_data_get(ScpTreeStore *store, GtkTreePath *path,
1123 GtkSelectionData *selection_data)
1124 {
1125 /* Note that we don't need to handle the GTK_TREE_MODEL_ROW
1126 * target, because the default handler does it for us, but
1127 * we do anyway for the convenience of someone maybe overriding the
1128 * default handler.
1129 */
1130 return gtk_tree_set_row_drag_data(selection_data, SCP_TREE_MODEL(store), path);
1131 }
1132
scp_copy_element(ScpTreeStore * store,GtkTreeIter * src_iter,GtkTreeIter * dest_iter)1133 static void scp_copy_element(ScpTreeStore *store, GtkTreeIter *src_iter,
1134 GtkTreeIter *dest_iter)
1135 {
1136 ScpTreeStorePrivate *priv = store->priv;
1137 AElem *elem = ITER_ELEM(src_iter);
1138 AElem *dest = ITER_ELEM(dest_iter);
1139 GtkTreePath *path = scp_tree_store_get_path(store, dest_iter);
1140 guint i;
1141
1142 for (i = 0; i < priv->n_columns; i++)
1143 scp_tree_data_copy(elem->data + i, dest->data + i, priv->headers[i].type);
1144 gtk_tree_model_row_changed(SCP_TREE_MODEL(store), path, dest_iter);
1145 gtk_tree_path_free(path);
1146
1147 if (elem->children)
1148 {
1149 GtkTreeIter child;
1150
1151 child.stamp = priv->stamp;
1152 child.user_data = elem->children;
1153
1154 for (i = 0; i < elem->children->len; i++)
1155 {
1156 GtkTreeIter copy;
1157
1158 child.user_data2 = GINT_TO_POINTER(i);
1159 scp_tree_store_append(store, ©, dest_iter);
1160 scp_copy_element(store, &child, ©);
1161 }
1162 }
1163 }
1164
scp_tree_store_drag_data_received(ScpTreeStore * store,GtkTreePath * dest_path,GtkSelectionData * selection_data)1165 gboolean scp_tree_store_drag_data_received(ScpTreeStore *store, GtkTreePath *dest_path,
1166 GtkSelectionData *selection_data)
1167 {
1168 GtkTreeModel *src_model = NULL;
1169 GtkTreePath *src_path = NULL;
1170 GtkTreeIter src_iter;
1171 gboolean result = FALSE;
1172
1173 validate_store(store);
1174
1175 if (gtk_tree_get_row_drag_data(selection_data, &src_model, &src_path) &&
1176 src_model == SCP_TREE_MODEL(store) && /* can only receive from ourselves */
1177 scp_tree_store_get_iter(store, &src_iter, src_path))
1178 {
1179 GtkTreeIter parent_iter1;
1180 GtkTreeIter *parent_iter;
1181 gint depth = gtk_tree_path_get_depth(dest_path);
1182 gint src_index = GPOINTER_TO_INT(src_iter.user_data2);
1183 GtkTreeIter dest_iter;
1184
1185 if (depth == 1)
1186 parent_iter = NULL;
1187 else
1188 {
1189 GtkTreePath *parent_path = gtk_tree_path_copy(dest_path);
1190
1191 gtk_tree_path_up(parent_path);
1192 scp_tree_store_get_iter(store, &parent_iter1, parent_path);
1193 parent_iter = &parent_iter1;
1194 gtk_tree_path_free(parent_path);
1195 }
1196
1197 scp_tree_store_insert(store, &dest_iter, parent_iter,
1198 gtk_tree_path_get_indices(dest_path)[depth - 1]);
1199
1200 if (src_iter.user_data == dest_iter.user_data &&
1201 ITER_INDEX(&dest_iter) <= src_index)
1202 {
1203 /* inserting dest moved src, so adjust iter */
1204 src_iter.user_data2 = GINT_TO_POINTER(src_index + 1);
1205 }
1206
1207 scp_copy_element(store, &src_iter, &dest_iter);
1208 result = TRUE;
1209 }
1210
1211 if (src_path)
1212 gtk_tree_path_free(src_path);
1213
1214 return result;
1215 }
1216
scp_tree_store_row_drop_possible(ScpTreeStore * store,GtkTreePath * dest_path,GtkSelectionData * selection_data)1217 gboolean scp_tree_store_row_drop_possible(ScpTreeStore *store, GtkTreePath *dest_path,
1218 GtkSelectionData *selection_data)
1219 {
1220 GtkTreeModel *src_model = NULL;
1221 GtkTreePath *src_path = NULL;
1222 gboolean result = FALSE;
1223
1224 if (!store->priv->sort_func && /* reject drops if sorted */
1225 gtk_tree_get_row_drag_data(selection_data, &src_model, &src_path) &&
1226 src_model == SCP_TREE_MODEL(store) && /* can only drag to ourselves */
1227 !gtk_tree_path_is_ancestor(src_path, dest_path)) /* Can't drop into ourself. */
1228 {
1229 /* Can't drop if dest_path's parent doesn't exist */
1230 GtkTreeIter iter;
1231 GtkTreePath *parent_path = gtk_tree_path_copy(dest_path);
1232
1233 gtk_tree_path_up(parent_path);
1234 result = gtk_tree_path_get_depth(parent_path) == 0 ||
1235 scp_tree_store_get_iter(store, &iter, parent_path);
1236 gtk_tree_path_free(parent_path);
1237 }
1238
1239 if (src_path)
1240 gtk_tree_path_free(src_path);
1241
1242 return result;
1243 }
1244
1245 /* Sortable */
1246
scp_tree_store_get_sort_column_id(ScpTreeStore * store,gint * sort_column_id,GtkSortType * order)1247 gboolean scp_tree_store_get_sort_column_id(ScpTreeStore *store, gint *sort_column_id,
1248 GtkSortType *order)
1249 {
1250 ScpTreeStorePrivate *priv = store->priv;
1251
1252 if (sort_column_id)
1253 *sort_column_id = priv->sort_column_id;
1254
1255 if (order)
1256 *order = priv->sort_order;
1257
1258 return priv->sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID &&
1259 priv->sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
1260 }
1261
1262 typedef struct _ScpSortData
1263 {
1264 ScpTreeStore *store;
1265 GPtrArray *array;
1266 } ScpSortData;
1267
scp_index_compare(gint * a,gint * b,ScpSortData * sort_data)1268 static gint scp_index_compare(gint *a, gint *b, ScpSortData *sort_data)
1269 {
1270 ScpTreeStorePrivate *priv = sort_data->store->priv;
1271 GtkTreeIter iter_a, iter_b;
1272
1273 iter_a.stamp = iter_b.stamp = priv->stamp;
1274 iter_a.user_data = iter_b.user_data = sort_data->array;
1275 iter_a.user_data2 = GINT_TO_POINTER(*a);
1276 iter_b.user_data2 = GINT_TO_POINTER(*b);
1277
1278 return scp_store_compare(sort_data->store, priv->sort_func, &iter_a, &iter_b,
1279 priv->headers[priv->sort_column_id].data);
1280 }
1281
scp_sort_children(ScpTreeStore * store,GtkTreeIter * parent)1282 static void scp_sort_children(ScpTreeStore *store, GtkTreeIter *parent)
1283 {
1284 GPtrArray *array = (parent ? ITER_ELEM(parent) : store->priv->root)->children;
1285
1286 if (array && array->len)
1287 {
1288 gint *new_order = g_new(gint, array->len);
1289 ScpSortData sort_data = { store, array };
1290 GtkTreeIter iter;
1291 guint i;
1292
1293 for (i = 0; i < array->len; i++)
1294 new_order[i] = i;
1295
1296 g_qsort_with_data(new_order, array->len, sizeof(gint),
1297 (GCompareDataFunc) scp_index_compare, &sort_data);
1298 scp_reorder_array(store, parent, array, new_order);
1299 g_free(new_order);
1300
1301 iter.stamp = store->priv->stamp;
1302 iter.user_data = array;
1303
1304 for (i = 0; i < array->len; i++)
1305 {
1306 iter.user_data2 = GINT_TO_POINTER(i);
1307 scp_sort_children(store, &iter);
1308 }
1309 }
1310 }
1311
scp_store_sort(ScpTreeStore * store)1312 static void scp_store_sort(ScpTreeStore *store)
1313 {
1314 if (store->priv->sort_func)
1315 scp_sort_children(store, NULL);
1316 }
1317
scp_tree_store_set_sort_column_id(ScpTreeStore * store,gint sort_column_id,GtkSortType order)1318 void scp_tree_store_set_sort_column_id(ScpTreeStore *store, gint sort_column_id,
1319 GtkSortType order)
1320 {
1321 ScpTreeStorePrivate *priv = store->priv;
1322
1323 if (priv->sort_column_id != sort_column_id || priv->sort_order != order)
1324 {
1325
1326 if (sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1327 priv->sort_func = NULL;
1328 else
1329 {
1330 g_return_if_fail((guint) (sort_column_id + 1) < priv->n_columns + 1);
1331 g_return_if_fail(priv->headers[sort_column_id].func != NULL);
1332 priv->sort_func = priv->headers[sort_column_id].func;
1333 }
1334
1335 priv->sort_column_id = sort_column_id;
1336 priv->sort_order = order;
1337 gtk_tree_sortable_sort_column_changed(GTK_TREE_SORTABLE(store));
1338 scp_store_sort(store);
1339 }
1340 }
1341
scp_tree_store_set_sort_func(ScpTreeStore * store,gint sort_column_id,GtkTreeIterCompareFunc func,gpointer data,GDestroyNotify destroy)1342 void scp_tree_store_set_sort_func(ScpTreeStore *store, gint sort_column_id,
1343 GtkTreeIterCompareFunc func, gpointer data, GDestroyNotify destroy)
1344 {
1345 ScpTreeStorePrivate *priv = store->priv;
1346
1347 scp_tree_data_set_header(priv->headers, sort_column_id, func, data, destroy);
1348
1349 if (priv->sort_column_id == sort_column_id)
1350 {
1351 priv->sort_func = func;
1352 scp_store_sort(store);
1353 }
1354 }
1355
scp_tree_store_set_default_sort_func(ScpTreeStore * store,GtkTreeIterCompareFunc func,gpointer data,GDestroyNotify destroy)1356 void scp_tree_store_set_default_sort_func(ScpTreeStore *store, GtkTreeIterCompareFunc func,
1357 gpointer data, GDestroyNotify destroy)
1358 {
1359 ScpTreeStorePrivate *priv = store->priv;
1360
1361 scp_tree_data_set_header(priv->headers, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, func,
1362 data, destroy);
1363
1364 if (priv->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
1365 {
1366 priv->sort_func = func;
1367 scp_store_sort(store);
1368 }
1369 }
1370
scp_tree_store_has_default_sort_func(ScpTreeStore * store)1371 gboolean scp_tree_store_has_default_sort_func(ScpTreeStore *store)
1372 {
1373 return store->priv->headers[GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID].func != NULL;
1374 }
1375
1376 /* Buildable */
1377
1378 /* GtkBuildable custom tags implementation
1379 *
1380 * <columns>
1381 * <column type="..."/>
1382 * <column type="string" utf8_collate="false"/>
1383 * </columns>
1384 */
1385 typedef struct _GSListSubParserData
1386 {
1387 GtkBuilder *builder;
1388 GObject *object;
1389 const gchar *name;
1390 GArray *types;
1391 GArray *collates;
1392 } GSListSubParserData;
1393
tree_model_start_element(G_GNUC_UNUSED GMarkupParseContext * context,G_GNUC_UNUSED const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,G_GNUC_UNUSED GError ** error)1394 static void tree_model_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
1395 G_GNUC_UNUSED const gchar *element_name, const gchar **names, const gchar **values,
1396 gpointer user_data, G_GNUC_UNUSED GError **error)
1397 {
1398 guint i;
1399 GSListSubParserData *data = (GSListSubParserData *) user_data;
1400 gboolean type_processed = FALSE;
1401
1402 for (i = 0; names[i]; i++)
1403 {
1404 if (!strcmp(names[i], "type"))
1405 {
1406 GType type = gtk_builder_get_type_from_name(data->builder, values[i]);
1407 gboolean collate = g_type_is_a(type, G_TYPE_STRING);
1408
1409 if (type == G_TYPE_INVALID)
1410 {
1411 g_warning("%s: Unknown type %s specified in treemodel %s", G_STRFUNC,
1412 values[i], data->name);
1413 }
1414
1415 g_array_append_val(data->types, type);
1416 g_array_append_val(data->collates, collate);
1417 type_processed = TRUE;
1418 }
1419 else if (!strcmp(names[i], "utf8_collate"))
1420 {
1421 GValue value = G_VALUE_INIT;
1422 GError *error = NULL;
1423
1424 if (!type_processed)
1425 {
1426 g_warning("%s: Attribute %s must be preceded by type in treemodel %s",
1427 G_STRFUNC, names[i], data->name);
1428 }
1429 else if (!gtk_builder_value_from_string_type(data->builder, G_TYPE_BOOLEAN,
1430 values[i], &value, &error))
1431 {
1432 g_warning("%s: %s for %s in treemodel %s", G_STRFUNC, error->message,
1433 names[i], data->name);
1434 g_error_free(error);
1435 }
1436 else
1437 {
1438 g_array_index(data->collates, gboolean, data->collates->len - 1) =
1439 g_value_get_boolean(&value);
1440 g_value_unset(&value);
1441 }
1442 }
1443 }
1444 }
1445
tree_model_end_element(G_GNUC_UNUSED GMarkupParseContext * context,const gchar * element_name,gpointer user_data,G_GNUC_UNUSED GError ** error)1446 static void tree_model_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
1447 const gchar *element_name, gpointer user_data, G_GNUC_UNUSED GError **error)
1448 {
1449 GSListSubParserData *data = (GSListSubParserData *) user_data;
1450
1451 g_assert(data->builder);
1452
1453 if (!strcmp(element_name, "columns"))
1454 {
1455 guint i;
1456
1457 scp_tree_store_set_column_types(SCP_TREE_STORE(data->object), data->types->len,
1458 (GType *) data->types->data);
1459
1460 for (i = 0; i < data->collates->len; i++)
1461 if (g_array_index(data->collates, gboolean, i))
1462 scp_tree_store_set_utf8_collate(SCP_TREE_STORE(data->object), i, TRUE);
1463 }
1464 }
1465
1466 static const GMarkupParser tree_model_parser =
1467 {
1468 tree_model_start_element,
1469 tree_model_end_element,
1470 NULL, NULL, NULL
1471 };
1472
scp_tree_store_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * user_data)1473 static gboolean scp_tree_store_buildable_custom_tag_start(GtkBuildable *buildable,
1474 GtkBuilder *builder, GObject *child, const gchar *tagname, GMarkupParser *parser,
1475 gpointer *user_data)
1476 {
1477 if (!child && !strcmp(tagname, "columns"))
1478 {
1479 GSListSubParserData *parser_data = g_slice_new0(GSListSubParserData);
1480
1481 parser_data->builder = builder;
1482 parser_data->object = G_OBJECT(buildable);
1483 parser_data->name = gtk_buildable_get_name(buildable);
1484 parser_data->types = g_array_new(FALSE, FALSE, sizeof(GType));
1485 parser_data->collates = g_array_new(FALSE, FALSE, sizeof(gboolean));
1486 *parser = tree_model_parser;
1487 *user_data = parser_data;
1488 return TRUE;
1489 }
1490
1491 return FALSE;
1492 }
1493
scp_tree_store_buildable_custom_finished(G_GNUC_UNUSED GtkBuildable * buildable,G_GNUC_UNUSED GtkBuilder * builder,G_GNUC_UNUSED GObject * child,const gchar * tagname,gpointer user_data)1494 static void scp_tree_store_buildable_custom_finished(G_GNUC_UNUSED GtkBuildable *buildable,
1495 G_GNUC_UNUSED GtkBuilder *builder, G_GNUC_UNUSED GObject *child, const gchar *tagname,
1496 gpointer user_data)
1497 {
1498 if (!strcmp(tagname, "columns"))
1499 {
1500 GSListSubParserData *data = (GSListSubParserData *) user_data;
1501
1502 g_array_free(data->types, TRUE);
1503 g_array_free(data->collates, TRUE);
1504 g_slice_free(GSListSubParserData, data);
1505 }
1506 }
1507
1508 /* Extra */
1509
scp_tree_store_set_allocation(ScpTreeStore * store,guint toplevel_reserved,guint sublevel_reserved,gboolean sublevel_discard)1510 void scp_tree_store_set_allocation(ScpTreeStore *store, guint toplevel_reserved,
1511 guint sublevel_reserved, gboolean sublevel_discard)
1512 {
1513 g_object_set(G_OBJECT(store), "sublevel-discard", sublevel_discard,
1514 "sublevel_reserved", sublevel_reserved, toplevel_reserved ?
1515 "toplevel-reserved" : NULL, toplevel_reserved, NULL);
1516 }
1517
scp_tree_store_set_utf8_collate(ScpTreeStore * store,gint column,gboolean collate)1518 void scp_tree_store_set_utf8_collate(ScpTreeStore *store, gint column, gboolean collate)
1519 {
1520 ScpTreeStorePrivate *priv = store->priv;
1521
1522 g_return_if_fail(SCP_IS_TREE_STORE(store));
1523 g_return_if_fail((guint) column < priv->n_columns);
1524
1525 if (g_type_is_a(priv->headers[column].type, G_TYPE_STRING))
1526 {
1527 if (priv->headers[column].utf8_collate != collate)
1528 {
1529 priv->headers[column].utf8_collate = collate;
1530
1531 if (priv->sort_func && (priv->sort_column_id == column ||
1532 priv->sort_func != scp_tree_model_compare_func))
1533 {
1534 scp_store_sort(store);
1535 }
1536 }
1537 }
1538 else if (collate)
1539 g_warning("%s: Attempt to set uft8_collate for a non-string type\n", G_STRFUNC);
1540 }
1541
scp_tree_store_get_utf8_collate(ScpTreeStore * store,gint column)1542 gboolean scp_tree_store_get_utf8_collate(ScpTreeStore *store, gint column)
1543 {
1544 ScpTreeStorePrivate *priv = store->priv;
1545
1546 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
1547 g_return_val_if_fail((guint) column < priv->n_columns, FALSE);
1548 return priv->headers[column].utf8_collate;
1549 }
1550
1551 #define scp_data_string(data) ((data)->v_string ? (data)->v_string : "")
1552
scp_tree_store_compare_func(ScpTreeStore * store,GtkTreeIter * a,GtkTreeIter * b,gpointer data)1553 gint scp_tree_store_compare_func(ScpTreeStore *store, GtkTreeIter *a, GtkTreeIter *b,
1554 gpointer data)
1555 {
1556 ScpTreeStorePrivate *priv = store->priv;
1557 gint column = GPOINTER_TO_INT(data);
1558 GType type = priv->headers[column].type;
1559 ScpTreeData data_a, data_b;
1560
1561 scp_tree_store_get(store, a, column, &data_a, -1);
1562 scp_tree_store_get(store, b, column, &data_b, -1);
1563 return priv->headers[column].utf8_collate ?
1564 g_utf8_collate(scp_data_string(&data_a), scp_data_string(&data_b)) :
1565 scp_tree_data_compare_func(&data_a, &data_b, type);
1566 }
1567
scp_tree_store_iter_seek(VALIDATE_ONLY ScpTreeStore * store,GtkTreeIter * iter,gint position)1568 gboolean scp_tree_store_iter_seek(VALIDATE_ONLY ScpTreeStore *store, GtkTreeIter *iter,
1569 gint position)
1570 {
1571 GPtrArray *array = ITER_ARRAY(iter);
1572
1573 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
1574 g_return_val_if_fail(VALID_ITER(iter, store), FALSE);
1575
1576 if (position == -1)
1577 iter->user_data2 = GINT_TO_POINTER(array->len - 1);
1578 else if ((guint) position < array->len)
1579 iter->user_data2 = GINT_TO_POINTER(position);
1580 else
1581 {
1582 iter->stamp = 0;
1583 return FALSE;
1584 }
1585
1586 return TRUE;
1587 }
1588
scp_collate_data(const gchar * key,const gchar * data)1589 static gint scp_collate_data(const gchar *key, const gchar *data)
1590 {
1591 gchar *key1 = g_utf8_collate_key(data, -1);
1592 gint result = strcmp(key, key1);
1593
1594 g_free(key1);
1595 return result;
1596 }
1597
1598 #define scp_compare_data(a, b, type) \
1599 ((type) == G_TYPE_NONE ? scp_collate_data((a)->v_string, scp_data_string(b)) : \
1600 scp_tree_data_compare_func((a), (b), (type)))
1601
scp_linear_search(GPtrArray * array,gint column,ScpTreeData * data,GType type,GtkTreeIter * iter,gboolean sublevels)1602 static gboolean scp_linear_search(GPtrArray *array, gint column, ScpTreeData *data, GType type,
1603 GtkTreeIter *iter, gboolean sublevels)
1604 {
1605 if (array)
1606 {
1607 guint i;
1608
1609 for (i = 0; i < array->len; i++)
1610 {
1611 AElem *elem = ((AElem *) (array)->pdata[i]);
1612
1613 if (!scp_compare_data(data, elem->data + column, type))
1614 {
1615 iter->user_data = array;
1616 iter->user_data2 = GINT_TO_POINTER(i);
1617 return TRUE;
1618 }
1619
1620 if (sublevels)
1621 if (scp_linear_search(elem->children, column, data, type, iter, TRUE))
1622 return TRUE;
1623 }
1624 }
1625
1626 return FALSE;
1627 }
1628
scp_binary_search(GPtrArray * array,gint column,ScpTreeData * data,GType type,GtkTreeIter * iter,gboolean sublevels)1629 static gboolean scp_binary_search(GPtrArray *array, gint column, ScpTreeData *data, GType type,
1630 GtkTreeIter *iter, gboolean sublevels)
1631 {
1632 if (array)
1633 {
1634 gint low = 0, high = array->len - 1;
1635
1636 while (low <= high)
1637 {
1638 gint mid = (low + high) / 2;
1639 AElem *elem = ((AElem *) (array)->pdata[mid]);
1640 gint result = scp_compare_data(data, elem->data + column, type);
1641
1642 if (!result)
1643 {
1644 iter->user_data = array;
1645 iter->user_data2 = GINT_TO_POINTER(mid);
1646 return TRUE;
1647 }
1648
1649 if (result > 0)
1650 low = mid + 1;
1651 else
1652 high = mid - 1;
1653 }
1654
1655 if (sublevels)
1656 {
1657 guint i;
1658
1659 for (i = 0; i < array->len; i++)
1660 {
1661 AElem *elem = ((AElem *) (array)->pdata[i]);
1662
1663 if (scp_binary_search(elem->children, column, data, type, iter, TRUE))
1664 return TRUE;
1665 }
1666 }
1667 }
1668
1669 return FALSE;
1670 }
1671
scp_tree_store_search(ScpTreeStore * store,gboolean sublevels,gboolean linear_order,GtkTreeIter * iter,GtkTreeIter * parent,gint column,...)1672 gboolean scp_tree_store_search(ScpTreeStore *store, gboolean sublevels, gboolean linear_order,
1673 GtkTreeIter *iter, GtkTreeIter *parent, gint column, ...)
1674 {
1675 ScpTreeStorePrivate *priv = store->priv;
1676 GPtrArray *array;
1677 GType type;
1678 va_list ap;
1679 ScpTreeData data;
1680 gboolean found;
1681
1682 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
1683 g_return_val_if_fail(VALID_ITER_OR_NULL(parent, store), FALSE);
1684 g_return_val_if_fail((guint) column < priv->n_columns, FALSE);
1685 g_return_val_if_fail(sublevels == FALSE || priv->sublevels == TRUE, FALSE);
1686
1687 array = (parent ? ITER_ELEM(parent) : priv->root)->children;
1688 type = priv->headers[column].type;
1689 iter->stamp = priv->stamp;
1690 iter->user_data = NULL;
1691 va_start(ap, column);
1692 scp_tree_data_from_stack(&data, type, ap, FALSE);
1693 va_end(ap);
1694
1695 if (priv->headers[column].utf8_collate)
1696 {
1697 type = G_TYPE_NONE;
1698 data.v_string = g_utf8_collate_key(scp_data_string(&data), -1);
1699 }
1700
1701 found = !linear_order && column == priv->sort_column_id &&
1702 priv->sort_func == scp_tree_model_compare_func ?
1703 scp_binary_search(array, column, &data, type, iter, sublevels) :
1704 scp_linear_search(array, column, &data, type, iter, sublevels);
1705
1706 if (type == G_TYPE_NONE)
1707 g_free(data.v_string);
1708
1709 return found;
1710 }
1711
scp_traverse(ScpTreeStore * store,GPtrArray * array,GtkTreeIter * iter,gboolean sublevels,ScpTreeStoreTraverseFunc func,gpointer gdata)1712 static gboolean scp_traverse(ScpTreeStore *store, GPtrArray *array, GtkTreeIter *iter,
1713 gboolean sublevels, ScpTreeStoreTraverseFunc func, gpointer gdata)
1714 {
1715 if (array)
1716 {
1717 guint i = 0;
1718
1719 iter->user_data = array;
1720 iter->user_data2 = GINT_TO_POINTER(0);
1721
1722 while (i < array->len)
1723 {
1724 gint result = func(store, iter, gdata);
1725
1726 if (result > 0)
1727 return TRUE;
1728
1729 if (!result)
1730 {
1731 if (sublevels)
1732 {
1733 if (scp_traverse(store, ((AElem *) array->pdata[i])->children,
1734 iter, TRUE, func, gdata) > 0)
1735 {
1736 return TRUE;
1737 }
1738
1739 iter->user_data = array;
1740 }
1741
1742 iter->user_data2 = GINT_TO_POINTER(++i);
1743 }
1744 else
1745 scp_tree_store_remove(store, iter);
1746 }
1747 }
1748
1749 return FALSE;
1750 }
1751
scp_tree_store_traverse(ScpTreeStore * store,gboolean sublevels,GtkTreeIter * iter,GtkTreeIter * parent,ScpTreeStoreTraverseFunc func,gpointer gdata)1752 gboolean scp_tree_store_traverse(ScpTreeStore *store, gboolean sublevels, GtkTreeIter *iter,
1753 GtkTreeIter *parent, ScpTreeStoreTraverseFunc func, gpointer gdata)
1754 {
1755 ScpTreeStorePrivate *priv = store->priv;
1756 GtkTreeIter iter1;
1757
1758 g_return_val_if_fail(SCP_IS_TREE_STORE(store), FALSE);
1759 g_return_val_if_fail(VALID_ITER_OR_NULL(parent, store), FALSE);
1760 g_return_val_if_fail(sublevels == FALSE || priv->sublevels == TRUE, FALSE);
1761 g_return_val_if_fail(func != NULL, FALSE);
1762
1763 if (!iter)
1764 iter = &iter1;
1765
1766 iter->stamp = priv->stamp;
1767
1768 if (!scp_traverse(store, (parent ? ITER_ELEM(parent) : priv->root)->children, iter,
1769 sublevels, func, gdata))
1770 {
1771 iter->stamp = 0;
1772 return FALSE;
1773 }
1774
1775 return TRUE;
1776 }
1777
1778 /* Class */
1779
scp_tree_store_tree_model_init(GtkTreeModelIface * iface)1780 static void scp_tree_store_tree_model_init(GtkTreeModelIface *iface)
1781 {
1782 iface->get_flags = (GtkTreeModelFlags (*)(GtkTreeModel *)) scp_tree_store_get_flags;
1783 iface->get_n_columns = (gint (*)(GtkTreeModel *)) scp_tree_store_get_n_columns;
1784 iface->get_column_type = (GType (*)(GtkTreeModel *, gint)) scp_tree_store_get_column_type;
1785 iface->get_iter = (gboolean (*)(GtkTreeModel *, GtkTreeIter *, GtkTreePath *))
1786 scp_tree_store_get_iter;
1787 iface->get_path = (GtkTreePath *(*)(GtkTreeModel *, GtkTreeIter *)) scp_tree_store_get_path;
1788 iface->get_value = (void (*)(GtkTreeModel *, GtkTreeIter *, gint, GValue *))
1789 scp_tree_store_get_value;
1790 iface->iter_next = (gboolean (*)(GtkTreeModel *, GtkTreeIter *)) scp_tree_store_iter_next;
1791 #if GTK_CHECK_VERSION(3, 0, 0)
1792 iface->iter_previous = (gboolean (*)(GtkTreeModel *, GtkTreeIter *))
1793 scp_tree_store_iter_previous;
1794 #endif
1795 iface->iter_children = (gboolean (*)(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *))
1796 scp_tree_store_iter_children;
1797 iface->iter_has_child = (gboolean (*)(GtkTreeModel *, GtkTreeIter *))
1798 scp_tree_store_iter_has_child;
1799 iface->iter_n_children = (gint (*)(GtkTreeModel *, GtkTreeIter *))
1800 scp_tree_store_iter_n_children;
1801 iface->iter_nth_child = (gboolean (*)(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *,
1802 gint)) scp_tree_store_iter_nth_child;
1803 iface->iter_parent = (gboolean (*)(GtkTreeModel *model, GtkTreeIter *, GtkTreeIter *))
1804 scp_tree_store_iter_parent;
1805 }
1806
scp_tree_store_drag_source_init(GtkTreeDragSourceIface * iface)1807 static void scp_tree_store_drag_source_init(GtkTreeDragSourceIface *iface)
1808 {
1809 iface->row_draggable = (gboolean (*)(GtkTreeDragSource *, GtkTreePath *))
1810 scp_tree_store_row_draggable;
1811 iface->drag_data_delete = (gboolean (*)(GtkTreeDragSource *, GtkTreePath *))
1812 scp_tree_store_drag_data_delete;
1813 iface->drag_data_get = (gboolean (*)(GtkTreeDragSource *, GtkTreePath *,
1814 GtkSelectionData *)) scp_tree_store_drag_data_get;
1815 }
1816
scp_tree_store_drag_dest_init(GtkTreeDragDestIface * iface)1817 static void scp_tree_store_drag_dest_init(GtkTreeDragDestIface *iface)
1818 {
1819 iface->drag_data_received = (gboolean (*)(GtkTreeDragDest *, GtkTreePath *,
1820 GtkSelectionData *)) scp_tree_store_drag_data_received;
1821 iface->row_drop_possible = (gboolean (*)(GtkTreeDragDest *, GtkTreePath *,
1822 GtkSelectionData *)) scp_tree_store_row_drop_possible;
1823 }
1824
scp_tree_store_sortable_init(GtkTreeSortableIface * iface)1825 static void scp_tree_store_sortable_init(GtkTreeSortableIface *iface)
1826 {
1827 iface->get_sort_column_id = (gboolean (*)(GtkTreeSortable *, gint *, GtkSortType *))
1828 scp_tree_store_get_sort_column_id;
1829 iface->set_sort_column_id = (void (*)(GtkTreeSortable *, gint, GtkSortType))
1830 scp_tree_store_set_sort_column_id;
1831 iface->set_sort_func = (void (*)(GtkTreeSortable *, gint, GtkTreeIterCompareFunc,
1832 gpointer, GDestroyNotify)) scp_tree_store_set_sort_func;
1833 iface->set_default_sort_func = (void (*)(GtkTreeSortable *, GtkTreeIterCompareFunc,
1834 gpointer, GDestroyNotify)) scp_tree_store_set_default_sort_func;
1835 iface->has_default_sort_func = (gboolean (*)(GtkTreeSortable *))
1836 scp_tree_store_has_default_sort_func;
1837 }
1838
scp_tree_store_buildable_init(GtkBuildableIface * iface)1839 static void scp_tree_store_buildable_init(GtkBuildableIface *iface)
1840 {
1841 iface->custom_tag_start = scp_tree_store_buildable_custom_tag_start;
1842 iface->custom_finished = scp_tree_store_buildable_custom_finished;
1843 }
1844
1845 enum
1846 {
1847 PROP_0,
1848 PROP_SUBLEVELS,
1849 PROP_TOPLEVEL_RESERVED,
1850 PROP_SUBLEVEL_RESERVED,
1851 PROP_SUBLEVEL_DISCARD,
1852 PROP_UTF8_COLLATE
1853 };
1854
1855 static gpointer scp_tree_store_parent_class = NULL;
1856
scp_tree_store_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)1857 static GObject *scp_tree_store_constructor(GType type, guint n_construct_properties,
1858 GObjectConstructParam *construct_properties)
1859 {
1860 GObject *object = G_OBJECT_CLASS(scp_tree_store_parent_class)->constructor(type,
1861 n_construct_properties, construct_properties);
1862 ScpTreeStorePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(object, SCP_TYPE_TREE_STORE,
1863 ScpTreeStorePrivate);
1864
1865 ((ScpTreeStore *) object)->priv = priv;
1866 while ((priv->stamp = g_random_int()) == 0); /* we mark invalid iters with stamp 0 */
1867 priv->root = g_new0(AElem, 1);
1868 priv->roar = g_ptr_array_new();
1869 scp_ptr_array_insert_val(priv->roar, 0, priv->root);
1870 priv->headers = NULL;
1871 priv->n_columns = 0;
1872 priv->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
1873 priv->sort_order = GTK_SORT_ASCENDING;
1874 priv->sort_func = NULL;
1875 priv->toplevel_reserved = 0;
1876 priv->sublevel_reserved = 0;
1877 priv->sublevel_discard = FALSE;
1878 priv->columns_dirty = FALSE;
1879 return object;
1880 }
1881
scp_tree_store_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1882 static void scp_tree_store_get_property(GObject *object, guint prop_id, GValue *value,
1883 GParamSpec *pspec)
1884 {
1885 ScpTreeStorePrivate *priv = SCP_TREE_STORE(object)->priv;
1886
1887 switch (prop_id)
1888 {
1889 case PROP_SUBLEVELS :
1890 {
1891 g_value_set_boolean(value, priv->sublevels);
1892 break;
1893 }
1894 case PROP_TOPLEVEL_RESERVED :
1895 {
1896 g_value_set_uint(value, priv->toplevel_reserved);
1897 break;
1898 }
1899 case PROP_SUBLEVEL_RESERVED :
1900 {
1901 g_value_set_uint(value, priv->sublevel_reserved);
1902 break;
1903 }
1904 case PROP_SUBLEVEL_DISCARD :
1905 {
1906 g_value_set_boolean(value, priv->sublevel_reserved);
1907 break;
1908 }
1909 default : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1910 }
1911 }
1912
scp_tree_store_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1913 static void scp_tree_store_set_property(GObject *object, guint prop_id, const GValue *value,
1914 GParamSpec *pspec)
1915 {
1916 ScpTreeStorePrivate *priv = SCP_TREE_STORE(object)->priv;
1917
1918 switch (prop_id)
1919 {
1920 case PROP_SUBLEVELS :
1921 {
1922 priv = G_TYPE_INSTANCE_GET_PRIVATE(object, SCP_TYPE_TREE_STORE,
1923 ScpTreeStorePrivate); /* ctor, store->priv is not assigned */
1924 priv->sublevels = g_value_get_boolean(value);
1925 break;
1926 }
1927 case PROP_TOPLEVEL_RESERVED :
1928 {
1929 g_return_if_fail(priv->root->children == NULL);
1930 priv->toplevel_reserved = g_value_get_uint(value);
1931 break;
1932 }
1933 case PROP_SUBLEVEL_RESERVED :
1934 {
1935 g_return_if_fail(priv->sublevels);
1936 priv->sublevel_reserved = g_value_get_uint(value);
1937 break;
1938 }
1939 case PROP_SUBLEVEL_DISCARD :
1940 {
1941 g_return_if_fail(priv->sublevels);
1942 priv->sublevel_discard = g_value_get_boolean(value);
1943 break;
1944 }
1945 default :
1946 {
1947 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1948 return;
1949 }
1950 }
1951
1952 #if GLIB_CHECK_VERSION(2, 26, 0)
1953 g_object_notify_by_pspec(object, pspec);
1954 #else
1955 g_object_notify(object, pspec->name);
1956 #endif
1957 }
1958
scp_tree_store_finalize(GObject * object)1959 static void scp_tree_store_finalize(GObject *object)
1960 {
1961 ScpTreeStore *store = SCP_TREE_STORE(object);
1962 ScpTreeStorePrivate *priv = store->priv;
1963
1964 scp_free_array(store, priv->root->children);
1965 g_free(priv->root);
1966 g_ptr_array_free(priv->roar, TRUE);
1967
1968 if (priv->headers)
1969 scp_tree_data_headers_free(priv->n_columns, priv->headers);
1970
1971 G_OBJECT_CLASS(scp_tree_store_parent_class)->finalize(object);
1972 }
1973
scp_tree_store_gobject_init(GObjectClass * class)1974 static void scp_tree_store_gobject_init(GObjectClass *class)
1975 {
1976 scp_tree_store_parent_class = g_type_class_peek_parent(class);
1977 class->constructor = scp_tree_store_constructor;
1978 class->finalize = scp_tree_store_finalize;
1979 class->get_property = scp_tree_store_get_property;
1980 class->set_property = scp_tree_store_set_property;
1981 }
1982
scp_tree_store_class_init(GObjectClass * class)1983 static void scp_tree_store_class_init(GObjectClass *class)
1984 {
1985 scp_tree_store_gobject_init(class);
1986 g_type_class_add_private(class, sizeof(ScpTreeStorePrivate));
1987 g_assert(GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID == -1); /* headers[-1] = default */
1988
1989 g_object_class_install_property(class, PROP_SUBLEVELS,
1990 g_param_spec_boolean("sublevels", P_("Sublevels"),
1991 P_("Supports sublevels (as opposed to flat list)"), TRUE,
1992 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1993
1994 g_object_class_install_property(class, PROP_TOPLEVEL_RESERVED,
1995 g_param_spec_uint("toplevel-reserved", P_("Toplevel reserved"),
1996 P_("Number of pointers preallocated for top-level elements"),
1997 0, G_MAXINT32, 0, G_PARAM_READWRITE));
1998
1999 g_object_class_install_property(class, PROP_SUBLEVEL_RESERVED,
2000 g_param_spec_uint("sublevel-reserved", P_("Sublevel reserved"),
2001 P_("Number of pointers preallocated for sublevel elements"),
2002 0, G_MAXINT32, 0, G_PARAM_READWRITE));
2003
2004 g_object_class_install_property(class, PROP_SUBLEVEL_DISCARD,
2005 g_param_spec_boolean("sublevel-discard", P_("Sublevel discard"),
2006 P_("Free sublevel arrays when their last element is removed"),
2007 FALSE, G_PARAM_READWRITE));
2008 }
2009
2010 static volatile gsize scp_tree_store_type_id_volatile = 0;
2011
scp_tree_store_get_type(void)2012 GType scp_tree_store_get_type(void)
2013 {
2014 if (g_once_init_enter(&scp_tree_store_type_id_volatile))
2015 {
2016 GType type = g_type_register_static_simple(G_TYPE_OBJECT,
2017 g_intern_string("ScpTreeStore"), sizeof(ScpTreeStoreClass),
2018 (GClassInitFunc) scp_tree_store_class_init, sizeof(ScpTreeStore), NULL, 0);
2019 GInterfaceInfo iface_info = { (GInterfaceInitFunc) scp_tree_store_tree_model_init,
2020 NULL, NULL };
2021
2022 g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL, &iface_info);
2023 iface_info.interface_init = (GInterfaceInitFunc) scp_tree_store_drag_source_init;
2024 g_type_add_interface_static(type, GTK_TYPE_TREE_DRAG_SOURCE, &iface_info);
2025 iface_info.interface_init = (GInterfaceInitFunc) scp_tree_store_drag_dest_init;
2026 g_type_add_interface_static(type, GTK_TYPE_TREE_DRAG_DEST, &iface_info);
2027 iface_info.interface_init = (GInterfaceInitFunc) scp_tree_store_sortable_init;
2028 g_type_add_interface_static(type, GTK_TYPE_TREE_SORTABLE, &iface_info);
2029 iface_info.interface_init = (GInterfaceInitFunc) scp_tree_store_buildable_init;
2030 g_type_add_interface_static(type, GTK_TYPE_BUILDABLE, &iface_info);
2031 g_once_init_leave(&scp_tree_store_type_id_volatile, type);
2032 }
2033
2034 return scp_tree_store_type_id_volatile;
2035 }
2036
scp_tree_store_register_dynamic(void)2037 void scp_tree_store_register_dynamic(void)
2038 {
2039 GType type = g_type_from_name("ScpTreeStore");
2040
2041 if (!type)
2042 {
2043 type = scp_tree_store_get_type();
2044 g_type_class_unref(g_type_class_ref(type)); /* force class creation */
2045 }
2046 else if (!scp_tree_store_type_id_volatile)
2047 {
2048 /* external registration, repair */
2049 gpointer class = g_type_class_peek(type);
2050 gpointer iface = g_type_interface_peek(class, GTK_TYPE_TREE_MODEL);
2051
2052 scp_tree_store_gobject_init((GObjectClass *) class);
2053 scp_tree_store_tree_model_init((GtkTreeModelIface *) iface);
2054 iface = g_type_interface_peek(class, GTK_TYPE_TREE_DRAG_SOURCE);
2055 scp_tree_store_drag_source_init((GtkTreeDragSourceIface *) iface);
2056 iface = g_type_interface_peek(class, GTK_TYPE_TREE_DRAG_DEST);
2057 scp_tree_store_drag_dest_init((GtkTreeDragDestIface *) iface);
2058 iface = g_type_interface_peek(class, GTK_TYPE_TREE_SORTABLE);
2059 scp_tree_store_sortable_init((GtkTreeSortableIface *) iface);
2060 iface = g_type_interface_peek(class, GTK_TYPE_BUILDABLE);
2061 scp_tree_store_buildable_init((GtkBuildableIface *) iface);
2062 scp_tree_store_type_id_volatile = type;
2063 }
2064 }
2065