1 /* gtktreedatalist.c
2  * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
3  *
4  * scptreedata.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 
30 #if !GLIB_CHECK_VERSION(2, 30, 0)
31 #define G_VALUE_INIT { 0, { { 0 } } }
32 #endif
33 
scp_tree_data_get_fundamental_type(GType type)34 GType scp_tree_data_get_fundamental_type(GType type)
35 {
36 	type = G_TYPE_FUNDAMENTAL(type);
37 	return G_UNLIKELY(type == G_TYPE_INTERFACE && g_type_is_a(type, G_TYPE_OBJECT))
38 		? G_TYPE_OBJECT : type;
39 }
40 
scp_tree_data_free(ScpTreeData * data,GType type)41 void scp_tree_data_free(ScpTreeData *data, GType type)
42 {
43 	if (data->v_pointer)
44 	{
45 		switch (scp_tree_data_get_fundamental_type(type))
46 		{
47 			case G_TYPE_STRING  : g_free(data->v_string); break;
48 			case G_TYPE_OBJECT  : g_object_unref(data->v_pointer); break;
49 			case G_TYPE_BOXED   : g_boxed_free(type, data->v_pointer); break;
50 		#if GLIB_CHECK_VERSION(2, 26, 0)
51 			case G_TYPE_VARIANT : g_variant_unref(data->v_pointer);
52 		#endif
53 		}
54 	}
55 }
56 
scp_tree_data_warn_unsupported_type(const char * prefix,GType type)57 void scp_tree_data_warn_unsupported_type(const char *prefix, GType type)
58 {
59 	g_warning("%s: Unsupported type %s", prefix, g_type_name(type));
60 }
61 
scp_tree_data_check_type(GType type)62 gboolean scp_tree_data_check_type(GType type)
63 {
64 	static const GType types[] =
65 	{
66 		G_TYPE_INT,
67 		G_TYPE_UINT,
68 		G_TYPE_STRING,
69 		G_TYPE_BOOLEAN,
70 		G_TYPE_LONG,
71 		G_TYPE_ULONG,
72 		G_TYPE_FLOAT,
73 		G_TYPE_DOUBLE,
74 		G_TYPE_CHAR,
75 		G_TYPE_UCHAR,
76 		G_TYPE_INT64,
77 		G_TYPE_UINT64,
78 		G_TYPE_ENUM,
79 		G_TYPE_FLAGS,
80 		G_TYPE_POINTER,
81 		G_TYPE_OBJECT,
82 		G_TYPE_BOXED,
83 	#if GLIB_CHECK_VERSION(2, 26, 0)
84 		G_TYPE_VARIANT,
85 	#endif
86 		G_TYPE_INVALID
87 	};
88 
89 	gint i;
90 	type = scp_tree_data_get_fundamental_type(type);
91 
92 	for (i = 0; types[i] != G_TYPE_INVALID; i++)
93 		if (types[i] == type)
94 			return TRUE;
95 
96 	return FALSE;
97 }
98 
scp_tree_data_to_value(const ScpTreeData * data,GType type,GValue * value)99 void scp_tree_data_to_value(const ScpTreeData *data, GType type, GValue *value)
100 {
101 	g_value_init(value, type);
102 
103 	switch (scp_tree_data_get_fundamental_type(type))
104 	{
105 		case G_TYPE_INT     : g_value_set_int(value, data->v_int); break;
106 		case G_TYPE_UINT    : g_value_set_uint(value, data->v_uint); break;
107 		case G_TYPE_STRING  : g_value_set_string(value, data->v_string); break;
108 		case G_TYPE_BOOLEAN : g_value_set_boolean(value, data->v_int); break;
109 		case G_TYPE_LONG    : g_value_set_long(value, data->v_long); break;
110 		case G_TYPE_ULONG   : g_value_set_ulong(value, data->v_ulong); break;
111 		case G_TYPE_FLOAT   : g_value_set_float(value, data->v_float); break;
112 		case G_TYPE_DOUBLE  : g_value_set_double(value, data->v_double); break;
113 	#if GLIB_CHECK_VERSION(2, 32, 0)
114 		case G_TYPE_CHAR    : g_value_set_schar(value, data->v_char); break;
115 	#else
116 		case G_TYPE_CHAR    : g_value_set_char(value, data->v_char); break;
117 	#endif
118 		case G_TYPE_UCHAR   : g_value_set_uchar(value, data->v_uchar); break;
119 		case G_TYPE_INT64   : g_value_set_int64(value, data->v_int64); break;
120 		case G_TYPE_UINT64  : g_value_set_uint64 (value, data->v_uint64); break;
121 		case G_TYPE_ENUM    : g_value_set_enum(value, data->v_int); break;
122 		case G_TYPE_FLAGS   : g_value_set_flags(value, data->v_uint); break;
123 		case G_TYPE_POINTER : g_value_set_pointer(value, data->v_pointer); break;
124 		case G_TYPE_OBJECT  : g_value_set_object(value, (GObject *) data->v_pointer); break;
125 		case G_TYPE_BOXED   : g_value_set_boxed(value, data->v_pointer); break;
126 	#if GLIB_CHECK_VERSION(2, 26, 0)
127 		case G_TYPE_VARIANT : g_value_set_variant(value, data->v_pointer); break;
128 	#endif
129 		default : scp_tree_data_warn_unsupported_type(G_STRFUNC, type);
130 	}
131 }
132 
scp_tree_data_to_pointer(const ScpTreeData * data,GType type,gpointer dest)133 void scp_tree_data_to_pointer(const ScpTreeData *data, GType type, gpointer dest)
134 {
135 	switch (scp_tree_data_get_fundamental_type(type))
136 	{
137 		case G_TYPE_INT     :
138 		case G_TYPE_ENUM    : *(gint *) dest = data->v_int; break;
139 		case G_TYPE_UINT    :
140 		case G_TYPE_FLAGS   : *(guint *) dest = data->v_uint; break;
141 		case G_TYPE_BOOLEAN : *(gboolean *) dest = data->v_int != 0; break;
142 		case G_TYPE_LONG    : *(glong *) dest = data->v_long; break;
143 		case G_TYPE_ULONG   : *(gulong *) dest = data->v_ulong; break;
144 		case G_TYPE_FLOAT   : *(gfloat *) dest = data->v_float; break;
145 		case G_TYPE_DOUBLE  : *(gdouble *) dest = data->v_double; break;
146 		case G_TYPE_CHAR    : *(gint8 *) dest = data->v_char; break;
147 		case G_TYPE_UCHAR   : *(guint8 *) dest = data->v_uchar; break;
148 		case G_TYPE_INT64   : *(gint64 *) dest = data->v_int64; break;
149 		case G_TYPE_UINT64  : *(guint64 *) dest = data->v_uint64; break;
150 		case G_TYPE_STRING  : *(char **) dest = data->v_string; break;
151 		case G_TYPE_POINTER :
152 		case G_TYPE_OBJECT  :
153 	#if GLIB_CHECK_VERSION(2, 26, 0)
154 		case G_TYPE_VARIANT :
155 	#endif
156 		case G_TYPE_BOXED   : *(gpointer *) dest = data->v_pointer; break;
157 		default : scp_tree_data_warn_unsupported_type(G_STRFUNC, type);
158 	}
159 }
160 
scp_tree_data_from_value(ScpTreeData * data,GValue * value,gboolean copy)161 void scp_tree_data_from_value(ScpTreeData *data, GValue *value, gboolean copy)
162 {
163 	switch (scp_tree_data_get_fundamental_type(G_VALUE_TYPE(value)))
164 	{
165 		case G_TYPE_INT     : data->v_int = g_value_get_int(value); break;
166 		case G_TYPE_UINT    : data->v_uint = g_value_get_uint(value); break;
167 		case G_TYPE_STRING  :
168 		{
169 			data->v_string = copy ? g_value_dup_string(value) :
170 				(char *) g_value_get_string(value);
171 			break;
172 		}
173 		case G_TYPE_BOOLEAN : data->v_int = g_value_get_boolean(value); break;
174 		case G_TYPE_LONG    : data->v_long = g_value_get_long(value); break;
175 		case G_TYPE_ULONG   : data->v_ulong = g_value_get_ulong(value); break;
176 		case G_TYPE_FLOAT   : data->v_float = g_value_get_float(value); break;
177 		case G_TYPE_DOUBLE  : data->v_double = g_value_get_double(value); break;
178 	#if GLIB_CHECK_VERSION(2, 32, 0)
179 		case G_TYPE_CHAR    : data->v_char = g_value_get_schar(value); break;
180 	#else
181 		case G_TYPE_CHAR    : data->v_char = g_value_get_char(value); break;
182 	#endif
183 		case G_TYPE_UCHAR   : data->v_uchar = g_value_get_uchar(value); break;
184 		case G_TYPE_INT64   : data->v_int64 = g_value_get_int64(value); break;
185 		case G_TYPE_UINT64  : data->v_uint64 = g_value_get_uint64(value); break;
186 		case G_TYPE_ENUM    : data->v_int = g_value_get_enum(value); break;
187 		case G_TYPE_FLAGS   : data->v_uint = g_value_get_flags(value); break;
188 		case G_TYPE_POINTER : data->v_pointer = g_value_get_pointer(value); break;
189 		case G_TYPE_OBJECT  :
190 		{
191 			data->v_pointer = copy ? g_value_dup_object(value) :
192 				g_value_get_object(value);
193 			break;
194 		}
195 		case G_TYPE_BOXED :
196 		{
197 			data->v_pointer = copy ? g_value_dup_boxed(value) :
198 				g_value_get_boxed(value);
199 			break;
200 		}
201 	#if GLIB_CHECK_VERSION(2, 26, 0)
202 		case G_TYPE_VARIANT :
203 		{
204 			data->v_pointer = copy ? g_value_dup_variant(value) :
205 				g_value_get_variant(value);
206 			break;
207 		}
208 	#endif
209 		default : scp_tree_data_warn_unsupported_type(G_STRFUNC, G_VALUE_TYPE(value));
210 	}
211 }
212 
scp_tree_data_copy(const ScpTreeData * data,ScpTreeData * new_data,GType type)213 void scp_tree_data_copy(const ScpTreeData *data, ScpTreeData *new_data, GType type)
214 {
215 	switch (scp_tree_data_get_fundamental_type(type))
216 	{
217 		case G_TYPE_INT     :
218 		case G_TYPE_UINT    :
219 		case G_TYPE_BOOLEAN :
220 		case G_TYPE_LONG    :
221 		case G_TYPE_ULONG   :
222 		case G_TYPE_FLOAT   :
223 		case G_TYPE_DOUBLE  :
224 		case G_TYPE_CHAR    :
225 		case G_TYPE_UCHAR   :
226 		case G_TYPE_INT64   :
227 		case G_TYPE_UINT64  :
228 		case G_TYPE_ENUM    :
229 		case G_TYPE_FLAGS   : *new_data = *data; break;
230 		default : scp_tree_data_assign_pointer(new_data, type, data->v_pointer, TRUE);
231 	}
232 }
233 
scp_tree_data_assign_pointer(ScpTreeData * data,GType type,gpointer ptr,gboolean copy)234 void scp_tree_data_assign_pointer(ScpTreeData *data, GType type, gpointer ptr, gboolean copy)
235 {
236 	switch (scp_tree_data_get_fundamental_type(type))
237 	{
238 		case G_TYPE_STRING :
239 		{
240 			data->v_pointer = copy ? g_strdup((const char *) ptr) : ptr;
241 			break;
242 		}
243 		case G_TYPE_POINTER :
244 		{
245 			data->v_pointer = ptr;
246 			break;
247 		}
248 		case G_TYPE_OBJECT :
249 		{
250 			data->v_pointer = copy && ptr ? g_object_ref(ptr) : ptr;
251 			break;
252 		}
253 		case G_TYPE_BOXED :
254 		{
255 			data->v_pointer = copy && ptr ? g_boxed_copy(type, data->v_pointer) : ptr;
256 			break;
257 		}
258 	#if GLIB_CHECK_VERSION(2, 26, 0)
259 		case G_TYPE_VARIANT :
260 		{
261 			data->v_pointer = copy && ptr ? g_variant_ref(ptr) : ptr;
262 			break;
263 		}
264 	#endif
265 		default : scp_tree_data_warn_unsupported_type(G_STRFUNC, type);
266 	}
267 }
268 
269 #define scp_compare(a, b) ((a) < (b) ? -1 : (a) > (b))
270 
scp_tree_data_compare_func(ScpTreeData * a,ScpTreeData * b,GType type)271 gint scp_tree_data_compare_func(ScpTreeData *a, ScpTreeData *b, GType type)
272 {
273 	switch (scp_tree_data_get_fundamental_type(type))
274 	{
275 		case G_TYPE_ENUM    : /* this is somewhat bogus */
276 		case G_TYPE_INT     : return scp_compare(a->v_int, b->v_int);
277 		case G_TYPE_FLAGS   : /* this is even more bogus */
278 		case G_TYPE_UINT    : return scp_compare(a->v_uint, b->v_uint);
279 		case G_TYPE_STRING  : return g_strcmp0(a->v_string, b->v_string);
280 		case G_TYPE_BOOLEAN : return (a->v_int != 0) - (b->v_int != 0);
281 		case G_TYPE_LONG    : return scp_compare(a->v_long, b->v_long);
282 		case G_TYPE_ULONG   : return scp_compare(a->v_ulong, b->v_ulong);
283 		case G_TYPE_FLOAT   : return scp_compare(a->v_float, b->v_float);
284 		case G_TYPE_DOUBLE  : return scp_compare(a->v_double, b->v_double);
285 		case G_TYPE_CHAR    : return a->v_char - b->v_char;
286 		case G_TYPE_UCHAR   : return (gint) a->v_uchar - (gint) b->v_uchar;
287 		case G_TYPE_INT64   : return scp_compare(a->v_int64, b->v_int64);
288 		case G_TYPE_UINT64  : return scp_compare(a->v_uint64, b->v_uint64);
289 	}
290 
291 	scp_tree_data_warn_unsupported_type(G_STRFUNC, type);
292 	return 0;
293 }
294 
scp_tree_data_value_compare_func(GValue * a,GValue * b)295 gint scp_tree_data_value_compare_func(GValue *a, GValue *b)
296 {
297 	ScpTreeData data_a, data_b;
298 
299 	scp_tree_data_from_value(&data_a, a, FALSE);
300 	scp_tree_data_from_value(&data_b, b, FALSE);
301 	return scp_tree_data_compare_func(&data_a, &data_b, G_VALUE_TYPE(a));
302 }
303 
scp_tree_data_model_compare_func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer gdata)304 gint scp_tree_data_model_compare_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
305 	gpointer gdata)
306 {
307 	gint column = GPOINTER_TO_INT(gdata);
308 	GValue value_a = G_VALUE_INIT;
309 	GValue value_b = G_VALUE_INIT;
310 	gint result;
311 
312 	gtk_tree_model_get_value(model, a, column, &value_a);
313 	gtk_tree_model_get_value(model, b, column, &value_b);
314 	result = scp_tree_data_value_compare_func(&value_a, &value_b);
315 	g_value_unset(&value_a);
316 	g_value_unset(&value_b);
317 
318 	return result;
319 }
320 
321 /* Header code */
322 
scp_tree_data_headers_new(gint n_columns,GType * types,GtkTreeIterCompareFunc func)323 ScpTreeDataHeader *scp_tree_data_headers_new(gint n_columns, GType *types,
324 	GtkTreeIterCompareFunc func)
325 {
326 	ScpTreeDataHeader *headers = g_new0(ScpTreeDataHeader, n_columns + 1);
327 	ScpTreeDataHeader *header = headers + 1;
328 	gint i;
329 
330 	for (i = 0; i < n_columns; i++, header++)
331 	{
332 		header->type = types[i];
333 		if (!scp_tree_data_check_type(header->type))
334 			scp_tree_data_warn_unsupported_type(G_STRFUNC, header->type);
335 		header->utf8_collate = g_type_is_a(header->type, G_TYPE_STRING);
336 		header->func = func;
337 		header->data = GINT_TO_POINTER(i);
338 		header->destroy = NULL;
339 	}
340 
341 	return headers + 1;
342 }
343 
scp_destroy_header(ScpTreeDataHeader * header)344 static void scp_destroy_header(ScpTreeDataHeader *header)
345 {
346 	if (header->destroy)
347 	{
348 		GDestroyNotify destroy = header->destroy;
349 		header->destroy = NULL;
350 		destroy(header->data);
351 	}
352 }
353 
scp_tree_data_headers_free(gint n_columns,ScpTreeDataHeader * headers)354 void scp_tree_data_headers_free(gint n_columns, ScpTreeDataHeader *headers)
355 {
356 	gint i;
357 
358 	for (i = 0; i < n_columns; i++)
359 		scp_destroy_header(headers + i);
360 
361 	g_free(headers - 1);
362 }
363 
scp_tree_data_set_header(ScpTreeDataHeader * headers,gint sort_column_id,GtkTreeIterCompareFunc func,gpointer data,GDestroyNotify destroy)364 void scp_tree_data_set_header(ScpTreeDataHeader *headers, gint sort_column_id,
365 	GtkTreeIterCompareFunc func, gpointer data, GDestroyNotify destroy)
366 {
367 	ScpTreeDataHeader *header = headers + sort_column_id;
368 
369 	scp_destroy_header(header);
370 	header->func = func;
371 	header->data = data;
372 	header->destroy = destroy;
373 }
374