]> Pileus Git - ~andy/gtk/blob - gtk/gtkcoloreditor.c
50f82a35753c037d0ee9d10d7b3cdce214279f73
[~andy/gtk] / gtk / gtkcoloreditor.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 Red Hat, Inc.
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /* TODO
21  * - custom sliders
22  * - pop-up entries
23  */
24
25 #include "config.h"
26
27 #include "gtkcolorchooserprivate.h"
28 #include "gtkcoloreditor.h"
29 #include "gtkcolorplane.h"
30 #include "gtkgrid.h"
31 #include "gtkscale.h"
32 #include "gtkaspectframe.h"
33 #include "gtkdrawingarea.h"
34 #include "gtkentry.h"
35 #include "gtkhsv.h"
36 #include "gtkadjustment.h"
37 #include "gtkintl.h"
38
39 #include <math.h>
40
41 struct _GtkColorEditorPrivate
42 {
43   GtkWidget *grid;
44   GtkWidget *swatch;
45   GtkWidget *entry;
46   GtkWidget *h_slider;
47   GtkWidget *sv_plane;
48   GtkWidget *a_slider;
49
50   GtkAdjustment *h_adj;
51   GtkAdjustment *a_adj;
52   GdkRGBA color;
53   gdouble h, s, v;
54   guint text_changed : 1;
55   guint show_alpha   : 1;
56 };
57
58 enum
59 {
60   PROP_ZERO,
61   PROP_COLOR,
62   PROP_SHOW_ALPHA
63 };
64
65 static void gtk_color_editor_iface_init (GtkColorChooserInterface *iface);
66
67 G_DEFINE_TYPE_WITH_CODE (GtkColorEditor, gtk_color_editor, GTK_TYPE_BOX,
68                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
69                                                 gtk_color_editor_iface_init))
70
71 static guint
72 scale_round (gdouble value, gdouble scale)
73 {
74   value = floor (value * scale + 0.5);
75   value = MAX (value, 0);
76   value = MIN (value, scale);
77   return (guint)value;
78 }
79
80 static void
81 update_entry (GtkColorEditor *editor)
82 {
83   gchar *text;
84
85   text = g_strdup_printf ("#%02X%02X%02X",
86                           scale_round (editor->priv->color.red, 255),
87                           scale_round (editor->priv->color.green, 255),
88                           scale_round (editor->priv->color.blue, 255));
89   gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text);
90   editor->priv->text_changed = FALSE;
91   g_free (text);
92 }
93
94 static void
95 entry_apply (GtkWidget      *entry,
96              GtkColorEditor *editor)
97 {
98   GdkRGBA color;
99   gchar *text;
100
101   if (!editor->priv->text_changed)
102     return;
103
104   text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1);
105   if (gdk_rgba_parse (&color, text))
106     {
107       color.alpha = editor->priv->color.alpha;
108       gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (editor), &color);
109     }
110
111   editor->priv->text_changed = FALSE;
112
113   g_free (text);
114 }
115
116 static gboolean
117 entry_focus_out (GtkWidget      *entry,
118                  GdkEventFocus  *event,
119                  GtkColorEditor *editor)
120 {
121   entry_apply (entry, editor);
122   return FALSE;
123 }
124
125 static void
126 entry_text_changed (GtkWidget      *entry,
127                     GParamSpec     *pspec,
128                     GtkColorEditor *editor)
129 {
130   editor->priv->text_changed = TRUE;
131 }
132
133 static void
134 h_changed (GtkAdjustment  *adj,
135            GtkColorEditor *editor)
136 {
137   editor->priv->h = gtk_adjustment_get_value (adj);
138   gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v,
139                   &editor->priv->color.red,
140                   &editor->priv->color.green,
141                   &editor->priv->color.blue);
142   gtk_color_plane_set_h (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->h);
143   gtk_widget_queue_draw (editor->priv->swatch);
144   update_entry (editor);
145   g_object_notify (G_OBJECT (editor), "color");
146 }
147
148 static void
149 sv_changed (GtkColorPlane  *plane,
150             GtkColorEditor *editor)
151 {
152   editor->priv->s = gtk_color_plane_get_s (plane);
153   editor->priv->v = gtk_color_plane_get_v (plane);
154   gtk_hsv_to_rgb (editor->priv->h, editor->priv->s, editor->priv->v,
155                   &editor->priv->color.red,
156                   &editor->priv->color.green,
157                   &editor->priv->color.blue);
158   update_entry (editor);
159   gtk_widget_queue_draw (editor->priv->swatch);
160 }
161
162 static void
163 a_changed (GtkAdjustment  *adj,
164            GtkColorEditor *editor)
165 {
166   editor->priv->color.alpha = gtk_adjustment_get_value (adj);
167   gtk_widget_queue_draw (editor->priv->swatch);
168   g_object_notify (G_OBJECT (editor), "color");
169 }
170
171 static cairo_pattern_t *
172 get_checkered_pattern (void)
173 {
174   /* need to respect pixman's stride being a multiple of 4 */
175   static unsigned char data[8] = { 0xFF, 0x00, 0x00, 0x00,
176                                    0x00, 0xFF, 0x00, 0x00 };
177   static cairo_surface_t *checkered = NULL;
178   cairo_pattern_t *pattern;
179
180   if (checkered == NULL)
181     checkered = cairo_image_surface_create_for_data (data,
182                                                      CAIRO_FORMAT_A8,
183                                                      2, 2, 4);
184
185   pattern = cairo_pattern_create_for_surface (checkered);
186   cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
187   cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
188
189   return pattern;
190 }
191
192 static gboolean
193 swatch_draw (GtkWidget      *widget,
194              cairo_t        *cr,
195              GtkColorEditor *editor)
196 {
197   cairo_pattern_t *checkered;
198
199   if (editor->priv->show_alpha)
200     {
201       cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
202       cairo_paint (cr);
203
204       cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
205       cairo_scale (cr, 8, 8);
206
207       checkered = get_checkered_pattern ();
208       cairo_mask (cr, checkered);
209       cairo_pattern_destroy (checkered);
210     }
211
212   gdk_cairo_set_source_rgba (cr, &editor->priv->color);
213   cairo_paint (cr);
214
215   return TRUE;
216 }
217
218 static void
219 gtk_color_editor_init (GtkColorEditor *editor)
220 {
221   GtkWidget *grid;
222   GtkAdjustment *adj;
223
224   editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor,
225                                               GTK_TYPE_COLOR_EDITOR,
226                                               GtkColorEditorPrivate);
227   editor->priv->show_alpha = TRUE;
228
229   gtk_widget_push_composite_child ();
230
231   editor->priv->grid = grid = gtk_grid_new ();
232   gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
233   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
234
235   editor->priv->swatch = gtk_drawing_area_new ();
236   g_signal_connect (editor->priv->swatch, "draw", G_CALLBACK (swatch_draw), editor);
237   editor->priv->entry = gtk_entry_new ();
238   g_signal_connect (editor->priv->entry, "activate",
239                     G_CALLBACK (entry_apply), editor);
240   g_signal_connect (editor->priv->entry, "notify::text",
241                     G_CALLBACK (entry_text_changed), editor);
242   g_signal_connect (editor->priv->entry, "focus-out-event",
243                     G_CALLBACK (entry_focus_out), editor);
244
245   adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
246   g_signal_connect (adj, "value-changed", G_CALLBACK (h_changed), editor);
247   editor->priv->h_slider = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj);
248   editor->priv->h_adj = adj;
249
250   gtk_scale_set_draw_value (GTK_SCALE (editor->priv->h_slider), FALSE);
251   editor->priv->sv_plane = gtk_color_plane_new ();
252   gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300);
253   gtk_widget_set_hexpand (editor->priv->sv_plane, TRUE);
254   gtk_widget_set_vexpand (editor->priv->sv_plane, TRUE);
255
256   g_signal_connect (editor->priv->sv_plane, "changed", G_CALLBACK (sv_changed), editor);
257
258   adj = gtk_adjustment_new (1, 0, 1, 0.01, 0.1, 0);
259   editor->priv->a_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adj);
260   g_signal_connect (adj, "value-changed", G_CALLBACK (a_changed), editor);
261   gtk_scale_set_draw_value (GTK_SCALE (editor->priv->a_slider), FALSE);
262   editor->priv->a_adj = adj;
263
264   gtk_grid_attach (GTK_GRID (grid), editor->priv->swatch,   1, 0, 1, 1);
265   gtk_grid_attach (GTK_GRID (grid), editor->priv->entry,    2, 0, 1, 1);
266   gtk_grid_attach (GTK_GRID (grid), editor->priv->h_slider, 0, 1, 1, 1);
267   gtk_grid_attach (GTK_GRID (grid), editor->priv->sv_plane, 1, 1, 2, 1);
268   gtk_grid_attach (GTK_GRID (grid), editor->priv->a_slider, 1, 2, 2, 1);
269   gtk_widget_show_all (grid);
270
271   gtk_container_add (GTK_CONTAINER (editor), grid);
272   gtk_widget_pop_composite_child ();
273 }
274
275 static void
276 gtk_color_editor_get_property (GObject    *object,
277                                guint       prop_id,
278                                GValue     *value,
279                                GParamSpec *pspec)
280 {
281   GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
282   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
283
284   switch (prop_id)
285     {
286     case PROP_COLOR:
287       {
288         GdkRGBA color;
289         gtk_color_chooser_get_color (cc, &color);
290         g_value_set_boxed (value, &color);
291       }
292       break;
293     case PROP_SHOW_ALPHA:
294       g_value_set_boolean (value, gtk_widget_get_visible (ce->priv->a_slider));
295       break;
296     default:
297       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
298       break;
299     }
300 }
301
302 static void
303 gtk_color_editor_set_show_alpha (GtkColorEditor *editor,
304                                  gboolean        show_alpha)
305 {
306   if (editor->priv->show_alpha != show_alpha)
307     {
308       editor->priv->show_alpha = show_alpha;
309
310       if (show_alpha)
311         gtk_widget_show (editor->priv->a_slider);
312       else
313         gtk_widget_hide (editor->priv->a_slider);
314     }
315 }
316
317 static void
318 gtk_color_editor_set_property (GObject      *object,
319                                guint         prop_id,
320                                const GValue *value,
321                                GParamSpec   *pspec)
322 {
323   GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
324   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
325
326   switch (prop_id)
327     {
328     case PROP_COLOR:
329       gtk_color_chooser_set_color (cc, g_value_get_boxed (value));
330       break;
331     case PROP_SHOW_ALPHA:
332       gtk_color_editor_set_show_alpha (ce, g_value_get_boolean (value));
333       break;
334     default:
335       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
336       break;
337     }
338 }
339
340 static void
341 gtk_color_editor_class_init (GtkColorEditorClass *class)
342 {
343   GObjectClass *object_class = G_OBJECT_CLASS (class);
344
345   object_class->get_property = gtk_color_editor_get_property;
346   object_class->set_property = gtk_color_editor_set_property;
347
348   g_object_class_override_property (object_class, PROP_COLOR, "color");
349   g_object_class_override_property (object_class, PROP_SHOW_ALPHA, "show-alpha");
350
351   g_type_class_add_private (class, sizeof (GtkColorEditorPrivate));
352 }
353
354 static void
355 gtk_color_editor_get_color (GtkColorChooser *chooser,
356                             GdkRGBA         *color)
357 {
358   GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
359
360   color->red = editor->priv->color.red;
361   color->green = editor->priv->color.green;
362   color->blue = editor->priv->color.blue;
363   color->alpha = editor->priv->color.alpha;
364 }
365
366 static void
367 gtk_color_editor_set_color (GtkColorChooser *chooser,
368                             const GdkRGBA   *color)
369 {
370   GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
371
372   editor->priv->color.red = color->red;
373   editor->priv->color.green = color->green;
374   editor->priv->color.blue = color->blue;
375   gtk_rgb_to_hsv (editor->priv->color.red,
376                   editor->priv->color.green,
377                   editor->priv->color.blue,
378                   &editor->priv->h, &editor->priv->s, &editor->priv->v);
379   gtk_color_plane_set_h (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->h);
380   gtk_color_plane_set_s (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->s);
381   gtk_color_plane_set_v (GTK_COLOR_PLANE (editor->priv->sv_plane), editor->priv->v);
382   gtk_adjustment_set_value (editor->priv->h_adj, editor->priv->h);
383   update_entry (editor);
384
385   editor->priv->color.alpha = color->alpha;
386   gtk_adjustment_set_value (editor->priv->a_adj, editor->priv->color.alpha);
387
388   gtk_widget_queue_draw (GTK_WIDGET (editor));
389
390   g_object_notify (G_OBJECT (editor), "color");
391 }
392
393 static void
394 gtk_color_editor_iface_init (GtkColorChooserInterface *iface)
395 {
396   iface->get_color = gtk_color_editor_get_color;
397   iface->set_color = gtk_color_editor_set_color;
398 }
399
400 GtkWidget *
401 gtk_color_editor_new (void)
402 {
403   return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_EDITOR, NULL);
404 }