1 /* e-color-combo.c
2  *
3  * Copyright (C) 2012 Dan Vrátil <dvratil@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include "evolution-config.h"
21 
22 #include "e-color-combo.h"
23 #include "e-color-chooser-widget.h"
24 
25 #include <glib/gi18n-lib.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <cairo/cairo.h>
28 
29 #define E_COLOR_COMBO_GET_PRIVATE(obj) \
30 	(G_TYPE_INSTANCE_GET_PRIVATE \
31 	((obj), E_TYPE_COLOR_COMBO, EColorComboPrivate))
32 
33 struct _EColorComboPrivate {
34 	GtkWidget *color_frame;		/* not referenced */
35 	GtkWidget *arrow;		/* not referenced */
36 
37 	GtkWidget *popover;
38 	GtkWidget *default_button;	/* not referenced */
39 	GtkWidget *chooser_widget;	/* not referenced */
40 
41 	guint popup_shown	: 1;
42 	guint popup_in_progress : 1;
43 
44 	guint default_transparent: 1;
45 	GdkRGBA *current_color;
46 	GdkRGBA *default_color;
47 
48 	GList *palette;
49 };
50 
51 enum {
52 	PROP_0,
53 	PROP_CURRENT_COLOR,
54 	PROP_DEFAULT_COLOR,
55 	PROP_DEFAULT_LABEL,
56 	PROP_DEFAULT_TRANSPARENT,
57 	PROP_PALETTE,
58 	PROP_POPUP_SHOWN
59 };
60 
61 enum {
62 	ACTIVATED,
63 	POPUP,
64 	POPDOWN,
65 	LAST_SIGNAL
66 };
67 
68 static guint signals[LAST_SIGNAL];
69 static GdkRGBA black = { 0, 0, 0, 1 };
70 
71 G_DEFINE_TYPE (
72 	EColorCombo,
73 	e_color_combo,
74 	GTK_TYPE_BUTTON);
75 
76 static void
color_combo_popup(EColorCombo * combo)77 color_combo_popup (EColorCombo *combo)
78 {
79 	if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
80 		return;
81 
82 	if (combo->priv->popup_shown)
83 		return;
84 
85 	/* Always make sure the editor-mode is OFF */
86 	g_object_set (
87 		G_OBJECT (combo->priv->chooser_widget),
88 		"show-editor", FALSE, NULL);
89 
90 	/* Show the pop-up. */
91 	gtk_widget_show_all (combo->priv->popover);
92 	gtk_widget_grab_focus (combo->priv->chooser_widget);
93 }
94 
95 static void
color_combo_popdown(EColorCombo * combo)96 color_combo_popdown (EColorCombo *combo)
97 {
98 	if (!gtk_widget_get_realized (GTK_WIDGET (combo)))
99 		return;
100 
101 	if (!combo->priv->popup_shown)
102 		return;
103 
104 	gtk_widget_hide (combo->priv->popover);
105 }
106 
107 static gboolean
color_combo_window_button_press_event_cb(EColorCombo * combo,GdkEvent * event,gpointer user_data)108 color_combo_window_button_press_event_cb (EColorCombo *combo,
109                                           GdkEvent *event,
110                                           gpointer user_data)
111 {
112 	GtkWidget *event_widget;
113 
114 	event_widget = gtk_get_event_widget ((GdkEvent *) event);
115 
116 	if (event_widget == combo->priv->popover)
117 		return TRUE;
118 
119 	if (combo->priv->popup_shown)
120 		return FALSE;
121 
122 	combo->priv->popup_in_progress = TRUE;
123 	color_combo_popup (combo);
124 
125 	return TRUE;
126 }
127 
128 static gboolean
color_combo_window_button_release_event_cb(EColorCombo * combo,GdkEvent * event,gpointer user_data)129 color_combo_window_button_release_event_cb (EColorCombo *combo,
130                                             GdkEvent *event,
131                                             gpointer user_data)
132 {
133 	gboolean popup_in_progress;
134 
135 	popup_in_progress = combo->priv->popup_in_progress;
136 	combo->priv->popup_in_progress = FALSE;
137 
138 	if (popup_in_progress)
139 		return FALSE;
140 
141 	if (combo->priv->popup_shown)
142 		goto popdown;
143 
144 	return FALSE;
145 
146 popdown:
147 	color_combo_popdown (combo);
148 
149 	return TRUE;
150 }
151 
152 static void
color_combo_child_show_cb(EColorCombo * combo)153 color_combo_child_show_cb (EColorCombo *combo)
154 {
155 	combo->priv->popup_shown = TRUE;
156 	g_object_notify (G_OBJECT (combo), "popup-shown");
157 }
158 
159 static void
color_combo_child_hide_cb(EColorCombo * combo)160 color_combo_child_hide_cb (EColorCombo *combo)
161 {
162 	combo->priv->popup_shown = FALSE;
163 	g_object_notify (G_OBJECT (combo), "popup-shown");
164 }
165 
166 static void
color_combo_get_preferred_width(GtkWidget * widget,gint * min_width,gint * natural_width)167 color_combo_get_preferred_width (GtkWidget *widget,
168                                  gint *min_width,
169                                  gint *natural_width)
170 {
171 	GtkWidgetClass *widget_class;
172 
173 	widget_class = GTK_WIDGET_CLASS (e_color_combo_parent_class);
174 	widget_class->get_preferred_width (widget, min_width, natural_width);
175 
176 	/* Make sure the box with color sample is always visible */
177 	if (min_width)
178 		*min_width += 20;
179 
180 	if (natural_width)
181 		*natural_width += 20;
182 }
183 
184 static gboolean
color_combo_button_press_event_cb(GtkWidget * widget,GdkEventButton * event)185 color_combo_button_press_event_cb (GtkWidget *widget,
186                                    GdkEventButton *event)
187 {
188 	EColorCombo *combo = E_COLOR_COMBO (widget);
189 	GdkWindow *window;
190 	gint x, y, width, height;
191 
192 	window = gtk_widget_get_window (combo->priv->color_frame);
193 	gdk_window_get_position (window, &x, &y);
194 	/* Width - only width of the frame with color box */
195 	width = gtk_widget_get_allocated_width (combo->priv->color_frame);
196 
197 	/* Height - height of the entire button (widget) */
198 	height = gtk_widget_get_allocated_height (widget);
199 
200 	/* Check whether user clicked on the color frame - in such case
201 	 * apply the color immediatelly without displaying the popup widget */
202 	if ((event->x_root >= x) && (event->x_root <= x + width) &&
203 	    (event->y_root >= y) && (event->y_root <= y + height)) {
204 		GdkRGBA color;
205 
206 		e_color_combo_get_current_color (combo, &color);
207 		g_signal_emit (combo, signals[ACTIVATED], 0, &color);
208 
209 		return TRUE;
210 	}
211 
212 	/* Otherwise display the popup widget */
213 	if (combo->priv->popup_shown) {
214 		color_combo_popdown (combo);
215 	} else {
216 		combo->priv->popup_in_progress = TRUE;
217 		color_combo_popup (combo);
218 	}
219 
220 	return FALSE;
221 }
222 
223 static void
color_combo_swatch_color_changed(EColorCombo * combo,GdkRGBA * color,gpointer user_data)224 color_combo_swatch_color_changed (EColorCombo *combo,
225                                   GdkRGBA *color,
226                                   gpointer user_data)
227 {
228 	g_signal_emit (combo, signals[ACTIVATED], 0, color);
229 
230 	e_color_combo_set_current_color (combo, color);
231 
232 	color_combo_popdown (combo);
233 }
234 
235 static void
draw_transparent_graphic(cairo_t * cr,gint width,gint height)236 draw_transparent_graphic (cairo_t *cr,
237                           gint width,
238                           gint height)
239 {
240 	gint ii, step, x_offset, y_offset;
241 
242 	step = height / 2;
243 	x_offset = width % step;
244 	y_offset = height % step;
245 
246 	for (ii = 0; ii < width; ii += step) {
247 		if (ii % 2)
248 			cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
249 		else
250 			cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
251 
252 		if (ii + step >= width)
253 			cairo_rectangle (cr, ii, 0, step + x_offset, step);
254 		else
255 			cairo_rectangle (cr, ii, 0, step, step);
256 
257 		cairo_fill (cr);
258 
259 		if (ii % 2)
260 			cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
261 		else
262 			cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
263 
264 		if (ii + step >= width)
265 			cairo_rectangle (cr, ii, step, step + x_offset, step + y_offset);
266 		else
267 			cairo_rectangle (cr, ii, step, step, step + y_offset);
268 
269 		cairo_fill (cr);
270 	}
271 }
272 
273 static void
color_combo_draw_frame_cb(GtkWidget * widget,cairo_t * cr,gpointer user_data)274 color_combo_draw_frame_cb (GtkWidget *widget,
275                            cairo_t *cr,
276                            gpointer user_data)
277 {
278 	EColorCombo *combo = user_data;
279 	GdkRGBA rgba;
280 	GtkAllocation allocation;
281 	gint height, width;
282 
283 	e_color_combo_get_current_color (combo, &rgba);
284 
285 	gtk_widget_get_allocation (widget, &allocation);
286 	width = allocation.width;
287 	height = allocation.height;
288 
289 	if (rgba.alpha == 0) {
290 		draw_transparent_graphic (cr, width, height);
291 	} else {
292 		cairo_set_source_rgba (cr, rgba.red, rgba.green, rgba.blue, rgba.alpha);
293 		cairo_rectangle (cr, 0, 0, width, height);
294 		cairo_fill (cr);
295 	}
296 }
297 
298 static void
color_combo_set_default_color_cb(EColorCombo * combo,gpointer user_data)299 color_combo_set_default_color_cb (EColorCombo *combo,
300                                   gpointer user_data)
301 {
302 	GdkRGBA color;
303 
304 	e_color_combo_get_default_color (combo, &color);
305 	e_color_combo_set_current_color (combo, &color);
306 
307 	g_signal_emit (combo, signals[ACTIVATED], 0, &color);
308 }
309 
310 static void
color_combo_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)311 color_combo_set_property (GObject *object,
312                           guint property_id,
313                           const GValue *value,
314                           GParamSpec *pspec)
315 {
316 	switch (property_id) {
317 		case PROP_CURRENT_COLOR:
318 			e_color_combo_set_current_color (
319 				E_COLOR_COMBO (object),
320 				g_value_get_boxed (value));
321 			return;
322 
323 		case PROP_DEFAULT_COLOR:
324 			e_color_combo_set_default_color (
325 				E_COLOR_COMBO (object),
326 				g_value_get_boxed (value));
327 			return;
328 
329 		case PROP_DEFAULT_LABEL:
330 			e_color_combo_set_default_label (
331 				E_COLOR_COMBO (object),
332 				g_value_get_string (value));
333 			return;
334 
335 		case PROP_DEFAULT_TRANSPARENT:
336 			e_color_combo_set_default_transparent (
337 				E_COLOR_COMBO (object),
338 				g_value_get_boolean (value));
339 			return;
340 
341 		case PROP_PALETTE:
342 			e_color_combo_set_palette (
343 				E_COLOR_COMBO (object),
344 				g_value_get_object (value));
345 			return;
346 
347 		case PROP_POPUP_SHOWN:
348 			if (g_value_get_boolean (value))
349 				e_color_combo_popup (
350 					E_COLOR_COMBO (object));
351 			else
352 				e_color_combo_popdown (
353 					E_COLOR_COMBO (object));
354 			return;
355 	}
356 
357 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
358 }
359 
360 static void
color_combo_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)361 color_combo_get_property (GObject *object,
362                           guint property_id,
363                           GValue *value,
364                           GParamSpec *pspec)
365 {
366 	EColorComboPrivate *priv;
367 	GdkRGBA color;
368 
369 	priv = E_COLOR_COMBO_GET_PRIVATE (object);
370 
371 	switch (property_id) {
372 		case PROP_CURRENT_COLOR:
373 			e_color_combo_get_current_color (
374 				E_COLOR_COMBO (object), &color);
375 			g_value_set_boxed (value, &color);
376 			return;
377 
378 		case PROP_DEFAULT_COLOR:
379 			e_color_combo_get_default_color (
380 				E_COLOR_COMBO (object), &color);
381 			g_value_set_boxed (value, &color);
382 			return;
383 
384 		case PROP_DEFAULT_LABEL:
385 			g_value_set_string (
386 				value, e_color_combo_get_default_label (
387 				E_COLOR_COMBO (object)));
388 			return;
389 
390 		case PROP_DEFAULT_TRANSPARENT:
391 			g_value_set_boolean (
392 				value,
393 				e_color_combo_get_default_transparent (
394 				E_COLOR_COMBO (object)));
395 			return;
396 
397 		case PROP_PALETTE:
398 			g_value_set_object (
399 				value, e_color_combo_get_palette (
400 				E_COLOR_COMBO (object)));
401 			return;
402 
403 		case PROP_POPUP_SHOWN:
404 			g_value_set_boolean (value, priv->popup_shown);
405 			return;
406 	}
407 
408 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
409 }
410 
411 static void
color_combo_dispose(GObject * object)412 color_combo_dispose (GObject *object)
413 {
414 	EColorComboPrivate *priv;
415 
416 	priv = E_COLOR_COMBO_GET_PRIVATE (object);
417 	g_clear_pointer (&priv->popover, gtk_widget_destroy);
418 	g_clear_pointer (&priv->current_color, gdk_rgba_free);
419 	g_clear_pointer (&priv->default_color, gdk_rgba_free);
420 
421 	g_list_free_full (priv->palette, (GDestroyNotify) gdk_rgba_free);
422 	priv->palette = NULL;
423 
424 	/* Chain up to parent's dispose() method. */
425 	G_OBJECT_CLASS (e_color_combo_parent_class)->dispose (object);
426 }
427 
428 static void
e_color_combo_class_init(EColorComboClass * class)429 e_color_combo_class_init (EColorComboClass *class)
430 {
431 	GObjectClass *object_class;
432 	GtkWidgetClass *widget_class;
433 
434 	g_type_class_add_private (class, sizeof (EColorComboPrivate));
435 
436 	object_class = G_OBJECT_CLASS (class);
437 	object_class->set_property = color_combo_set_property;
438 	object_class->get_property = color_combo_get_property;
439 	object_class->dispose = color_combo_dispose;
440 
441 	widget_class = GTK_WIDGET_CLASS (class);
442 	widget_class->get_preferred_width = color_combo_get_preferred_width;
443 	widget_class->button_press_event = color_combo_button_press_event_cb;
444 
445 	class->popup = color_combo_popup;
446 	class->popdown = color_combo_popdown;
447 
448 	g_object_class_install_property (
449 		object_class,
450 		PROP_CURRENT_COLOR,
451 		g_param_spec_boxed (
452 			"current-color",
453 			"Current color",
454 			"The currently selected color",
455 			GDK_TYPE_RGBA,
456 			G_PARAM_READWRITE));
457 
458 	g_object_class_install_property (
459 		object_class,
460 		PROP_DEFAULT_COLOR,
461 		g_param_spec_boxed (
462 			"default-color",
463 			"Default color",
464 			"The color associated with the default button",
465 			GDK_TYPE_RGBA,
466 			G_PARAM_CONSTRUCT |
467 			G_PARAM_READWRITE));
468 
469 	g_object_class_install_property (
470 		object_class,
471 		PROP_DEFAULT_LABEL,
472 		g_param_spec_string (
473 			"default-label",
474 			"Default label",
475 			"The label for the default button",
476 			_("Default"),
477 			G_PARAM_CONSTRUCT |
478 			G_PARAM_READWRITE));
479 
480 	g_object_class_install_property (
481 		object_class,
482 		PROP_DEFAULT_TRANSPARENT,
483 		g_param_spec_boolean (
484 			"default-transparent",
485 			"Default is transparent",
486 			"Whether the default color is transparent",
487 			FALSE,
488 			G_PARAM_CONSTRUCT |
489 			G_PARAM_READWRITE));
490 
491 	g_object_class_install_property (
492 		object_class,
493 		PROP_PALETTE,
494 		g_param_spec_pointer (
495 			"palette",
496 			"Color palette",
497 			"Custom color palette",
498 			G_PARAM_READWRITE));
499 
500 	g_object_class_install_property (
501 		object_class,
502 		PROP_POPUP_SHOWN,
503 		g_param_spec_boolean (
504 			"popup-shown",
505 			"Popup shown",
506 			"Whether the combo's dropdown is shown",
507 			FALSE,
508 			G_PARAM_READWRITE));
509 
510 	signals[ACTIVATED] = g_signal_new (
511 		"activated",
512 		G_OBJECT_CLASS_TYPE (class),
513 		G_SIGNAL_RUN_LAST,
514 		G_STRUCT_OFFSET (EColorComboClass, activated),
515 		NULL, NULL,
516 		g_cclosure_marshal_VOID__VOID,
517 		G_TYPE_NONE, 0);
518 
519 	signals[POPUP] = g_signal_new (
520 		"popup",
521 		G_OBJECT_CLASS_TYPE (class),
522 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
523 		G_STRUCT_OFFSET (EColorComboClass, popup),
524 		NULL, NULL,
525 		g_cclosure_marshal_VOID__VOID,
526 		G_TYPE_NONE, 0);
527 
528 	signals[POPDOWN] = g_signal_new (
529 		"popdown",
530 		G_OBJECT_CLASS_TYPE (class),
531 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
532 		G_STRUCT_OFFSET (EColorComboClass, popdown),
533 		NULL, NULL,
534 		g_cclosure_marshal_VOID__VOID,
535 		G_TYPE_NONE, 0);
536 
537 	gtk_binding_entry_add_signal (
538 		gtk_binding_set_by_class (class),
539 		GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
540 	gtk_binding_entry_add_signal (
541 		gtk_binding_set_by_class (class),
542 		GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
543 
544 	gtk_binding_entry_add_signal (
545 		gtk_binding_set_by_class (class),
546 		GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
547 	gtk_binding_entry_add_signal (
548 		gtk_binding_set_by_class (class),
549 		GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
550 	gtk_binding_entry_add_signal (
551 		gtk_binding_set_by_class (class),
552 		GDK_KEY_Escape, 0, "popdown", 0);
553 }
554 
555 static void
e_color_combo_init(EColorCombo * combo)556 e_color_combo_init (EColorCombo *combo)
557 {
558 	struct {
559 		const gchar *color;
560 		const gchar *tooltip;
561 		GdkRGBA rgba;
562 	} default_colors[] = {
563 
564 		{ "#000000", N_("black"), { 0, } },
565 		{ "#993300", N_("light brown"), { 0, } },
566 		{ "#333300", N_("brown gold"), { 0, } },
567 		{ "#003300", N_("dark green #2"), { 0, } },
568 		{ "#003366", N_("navy"), { 0, } },
569 		{ "#000080", N_("dark blue"), { 0, } },
570 		{ "#333399", N_("purple #2"), { 0, } },
571 		{ "#333333", N_("very dark gray"), { 0, } },
572 
573 		{ "#800000", N_("dark red"), { 0, } },
574 		{ "#FF6600", N_("red-orange"), { 0, } },
575 		{ "#808000", N_("gold"), { 0, } },
576 		{ "#008000", N_("dark green"), { 0, } },
577 		{ "#008080", N_("dull blue"), { 0, } },
578 		{ "#0000FF", N_("blue"), { 0, } },
579 		{ "#666699", N_("dull purple"), { 0, } },
580 		{ "#808080", N_("dark grey"), { 0, } },
581 
582 		{ "#FF0000", N_("red"), { 0, } },
583 		{ "#FF9900", N_("orange"), { 0, } },
584 		{ "#99CC00", N_("lime"), { 0, } },
585 		{ "#339966", N_("dull green"), { 0, } },
586 		{ "#33CCCC", N_("dull blue #2"), { 0, } },
587 		{ "#3366FF", N_("sky blue #2"), { 0, } },
588 		{ "#800080", N_("purple"), { 0, } },
589 		{ "#969696", N_("gray"), { 0, } },
590 
591 		{ "#FF00FF", N_("magenta"), { 0, } },
592 		{ "#FFCC00", N_("bright orange"), { 0, } },
593 		{ "#FFFF00", N_("yellow"), { 0, } },
594 		{ "#00FF00", N_("green"), { 0, } },
595 		{ "#00FFFF", N_("cyan"), { 0, } },
596 		{ "#00CCFF", N_("bright blue"), { 0, } },
597 		{ "#993366", N_("red purple"), { 0, } },
598 		{ "#C0C0C0", N_("light grey"), { 0, } },
599 
600 		{ "#FF99CC", N_("pink"), { 0, } },
601 		{ "#FFCC99", N_("light orange"), { 0, } },
602 		{ "#FFFF99", N_("light yellow"), { 0, } },
603 		{ "#CCFFCC", N_("light green"), { 0, } },
604 		{ "#CCFFFF", N_("light cyan"), { 0, } },
605 		{ "#99CCFF", N_("light blue"), { 0, } },
606 		{ "#CC99FF", N_("light purple"), { 0, } },
607 		{ "#FFFFFF", N_("white"), { 0, } }
608 	};
609 	GtkWidget *container;
610 	GtkWidget *widget;
611 	GList *palette;
612 	gint ii;
613 
614 	combo->priv = E_COLOR_COMBO_GET_PRIVATE (combo);
615 
616 	widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
617 	gtk_container_add (GTK_CONTAINER (combo), widget);
618 
619 	container = widget;
620 
621 	/* Build the combo button. */
622 	widget = gtk_frame_new (NULL);
623 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
624 	g_signal_connect (
625 		widget, "draw",
626 		G_CALLBACK (color_combo_draw_frame_cb), combo);
627 	combo->priv->color_frame = widget;  /* do not reference */
628 
629 	widget = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
630 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
631 
632 	widget = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
633 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
634 	combo->priv->arrow = widget;  /* do not reference */
635 
636 	gtk_widget_show_all (container);
637 
638 	/* Build the drop-down menu */
639 	widget = gtk_popover_new (GTK_WIDGET (combo));
640 	gtk_popover_set_position (GTK_POPOVER (widget), GTK_POS_BOTTOM);
641 	gtk_popover_set_modal (GTK_POPOVER (widget), TRUE);
642 	combo->priv->popover = g_object_ref_sink (widget);
643 
644 	g_signal_connect_swapped (
645 		widget, "show",
646 		G_CALLBACK (color_combo_child_show_cb), combo);
647 	g_signal_connect_swapped (
648 		widget, "hide",
649 		G_CALLBACK (color_combo_child_hide_cb), combo);
650 	g_signal_connect_swapped (
651 		widget, "button-press-event",
652 		G_CALLBACK (color_combo_window_button_press_event_cb), combo);
653 	g_signal_connect_swapped (
654 		widget, "button-release-event",
655 		G_CALLBACK (color_combo_window_button_release_event_cb), combo);
656 
657 	container = widget;
658 
659 	widget = gtk_grid_new ();
660 	gtk_grid_set_row_spacing (GTK_GRID (widget), 5);
661 	gtk_container_add (GTK_CONTAINER (container), widget);
662 
663 	container = widget;
664 
665 	widget = gtk_button_new ();
666 	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
667 	combo->priv->default_button = widget;  /* do not reference */
668 
669 	g_signal_connect_swapped (
670 		widget, "clicked",
671 		G_CALLBACK (color_combo_set_default_color_cb), combo);
672 	g_signal_connect_swapped (
673 		widget, "clicked",
674 		G_CALLBACK (color_combo_popdown), combo);
675 
676 	widget = e_color_chooser_widget_new ();
677 	g_object_set_data (G_OBJECT (widget), "window", combo->priv->popover);
678 	gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1);
679 	combo->priv->chooser_widget = widget;  /* do not reference */
680 
681 	g_signal_connect_swapped (
682 		widget, "color-activated",
683 		G_CALLBACK (color_combo_swatch_color_changed), combo);
684 	g_signal_connect_swapped (
685 		widget, "editor-activated",
686 		G_CALLBACK (color_combo_popdown), combo);
687 
688 	palette = NULL;
689 	for (ii = G_N_ELEMENTS (default_colors) - 1; ii >= 0 ; ii--) {
690 		gdk_rgba_parse (&(default_colors[ii].rgba), default_colors[ii].color);
691 
692 		palette = g_list_prepend (palette, &(default_colors[ii].rgba));
693 	}
694 	e_color_combo_set_palette (combo, palette);
695 	g_list_free (palette);
696 
697 	combo->priv->current_color = gdk_rgba_copy (&black);
698 	combo->priv->default_color = gdk_rgba_copy (&black);
699 }
700 
701 GtkWidget *
e_color_combo_new(void)702 e_color_combo_new (void)
703 {
704 	return g_object_new (E_TYPE_COLOR_COMBO, NULL);
705 }
706 
707 GtkWidget *
e_color_combo_new_defaults(GdkRGBA * default_color,const gchar * default_label)708 e_color_combo_new_defaults (GdkRGBA *default_color,
709                             const gchar *default_label)
710 {
711 	g_return_val_if_fail (default_color != NULL, NULL);
712 	g_return_val_if_fail (default_label != NULL, NULL);
713 
714 	return g_object_new (
715 		E_TYPE_COLOR_COMBO,
716 		"default-color", default_color,
717 		"default-label", default_label,
718 		NULL);
719 }
720 
721 void
e_color_combo_popup(EColorCombo * combo)722 e_color_combo_popup (EColorCombo *combo)
723 {
724 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
725 
726 	g_signal_emit (combo, signals[POPUP], 0);
727 }
728 
729 void
e_color_combo_popdown(EColorCombo * combo)730 e_color_combo_popdown (EColorCombo *combo)
731 {
732 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
733 
734 	g_signal_emit (combo, signals[POPDOWN], 0);
735 }
736 
737 void
e_color_combo_get_current_color(EColorCombo * combo,GdkRGBA * color)738 e_color_combo_get_current_color (EColorCombo *combo,
739                                  GdkRGBA *color)
740 {
741 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
742 	g_return_if_fail (color != NULL);
743 
744 	color->red = combo->priv->current_color->red;
745 	color->green = combo->priv->current_color->green;
746 	color->blue = combo->priv->current_color->blue;
747 	color->alpha = combo->priv->current_color->alpha;
748 }
749 
750 void
e_color_combo_set_current_color(EColorCombo * combo,const GdkRGBA * color)751 e_color_combo_set_current_color (EColorCombo *combo,
752                                  const GdkRGBA *color)
753 {
754 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
755 
756 	if (color == NULL)
757 		color = &black;
758 
759 	if (combo->priv->current_color) {
760 
761 		if (gdk_rgba_equal (color, combo->priv->current_color)) {
762 			return;
763 		}
764 
765 		gdk_rgba_free (combo->priv->current_color);
766 	}
767 
768 	combo->priv->current_color = gdk_rgba_copy (color);
769 
770 	gtk_color_chooser_set_rgba (
771 		GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
772 	gtk_widget_queue_draw (combo->priv->color_frame);
773 
774 	g_object_notify (G_OBJECT (combo), "current-color");
775 }
776 
777 void
e_color_combo_get_default_color(EColorCombo * combo,GdkRGBA * color)778 e_color_combo_get_default_color (EColorCombo *combo,
779                                  GdkRGBA *color)
780 {
781 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
782 	g_return_if_fail (color != NULL);
783 
784 	color->red = combo->priv->default_color->red;
785 	color->green = combo->priv->default_color->green;
786 	color->blue = combo->priv->default_color->blue;
787 	color->alpha = combo->priv->default_color->alpha;
788 }
789 
790 void
e_color_combo_set_default_color(EColorCombo * combo,const GdkRGBA * color)791 e_color_combo_set_default_color (EColorCombo *combo,
792                                  const GdkRGBA *color)
793 {
794 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
795 
796 	if (color == NULL)
797 		color = &black;
798 
799 	if (combo->priv->default_color) {
800 		if (gdk_rgba_equal (color, combo->priv->default_color))
801 			return;
802 
803 		gdk_rgba_free (combo->priv->default_color);
804 	}
805 	combo->priv->default_color = gdk_rgba_copy (color);
806 
807 	gtk_color_chooser_set_rgba (
808 		GTK_COLOR_CHOOSER (combo->priv->chooser_widget), color);
809 
810 	g_object_notify (G_OBJECT (combo), "default-color");
811 }
812 
813 const gchar *
e_color_combo_get_default_label(EColorCombo * combo)814 e_color_combo_get_default_label (EColorCombo *combo)
815 {
816 	g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
817 
818 	return gtk_button_get_label (GTK_BUTTON (combo->priv->default_button));
819 }
820 
821 void
e_color_combo_set_default_label(EColorCombo * combo,const gchar * text)822 e_color_combo_set_default_label (EColorCombo *combo,
823                                  const gchar *text)
824 {
825 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
826 
827 	gtk_button_set_label (GTK_BUTTON (combo->priv->default_button), text);
828 
829 	g_object_notify (G_OBJECT (combo), "default-label");
830 }
831 
832 gboolean
e_color_combo_get_default_transparent(EColorCombo * combo)833 e_color_combo_get_default_transparent (EColorCombo *combo)
834 {
835 	g_return_val_if_fail (E_IS_COLOR_COMBO (combo), FALSE);
836 
837 	return combo->priv->default_transparent;
838 }
839 
840 void
e_color_combo_set_default_transparent(EColorCombo * combo,gboolean transparent)841 e_color_combo_set_default_transparent (EColorCombo *combo,
842                                        gboolean transparent)
843 {
844 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
845 
846 	combo->priv->default_transparent = transparent;
847 	if (transparent)
848 		combo->priv->default_color->alpha = 0;
849 
850 	g_object_notify (G_OBJECT (combo), "default-transparent");
851 }
852 
853 GList *
e_color_combo_get_palette(EColorCombo * combo)854 e_color_combo_get_palette (EColorCombo *combo)
855 {
856 	g_return_val_if_fail (E_IS_COLOR_COMBO (combo), NULL);
857 
858 	return g_list_copy (combo->priv->palette);
859 }
860 
861 void
e_color_combo_set_palette(EColorCombo * combo,GList * palette)862 e_color_combo_set_palette (EColorCombo *combo,
863                            GList *palette)
864 {
865 	gint ii, count, colors_per_line;
866 	GList *iter;
867 	GdkRGBA *colors;
868 
869 	g_return_if_fail (E_IS_COLOR_COMBO (combo));
870 
871 	count = g_list_length (palette);
872 	colors_per_line = (count % 10 == 0) ? 10 : 9;
873 
874 	colors = g_malloc_n (count, sizeof (GdkRGBA));
875 	g_list_free_full (combo->priv->palette, (GDestroyNotify) gdk_rgba_free);
876 	ii = 0;
877 	combo->priv->palette = NULL;
878 	for (iter = palette; iter; iter = g_list_next (iter)) {
879 		combo->priv->palette = g_list_prepend (
880 			combo->priv->palette, gdk_rgba_copy (iter->data));
881 
882 		colors[ii] = *((GdkRGBA *) iter->data);
883 		ii++;
884 	}
885 	combo->priv->palette = g_list_reverse (combo->priv->palette);
886 
887 	gtk_color_chooser_add_palette (
888 		GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
889 		GTK_ORIENTATION_HORIZONTAL, 0, 0, NULL);
890 	gtk_color_chooser_add_palette (
891 		GTK_COLOR_CHOOSER (combo->priv->chooser_widget),
892 		GTK_ORIENTATION_HORIZONTAL, colors_per_line, count, colors);
893 	g_free (colors);
894 }
895