1 /*
2  * go-color-selector.c - A color selector
3  *
4  * Copyright (c) 2006 Emmanuel pacaud (emmanuel.pacaud@lapp.in2p3.fr)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) version 3.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA.
20  */
21 
22 #include <goffice/goffice-config.h>
23 
24 #include "go-color-selector.h"
25 
26 #include <goffice/utils/go-color.h>
27 #include <goffice/gtk/go-color-group.h>
28 
29 #include <glib/gi18n-lib.h>
30 #include <gtk/gtk.h>
31 
32 #define COLOR_DIALOG_KEY "GOColorSelector::color-dialog"
33 #define CCW_KEY "GOColorSelector::ccw"
34 
35 typedef struct {
36 	int n_swatches;
37 	GOColorGroup *color_group;
38 	GOColor default_color;
39 	gboolean allow_alpha;
40 } GOColorSelectorState;
41 
42 static void
go_color_selector_state_free(gpointer data)43 go_color_selector_state_free (gpointer data)
44 {
45 	GOColorSelectorState *state = data;
46 
47 	g_object_unref (state->color_group);
48 	g_free (state);
49 }
50 
51 static int
get_index(int n_swatches,GOColorGroup * color_group,GOColor color)52 get_index (int n_swatches, GOColorGroup *color_group, GOColor color)
53 {
54 	int i = 0;
55 	int index = -1;
56 	const GONamedColor *default_color_set =	_go_color_palette_default_color_set ();
57 
58 	while (default_color_set[i].name != NULL) {
59 		if (default_color_set[i].color == color && index < 0) {
60 			index = i;
61 			continue;
62 		}
63 		i++;
64 	}
65 	if (index < 0) {
66 		go_color_group_add_color (color_group, color);
67 		index = n_swatches - 1;
68 	}
69 
70 	if (index < 0) {
71 		g_warning ("[GOColorSelector::get_index] Didn't find color in history");
72 		return 0;
73 	}
74 
75 	return index;
76 }
77 
78 static GOColor
get_color(int n_swatches,GOColorGroup * color_group,int index)79 get_color (int n_swatches, GOColorGroup *color_group, int index)
80 {
81 	const GONamedColor *default_color_set =	_go_color_palette_default_color_set ();
82 
83 	if (index < 0 || index >= (n_swatches))
84 		index = 0;
85 
86        	if (index < n_swatches - GO_COLOR_GROUP_HISTORY_SIZE)
87 		return default_color_set[index].color;
88 	else
89 		return color_group->history[index - (n_swatches - GO_COLOR_GROUP_HISTORY_SIZE)];
90 }
91 
92 static cairo_status_t
draw_check(cairo_surface_t * surface,int width,int height)93 draw_check (cairo_surface_t *surface, int width, int height)
94 {
95     cairo_t *cr;
96     cairo_status_t status;
97 
98     cr = cairo_create (surface);
99     cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
100     cairo_paint (cr);
101 
102     cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); /* dark gray */
103     cairo_rectangle (cr, width / 2,  0, width / 2, height / 2);
104     cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
105     cairo_fill (cr);
106 
107     status = cairo_status (cr);
108 
109     cairo_destroy (cr);
110 
111     return status;
112 }
113 
114 static void
go_color_palette_render_func(cairo_t * cr,GdkRectangle const * area,int index,gpointer data)115 go_color_palette_render_func (cairo_t *cr,
116 			      GdkRectangle const *area,
117 			      int index,
118 			      gpointer data)
119 {
120 	GOColor color;
121 	GOColorSelectorState *state = data;
122 	cairo_surface_t *check;
123 
124 	color = get_color (state->n_swatches, state->color_group, index);
125 
126 	check = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
127 	draw_check (check, 12, 12);
128 
129 	cairo_save (cr);
130 	cairo_set_source_rgb (cr, 1., 1., 1.);
131 	cairo_paint (cr);
132 	cairo_set_source_surface (cr, check, 0, 0);
133 	cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
134 	cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
135 	cairo_move_to (cr, area->x, area->y + area->height);
136 	cairo_rel_line_to (cr, area->width, 0);
137 	cairo_rel_line_to (cr, 0, -area->height);
138 	cairo_close_path (cr);
139 	cairo_fill (cr);
140 	cairo_restore (cr);
141 
142 	cairo_surface_destroy (check);
143 
144 	cairo_set_line_width (cr, 1);
145 	cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (color));
146 	cairo_rectangle (cr, area->x + .5 , area->y + .5 , area->width - 1, area->height - 1);
147 	cairo_fill_preserve (cr);
148 	cairo_set_source_rgb (cr, .75, .75, .75);
149 	cairo_stroke (cr);
150 }
151 
152 static void
cb_color_dialog_response(GtkDialog * color_dialog,gint response,GOSelector * selector)153 cb_color_dialog_response (GtkDialog *color_dialog,
154 			  gint response,
155 			  GOSelector *selector)
156 {
157 	GtkColorChooser *chooser =
158 		g_object_get_data (G_OBJECT (color_dialog), CCW_KEY);
159 
160 	if (response == GTK_RESPONSE_OK) {
161 		GdkRGBA gdk_color;
162 		GOColor color;
163 
164 		gtk_color_chooser_get_rgba (chooser, &gdk_color);
165 
166 		color = GO_COLOR_FROM_GDK_RGBA (gdk_color);
167 		if (!go_color_selector_set_color (selector, color))
168 			/* Index is not necessarly changed, but swatch may change */
169 			go_selector_activate (selector);
170 	}
171 
172 	g_object_set_data (G_OBJECT (selector), COLOR_DIALOG_KEY, NULL);
173 }
174 
175 static void
cb_combo_custom_activate(GOPalette * palette,GOSelector * selector)176 cb_combo_custom_activate (GOPalette *palette, GOSelector *selector)
177 {
178 	GtkWidget *color_dialog;
179 	GtkWidget *ccw, *dca;
180 	GdkRGBA gdk_color;
181 	GOColorSelectorState *state = go_selector_get_user_data (selector);
182 	GOColor color = go_color_selector_get_color (selector, NULL);
183 	GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (selector));
184 
185 	go_color_to_gdk_rgba (color, &gdk_color);
186 
187 	color_dialog = g_object_get_data (G_OBJECT (selector), COLOR_DIALOG_KEY);
188 	if (color_dialog != NULL) {
189 		GtkColorChooser *chooser =
190 			g_object_get_data (G_OBJECT (color_dialog), CCW_KEY);
191 		gtk_color_chooser_set_rgba (chooser, &gdk_color);
192 		gtk_window_present (GTK_WINDOW (color_dialog));
193 		return;
194 	}
195 
196 	color_dialog = gtk_dialog_new_with_buttons
197 		(_("Custom color..."),
198 		 (gtk_widget_is_toplevel (toplevel)
199 		  ? GTK_WINDOW (toplevel)
200 		  : NULL),
201 		 GTK_DIALOG_DESTROY_WITH_PARENT,
202 		 GTK_STOCK_OK, GTK_RESPONSE_OK,
203 		 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
204 		 NULL);
205 	ccw = gtk_color_chooser_widget_new ();
206 	g_object_set_data (G_OBJECT (color_dialog), CCW_KEY, ccw);
207 	dca = gtk_dialog_get_content_area (GTK_DIALOG (color_dialog));
208 	gtk_container_add (GTK_CONTAINER (dca), ccw);
209 
210 	g_object_set (G_OBJECT (ccw),
211 		      "use-alpha", state->allow_alpha,
212 		      "rgba", &gdk_color,
213 		      "show-editor", TRUE,
214 		      NULL);
215 
216 	g_object_set_data_full (G_OBJECT (selector),
217 				COLOR_DIALOG_KEY, color_dialog,
218 				(GDestroyNotify)gtk_widget_destroy);
219 
220 	g_signal_connect (color_dialog,
221 			  "response", G_CALLBACK (cb_color_dialog_response),
222 			  selector);
223 
224 	gtk_widget_show_all (color_dialog);
225 }
226 
227 static void
go_color_selector_drag_data_received(GOSelector * selector,guchar const * data)228 go_color_selector_drag_data_received (GOSelector *selector, guchar const *data)
229 {
230 	GOColor color = GO_COLOR_WHITE;
231 	guint16 const *color_data = (guint16 const *) data;
232 
233 	color = GO_COLOR_CHANGE_R (color, color_data[0] >> 8);
234 	color = GO_COLOR_CHANGE_G (color, color_data[1] >> 8);
235 	color = GO_COLOR_CHANGE_B (color, color_data[2] >> 8);
236 	color = GO_COLOR_CHANGE_A (color, color_data[3] >> 8);
237 
238 	go_color_selector_set_color (selector, color);
239 }
240 
241 static gpointer
go_color_selector_drag_data_get(GOSelector * selector)242 go_color_selector_drag_data_get (GOSelector *selector)
243 {
244 	GOColorSelectorState *state;
245 	GOColor color;
246 	int index;
247 	guint16 *data;
248 
249 	state = go_selector_get_user_data (selector);
250 
251 	data = g_new0 (guint16, 4);
252 	index = go_selector_get_active (selector, NULL);
253 	color = get_color (state->n_swatches, state->color_group, index);
254 
255 	data[0] = GO_COLOR_UINT_R (color) << 8;
256 	data[1] = GO_COLOR_UINT_G (color) << 8;
257 	data[2] = GO_COLOR_UINT_B (color) << 8;
258 	data[3] = GO_COLOR_UINT_A (color) << 8;
259 
260 	return data;
261 }
262 
263 static void
go_color_selector_drag_fill_icon(GOSelector * selector,GdkPixbuf * pixbuf)264 go_color_selector_drag_fill_icon (GOSelector *selector, GdkPixbuf *pixbuf)
265 {
266 	GOColorSelectorState *state;
267 	GOColor color;
268 	int index;
269 
270 	state = go_selector_get_user_data (selector);
271 	index = go_selector_get_active (selector, NULL);
272 	color = get_color (state->n_swatches, state->color_group, index);
273 
274 	gdk_pixbuf_fill (pixbuf, color);
275 }
276 
277 /**
278  * go_selector_new_color:
279  * @initial_color: initially selected color
280  * @default_color: automatic color
281  * @color_group: a #GOColorGroup name
282  *
283  * Creates a new color selector, with @initial_color selected.
284  * Palette will contain an automatic button, which can be used to
285  * select @default_color. This widget supports color drag and drop.
286  *
287  * Since: 0.9.6
288  * Returns: (transfer full): a #GtkWidget.
289  **/
290 GtkWidget *
go_selector_new_color(GOColor initial_color,GOColor default_color,char const * group)291 go_selector_new_color (GOColor initial_color,
292 		       GOColor default_color,
293 		       char const *group)
294 {
295 	GtkWidget *palette;
296 	GtkWidget *selector;
297 	GOColorSelectorState *state;
298 	int count = 0;
299 	int initial_index;
300 	int default_index;
301 
302 	state = g_new (GOColorSelectorState, 1);
303 	state->default_color = default_color;
304 	state->allow_alpha = TRUE;
305 
306 	while (_go_color_palette_default_color_set ()[count].name)
307 		count++;
308 	state->n_swatches = count + GO_COLOR_GROUP_HISTORY_SIZE;
309 	state->color_group = go_color_group_fetch (group, NULL);
310 
311 	get_index (state->n_swatches, state->color_group, initial_color);
312 	default_index = get_index (state->n_swatches, state->color_group, default_color);
313 	/* In case default_color is not in standard palette */
314 	initial_index = get_index (state->n_swatches, state->color_group, initial_color);
315 
316 	palette = go_palette_new (state->n_swatches, 1.0, 8,
317 				  go_color_palette_render_func, NULL,
318 				  state, go_color_selector_state_free);
319 	go_palette_show_automatic (GO_PALETTE (palette), default_index, NULL);
320 	go_palette_show_custom (GO_PALETTE (palette), "Custom color...");
321 	selector = go_selector_new (GO_PALETTE (palette));
322 	go_selector_set_active (GO_SELECTOR (selector), initial_index);
323 	g_signal_connect (palette, "custom-activate", G_CALLBACK (cb_combo_custom_activate), selector);
324 
325 	go_selector_setup_dnd (GO_SELECTOR (selector), "application/x-color", 8,
326 			       go_color_selector_drag_data_get,
327 			       go_color_selector_drag_data_received,
328 			       go_color_selector_drag_fill_icon);
329 
330 	return selector;
331 }
332 
333 /**
334  * go_color_selector_new:
335  * @initial_color: initially selected color
336  * @default_color: automatic color
337  * @color_group: a #GOColorGroup name
338  *
339  * Creates a new color selector, with @initial_color selected.
340  * Palette will contain an automatic button, which can be used to
341  * select @default_color. This widget supports color drag and drop.
342  *
343  * Deprecated: 0.9.6 use go_selector_new_color()
344  * Returns: (transfer full): a #GtkWidget.
345  **/
346 GtkWidget *
go_color_selector_new(GOColor initial_color,GOColor default_color,char const * group)347 go_color_selector_new (GOColor initial_color,
348 		       GOColor default_color,
349 		       char const *group)
350 {
351 	return go_selector_new_color (initial_color, default_color, group);
352 }
353 
354 /**
355  * go_color_selector_set_color:
356  * @selector: #GOColorSelector to change
357  * @color: a #GOColor
358  *
359  * Sets current selection to @color. An "activate" signal will be emitted.
360  *
361  * Returns: %TRUE if selection changed.
362  **/
363 gboolean
go_color_selector_set_color(GOSelector * selector,GOColor color)364 go_color_selector_set_color (GOSelector *selector, GOColor color)
365 {
366 	GOColorSelectorState *state;
367 	int index;
368 
369 	g_return_val_if_fail (GO_IS_SELECTOR (selector), FALSE);
370 
371 	state =  go_selector_get_user_data (selector);
372 	g_return_val_if_fail (state != NULL, FALSE);
373 
374 	index = get_index (state->n_swatches, state->color_group, color);
375 
376 	return go_selector_set_active (selector, index);
377 }
378 
379 
380 /**
381  * go_color_selector_get_color:
382  * @selector: a #GOSelector
383  * @is_auto: (out) (optional): %TRUE if the current selection was set
384  * by clicking on automatic palette item.
385  *
386  * Returns: current color selection of @selector.
387  **/
388 GOColor
go_color_selector_get_color(GOSelector * selector,gboolean * is_auto)389 go_color_selector_get_color (GOSelector *selector, gboolean *is_auto)
390 {
391 	GOColorSelectorState *state;
392 	int index;
393 	gboolean flag;
394 
395 	g_return_val_if_fail (GO_IS_SELECTOR (selector), GO_COLOR_WHITE);
396 
397 	index = go_selector_get_active (selector, &flag);
398 	state = go_selector_get_user_data (selector);
399 	if (is_auto != NULL)
400 		*is_auto = flag;
401 
402 	/* In case default_color is in palette history and its index was moved by the
403 	 * successive color choices. */
404 	if (flag)
405 		return state->default_color;
406 
407 	return get_color (state->n_swatches, state->color_group, index);
408 }
409 
410 /**
411  * go_color_selector_set_allow_alpha:
412  * @selector: #GOColorSelector
413  * @allow_alpha: If %TRUE, the selector will have an alpha channel.
414  *
415  * Seta whether the custom colour selector should allow the use of opacity.
416  **/
417 void
go_color_selector_set_allow_alpha(GOSelector * selector,gboolean allow_alpha)418 go_color_selector_set_allow_alpha (GOSelector *selector, gboolean allow_alpha)
419 {
420 	GOColorSelectorState *state = go_selector_get_user_data (selector);
421 	state->allow_alpha = allow_alpha;
422 }
423