1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2012 Hiroyuki Yamamoto & The Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 /* (alfons) - based on a contribution by Satoshi Nagayasu; revised for colorful
21  * menu and more Sylpheed integration. The idea to put the code in a separate
22  * file is just that it make it easier to allow "user changeable" label colors.
23  */
24 
25 #include "defs.h"
26 
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 
32 #include "colorlabel.h"
33 #include "utils.h"
34 #include "gtkutils.h"
35 #include "prefs_common.h"
36 
37 static gchar *labels[COLORLABELS] = {
38 	N_("Orange"),
39 	N_("Red") ,
40 	N_("Pink"),
41 	N_("Sky blue"),
42 	N_("Blue"),
43 	N_("Green"),
44 	N_("Brown"),
45 	N_("Grey"),
46 	N_("Light brown"),
47 	N_("Dark red"),
48 	N_("Dark pink"),
49 	N_("Steel blue"),
50 	N_("Gold"),
51 	N_("Bright green"),
52 	N_("Magenta")
53 };
54 
55 static GdkColor default_colors[COLORLABELS] = {
56 	{ 0, 0xffff, (0x99 << 8), 0x0 },
57 	{ 0, 0xffff, 0x0, 0x0 },
58 	{ 0, 0xffff, (0x66 << 8), 0xffff },
59 	{ 0, 0x0, (0xcc << 8), 0xffff },
60 	{ 0, 0x0, 0x0, 0xffff },
61 	{ 0, 0x0, (0x99 << 8), 0x0 },
62 	{ 0, (0x66 << 8), (0x33 << 8), (0x33 << 8) },
63 	{ 0, (0xaa << 8), (0xaa << 8), (0xaa << 8) },
64 	{ 0, (0xc0 << 8), (0x72 << 8), (0x54 << 8) },
65 	{ 0, (0xc0 << 8), 0x0, 0x0 },
66 	{ 0, (0xcc << 8), (0x10 << 8), (0x74 << 8) },
67 	{ 0, (0x50 << 8), (0x94 << 8), (0xcd << 8) },
68 	{ 0, 0xffff, (0xd5 << 8), 0x0 },
69 	{ 0, 0x0, (0xd8 << 8), 0x0 },
70 	{ 0, (0xc0 << 8), (0x60 << 8), (0xc0 << 8) }
71 };
72 
73 
74 typedef enum LabelColorChangeFlags_ {
75 	LCCF_COLOR = 1 << 0,
76 	LCCF_LABEL = 1 << 1,
77 	LCCF_ALL   = LCCF_COLOR | LCCF_LABEL
78 } LabelColorChangeFlags;
79 
80 /* XXX: if you add colors, make sure you also check the procmsg.h.
81  * color indices are stored as 3 bits; that explains the max. of 7 colors */
82 static struct
83 {
84 	LabelColorChangeFlags	changed;
85 	/* color here is initialized from default_colors[] at startup */
86 	GdkColor		color;
87 
88 	/* XXX: note that the label member is supposed to be dynamically
89 	 * allocated and freed */
90 	gchar			*label;
91 	GtkWidget		*widget;
92 } label_colors[NUM_MENUS][COLORLABELS] = {
93     {
94 	{ LCCF_ALL, { 0 }, NULL, NULL },
95 	{ LCCF_ALL, { 0 }, NULL, NULL },
96 	{ LCCF_ALL, { 0 }, NULL, NULL },
97 	{ LCCF_ALL, { 0 }, NULL, NULL },
98 	{ LCCF_ALL, { 0 }, NULL, NULL },
99 	{ LCCF_ALL, { 0 }, NULL, NULL },
100 	{ LCCF_ALL, { 0 }, NULL, NULL },
101 	{ LCCF_ALL, { 0 }, NULL, NULL },
102 	{ LCCF_ALL, { 0 }, NULL, NULL },
103 	{ LCCF_ALL, { 0 }, NULL, NULL },
104 	{ LCCF_ALL, { 0 }, NULL, NULL },
105 	{ LCCF_ALL, { 0 }, NULL, NULL },
106 	{ LCCF_ALL, { 0 }, NULL, NULL },
107 	{ LCCF_ALL, { 0 }, NULL, NULL },
108 	{ LCCF_ALL, { 0 }, NULL, NULL }},
109     {
110 	{ LCCF_ALL, { 0 }, NULL, NULL },
111 	{ LCCF_ALL, { 0 }, NULL, NULL },
112 	{ LCCF_ALL, { 0 }, NULL, NULL },
113 	{ LCCF_ALL, { 0 }, NULL, NULL },
114 	{ LCCF_ALL, { 0 }, NULL, NULL },
115 	{ LCCF_ALL, { 0 }, NULL, NULL },
116 	{ LCCF_ALL, { 0 }, NULL, NULL },
117 	{ LCCF_ALL, { 0 }, NULL, NULL },
118 	{ LCCF_ALL, { 0 }, NULL, NULL },
119 	{ LCCF_ALL, { 0 }, NULL, NULL },
120 	{ LCCF_ALL, { 0 }, NULL, NULL },
121 	{ LCCF_ALL, { 0 }, NULL, NULL },
122 	{ LCCF_ALL, { 0 }, NULL, NULL },
123 	{ LCCF_ALL, { 0 }, NULL, NULL },
124 	{ LCCF_ALL, { 0 }, NULL, NULL }}
125 };
126 
127 #define LABEL_COLOR_WIDTH	28
128 #define LABEL_COLOR_HEIGHT	16
129 
130 #define LABEL_COLORS_ELEMS (sizeof label_colors[0] / sizeof label_colors[0][0])
131 
132 #define G_RETURN_VAL_IF_INVALID_COLOR(color, val) \
133 	do if ((color) < 0 || (color) >= LABEL_COLORS_ELEMS) {	\
134 		return val;				    	\
135 	} while(0)
136 
137 #define INTCOLOR_TO_GDKCOLOR(intcolor, gdkcolor) \
138 	gdkcolor.red   = ((intcolor >> 16UL) & 0xFFUL) << 8UL; \
139 	gdkcolor.green = ((intcolor >>  8UL) & 0xFFUL) << 8UL; \
140 	gdkcolor.blue  = ((intcolor)         & 0xFFUL) << 8UL;
141 
142 static void colorlabel_recreate        (gint);
143 static void colorlabel_recreate_label  (gint);
144 
colorlabel_update_colortable_from_prefs(void)145 void colorlabel_update_colortable_from_prefs(void)
146 {
147 	gint i, c;
148 
149 	for (i = 0; i < NUM_MENUS; i++) {
150 		for (c = 0; c < COLORLABELS; c++) {
151 			INTCOLOR_TO_GDKCOLOR(prefs_common.custom_colorlabel[c].color,
152 					label_colors[i][c].color);
153 			g_free(label_colors[i][c].label);
154 			label_colors[i][c].label =
155 					g_strdup(prefs_common.custom_colorlabel[c].label);
156 		}
157 	}
158 }
159 
160 
colorlabel_get_color_count(void)161 gint colorlabel_get_color_count(void)
162 {
163 	return LABEL_COLORS_ELEMS;
164 }
165 
colorlabel_get_color(gint color_index)166 GdkColor colorlabel_get_color(gint color_index)
167 {
168 	GdkColor invalid = { 0 };
169 
170 	G_RETURN_VAL_IF_INVALID_COLOR(color_index, invalid);
171 
172 	return label_colors[0][color_index].color;
173 }
174 
colorlabel_get_default_color(gint color_index)175 GdkColor colorlabel_get_default_color(gint color_index)
176 {
177 	GdkColor invalid = { 0 };
178 
179 	G_RETURN_VAL_IF_INVALID_COLOR(color_index, invalid);
180 
181 	return default_colors[color_index];
182 }
183 
colorlabel_get_color_default_text(gint color_index)184 gchar *colorlabel_get_color_default_text(gint color_index)
185 {
186 	G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL);
187 
188 	return labels[color_index];
189 }
190 
colorlabel_drawing_area_expose_event_cb(GtkWidget * widget,GdkEventExpose * expose,gpointer data)191 static gboolean colorlabel_drawing_area_expose_event_cb
192 	(GtkWidget *widget, GdkEventExpose *expose, gpointer data)
193 {
194 	cairo_t *cr;
195 	GdkWindow *drawable = gtk_widget_get_window(widget);
196 	GtkAllocation allocation;
197 	gulong c = (gulong) GPOINTER_TO_INT(data);
198 	GdkColor color;
199 
200 	INTCOLOR_TO_GDKCOLOR(c, color)
201 
202 	gdk_colormap_alloc_color(gtk_widget_get_colormap(widget), &color, FALSE, TRUE);
203 	cr = gdk_cairo_create(drawable);
204 	gtk_widget_get_allocation(widget, &allocation);
205 
206 	cairo_set_source_rgb(cr, 0., 0., 0.);
207 	cairo_rectangle(cr, 0, 0,
208 	    allocation.width - 1,
209 	    allocation.height - 1);
210 	cairo_stroke(cr);
211 	gdk_cairo_set_source_color(cr, &color);
212 	cairo_rectangle(cr, 1, 1,
213 	    allocation.width - 2,
214 	    allocation.height - 2);
215 	cairo_fill(cr);
216 	cairo_destroy(cr);
217 
218 	return FALSE;
219 }
220 
colorlabel_create_color_widget(GdkColor color)221 static GtkWidget *colorlabel_create_color_widget(GdkColor color)
222 {
223 	GtkWidget *widget;
224 
225 	widget = gtk_drawing_area_new();
226 	gtk_widget_set_size_request(widget, LABEL_COLOR_WIDTH - 2,
227 				    LABEL_COLOR_HEIGHT - 4);
228 
229 #define CL(x)		(((gulong) (x) >> (gulong) 8) & 0xFFUL)
230 #define CR(r, g, b)	((CL(r) << (gulong) 16) | \
231 			 (CL(g) << (gulong)  8) | \
232 			 (CL(b)))
233 
234 	g_signal_connect(G_OBJECT(widget), "expose_event",
235 			 G_CALLBACK
236 			   	(colorlabel_drawing_area_expose_event_cb),
237 			 GINT_TO_POINTER
238 			   	((gint)CR(color.red, color.green, color.blue)));
239 
240 	return widget;
241 }
242 
243 /* XXX: colorlabel_recreate_XXX are there to make sure everything
244  * is initialized ok, without having to call a global _xxx_init_
245  * function */
colorlabel_recreate_color(gint color)246 static void colorlabel_recreate_color(gint color)
247 {
248 	GtkWidget *widget;
249 	int i;
250 
251 	for (i = 0; i < NUM_MENUS; i++) {
252 		if (!(label_colors[i][color].changed & LCCF_COLOR))
253 			continue;
254 
255 		widget = colorlabel_create_color_widget(label_colors[i][color].color);
256 		cm_return_if_fail(widget);
257 
258 		if (label_colors[i][color].widget)
259 			gtk_widget_destroy(label_colors[i][color].widget);
260 
261 		label_colors[i][color].widget = widget;
262 		label_colors[i][color].changed &= ~LCCF_COLOR;
263 	}
264 }
265 
colorlabel_recreate_label(gint color)266 static void colorlabel_recreate_label(gint color)
267 {
268 	int i;
269 
270 	for (i = 0; i < NUM_MENUS; i++) {
271 		if (!(label_colors[i][color].changed & LCCF_LABEL))
272 			continue;
273 
274 		if (label_colors[i][color].label == NULL)
275 			label_colors[i][color].label = g_strdup(gettext(labels[color]));
276 
277 		label_colors[i][color].changed &= ~LCCF_LABEL;
278 	}
279 }
280 
281 /* XXX: call this function everytime when you're doing important
282  * stuff with the label_colors[] array */
colorlabel_recreate(gint color)283 static void colorlabel_recreate(gint color)
284 {
285 	colorlabel_recreate_label(color);
286 	colorlabel_recreate_color(color);
287 }
288 
colorlabel_recreate_all(void)289 static void colorlabel_recreate_all(void)
290 {
291 	gint n;
292 
293 	for ( n = 0; n < LABEL_COLORS_ELEMS; n++)
294 		colorlabel_recreate(n);
295 }
296 
297 /* colorlabel_create_check_color_menu_item() - creates a color
298  * menu item with a check box */
colorlabel_create_check_color_menu_item(gint color_index,gboolean force,gint menu_index)299 GtkWidget *colorlabel_create_check_color_menu_item(gint color_index, gboolean force, gint menu_index)
300 {
301 	GtkWidget *label;
302 	GtkWidget *hbox;
303 	GtkWidget *vbox;
304 	GtkWidget *item;
305 	gchar *accel;
306 
307 	G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL);
308 
309 	item = gtk_check_menu_item_new();
310 
311 	if (force) {
312 		label_colors[menu_index][color_index].changed |= LCCF_COLOR;
313 		label_colors[menu_index][color_index].changed |= LCCF_LABEL;
314 	}
315 	colorlabel_recreate(color_index);
316 
317 	/* XXX: gnome-core::panel::menu.c is a great example of
318 	 * how to create pixmap menus */
319 	label = gtk_label_new(label_colors[menu_index][color_index].label);
320 
321 	gtk_widget_show(label);
322 	hbox = gtk_hbox_new(FALSE, 0);
323 	gtk_widget_show(hbox);
324 	gtk_container_add(GTK_CONTAINER(item), hbox);
325 
326 	vbox = gtk_vbox_new(TRUE, 0);
327 	gtk_widget_show(vbox);
328 	gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
329 
330 	gtk_container_add(GTK_CONTAINER(vbox),
331 			  label_colors[menu_index][color_index].widget);
332 	gtk_widget_show(label_colors[menu_index][color_index].widget);
333 
334 	gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
335 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
336 	if (color_index < 9) {
337 		accel = gtk_accelerator_get_label(GDK_KEY_1+color_index, GDK_CONTROL_MASK);
338 		label = gtk_label_new(accel);
339 		gtk_widget_show(label);
340 		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
341 		g_free(accel);
342 		gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
343 		g_object_set_data(G_OBJECT(item), "accel_label", label);
344 	} else {
345 		label = gtk_label_new("");
346 		gtk_widget_show(label);
347 		gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
348 		gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
349 		g_object_set_data(G_OBJECT(item), "accel_label", label);
350 	}
351 	return item;
352 }
353 
354 /* colorlabel_create_color_menu() - creates a color menu without
355  * checkitems, probably for use in combo items */
colorlabel_create_color_menu(void)356 GtkWidget *colorlabel_create_color_menu(void)
357 {
358 	GtkWidget *label;
359 	GtkWidget *item;
360 	GtkWidget *menu;
361 	gint i;
362 
363 	colorlabel_recreate_all();
364 
365 	/* create the menu items. each item has its color code attached */
366 	menu = gtk_menu_new();
367 	g_object_set_data(G_OBJECT(menu), "label_color_menu", menu);
368 
369 	item = gtk_menu_item_new_with_label(_("None"));
370 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
371 	g_object_set_data(G_OBJECT(item), "color", GUINT_TO_POINTER(0));
372 	gtk_widget_show(item);
373 
374 	item = gtk_separator_menu_item_new();
375 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
376 	gtk_widget_show(item);
377 
378 	/* and the color items */
379 	for (i = 0; i < LABEL_COLORS_ELEMS; i++) {
380 		GtkWidget *hbox;
381 		GtkWidget *vbox;
382 		GtkWidget *widget;
383 
384 		item  = gtk_menu_item_new();
385 		g_object_set_data(G_OBJECT(item), "color",
386 				  GUINT_TO_POINTER(i + 1));
387 
388 		label = gtk_label_new(label_colors[0][i].label);
389 
390 		gtk_widget_show(label);
391 		hbox = gtk_hbox_new(FALSE, 0);
392 		gtk_widget_show(hbox);
393 		gtk_container_add(GTK_CONTAINER(item), hbox);
394 
395 		vbox = gtk_vbox_new(TRUE, 0);
396 		gtk_widget_show(vbox);
397 		gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
398 
399 		widget = colorlabel_create_color_widget(label_colors[0][i].color);
400 		gtk_widget_show(widget);
401 		gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
402 
403 		gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
404 		gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
405 
406 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
407 		gtk_widget_show(item);
408 	}
409 
410 	gtk_widget_show(menu);
411 
412 	return menu;
413 }
414 
colorlabel_get_color_menu_active_item(GtkWidget * menu)415 guint colorlabel_get_color_menu_active_item(GtkWidget *menu)
416 {
417 	GtkWidget *menuitem;
418 	guint color;
419 
420 	menuitem = gtk_menu_get_active(GTK_MENU(menu));
421 	color = GPOINTER_TO_UINT
422 		(g_object_get_data(G_OBJECT(menuitem), "color"));
423 	return color;
424 }
425