f8d84036b4bc4bfb8dbc4573d28be3e38cef59f0
[claws.git] / src / gtk / colorlabel.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2007 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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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 <gtk/gtkwidget.h>
31 #include <gtk/gtkimage.h>
32 #include <gtk/gtkmenu.h>
33 #include <gtk/gtkcheckmenuitem.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkalignment.h>
37 #include <gtk/gtkhbox.h>
38 #include <gtk/gtkvbox.h>
39 #include <gtk/gtkwindow.h>
40 #include <gtk/gtkdrawingarea.h>
41
42 #include "colorlabel.h"
43 #include "utils.h"
44 #include "gtkutils.h"
45 #include "prefs_common.h"
46
47 static gchar *labels[COLORLABELS] = {
48         N_("Orange"),
49         N_("Red") ,
50         N_("Pink"),
51         N_("Sky blue"),
52         N_("Blue"),
53         N_("Green"),
54         N_("Brown"),
55         N_("Grey"),
56         N_("Light brown"),
57         N_("Dark red"),
58         N_("Dark pink"),
59         N_("Steel blue"),
60         N_("Gold"),
61         N_("Bright green"),
62         N_("Magenta")
63 };
64
65 static GdkColor default_colors[COLORLABELS] = {
66         { 0, 0xffff, (0x99 << 8), 0x0 },
67         { 0, 0xffff, 0x0, 0x0 },
68         { 0, 0xffff, (0x66 << 8), 0xffff },
69         { 0, 0x0, (0xcc << 8), 0xffff },
70         { 0, 0x0, 0x0, 0xffff },
71         { 0, 0x0, (0x99 << 8), 0x0 },
72         { 0, (0x66 << 8), (0x33 << 8), (0x33 << 8) },
73         { 0, (0xaa << 8), (0xaa << 8), (0xaa << 8) },
74         { 0, (0xc0 << 8), (0x72 << 8), (0x54 << 8) },
75         { 0, (0xc0 << 8), 0x0, 0x0 },
76         { 0, (0xcc << 8), (0x10 << 8), (0x74 << 8) },
77         { 0, (0x50 << 8), (0x94 << 8), (0xcd << 8) },
78         { 0, 0xffff, (0xd5 << 8), 0x0 },
79         { 0, 0x0, (0xd8 << 8), 0x0 },
80         { 0, (0xc0 << 8), (0x60 << 8), (0xc0 << 8) }
81 };
82
83         
84 typedef enum LabelColorChangeFlags_ {
85         LCCF_COLOR = 1 << 0,
86         LCCF_LABEL = 1 << 1,
87         LCCF_ALL   = LCCF_COLOR | LCCF_LABEL
88 } LabelColorChangeFlags;
89
90 /* XXX: if you add colors, make sure you also check the procmsg.h.
91  * color indices are stored as 3 bits; that explains the max. of 7 colors */
92 static struct 
93 {
94         LabelColorChangeFlags   changed; 
95         /* color here is initialized from default_colors[] at startup */
96         GdkColor                color;
97
98         /* XXX: note that the label member is supposed to be dynamically 
99          * allocated and freed */
100         gchar                   *label;
101         GtkWidget               *widget;
102 } label_colors[NUM_MENUS][COLORLABELS] = {
103     {
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         { LCCF_ALL, { 0 }, NULL, NULL },
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     {
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         { LCCF_ALL, { 0 }, NULL, NULL },
126         { LCCF_ALL, { 0 }, NULL, NULL },
127         { LCCF_ALL, { 0 }, NULL, NULL },
128         { LCCF_ALL, { 0 }, NULL, NULL },
129         { LCCF_ALL, { 0 }, NULL, NULL },
130         { LCCF_ALL, { 0 }, NULL, NULL },
131         { LCCF_ALL, { 0 }, NULL, NULL },
132         { LCCF_ALL, { 0 }, NULL, NULL },
133         { LCCF_ALL, { 0 }, NULL, NULL },
134         { LCCF_ALL, { 0 }, NULL, NULL }}
135 };
136
137 #define LABEL_COLOR_WIDTH       28
138 #define LABEL_COLOR_HEIGHT      16
139
140 #define LABEL_COLORS_ELEMS (sizeof label_colors[0] / sizeof label_colors[0][0])
141
142 #define G_RETURN_VAL_IF_INVALID_COLOR(color, val) \
143         g_return_val_if_fail((color) >= 0 && (color) < LABEL_COLORS_ELEMS, (val))
144
145 #define INTCOLOR_TO_GDKCOLOR(intcolor, gdkcolor) \
146         gdkcolor.red   = ((intcolor >> 16UL) & 0xFFUL) << 8UL; \
147         gdkcolor.green = ((intcolor >>  8UL) & 0xFFUL) << 8UL; \
148         gdkcolor.blue  = ((intcolor)         & 0xFFUL) << 8UL;
149
150 static void colorlabel_recreate        (gint);
151 static void colorlabel_recreate_label  (gint);
152
153 void colorlabel_update_colortable_from_prefs(void)
154 {
155         gint i, c;
156
157         for (i = 0; i < NUM_MENUS; i++) {
158                 for (c = 0; c < COLORLABELS; c++) {
159                         INTCOLOR_TO_GDKCOLOR(prefs_common.custom_colorlabel[c].color,
160                                         label_colors[i][c].color);
161                         g_free(label_colors[i][c].label);
162                         label_colors[i][c].label =
163                                         g_strdup(prefs_common.custom_colorlabel[c].label);
164                 }
165         }
166 }
167
168
169 gint colorlabel_get_color_count(void)
170 {
171         return LABEL_COLORS_ELEMS;
172 }
173
174 GdkColor colorlabel_get_color(gint color_index)
175 {
176         GdkColor invalid = { 0 };
177
178         G_RETURN_VAL_IF_INVALID_COLOR(color_index, invalid);
179
180         return label_colors[0][color_index].color;
181 }
182
183 GdkColor colorlabel_get_default_color(gint color_index)
184 {
185         GdkColor invalid = { 0 };
186
187         G_RETURN_VAL_IF_INVALID_COLOR(color_index, invalid);
188
189         return default_colors[color_index];
190 }
191                 
192 gchar *colorlabel_get_color_default_text(gint color_index)
193 {
194         G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL);
195
196         return labels[color_index];
197 }
198
199 static gboolean colorlabel_drawing_area_expose_event_cb
200         (GtkWidget *widget, GdkEventExpose *expose, gpointer data)
201 {
202         GdkDrawable *drawable = widget->window;
203         gulong c = (gulong) GPOINTER_TO_INT(data);
204         GdkColor color;
205         GdkGC *gc;
206
207         INTCOLOR_TO_GDKCOLOR(c, color)
208
209         gdk_colormap_alloc_color(gtk_widget_get_colormap(widget), &color, FALSE, TRUE);
210
211         gc = gdk_gc_new(drawable);
212
213         gdk_gc_set_foreground(gc, &color);
214         gdk_draw_rectangle(drawable, widget->style->black_gc,
215                            FALSE, 0, 0, widget->allocation.width - 1,
216                            widget->allocation.height - 1);
217         gdk_draw_rectangle(drawable, gc,
218                            TRUE, 1, 1, widget->allocation.width - 2,
219                            widget->allocation.height - 2);
220
221         gdk_gc_unref(gc);                          
222         
223         return FALSE;
224 }
225
226 static GtkWidget *colorlabel_create_color_widget(GdkColor color)
227 {
228         GtkWidget *widget;
229
230         widget = gtk_drawing_area_new();
231         gtk_widget_set_size_request(widget, LABEL_COLOR_WIDTH - 2, 
232                                     LABEL_COLOR_HEIGHT - 4);
233
234 #define CL(x)           (((gulong) (x) >> (gulong) 8) & 0xFFUL) 
235 #define CR(r, g, b)     ((CL(r) << (gulong) 16) | \
236                          (CL(g) << (gulong)  8) | \
237                          (CL(b)))
238
239         g_signal_connect(G_OBJECT(widget), "expose_event", 
240                          G_CALLBACK
241                                 (colorlabel_drawing_area_expose_event_cb),
242                          GINT_TO_POINTER
243                                 ((gint)CR(color.red, color.green, color.blue)));
244
245         return widget;
246 }
247
248 /* XXX: colorlabel_recreate_XXX are there to make sure everything
249  * is initialized ok, without having to call a global _xxx_init_
250  * function */
251 static void colorlabel_recreate_color(gint color)
252 {
253         GtkWidget *widget;
254         int i;
255         
256         for (i = 0; i < NUM_MENUS; i++) {
257                 if (!(label_colors[i][color].changed & LCCF_COLOR))
258                         continue;
259
260                 widget = colorlabel_create_color_widget(label_colors[i][color].color);
261                 g_return_if_fail(widget);
262
263                 if (label_colors[i][color].widget) 
264                         gtk_widget_destroy(label_colors[i][color].widget);
265
266                 label_colors[i][color].widget = widget;         
267                 label_colors[i][color].changed &= ~LCCF_COLOR;
268         }
269 }
270
271 static void colorlabel_recreate_label(gint color)
272 {
273         int i;
274         
275         for (i = 0; i < NUM_MENUS; i++) {
276                 if (!label_colors[i][color].changed & LCCF_LABEL)
277                         continue;
278
279                 if (label_colors[i][color].label == NULL) 
280                         label_colors[i][color].label = g_strdup(gettext(labels[color]));
281
282                 label_colors[i][color].changed &= ~LCCF_LABEL;
283         }
284 }
285
286 /* XXX: call this function everytime when you're doing important
287  * stuff with the label_colors[] array */
288 static void colorlabel_recreate(gint color)
289 {
290         colorlabel_recreate_label(color);
291         colorlabel_recreate_color(color);
292 }
293
294 static void colorlabel_recreate_all(void)
295 {
296         gint n;
297
298         for ( n = 0; n < LABEL_COLORS_ELEMS; n++) 
299                 colorlabel_recreate(n);
300 }
301
302 /* colorlabel_create_check_color_menu_item() - creates a color
303  * menu item with a check box */
304 GtkWidget *colorlabel_create_check_color_menu_item(gint color_index, gboolean force, gint menu_index)
305 {
306         GtkWidget *label; 
307         GtkWidget *hbox; 
308         GtkWidget *vbox; 
309         GtkWidget *item;
310         gchar *accel;
311         
312         G_RETURN_VAL_IF_INVALID_COLOR(color_index, NULL);
313
314         item = gtk_check_menu_item_new();
315
316         if (force) {
317                 label_colors[menu_index][color_index].changed |= LCCF_COLOR;
318                 label_colors[menu_index][color_index].changed |= LCCF_LABEL;
319         }
320         colorlabel_recreate(color_index);
321
322         /* XXX: gnome-core::panel::menu.c is a great example of
323          * how to create pixmap menus */
324         label = gtk_label_new(label_colors[menu_index][color_index].label);
325
326         gtk_widget_show(label);
327         hbox = gtk_hbox_new(FALSE, 0);
328         gtk_widget_show(hbox);
329         gtk_container_add(GTK_CONTAINER(item), hbox);
330
331         vbox = gtk_vbox_new(TRUE, 0);
332         gtk_widget_show(vbox);
333         gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
334
335         gtk_container_add(GTK_CONTAINER(vbox),
336                           label_colors[menu_index][color_index].widget);
337         gtk_widget_show(label_colors[menu_index][color_index].widget);
338
339         gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
340         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
341         if (color_index < 9) {
342                 accel = g_strdup_printf("Ctrl+%c", '1'+color_index);
343                 label = gtk_label_new(accel);
344                 gtk_widget_show(label);
345                 gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
346                 g_free(accel);
347                 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 4);
348         }
349         return item;
350 }
351
352 /* Work around a gtk bug (?): without that, the selected menu item's 
353  * colored rectangle is drawn at 0,0 in the window...
354  */
355 static void refresh_menu (GtkWidget *menushell, gpointer data)
356 {
357         GtkMenu *menu = (GtkMenu *)data;
358         GtkWidget *widget = gtk_menu_get_attach_widget(menu);
359         gtk_widget_hide_all(widget);
360         gtk_widget_unrealize(widget);
361         gtk_widget_show_all(widget);
362         gtk_widget_queue_draw(widget);
363 }
364
365 /* colorlabel_create_color_menu() - creates a color menu without 
366  * checkitems, probably for use in combo items */
367 GtkWidget *colorlabel_create_color_menu(void)
368 {
369         GtkWidget *label; 
370         GtkWidget *item;
371         GtkWidget *menu;
372         gint i;
373
374         colorlabel_recreate_all();
375
376         /* create the menu items. each item has its color code attached */
377         menu = gtk_menu_new();
378         g_object_set_data(G_OBJECT(menu), "label_color_menu", menu);
379
380         item = gtk_menu_item_new_with_label(_("None"));
381         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
382         g_object_set_data(G_OBJECT(item), "color", GUINT_TO_POINTER(0));
383         gtk_widget_show(item);
384
385         /* and the color items */
386         for (i = 0; i < LABEL_COLORS_ELEMS; i++) {
387                 GtkWidget *hbox; 
388                 GtkWidget *vbox;
389                 GtkWidget *widget;
390
391                 item  = gtk_menu_item_new();
392                 g_object_set_data(G_OBJECT(item), "color",
393                                   GUINT_TO_POINTER(i + 1));
394
395                 label = gtk_label_new(label_colors[0][i].label);
396                 
397                 gtk_widget_show(label);
398                 hbox = gtk_hbox_new(FALSE, 0);
399                 gtk_widget_show(hbox);
400                 gtk_container_add(GTK_CONTAINER(item), hbox);
401
402                 vbox = gtk_vbox_new(TRUE, 0);
403                 gtk_widget_show(vbox);
404                 gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
405
406                 widget = colorlabel_create_color_widget(label_colors[0][i].color);
407                 gtk_widget_show(widget);
408                 gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, FALSE, 0);
409
410                 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
411                 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
412                 
413                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
414                 gtk_widget_show(item);
415         }
416         
417         g_signal_connect(G_OBJECT(menu), "selection-done", 
418                         G_CALLBACK(refresh_menu), menu);
419         gtk_widget_show(menu);
420
421         return menu;
422 }
423
424 guint colorlabel_get_color_menu_active_item(GtkWidget *menu)
425 {
426         GtkWidget *menuitem;
427         guint color;
428
429         menuitem = gtk_menu_get_active(GTK_MENU(menu));
430         color = GPOINTER_TO_UINT
431                 (g_object_get_data(G_OBJECT(menuitem), "color"));
432         return color;
433 }