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