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, &copy, dest_iter);
1160 			scp_copy_element(store, &child, &copy);
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