]> git.sur5r.net Git - glabels/blob - src/mygal/color-group.c
Imported Upstream version 2.2.8
[glabels] / src / mygal / color-group.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * color-group.c - Utility to keep a shered memory of custom colors
4  *                 between arbitrary widgets.
5  * Copyright 2000, Michael Levy
6  * Copyright 2001, Almer S. Tigelaar
7  *
8  * Authors:
9  *      Michael Levy (mlevy@genoscope.cns.fr)
10  * Revised and polished by:
11  *   Almer S. Tigelaar <almer@gnome.org>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public
15  * License, version 2, as published by the Free Software Foundation.
16  *
17  * This library is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25  * 02111-1307, USA.
26  */
27
28 #include "color-group.h"
29 #include <glib-object.h>
30 #include <gdk/gdkcolor.h>
31 #include "e-util.h"
32 #include <string.h>
33
34 #define PARENT_TYPE     G_TYPE_OBJECT
35
36 enum {
37         CUSTOM_COLOR_ADD,
38         LAST_SIGNAL
39 };
40
41 static GObjectClass *parent_class;
42
43 static GQuark color_group_signals [LAST_SIGNAL] = { 0 };
44
45 static void color_group_finalize (GObject *obj);
46
47 static void
48 color_group_class_init (ColorGroupClass *klass)
49 {
50         GObjectClass *object_class;
51
52         object_class = (GObjectClass*) klass;
53         
54         object_class->finalize = &color_group_finalize;
55         parent_class = g_type_class_peek (PARENT_TYPE);
56
57         color_group_signals [CUSTOM_COLOR_ADD] =
58                 g_signal_new ("custom_color_add",
59                         COLOR_GROUP_TYPE,
60                         G_SIGNAL_RUN_LAST,
61                         G_STRUCT_OFFSET (ColorGroupClass, custom_color_add),
62                         (GSignalAccumulator) NULL, NULL,
63                         g_cclosure_marshal_VOID__POINTER,
64                         G_TYPE_NONE,
65                         1, G_TYPE_POINTER);
66 }
67
68 static void
69 color_group_init (ColorGroup *cg)
70 {
71         cg->name = NULL;
72         cg->history = NULL;
73         cg->history_size = 0;
74 }
75
76 E_MAKE_TYPE(color_group,
77             "ColorGroup",
78             ColorGroup,
79             color_group_class_init,
80             color_group_init,
81             PARENT_TYPE)
82
83
84 /* Hash table used to ensure unicity in newly created names*/
85 static GHashTable *group_names = NULL;
86
87 static guint
88 cg_hash (gconstpointer  key)
89 {
90         /* Do NOT use smart type checking it will not work for the tmp_key */
91         return g_str_hash (((ColorGroup *)key)->name);
92 }
93
94 static gint
95 cg_cmp (gconstpointer a, gconstpointer  b)
96 {
97         /* Do NOT use smart type checking it will not work for the tmp_key */
98         ColorGroup const *cg_a = (ColorGroup *)a;
99         ColorGroup const *cg_b = (ColorGroup *)b;
100         if (cg_a == cg_b)
101                 return TRUE;
102         if (cg_a->context != cg_b->context)
103                 return FALSE;
104         return g_str_equal (cg_a->name, cg_b->name);
105 }
106
107 static void
108 initialize_group_names (void)
109 {
110         g_assert (group_names == NULL);
111         group_names = g_hash_table_new (cg_hash, cg_cmp);
112 }
113
114 /**
115  * color_group_get :
116  * @name :
117  * @context : 
118  *
119  * Look up the name/context specific color-group.  Return NULL if it is not found.
120  * No reference is added if it is found.
121  */
122 ColorGroup *
123 color_group_get (const gchar * name, gpointer context)
124 {
125         ColorGroup tmp_key;
126         gpointer res;
127
128         g_assert(group_names);
129
130         g_return_val_if_fail(name != NULL, NULL);
131
132         tmp_key.name = (char *)name;
133         tmp_key.context = context;
134         res = g_hash_table_lookup (group_names, &tmp_key);
135
136         if (res != NULL)
137                 return COLOR_GROUP (res);
138         else
139                 return NULL;
140 }
141
142 static gchar *
143 create_unique_name (gpointer context)
144 {
145         const gchar *prefix = "__cg_autogen_name__";
146         static gint latest_suff = 0;
147         gchar *new_name;
148
149         for(;;latest_suff++) {
150                 new_name = g_strdup_printf("%s%i", prefix, latest_suff);
151                 if (color_group_get (new_name, context) == NULL)
152                         return new_name;
153                 else
154                         g_free(new_name);
155         }
156         g_assert_not_reached();
157 }
158
159 static void
160 color_group_finalize (GObject *obj)
161 {
162         ColorGroup *cg;
163
164         g_return_if_fail(obj != NULL);
165         g_return_if_fail(IS_COLOR_GROUP(obj));
166         g_assert(group_names != NULL);
167
168         cg = COLOR_GROUP (obj);
169
170         /* make this name available */
171         if (cg->name) {
172                 g_hash_table_remove (group_names, cg);
173                 g_free (cg->name);
174                 cg->name = NULL;
175         }
176
177         if (cg->history) {
178                 /* Free the whole colour history */
179                 while ((int) cg->history->len > 0)
180                         gdk_color_free ((GdkColor *)
181                                         g_ptr_array_remove_index (cg->history, 0));
182                 g_ptr_array_free (cg->history, TRUE);
183                 cg->history = NULL;
184         }
185         
186         if (parent_class->finalize)
187                 (parent_class->finalize) (obj);
188 }
189
190 /*
191  * color_group_get_history_size:
192  * Get the size of the custom color history
193  */
194 gint
195 color_group_get_history_size (ColorGroup *cg)
196 {
197         g_return_val_if_fail (cg != NULL, 0);
198         
199         return cg->history_size;
200 }
201
202 /*
203  * Change the size of the custom color history.
204  */
205 void
206 color_group_set_history_size (ColorGroup *cg, gint size)
207 {
208         g_return_if_fail(cg != NULL);
209         g_return_if_fail(size >= 0);
210
211         /* Remove excess elements (begin with kicking out the oldest) */
212         while ((int) cg->history->len > size)
213                 gdk_color_free ((GdkColor *) g_ptr_array_remove_index (cg->history, 0));
214 }
215
216 /*
217  * color_group_fetch :
218  * @name :
219  * @context :
220  *
221  * if name is NULL or a name not currently in use by another group
222  * then a new group is created and returned. If name was NULL
223  * then the new group is given a unique name prefixed by "__cg_autogen_name__"
224  * (thereby insuring namespace separation).
225  * If name was already used by a group then the reference count is
226  * incremented and a pointer to the group is returned.
227  */
228 ColorGroup *
229 color_group_fetch (const gchar *name, gpointer context)
230 {
231         ColorGroup *cg;
232         gchar *new_name;
233
234         if (group_names == NULL)
235                 initialize_group_names();
236
237         if (name == NULL)
238                 new_name = create_unique_name (context);
239         else
240                 new_name = g_strdup (name);
241
242         cg = color_group_get (new_name, context);
243         if (cg != NULL) {
244                 g_free (new_name);
245                 g_object_ref (G_OBJECT (cg));
246                 return cg;
247         }
248
249         /* Take care of creating the new object */
250         cg = g_object_new (color_group_get_type (), NULL);
251         g_return_val_if_fail(cg != NULL, NULL);
252
253         cg->name = new_name;
254         cg->context = context;
255
256         /* Create history */
257         cg->history = g_ptr_array_new ();
258
259         /* FIXME: Why not 8? We never use more then 8 on the palette,
260          * maybe we can't free colors while they are still on the palette and
261          * need to be sure they are not on it when we free them and thus we
262          * make the upper limit twice the size of the number of displayed items
263          * (2 * 8) ?
264          */
265         cg->history_size = 16; 
266
267         /* lastly register this name */
268         g_hash_table_insert (group_names, cg, cg);
269
270         return cg;
271 }
272
273 /*
274  * color_group_get_custom_colors:
275  * Retrieve all custom colors currently in the history using a callback
276  * mechanism. The custom colors will be passed from the oldest to the newest.
277  */
278 void
279 color_group_get_custom_colors (ColorGroup *cg, CbCustomColors callback, gpointer user_data)
280 {
281         int i;
282
283         g_return_if_fail (cg != NULL);
284
285         /* Invoke the callback for our full history */
286         for (i = 0; i < (int) cg->history->len; i++) {
287                 GdkColor const * const color = g_ptr_array_index (cg->history, i);
288
289                 callback (color, user_data);
290         }
291 }
292
293 /*
294  * color_group_add_color:
295  * Changes the colors. The color to be set should always be a custom
296  * color! It has no use adding a color which is already in the default
297  * palette.
298  */
299 void
300 color_group_add_color (ColorGroup *cg, GdkColor const * const color)
301 {
302         int i;
303         
304         g_return_if_fail(cg != NULL);
305         g_return_if_fail(color != NULL); /* Can't be NULL */
306
307         /* Let's be smart and see if it's already in our history, no need to add it again*/
308         for (i = 0; i < (int) cg->history->len; i++) {
309                 GdkColor *current = g_ptr_array_index (cg->history, i);
310                 
311                 if (gdk_color_equal (color, current))
312                         return;
313         }
314
315         /*
316          * We make our own private copy of the color passed and put
317          * it in the history, this is freed later.
318          */
319         if (cg->history_size > 0)
320                 g_ptr_array_add (cg->history, gdk_color_copy (color));
321
322         /* Shift out the oldest item if we grow beyond our set size */
323         if ((int) cg->history->len > cg->history_size)
324                 gdk_color_free ((GdkColor *) g_ptr_array_remove_index (cg->history, 0));
325
326         /* Tell color-palette's that use this group that
327          * a new custom color was added.
328          */
329         g_signal_emit (G_OBJECT(cg),
330                 color_group_signals [CUSTOM_COLOR_ADD],
331                 0,
332                 color);
333 }