1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2012 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library 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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
27 #include "gtkcolorchooserprivate.h"
28 #include "gtkcoloreditor.h"
29 #include "gtkcolorplane.h"
32 #include "gtkaspectframe.h"
33 #include "gtkdrawingarea.h"
36 #include "gtkadjustment.h"
41 struct _GtkColorEditorPrivate
52 cairo_surface_t *h_surface;
53 cairo_surface_t *a_surface;
57 guint text_changed : 1;
68 static void gtk_color_editor_iface_init (GtkColorChooserInterface *iface);
70 G_DEFINE_TYPE_WITH_CODE (GtkColorEditor, gtk_color_editor, GTK_TYPE_BOX,
71 G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
72 gtk_color_editor_iface_init))
75 scale_round (gdouble value, gdouble scale)
77 value = floor (value * scale + 0.5);
78 value = MAX (value, 0);
79 value = MIN (value, scale);
84 update_entry (GtkColorEditor *editor)
88 text = g_strdup_printf ("#%02X%02X%02X",
89 scale_round (editor->priv->color.red, 255),
90 scale_round (editor->priv->color.green, 255),
91 scale_round (editor->priv->color.blue, 255));
92 gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text);
93 editor->priv->text_changed = FALSE;
98 entry_apply (GtkWidget *entry,
99 GtkColorEditor *editor)
104 if (!editor->priv->text_changed)
107 text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1);
108 if (gdk_rgba_parse (&color, text))
110 color.alpha = editor->priv->color.alpha;
111 gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (editor), &color);
114 editor->priv->text_changed = FALSE;
120 entry_focus_out (GtkWidget *entry,
121 GdkEventFocus *event,
122 GtkColorEditor *editor)
124 entry_apply (entry, editor);
129 entry_text_changed (GtkWidget *entry,
131 GtkColorEditor *editor)
133 editor->priv->text_changed = TRUE;
137 h_changed (GtkAdjustment *adj,
138 GtkColorEditor *editor)
140 editor->priv->h = gtk_adjustment_get_value (adj);
141 gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v,
142 &editor->priv->color.red,
143 &editor->priv->color.green,
144 &editor->priv->color.blue);
145 gtk_color_plane_set_h (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->h);
146 update_entry (editor);
147 gtk_widget_queue_draw (editor->priv->swatch);
148 gtk_widget_queue_draw (editor->priv->a_slider);
149 if (editor->priv->a_surface)
151 cairo_surface_destroy (editor->priv->a_surface);
152 editor->priv->a_surface = NULL;
154 g_object_notify (G_OBJECT (editor), "color");
158 sv_changed (GtkColorPlane *plane,
159 GtkColorEditor *editor)
161 editor->priv->s = gtk_color_plane_get_s (plane);
162 editor->priv->v = gtk_color_plane_get_v (plane);
163 gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v,
164 &editor->priv->color.red,
165 &editor->priv->color.green,
166 &editor->priv->color.blue);
167 update_entry (editor);
168 gtk_widget_queue_draw (editor->priv->swatch);
169 gtk_widget_queue_draw (editor->priv->a_slider);
170 if (editor->priv->a_surface)
172 cairo_surface_destroy (editor->priv->a_surface);
173 editor->priv->a_surface = NULL;
175 g_object_notify (G_OBJECT (editor), "color");
179 a_changed (GtkAdjustment *adj,
180 GtkColorEditor *editor)
182 editor->priv->color.alpha = gtk_adjustment_get_value (adj);
183 gtk_widget_queue_draw (editor->priv->swatch);
184 g_object_notify (G_OBJECT (editor), "color");
187 static cairo_pattern_t *
188 get_checkered_pattern (void)
190 /* need to respect pixman's stride being a multiple of 4 */
191 static unsigned char data[8] = { 0xFF, 0x00, 0x00, 0x00,
192 0x00, 0xFF, 0x00, 0x00 };
193 static cairo_surface_t *checkered = NULL;
194 cairo_pattern_t *pattern;
196 if (checkered == NULL)
197 checkered = cairo_image_surface_create_for_data (data,
201 pattern = cairo_pattern_create_for_surface (checkered);
202 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
203 cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
209 swatch_draw (GtkWidget *widget,
211 GtkColorEditor *editor)
213 cairo_pattern_t *checkered;
215 if (editor->priv->show_alpha)
217 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
220 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
221 cairo_scale (cr, 8, 8);
223 checkered = get_checkered_pattern ();
224 cairo_mask (cr, checkered);
225 cairo_pattern_destroy (checkered);
228 gdk_cairo_set_source_rgba (cr, &editor->priv->color);
234 static cairo_surface_t *
235 create_h_surface (GtkWidget *widget)
238 cairo_surface_t *surface;
239 gint width, height, stride;
240 cairo_surface_t *tmp;
241 guint red, green, blue;
248 if (!gtk_widget_get_realized (widget))
251 width = gtk_widget_get_allocated_width (widget);
252 height = gtk_widget_get_allocated_height (widget);
254 surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
258 if (width == 1 || height == 1)
261 stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
263 data = g_malloc (height * stride);
265 f = 1.0 / (height - 1);
266 for (y = 0; y < height; y++)
268 h = CLAMP (y * f, 0.0, 1.0);
269 p = data + y * (stride / 4);
270 for (x = 0; x < width; x++)
272 gtk_hsv_to_rgb (h, 1, 1, &r, &g, &b);
273 red = CLAMP (r * 255, 0, 255);
274 green = CLAMP (g * 255, 0, 255);
275 blue = CLAMP (b * 255, 0, 255);
276 p[x] = (red << 16) | (green << 8) | blue;
280 tmp = cairo_image_surface_create_for_data ((guchar *)data, CAIRO_FORMAT_RGB24,
281 width, height, stride);
282 cr = cairo_create (surface);
284 cairo_set_source_surface (cr, tmp, 0, 0);
288 cairo_surface_destroy (tmp);
295 h_draw (GtkWidget *widget,
297 GtkColorEditor *editor)
299 cairo_surface_t *surface;
302 width = gtk_widget_get_allocated_width (widget);
303 height = gtk_widget_get_allocated_height (widget);
305 if (!editor->priv->h_surface)
306 editor->priv->h_surface = create_h_surface (widget);
307 surface = editor->priv->h_surface;
311 cairo_rectangle (cr, 1, 1, width - 2, height - 2);
313 cairo_set_source_surface (cr, surface, 0, 0);
321 static cairo_surface_t *
322 create_a_surface (GtkWidget *widget,
326 cairo_surface_t *surface;
327 cairo_pattern_t *pattern;
328 cairo_matrix_t matrix;
331 if (!gtk_widget_get_realized (widget))
334 width = gtk_widget_get_allocated_width (widget);
335 height = gtk_widget_get_allocated_height (widget);
337 surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
341 if (width == 1 || height == 1)
344 cr = cairo_create (surface);
346 cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
348 cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
350 pattern = get_checkered_pattern ();
351 cairo_matrix_init_scale (&matrix, 0.125, 0.125);
352 cairo_pattern_set_matrix (pattern, &matrix);
353 cairo_mask (cr, pattern);
354 cairo_pattern_destroy (pattern);
356 pattern = cairo_pattern_create_linear (0, 0, width, 0);
357 cairo_pattern_add_color_stop_rgba (pattern, 0, color->red, color->green, color->blue, 0);
358 cairo_pattern_add_color_stop_rgba (pattern, width, color->red, color->green, color->blue, 1);
359 cairo_set_source (cr, pattern);
361 cairo_pattern_destroy (pattern);
369 a_draw (GtkWidget *widget,
371 GtkColorEditor *editor)
373 cairo_surface_t *surface;
376 width = gtk_widget_get_allocated_width (widget);
377 height = gtk_widget_get_allocated_height (widget);
379 if (!editor->priv->a_surface)
380 editor->priv->a_surface = create_a_surface (widget, &editor->priv->color);
381 surface = editor->priv->a_surface;
385 cairo_rectangle (cr, 1, 1, width - 2, height - 2);
387 cairo_set_source_surface (cr, surface, 0, 0);
396 gtk_color_editor_init (GtkColorEditor *editor)
401 editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor,
402 GTK_TYPE_COLOR_EDITOR,
403 GtkColorEditorPrivate);
404 editor->priv->show_alpha = TRUE;
406 gtk_widget_push_composite_child ();
408 editor->priv->grid = grid = gtk_grid_new ();
409 gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
410 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
412 editor->priv->swatch = gtk_drawing_area_new ();
413 g_signal_connect (editor->priv->swatch, "draw", G_CALLBACK (swatch_draw), editor);
414 editor->priv->entry = gtk_entry_new ();
415 g_signal_connect (editor->priv->entry, "activate",
416 G_CALLBACK (entry_apply), editor);
417 g_signal_connect (editor->priv->entry, "notify::text",
418 G_CALLBACK (entry_text_changed), editor);
419 g_signal_connect (editor->priv->entry, "focus-out-event",
420 G_CALLBACK (entry_focus_out), editor);
422 adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
423 g_signal_connect (adj, "value-changed", G_CALLBACK (h_changed), editor);
424 editor->priv->h_slider = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj);
425 editor->priv->h_adj = adj;
426 g_signal_connect (editor->priv->h_slider, "draw", G_CALLBACK (h_draw), editor);
428 gtk_scale_set_draw_value (GTK_SCALE (editor->priv->h_slider), FALSE);
429 editor->priv->sv_plane = gtk_color_plane_new ();
430 gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300);
431 gtk_widget_set_hexpand (editor->priv->sv_plane, TRUE);
432 gtk_widget_set_vexpand (editor->priv->sv_plane, TRUE);
434 g_signal_connect (editor->priv->sv_plane, "changed", G_CALLBACK (sv_changed), editor);
436 adj = gtk_adjustment_new (1, 0, 1, 0.01, 0.1, 0);
437 editor->priv->a_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj);
438 g_signal_connect (adj, "value-changed", G_CALLBACK (a_changed), editor);
439 gtk_scale_set_draw_value (GTK_SCALE (editor->priv->a_slider), FALSE);
440 editor->priv->a_adj = adj;
441 g_signal_connect (editor->priv->a_slider, "draw", G_CALLBACK (a_draw), editor);
443 gtk_grid_attach (GTK_GRID (grid), editor->priv->swatch, 1, 0, 1, 1);
444 gtk_grid_attach (GTK_GRID (grid), editor->priv->entry, 2, 0, 1, 1);
445 gtk_grid_attach (GTK_GRID (grid), editor->priv->h_slider, 0, 1, 1, 1);
446 gtk_grid_attach (GTK_GRID (grid), editor->priv->sv_plane, 1, 1, 2, 1);
447 gtk_grid_attach (GTK_GRID (grid), editor->priv->a_slider, 1, 2, 2, 1);
448 gtk_widget_show_all (grid);
450 gtk_container_add (GTK_CONTAINER (editor), grid);
451 gtk_widget_pop_composite_child ();
455 gtk_color_editor_get_property (GObject *object,
460 GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
461 GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
468 gtk_color_chooser_get_color (cc, &color);
469 g_value_set_boxed (value, &color);
472 case PROP_SHOW_ALPHA:
473 g_value_set_boolean (value, gtk_widget_get_visible (ce->priv->a_slider));
476 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
482 gtk_color_editor_set_show_alpha (GtkColorEditor *editor,
485 if (editor->priv->show_alpha != show_alpha)
487 editor->priv->show_alpha = show_alpha;
490 gtk_widget_show (editor->priv->a_slider);
492 gtk_widget_hide (editor->priv->a_slider);
497 gtk_color_editor_set_property (GObject *object,
502 GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
503 GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
508 gtk_color_chooser_set_color (cc, g_value_get_boxed (value));
510 case PROP_SHOW_ALPHA:
511 gtk_color_editor_set_show_alpha (ce, g_value_get_boolean (value));
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520 gtk_color_editor_finalize (GObject *object)
522 GtkColorEditor *editor = GTK_COLOR_EDITOR (object);
524 if (editor->priv->h_surface)
525 cairo_surface_destroy (editor->priv->h_surface);
526 if (editor->priv->a_surface)
527 cairo_surface_destroy (editor->priv->a_surface);
529 G_OBJECT_CLASS (gtk_color_editor_parent_class)->finalize (object);
533 gtk_color_editor_class_init (GtkColorEditorClass *class)
535 GObjectClass *object_class = G_OBJECT_CLASS (class);
537 object_class->finalize = gtk_color_editor_finalize;
538 object_class->get_property = gtk_color_editor_get_property;
539 object_class->set_property = gtk_color_editor_set_property;
541 g_object_class_override_property (object_class, PROP_COLOR, "color");
542 g_object_class_override_property (object_class, PROP_SHOW_ALPHA, "show-alpha");
544 g_type_class_add_private (class, sizeof (GtkColorEditorPrivate));
548 gtk_color_editor_get_color (GtkColorChooser *chooser,
551 GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
553 color->red = editor->priv->color.red;
554 color->green = editor->priv->color.green;
555 color->blue = editor->priv->color.blue;
556 color->alpha = editor->priv->color.alpha;
560 gtk_color_editor_set_color (GtkColorChooser *chooser,
561 const GdkRGBA *color)
563 GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
565 editor->priv->color.red = color->red;
566 editor->priv->color.green = color->green;
567 editor->priv->color.blue = color->blue;
568 gtk_rgb_to_hsv (editor->priv->color.red,
569 editor->priv->color.green,
570 editor->priv->color.blue,
571 &editor->priv->h, &editor->priv->s, &editor->priv->v);
572 gtk_color_plane_set_h (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->h);
573 gtk_color_plane_set_s (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->s);
574 gtk_color_plane_set_v (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->v);
575 gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h);
576 update_entry (editor);
578 editor->priv->color.alpha = color->alpha;
579 gtk_adjustment_set_value (editor->priv->a_adj, editor->priv->color.alpha);
581 gtk_widget_queue_draw (GTK_WIDGET (editor));
582 if (editor->priv->a_surface)
584 cairo_surface_destroy (editor->priv->a_surface);
585 editor->priv->a_surface = NULL;
588 g_object_notify (G_OBJECT (editor), "color");
592 gtk_color_editor_iface_init (GtkColorChooserInterface *iface)
594 iface->get_color = gtk_color_editor_get_color;
595 iface->set_color = gtk_color_editor_set_color;
599 gtk_color_editor_new (void)
601 return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_EDITOR, NULL);