1 /*
2 * e-table-extras.c - Set of hash table sort of thingies.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 *
17 * Authors:
18 * Chris Lahey <clahey@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #include "evolution-config.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <gtk/gtk.h>
30
31 #include "e-cell-checkbox.h"
32 #include "e-cell-date.h"
33 #include "e-cell-date-int.h"
34 #include "e-cell-number.h"
35 #include "e-cell-pixbuf.h"
36 #include "e-cell-size.h"
37 #include "e-cell-text.h"
38 #include "e-cell-tree.h"
39 #include "e-table-extras.h"
40 #include "e-table-sorting-utils.h"
41
42 #define E_TABLE_EXTRAS_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), E_TYPE_TABLE_EXTRAS, ETableExtrasPrivate))
45
46 struct _ETableExtrasPrivate {
47 GHashTable *cells;
48 GHashTable *compares;
49 GHashTable *icon_names;
50 GHashTable *searches;
51 };
52
G_DEFINE_TYPE(ETableExtras,e_table_extras,G_TYPE_OBJECT)53 G_DEFINE_TYPE (
54 ETableExtras,
55 e_table_extras,
56 G_TYPE_OBJECT)
57
58 static void
59 ete_finalize (GObject *object)
60 {
61 ETableExtrasPrivate *priv;
62
63 priv = E_TABLE_EXTRAS_GET_PRIVATE (object);
64 g_clear_pointer (&priv->cells, g_hash_table_destroy);
65 g_clear_pointer (&priv->compares, g_hash_table_destroy);
66 g_clear_pointer (&priv->searches, g_hash_table_destroy);
67 g_clear_pointer (&priv->icon_names, g_hash_table_destroy);
68
69 G_OBJECT_CLASS (e_table_extras_parent_class)->finalize (object);
70 }
71
72 static void
e_table_extras_class_init(ETableExtrasClass * class)73 e_table_extras_class_init (ETableExtrasClass *class)
74 {
75 GObjectClass *object_class;
76
77 g_type_class_add_private (class, sizeof (ETableExtrasPrivate));
78
79 object_class = G_OBJECT_CLASS (class);
80 object_class->finalize = ete_finalize;
81 }
82
83 static gint
e_strint_compare(gconstpointer data1,gconstpointer data2)84 e_strint_compare (gconstpointer data1,
85 gconstpointer data2)
86 {
87 gint int1 = atoi (data1);
88 gint int2 = atoi (data2);
89
90 return e_int_compare (GINT_TO_POINTER (int1), GINT_TO_POINTER (int2));
91 }
92
93 static gint
e_int64ptr_compare(gconstpointer data1,gconstpointer data2)94 e_int64ptr_compare (gconstpointer data1,
95 gconstpointer data2)
96 {
97 const gint64 *pa = data1, *pb = data2;
98
99 if (pa && pb)
100 return (*pa == *pb) ? 0 : (*pa < *pb) ? -1 : 1;
101
102 /* sort unset values before set */
103 return (!pa && !pb) ? 0 : (pa ? 1 : -1);
104 }
105
106 /* UTF-8 strncasecmp - not optimized */
107
108 static gint
g_utf8_strncasecmp(const gchar * s1,const gchar * s2,guint n)109 g_utf8_strncasecmp (const gchar *s1,
110 const gchar *s2,
111 guint n)
112 {
113 gunichar c1, c2;
114
115 g_return_val_if_fail (s1 != NULL && g_utf8_validate (s1, -1, NULL), 0);
116 g_return_val_if_fail (s2 != NULL && g_utf8_validate (s2, -1, NULL), 0);
117
118 while (n && *s1 && *s2)
119 {
120
121 n -= 1;
122
123 c1 = g_unichar_tolower (g_utf8_get_char (s1));
124 c2 = g_unichar_tolower (g_utf8_get_char (s2));
125
126 /* Collation is locale-dependent, so this
127 * totally fails to do the right thing. */
128 if (c1 != c2)
129 return c1 < c2 ? -1 : 1;
130
131 s1 = g_utf8_next_char (s1);
132 s2 = g_utf8_next_char (s2);
133 }
134
135 if (n == 0 || (*s1 == '\0' && *s2 == '\0'))
136 return 0;
137
138 return *s1 ? 1 : -1;
139 }
140
141 static gboolean
e_string_search(gconstpointer haystack,const gchar * needle)142 e_string_search (gconstpointer haystack,
143 const gchar *needle)
144 {
145 gint length;
146 if (haystack == NULL)
147 return FALSE;
148
149 length = g_utf8_strlen (needle, -1);
150 if (g_utf8_strncasecmp (haystack, needle, length) == 0)
151 return TRUE;
152 else
153 return FALSE;
154 }
155
156 static gint
e_table_str_case_compare(gconstpointer x,gconstpointer y,gpointer cmp_cache)157 e_table_str_case_compare (gconstpointer x,
158 gconstpointer y,
159 gpointer cmp_cache)
160 {
161 const gchar *cx = NULL, *cy = NULL;
162
163 if (!cmp_cache)
164 return e_str_case_compare (x, y);
165
166 if (x == NULL || y == NULL) {
167 if (x == y)
168 return 0;
169 else
170 return x ? -1 : 1;
171 }
172
173 #define prepare_value(_z, _cz) \
174 _cz = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, _z); \
175 if (!_cz) { \
176 gchar *tmp = g_utf8_casefold (_z, -1); \
177 _cz = g_utf8_collate_key (tmp, -1); \
178 g_free (tmp); \
179 \
180 e_table_sorting_utils_add_to_cmp_cache ( \
181 cmp_cache, _z, (gchar *) _cz); \
182 }
183
184 prepare_value (x, cx);
185 prepare_value (y, cy);
186
187 #undef prepare_value
188
189 return strcmp (cx, cy);
190 }
191
192 static gint
e_table_collate_compare(gconstpointer x,gconstpointer y,gpointer cmp_cache)193 e_table_collate_compare (gconstpointer x,
194 gconstpointer y,
195 gpointer cmp_cache)
196 {
197 const gchar *cx = NULL, *cy = NULL;
198
199 if (!cmp_cache)
200 return e_collate_compare (x, y);
201
202 if (x == NULL || y == NULL) {
203 if (x == y)
204 return 0;
205 else
206 return x ? -1 : 1;
207 }
208
209 #define prepare_value(_z, _cz) \
210 _cz = e_table_sorting_utils_lookup_cmp_cache (cmp_cache, _z); \
211 if (!_cz) { \
212 _cz = g_utf8_collate_key (_z, -1); \
213 \
214 e_table_sorting_utils_add_to_cmp_cache ( \
215 cmp_cache, _z, (gchar *) _cz); \
216 }
217
218 prepare_value (x, cx);
219 prepare_value (y, cy);
220
221 #undef prepare_value
222
223 return strcmp (cx, cy);
224 }
225
226 static void
safe_unref(gpointer object)227 safe_unref (gpointer object)
228 {
229 if (object != NULL)
230 g_object_unref (object);
231 }
232
233 static void
e_table_extras_init(ETableExtras * extras)234 e_table_extras_init (ETableExtras *extras)
235 {
236 ECell *cell, *sub_cell;
237
238 extras->priv = E_TABLE_EXTRAS_GET_PRIVATE (extras);
239
240 extras->priv->cells = g_hash_table_new_full (
241 g_str_hash, g_str_equal,
242 (GDestroyNotify) g_free,
243 (GDestroyNotify) safe_unref);
244
245 extras->priv->compares = g_hash_table_new_full (
246 g_str_hash, g_str_equal,
247 (GDestroyNotify) g_free,
248 (GDestroyNotify) NULL);
249
250 extras->priv->icon_names = g_hash_table_new_full (
251 g_str_hash, g_str_equal,
252 (GDestroyNotify) g_free,
253 (GDestroyNotify) g_free);
254
255 extras->priv->searches = g_hash_table_new_full (
256 g_str_hash, g_str_equal,
257 (GDestroyNotify) g_free,
258 (GDestroyNotify) NULL);
259
260 e_table_extras_add_compare (
261 extras, "string",
262 (GCompareDataFunc) e_str_compare);
263 e_table_extras_add_compare (
264 extras, "stringcase",
265 (GCompareDataFunc) e_table_str_case_compare);
266 e_table_extras_add_compare (
267 extras, "collate",
268 (GCompareDataFunc) e_table_collate_compare);
269 e_table_extras_add_compare (
270 extras, "integer",
271 (GCompareDataFunc) e_int_compare);
272 e_table_extras_add_compare (
273 extras, "string-integer",
274 (GCompareDataFunc) e_strint_compare);
275 e_table_extras_add_compare (
276 extras, "pointer-integer64",
277 (GCompareDataFunc) e_int64ptr_compare);
278
279 e_table_extras_add_search (extras, "string", e_string_search);
280
281 cell = e_cell_checkbox_new ();
282 e_table_extras_add_cell (extras, "checkbox", cell);
283 g_object_unref (cell);
284
285 cell = e_cell_date_new (NULL, GTK_JUSTIFY_LEFT);
286 e_table_extras_add_cell (extras, "date", cell);
287 g_object_unref (cell);
288
289 cell = e_cell_date_int_new (NULL, GTK_JUSTIFY_LEFT);
290 e_table_extras_add_cell (extras, "date-int", cell);
291 g_object_unref (cell);
292
293 cell = e_cell_number_new (NULL, GTK_JUSTIFY_RIGHT);
294 e_table_extras_add_cell (extras, "number", cell);
295 g_object_unref (cell);
296
297 cell = e_cell_pixbuf_new ();
298 e_table_extras_add_cell (extras, "pixbuf", cell);
299 g_object_unref (cell);
300
301 cell = e_cell_size_new (NULL, GTK_JUSTIFY_RIGHT);
302 e_table_extras_add_cell (extras, "size", cell);
303 g_object_unref (cell);
304
305 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
306 e_table_extras_add_cell (extras, "string", cell);
307 g_object_unref (cell);
308
309 sub_cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
310 cell = e_cell_tree_new (TRUE, TRUE, sub_cell);
311 e_table_extras_add_cell (extras, "tree-string", cell);
312 g_object_unref (sub_cell);
313 g_object_unref (cell);
314 }
315
316 ETableExtras *
e_table_extras_new(void)317 e_table_extras_new (void)
318 {
319 return g_object_new (E_TYPE_TABLE_EXTRAS, NULL);
320 }
321
322 void
e_table_extras_add_cell(ETableExtras * extras,const gchar * id,ECell * cell)323 e_table_extras_add_cell (ETableExtras *extras,
324 const gchar *id,
325 ECell *cell)
326 {
327 g_return_if_fail (E_IS_TABLE_EXTRAS (extras));
328 g_return_if_fail (id != NULL);
329
330 if (cell != NULL)
331 g_object_ref_sink (cell);
332
333 g_hash_table_insert (extras->priv->cells, g_strdup (id), cell);
334 }
335
336 ECell *
e_table_extras_get_cell(ETableExtras * extras,const gchar * id)337 e_table_extras_get_cell (ETableExtras *extras,
338 const gchar *id)
339 {
340 g_return_val_if_fail (E_IS_TABLE_EXTRAS (extras), NULL);
341 g_return_val_if_fail (id != NULL, NULL);
342
343 return g_hash_table_lookup (extras->priv->cells, id);
344 }
345
346 void
e_table_extras_add_compare(ETableExtras * extras,const gchar * id,GCompareDataFunc compare)347 e_table_extras_add_compare (ETableExtras *extras,
348 const gchar *id,
349 GCompareDataFunc compare)
350 {
351 g_return_if_fail (E_IS_TABLE_EXTRAS (extras));
352 g_return_if_fail (id != NULL);
353
354 g_hash_table_insert (
355 extras->priv->compares,
356 g_strdup (id), (gpointer) compare);
357 }
358
359 GCompareDataFunc
e_table_extras_get_compare(ETableExtras * extras,const gchar * id)360 e_table_extras_get_compare (ETableExtras *extras,
361 const gchar *id)
362 {
363 g_return_val_if_fail (E_IS_TABLE_EXTRAS (extras), NULL);
364 g_return_val_if_fail (id != NULL, NULL);
365
366 return g_hash_table_lookup (extras->priv->compares, id);
367 }
368
369 void
e_table_extras_add_search(ETableExtras * extras,const gchar * id,ETableSearchFunc search)370 e_table_extras_add_search (ETableExtras *extras,
371 const gchar *id,
372 ETableSearchFunc search)
373 {
374 g_return_if_fail (E_IS_TABLE_EXTRAS (extras));
375 g_return_if_fail (id != NULL);
376
377 g_hash_table_insert (
378 extras->priv->searches,
379 g_strdup (id), (gpointer) search);
380 }
381
382 ETableSearchFunc
e_table_extras_get_search(ETableExtras * extras,const gchar * id)383 e_table_extras_get_search (ETableExtras *extras,
384 const gchar *id)
385 {
386 g_return_val_if_fail (E_IS_TABLE_EXTRAS (extras), NULL);
387 g_return_val_if_fail (id != NULL, NULL);
388
389 return g_hash_table_lookup (extras->priv->searches, id);
390 }
391
392 void
e_table_extras_add_icon_name(ETableExtras * extras,const gchar * id,const gchar * icon_name)393 e_table_extras_add_icon_name (ETableExtras *extras,
394 const gchar *id,
395 const gchar *icon_name)
396 {
397 g_return_if_fail (E_IS_TABLE_EXTRAS (extras));
398 g_return_if_fail (id != NULL);
399
400 g_hash_table_insert (
401 extras->priv->icon_names,
402 g_strdup (id), g_strdup (icon_name));
403 }
404
405 const gchar *
e_table_extras_get_icon_name(ETableExtras * extras,const gchar * id)406 e_table_extras_get_icon_name (ETableExtras *extras,
407 const gchar *id)
408 {
409 g_return_val_if_fail (E_IS_TABLE_EXTRAS (extras), NULL);
410 g_return_val_if_fail (id != NULL, NULL);
411
412 return g_hash_table_lookup (extras->priv->icon_names, id);
413 }
414