1 /*
2  * style-color.c: Color allocation on the Gnumeric spreadsheet
3  *
4  * Author:
5  *  Miguel de Icaza (miguel@kernel.org)
6  *
7  */
8 #include <gnumeric-config.h>
9 #include <gnumeric.h>
10 #include <style-color.h>
11 #include <style-border.h>
12 #include <gui-util.h>
13 
14 static GHashTable *style_color_hash;
15 static GnmColor *sc_black;
16 static GnmColor *sc_white;
17 static GnmColor *sc_auto_back;
18 static GnmColor *sc_auto_font;
19 static GnmColor *sc_auto_pattern;
20 
21 static GnmColor *
gnm_color_make(GOColor c,gboolean is_auto)22 gnm_color_make (GOColor c, gboolean is_auto)
23 {
24 	GnmColor key, *sc;
25 
26 	is_auto = !!is_auto;
27 
28 	key.go_color = c;
29 	key.is_auto = is_auto;
30 
31 	sc = g_hash_table_lookup (style_color_hash, &key);
32 	if (!sc) {
33 		sc = g_new (GnmColor, 1);
34 		sc->go_color = c;
35 		sc->is_auto = is_auto;
36 		sc->ref_count = 1;
37 
38 		g_hash_table_insert (style_color_hash, sc, sc);
39 	} else
40 		sc->ref_count++;
41 
42 	return sc;
43 }
44 
45 GnmColor *
gnm_color_new_rgba16(guint16 red,guint16 green,guint16 blue,guint16 alpha)46 gnm_color_new_rgba16 (guint16 red, guint16 green, guint16 blue, guint16 alpha)
47 {
48 	return gnm_color_new_rgba8 (red >> 8, green >> 8, blue >> 8, alpha >> 8);
49 }
50 
51 GnmColor *
gnm_color_new_pango(PangoColor const * c)52 gnm_color_new_pango (PangoColor const *c)
53 {
54 	return gnm_color_new_rgba16 (c->red, c->green, c->blue, 0xffff);
55 }
56 
57 GnmColor *
gnm_color_new_gdk(GdkRGBA const * c)58 gnm_color_new_gdk (GdkRGBA const *c)
59 {
60 	/*
61 	 * The important property here is that a color #rrggbb
62 	 * (i.e., an 8-bit color) roundtrips correctly when
63 	 * translated into GdkRGBA using /255 and back.  Using
64 	 * multiplication by 256 here makes rounding unnecessary.
65 	 */
66 
67 	guint8 r8 = CLAMP (c->red * 256, 0, 255);
68 	guint8 g8 = CLAMP (c->green * 256, 0, 255);
69 	guint8 b8 = CLAMP (c->blue * 256, 0, 255);
70 	guint8 a8 = CLAMP (c->alpha * 256, 0, 255);
71 
72 	return gnm_color_new_rgba8 (r8, g8, b8, a8);
73 }
74 
75 GnmColor *
gnm_color_new_rgba8(guint8 red,guint8 green,guint8 blue,guint8 alpha)76 gnm_color_new_rgba8 (guint8 red, guint8 green, guint8 blue, guint8 alpha)
77 {
78 	return gnm_color_new_go (GO_COLOR_FROM_RGBA (red, green, blue, alpha));
79 }
80 
81 GnmColor *
gnm_color_new_rgb8(guint8 red,guint8 green,guint8 blue)82 gnm_color_new_rgb8 (guint8 red, guint8 green, guint8 blue)
83 {
84 	return gnm_color_new_rgba8 (red, green, blue, 0xff);
85 }
86 
87 GnmColor *
gnm_color_new_go(GOColor c)88 gnm_color_new_go (GOColor c)
89 {
90 	return gnm_color_make (c, FALSE);
91 }
92 
93 GnmColor *
gnm_color_new_auto(GOColor c)94 gnm_color_new_auto (GOColor c)
95 {
96 	return gnm_color_make (c, TRUE);
97 }
98 
99 GnmColor *
style_color_black(void)100 style_color_black (void)
101 {
102 	if (!sc_black)
103 		sc_black = gnm_color_new_rgb8 (0, 0, 0);
104 	return style_color_ref (sc_black);
105 }
106 
107 GnmColor *
style_color_white(void)108 style_color_white (void)
109 {
110 	if (!sc_white)
111 		sc_white = gnm_color_new_rgb8 (0xff, 0xff, 0xff);
112 	return style_color_ref (sc_white);
113 }
114 
115 GnmColor *
style_color_grid(GtkStyleContext * context)116 style_color_grid (GtkStyleContext *context)
117 {
118 	if (context) {
119 		GdkRGBA color;
120 		gtk_style_context_save (context);
121 		gtk_style_context_add_class (context, "grid");
122 		gnm_style_context_get_color (context, GTK_STATE_FLAG_NORMAL,
123 					     &color);
124 		gnm_css_debug_color ("grid.color", &color);
125 		gtk_style_context_restore (context);
126 		return gnm_color_new_gdk (&color);
127 	} else
128 		return gnm_color_new_rgb8 (0xc7, 0xc7, 0xc7);
129 }
130 
131 /*
132  * Support for Excel auto-colors.
133  */
134 
135 /**
136  * style_color_auto_font:
137  *
138  * Always black, as far as we know.
139  */
140 GnmColor *
style_color_auto_font(void)141 style_color_auto_font (void)
142 {
143 	if (!sc_auto_font)
144 		sc_auto_font = gnm_color_new_auto (GO_COLOR_BLACK);
145 	return style_color_ref (sc_auto_font);
146 }
147 
148 /**
149  * style_color_auto_back:
150  *
151  * Always white, as far as we know.
152  */
153 GnmColor *
style_color_auto_back(void)154 style_color_auto_back (void)
155 {
156 	if (!sc_auto_back)
157 		sc_auto_back = gnm_color_new_auto (GO_COLOR_WHITE);
158 	return style_color_ref (sc_auto_back);
159 }
160 
161 /**
162  * style_color_auto_pattern:
163  *
164  * Normally black, but follows grid color if so told.
165  */
166 GnmColor *
style_color_auto_pattern(void)167 style_color_auto_pattern (void)
168 {
169 	if (!sc_auto_pattern)
170 		sc_auto_pattern = gnm_color_new_auto (GO_COLOR_BLACK);
171 	return style_color_ref (sc_auto_pattern);
172 }
173 
174 GnmColor *
style_color_ref(GnmColor * sc)175 style_color_ref (GnmColor *sc)
176 {
177 	if (sc != NULL)
178 		sc->ref_count++;
179 
180 	return sc;
181 }
182 
183 void
style_color_unref(GnmColor * sc)184 style_color_unref (GnmColor *sc)
185 {
186 	if (sc == NULL)
187 		return;
188 
189 	g_return_if_fail (sc->ref_count > 0);
190 
191 	sc->ref_count--;
192 	if (sc->ref_count != 0)
193 		return;
194 
195 	g_hash_table_remove (style_color_hash, sc);
196 	g_free (sc);
197 }
198 
199 GType
gnm_color_get_type(void)200 gnm_color_get_type (void)
201 {
202 	static GType t = 0;
203 
204 	if (t == 0)
205 		t = g_boxed_type_register_static ("GnmColor",
206 			 (GBoxedCopyFunc)style_color_ref,
207 			 (GBoxedFreeFunc)style_color_unref);
208 	return t;
209 }
210 
211 gint
style_color_equal(GnmColor const * k1,GnmColor const * k2)212 style_color_equal (GnmColor const *k1, GnmColor const *k2)
213 {
214 	return (k1->go_color == k2->go_color &&
215 		k1->is_auto == k2->is_auto);
216 }
217 
218 static guint
color_hash(gconstpointer v)219 color_hash (gconstpointer v)
220 {
221 	GnmColor const *k = (GnmColor const *)v;
222 	return k->go_color ^ k->is_auto;
223 }
224 
225 /**
226  * gnm_color_init: (skip)
227  */
228 void
gnm_color_init(void)229 gnm_color_init (void)
230 {
231 	style_color_hash = g_hash_table_new (color_hash,
232 					     (GEqualFunc)style_color_equal);
233 }
234 
235 static void
cb_color_leak(gpointer key,gpointer value,gpointer user_data)236 cb_color_leak (gpointer key, gpointer value, gpointer user_data)
237 {
238 	GnmColor *color = value;
239 
240 	g_printerr ("Leaking style-color at %p [%08x].\n",
241 		    (void *)color,
242 		    color->go_color);
243 }
244 
245 /**
246  * gnm_color_shutdown: (skip)
247  */
248 void
gnm_color_shutdown(void)249 gnm_color_shutdown (void)
250 {
251 	style_color_unref (sc_black);
252 	sc_black = NULL;
253 
254 	style_color_unref (sc_white);
255 	sc_white = NULL;
256 
257 	style_color_unref (sc_auto_back);
258 	sc_auto_back = NULL;
259 
260 	style_color_unref (sc_auto_font);
261 	sc_auto_font = NULL;
262 
263 	style_color_unref (sc_auto_pattern);
264 	sc_auto_pattern = NULL;
265 
266 	g_hash_table_foreach (style_color_hash, cb_color_leak, NULL);
267 	g_hash_table_destroy (style_color_hash);
268 	style_color_hash = NULL;
269 }
270