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