1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
9 * Michael Levy (mlevy@genoscope.cns.fr)
10 * Revised and polished by:
11 * Almer S. Tigelaar <almer@gnome.org>
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.
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.
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
28 #include "color-group.h"
29 #include <glib-object.h>
30 #include <gdk/gdkcolor.h>
34 #define PARENT_TYPE G_TYPE_OBJECT
41 static GObjectClass *parent_class;
43 static GQuark color_group_signals [LAST_SIGNAL] = { 0 };
45 static void color_group_finalize (GObject *obj);
48 color_group_class_init (ColorGroupClass *klass)
50 GObjectClass *object_class;
52 object_class = (GObjectClass*) klass;
54 object_class->finalize = &color_group_finalize;
55 parent_class = g_type_class_peek (PARENT_TYPE);
57 color_group_signals [CUSTOM_COLOR_ADD] =
58 g_signal_new ("custom_color_add",
61 G_STRUCT_OFFSET (ColorGroupClass, custom_color_add),
62 (GSignalAccumulator) NULL, NULL,
63 g_cclosure_marshal_VOID__POINTER,
69 color_group_init (ColorGroup *cg)
76 E_MAKE_TYPE(color_group,
79 color_group_class_init,
84 /* Hash table used to ensure unicity in newly created names*/
85 static GHashTable *group_names = NULL;
88 cg_hash (gconstpointer key)
90 /* Do NOT use smart type checking it will not work for the tmp_key */
91 return g_str_hash (((ColorGroup *)key)->name);
95 cg_cmp (gconstpointer a, gconstpointer b)
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;
102 if (cg_a->context != cg_b->context)
104 return g_str_equal (cg_a->name, cg_b->name);
108 initialize_group_names (void)
110 g_assert (group_names == NULL);
111 group_names = g_hash_table_new (cg_hash, cg_cmp);
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.
123 color_group_get (const gchar * name, gpointer context)
128 g_assert(group_names);
130 g_return_val_if_fail(name != NULL, NULL);
132 tmp_key.name = (char *)name;
133 tmp_key.context = context;
134 res = g_hash_table_lookup (group_names, &tmp_key);
137 return COLOR_GROUP (res);
143 create_unique_name (gpointer context)
145 const gchar *prefix = "__cg_autogen_name__";
146 static gint latest_suff = 0;
149 for(;;latest_suff++) {
150 new_name = g_strdup_printf("%s%i", prefix, latest_suff);
151 if (color_group_get (new_name, context) == NULL)
156 g_assert_not_reached();
160 color_group_finalize (GObject *obj)
164 g_return_if_fail(obj != NULL);
165 g_return_if_fail(IS_COLOR_GROUP(obj));
166 g_assert(group_names != NULL);
168 cg = COLOR_GROUP (obj);
170 /* make this name available */
172 g_hash_table_remove (group_names, cg);
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);
186 if (parent_class->finalize)
187 (parent_class->finalize) (obj);
191 * color_group_get_history_size:
192 * Get the size of the custom color history
195 color_group_get_history_size (ColorGroup *cg)
197 g_return_val_if_fail (cg != NULL, 0);
199 return cg->history_size;
203 * Change the size of the custom color history.
206 color_group_set_history_size (ColorGroup *cg, gint size)
208 g_return_if_fail(cg != NULL);
209 g_return_if_fail(size >= 0);
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));
217 * color_group_fetch :
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.
229 color_group_fetch (const gchar *name, gpointer context)
234 if (group_names == NULL)
235 initialize_group_names();
238 new_name = create_unique_name (context);
240 new_name = g_strdup (name);
242 cg = color_group_get (new_name, context);
245 g_object_ref (G_OBJECT (cg));
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);
254 cg->context = context;
257 cg->history = g_ptr_array_new ();
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
265 cg->history_size = 16;
267 /* lastly register this name */
268 g_hash_table_insert (group_names, cg, cg);
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.
279 color_group_get_custom_colors (ColorGroup *cg, CbCustomColors callback, gpointer user_data)
283 g_return_if_fail (cg != NULL);
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);
289 callback (color, user_data);
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
300 color_group_add_color (ColorGroup *cg, GdkColor const * const color)
304 g_return_if_fail(cg != NULL);
305 g_return_if_fail(color != NULL); /* Can't be NULL */
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);
311 if (gdk_color_equal (color, current))
316 * We make our own private copy of the color passed and put
317 * it in the history, this is freed later.
319 if (cg->history_size > 0)
320 g_ptr_array_add (cg->history, gdk_color_copy (color));
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));
326 /* Tell color-palette's that use this group that
327 * a new custom color was added.
329 g_signal_emit (G_OBJECT(cg),
330 color_group_signals [CUSTOM_COLOR_ADD],