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