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