]> Pileus Git - ~andy/gtk/blob - gtk/gtkcoloreditor.c
Change FSF Address
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /* TODO
19  * - touch
20  * - accessible relations for popups
21  * - saving per-application (?)
22  * - better popup theming
23  */
24
25 #include "config.h"
26
27 #include "gtkcoloreditorprivate.h"
28
29 #include "gtkcolorchooserprivate.h"
30 #include "gtkcolorplaneprivate.h"
31 #include "gtkcolorscaleprivate.h"
32 #include "gtkcolorswatchprivate.h"
33 #include "gtkcolorutils.h"
34 #include "gtkgrid.h"
35 #include "gtkorientable.h"
36 #include "gtkentry.h"
37 #include "gtkoverlay.h"
38 #include "gtkadjustment.h"
39 #include "gtklabel.h"
40 #include "gtkspinbutton.h"
41 #include "gtkintl.h"
42
43 #include <math.h>
44
45 struct _GtkColorEditorPrivate
46 {
47   GtkWidget *overlay;
48   GtkWidget *grid;
49   GtkWidget *swatch;
50   GtkWidget *entry;
51   GtkWidget *h_slider;
52   GtkWidget *h_popup;
53   GtkWidget *h_entry;
54   GtkWidget *a_slider;
55   GtkWidget *a_popup;
56   GtkWidget *a_entry;
57   GtkWidget *sv_plane;
58   GtkWidget *sv_popup;
59   GtkWidget *s_entry;
60   GtkWidget *v_entry;
61   GtkWidget *current_popup;
62   GtkWidget *popdown_focus;
63
64   GtkAdjustment *h_adj;
65   GtkAdjustment *s_adj;
66   GtkAdjustment *v_adj;
67   GtkAdjustment *a_adj;
68
69   guint text_changed : 1;
70   guint use_alpha    : 1;
71 };
72
73 enum
74 {
75   PROP_ZERO,
76   PROP_RGBA,
77   PROP_USE_ALPHA
78 };
79
80 static void gtk_color_editor_iface_init (GtkColorChooserInterface *iface);
81
82 G_DEFINE_TYPE_WITH_CODE (GtkColorEditor, gtk_color_editor, GTK_TYPE_BOX,
83                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
84                                                 gtk_color_editor_iface_init))
85
86 static guint
87 scale_round (gdouble value, gdouble scale)
88 {
89   value = floor (value * scale + 0.5);
90   value = MAX (value, 0);
91   value = MIN (value, scale);
92   return (guint)value;
93 }
94
95 static void
96 entry_set_rgba (GtkColorEditor *editor,
97                 const GdkRGBA  *color)
98 {
99   gchar *text;
100
101   text = g_strdup_printf ("#%02X%02X%02X",
102                           scale_round (color->red, 255),
103                           scale_round (color->green, 255),
104                           scale_round (color->blue, 255));
105   gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text);
106   editor->priv->text_changed = FALSE;
107   g_free (text);
108 }
109
110 static void
111 entry_apply (GtkWidget      *entry,
112              GtkColorEditor *editor)
113 {
114   GdkRGBA color;
115   gchar *text;
116
117   if (!editor->priv->text_changed)
118     return;
119
120   text = gtk_editable_get_chars (GTK_EDITABLE (editor->priv->entry), 0, -1);
121   if (gdk_rgba_parse (&color, text))
122     {
123       color.alpha = gtk_adjustment_get_value (editor->priv->a_adj);
124       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (editor), &color);
125     }
126
127   editor->priv->text_changed = FALSE;
128
129   g_free (text);
130 }
131
132 static gboolean
133 entry_focus_out (GtkWidget      *entry,
134                  GdkEventFocus  *event,
135                  GtkColorEditor *editor)
136 {
137   entry_apply (entry, editor);
138   return FALSE;
139 }
140
141 static void
142 entry_text_changed (GtkWidget      *entry,
143                     GParamSpec     *pspec,
144                     GtkColorEditor *editor)
145 {
146   editor->priv->text_changed = TRUE;
147 }
148
149 static void
150 hsv_changed (GtkColorEditor *editor)
151 {
152   GdkRGBA color;
153   gdouble h, s, v, a;
154
155   h = gtk_adjustment_get_value (editor->priv->h_adj);
156   s = gtk_adjustment_get_value (editor->priv->s_adj);
157   v = gtk_adjustment_get_value (editor->priv->v_adj);
158   a = gtk_adjustment_get_value (editor->priv->a_adj);
159
160   gtk_hsv_to_rgb (h, s, v, &color.red, &color.green, &color.blue);
161   color.alpha = a;
162
163   gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (editor->priv->swatch), &color);
164   gtk_color_scale_set_rgba (GTK_COLOR_SCALE (editor->priv->a_slider), &color);
165   entry_set_rgba (editor, &color);
166
167   g_object_notify (G_OBJECT (editor), "rgba");
168 }
169
170 static void
171 dismiss_current_popup (GtkColorEditor *editor)
172 {
173   if (editor->priv->current_popup)
174     {
175       gtk_widget_hide (editor->priv->current_popup);
176       editor->priv->current_popup = NULL;
177       if (editor->priv->popdown_focus)
178         {
179           gtk_widget_grab_focus (editor->priv->popdown_focus);
180           editor->priv->popdown_focus = NULL;
181         }
182     }
183 }
184
185 static void
186 popup_edit (GtkWidget      *widget,
187             GtkColorEditor *editor)
188 {
189   GtkWidget *popup = NULL;
190   GtkWidget *toplevel;
191   GtkWidget *focus;
192
193   if (widget == editor->priv->sv_plane)
194     {
195       popup = editor->priv->sv_popup;
196       focus = editor->priv->s_entry;
197     }
198   else if (widget == editor->priv->h_slider)
199     {
200       popup = editor->priv->h_popup;
201       focus = editor->priv->h_entry;
202     }
203   else if (widget == editor->priv->a_slider)
204     {
205       popup = editor->priv->a_popup;
206       focus = editor->priv->a_entry;
207     }
208
209   if (popup)
210     {
211       dismiss_current_popup (editor);
212       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (editor));
213       editor->priv->popdown_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
214       editor->priv->current_popup = popup;
215       gtk_widget_show (popup);
216       gtk_widget_grab_focus (focus);
217     }
218 }
219
220 static gboolean
221 popup_key_press (GtkWidget      *popup,
222                  GdkEventKey    *event,
223                  GtkColorEditor *editor)
224 {
225   if (event->keyval == GDK_KEY_Escape)
226     {
227       dismiss_current_popup (editor);
228       return TRUE;
229     }
230
231   return FALSE;
232 }
233
234 static gboolean
235 get_child_position (GtkOverlay     *overlay,
236                     GtkWidget      *widget,
237                     GtkAllocation  *allocation,
238                     GtkColorEditor *editor)
239 {
240   GtkRequisition req;
241   GtkAllocation alloc;
242   gint s, e;
243
244   gtk_widget_get_preferred_size (widget, &req, NULL);
245
246   allocation->width = req.width;
247   allocation->height = req.height;
248
249   if (widget == editor->priv->sv_popup)
250     {
251       if (gtk_widget_get_direction (GTK_WIDGET (overlay)) == GTK_TEXT_DIR_RTL)
252         allocation->x = 0;
253       else
254         allocation->x = gtk_widget_get_allocated_width (GTK_WIDGET (overlay)) - req.width;
255       allocation->y = req.height / 3;
256     }
257   else if (widget == editor->priv->h_popup)
258     {
259       gtk_widget_get_allocation (editor->priv->h_slider, &alloc);
260       gtk_range_get_slider_range (GTK_RANGE (editor->priv->h_slider), &s, &e);
261
262       if (gtk_widget_get_direction (GTK_WIDGET (overlay)) == GTK_TEXT_DIR_RTL)
263         gtk_widget_translate_coordinates (editor->priv->h_slider,
264                                           gtk_widget_get_parent (editor->priv->grid),
265                                           - req.width, (s + e - req.height) / 2,
266                                           &allocation->x, &allocation->y);
267       else
268         gtk_widget_translate_coordinates (editor->priv->h_slider,
269                                           gtk_widget_get_parent (editor->priv->grid),
270                                           alloc.width, (s + e - req.height) / 2,
271                                           &allocation->x, &allocation->y);
272     }
273   else if (widget == editor->priv->a_popup)
274     {
275       gtk_widget_get_allocation (editor->priv->a_slider, &alloc);
276       gtk_range_get_slider_range (GTK_RANGE (editor->priv->a_slider), &s, &e);
277
278       gtk_widget_translate_coordinates (editor->priv->a_slider,
279                                         gtk_widget_get_parent (editor->priv->grid),
280                                         (s + e - req.width) / 2, - req.height,
281                                         &allocation->x, &allocation->y);
282     }
283   else
284     return FALSE;
285
286   allocation->x = CLAMP (allocation->x, 0, gtk_widget_get_allocated_width (GTK_WIDGET (overlay)) - req.width);
287   allocation->y = CLAMP (allocation->y, 0, gtk_widget_get_allocated_height (GTK_WIDGET (overlay)) - req.height);
288
289   return TRUE;
290 }
291
292 static void
293 value_changed (GtkAdjustment *a,
294                GtkAdjustment *as)
295 {
296   gdouble scale;
297
298   scale = gtk_adjustment_get_upper (as) / gtk_adjustment_get_upper (a);
299   g_signal_handlers_block_by_func (as, value_changed, a);
300   gtk_adjustment_set_value (as, gtk_adjustment_get_value (a) * scale);
301   g_signal_handlers_unblock_by_func (as, value_changed, a);
302 }
303
304 static GtkAdjustment *
305 scaled_adjustment (GtkAdjustment *a,
306                    gdouble        scale)
307 {
308   GtkAdjustment *as;
309
310   as = gtk_adjustment_new (gtk_adjustment_get_value (a) * scale,
311                            gtk_adjustment_get_lower (a) * scale,
312                            gtk_adjustment_get_upper (a) * scale,
313                            gtk_adjustment_get_step_increment (a) * scale,
314                            gtk_adjustment_get_page_increment (a) * scale,
315                            gtk_adjustment_get_page_size (a) * scale);
316
317   g_signal_connect (a, "value-changed", G_CALLBACK (value_changed), as);
318   g_signal_connect (as, "value-changed", G_CALLBACK (value_changed), a);
319
320   return as;
321 }
322
323 static gboolean
324 popup_draw (GtkWidget      *popup,
325             cairo_t        *cr,
326             GtkColorEditor *editor)
327 {
328   GtkStyleContext *context;
329   gint width, height;
330
331   context = gtk_widget_get_style_context (popup);
332   width = gtk_widget_get_allocated_width (popup);
333   height = gtk_widget_get_allocated_height (popup);
334
335   gtk_render_background (context, cr, 0, 0, width, height);
336   gtk_render_frame (context, cr, 0, 0, width, height);
337
338   return FALSE;
339 }
340
341 static GtkWidget *
342 create_popup (GtkColorEditor *editor,
343               GtkWidget      *attach,
344               GtkWidget      *contents)
345 {
346   GtkWidget *popup;
347
348   g_object_set (contents, "margin", 12, NULL);
349
350   popup = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
351   gtk_style_context_add_class (gtk_widget_get_style_context (popup), GTK_STYLE_CLASS_TOOLTIP);
352   gtk_container_add (GTK_CONTAINER (popup), contents);
353
354   gtk_widget_show_all (contents);
355   gtk_widget_set_no_show_all (popup, TRUE);
356
357   g_signal_connect (popup, "draw", G_CALLBACK (popup_draw), editor);
358
359   gtk_overlay_add_overlay (GTK_OVERLAY (editor->priv->overlay), popup);
360   g_signal_connect (attach, "popup-menu", G_CALLBACK (popup_edit), editor);
361
362   return popup;
363 }
364
365 static void
366 gtk_color_editor_init (GtkColorEditor *editor)
367 {
368   GtkWidget *grid;
369   GtkWidget *slider;
370   GtkWidget *entry;
371   GtkWidget *swatch;
372   GtkAdjustment *h_adj, *s_adj, *v_adj, *a_adj;
373   AtkObject *atk_obj;
374   GdkRGBA transparent = { 0, 0, 0, 0 };
375
376   editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor,
377                                               GTK_TYPE_COLOR_EDITOR,
378                                               GtkColorEditorPrivate);
379   editor->priv->use_alpha = TRUE;
380
381   editor->priv->h_adj = h_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
382   editor->priv->s_adj = s_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
383   editor->priv->v_adj = v_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
384   editor->priv->a_adj = a_adj = gtk_adjustment_new (0, 0, 1, 0.01, 0.1, 0);
385
386   g_object_ref_sink (h_adj);
387   g_object_ref_sink (s_adj);
388   g_object_ref_sink (v_adj);
389   g_object_ref_sink (a_adj);
390
391   g_signal_connect_swapped (h_adj, "value-changed", G_CALLBACK (hsv_changed), editor);
392   g_signal_connect_swapped (s_adj, "value-changed", G_CALLBACK (hsv_changed), editor);
393   g_signal_connect_swapped (v_adj, "value-changed", G_CALLBACK (hsv_changed), editor);
394   g_signal_connect_swapped (a_adj, "value-changed", G_CALLBACK (hsv_changed), editor);
395
396   gtk_widget_push_composite_child ();
397
398   /* Construct the main UI */
399   editor->priv->swatch = swatch = gtk_color_swatch_new ();
400   gtk_color_swatch_set_selectable (GTK_COLOR_SWATCH (editor->priv->swatch), FALSE);
401   gtk_widget_set_events (swatch, gtk_widget_get_events (swatch)
402                                  & ~(GDK_BUTTON_PRESS_MASK
403                                      | GDK_BUTTON_RELEASE_MASK
404                                      | GDK_KEY_PRESS_MASK
405                                      | GDK_KEY_RELEASE_MASK));
406   gtk_widget_set_can_focus (swatch, FALSE);
407
408   editor->priv->entry = entry = gtk_entry_new ();
409   atk_obj = gtk_widget_get_accessible (entry);
410   atk_object_set_role (atk_obj, ATK_ROLE_ENTRY);
411   atk_object_set_name (atk_obj, _("Color Name"));
412   g_signal_connect (entry, "activate", G_CALLBACK (entry_apply), editor);
413   g_signal_connect (entry, "notify::text", G_CALLBACK (entry_text_changed), editor);
414   g_signal_connect (entry, "focus-out-event", G_CALLBACK (entry_focus_out), editor);
415
416   editor->priv->h_slider = slider = gtk_color_scale_new (h_adj, GTK_COLOR_SCALE_HUE);
417   gtk_orientable_set_orientation (GTK_ORIENTABLE (slider), GTK_ORIENTATION_VERTICAL);
418   if (gtk_widget_get_direction (slider) == GTK_TEXT_DIR_RTL)
419     gtk_style_context_add_class (gtk_widget_get_style_context (slider),
420                                  GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE);
421   else
422     gtk_style_context_add_class (gtk_widget_get_style_context (slider),
423                                  GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW);
424
425   editor->priv->sv_plane = gtk_color_plane_new (h_adj, s_adj, v_adj);
426   gtk_widget_set_size_request (editor->priv->sv_plane, 300, 300);
427
428   editor->priv->a_slider = slider = gtk_color_scale_new (a_adj, GTK_COLOR_SCALE_ALPHA);
429   gtk_orientable_set_orientation (GTK_ORIENTABLE (slider), GTK_ORIENTATION_HORIZONTAL);
430   gtk_style_context_add_class (gtk_widget_get_style_context (slider),
431                                GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE);
432
433   editor->priv->grid = grid = gtk_grid_new ();
434   gtk_grid_set_row_spacing (GTK_GRID (grid), 12);
435   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
436
437   gtk_grid_attach (GTK_GRID (grid), editor->priv->swatch,   1, 0, 1, 1);
438   gtk_grid_attach (GTK_GRID (grid), editor->priv->entry,    2, 0, 1, 1);
439   gtk_grid_attach (GTK_GRID (grid), editor->priv->h_slider, 0, 1, 1, 1);
440   gtk_grid_attach (GTK_GRID (grid), editor->priv->sv_plane, 1, 1, 2, 1);
441   gtk_grid_attach (GTK_GRID (grid), editor->priv->a_slider, 1, 2, 2, 1);
442
443   /* This extra margin is necessary so we have room to the sides
444    * to place the popups as desired
445    */
446   gtk_widget_set_margin_left (grid, 30);
447   gtk_widget_set_margin_right (grid, 30);
448
449   editor->priv->overlay = gtk_overlay_new ();
450   gtk_widget_override_background_color (editor->priv->overlay, 0, &transparent);
451   gtk_container_add (GTK_CONTAINER (editor->priv->overlay), grid);
452
453   /* Construct the sv popup */
454   editor->priv->s_entry = entry = gtk_spin_button_new (scaled_adjustment (s_adj, 100), 1, 0);
455   atk_obj = gtk_widget_get_accessible (entry);
456   atk_object_set_name (atk_obj, C_("Color channel", "Saturation"));
457   atk_object_set_role (atk_obj, ATK_ROLE_ENTRY);
458   g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor);
459
460   editor->priv->v_entry = entry = gtk_spin_button_new (scaled_adjustment (v_adj, 100), 1, 0);
461   atk_obj = gtk_widget_get_accessible (entry);
462   atk_object_set_name (atk_obj, C_("Color channel", "Value"));
463   atk_object_set_role (atk_obj, ATK_ROLE_ENTRY);
464   g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor);
465
466   grid = gtk_grid_new ();
467   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
468   gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
469
470   gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "S")), 0, 0, 1, 1);
471   gtk_grid_attach (GTK_GRID (grid), editor->priv->s_entry, 1, 0, 1, 1);
472   gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "V")), 0, 1, 1, 1);
473   gtk_grid_attach (GTK_GRID (grid), editor->priv->v_entry, 1, 1, 1, 1);
474
475   editor->priv->sv_popup = create_popup (editor, editor->priv->sv_plane, grid);
476
477   /* Construct the h popup */
478   editor->priv->h_entry = entry = gtk_spin_button_new (scaled_adjustment (h_adj, 100), 1, 0);
479   atk_obj = gtk_widget_get_accessible (entry);
480   atk_object_set_name (atk_obj, C_("Color channel", "Hue"));
481   atk_object_set_role (atk_obj, ATK_ROLE_ENTRY);
482   g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor);
483
484   grid = gtk_grid_new ();
485   gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
486
487   gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "H")), 0, 0, 1, 1);
488   gtk_grid_attach (GTK_GRID (grid), editor->priv->h_entry, 1, 0, 1, 1);
489
490   editor->priv->h_popup = create_popup (editor, editor->priv->h_slider, grid);
491
492   /* Construct the a popup */
493   editor->priv->a_entry = entry = gtk_spin_button_new (scaled_adjustment (a_adj, 100), 1, 0);
494   atk_obj = gtk_widget_get_accessible (entry);
495   atk_object_set_name (atk_obj, C_("Color channel", "Alpha"));
496   atk_object_set_role (atk_obj, ATK_ROLE_ENTRY);
497   g_signal_connect (entry, "key-press-event", G_CALLBACK (popup_key_press), editor);
498
499   grid = gtk_grid_new ();
500   gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
501
502   gtk_grid_attach (GTK_GRID (grid), gtk_label_new (C_("Color channel", "A")), 0, 0, 1, 1);
503   gtk_grid_attach (GTK_GRID (grid), editor->priv->a_entry, 1, 0, 1, 1);
504
505   editor->priv->a_popup = create_popup (editor, editor->priv->a_slider, grid);
506
507   /* Hook up popup positioning */
508   g_signal_connect (editor->priv->overlay, "get-child-position", G_CALLBACK (get_child_position), editor);
509   g_signal_connect (editor, "notify::visible", G_CALLBACK (dismiss_current_popup), NULL);
510
511   gtk_widget_show_all (editor->priv->overlay);
512   gtk_container_add (GTK_CONTAINER (editor), editor->priv->overlay);
513
514   gtk_widget_pop_composite_child ();
515 }
516
517 static void
518 gtk_color_editor_get_property (GObject    *object,
519                                guint       prop_id,
520                                GValue     *value,
521                                GParamSpec *pspec)
522 {
523   GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
524   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
525
526   switch (prop_id)
527     {
528     case PROP_RGBA:
529       {
530         GdkRGBA color;
531         gtk_color_chooser_get_rgba (cc, &color);
532         g_value_set_boxed (value, &color);
533       }
534       break;
535     case PROP_USE_ALPHA:
536       g_value_set_boolean (value, gtk_widget_get_visible (ce->priv->a_slider));
537       break;
538     default:
539       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
540       break;
541     }
542 }
543
544 static void
545 gtk_color_editor_set_use_alpha (GtkColorEditor *editor,
546                                 gboolean        use_alpha)
547 {
548   if (editor->priv->use_alpha != use_alpha)
549     {
550       editor->priv->use_alpha = use_alpha;
551       gtk_widget_set_visible (editor->priv->a_slider, use_alpha);
552       gtk_color_swatch_set_use_alpha (GTK_COLOR_SWATCH (editor->priv->swatch), use_alpha);
553     }
554 }
555
556 static void
557 gtk_color_editor_set_property (GObject      *object,
558                                guint         prop_id,
559                                const GValue *value,
560                                GParamSpec   *pspec)
561 {
562   GtkColorEditor *ce = GTK_COLOR_EDITOR (object);
563   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
564
565   switch (prop_id)
566     {
567     case PROP_RGBA:
568       gtk_color_chooser_set_rgba (cc, g_value_get_boxed (value));
569       break;
570     case PROP_USE_ALPHA:
571       gtk_color_editor_set_use_alpha (ce, g_value_get_boolean (value));
572       break;
573     default:
574       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
575       break;
576     }
577 }
578
579 static void
580 gtk_color_editor_finalize (GObject *object)
581 {
582   GtkColorEditor *editor = GTK_COLOR_EDITOR (object);
583
584   g_clear_object (&editor->priv->h_adj);
585   g_clear_object (&editor->priv->s_adj);
586   g_clear_object (&editor->priv->v_adj);
587   g_clear_object (&editor->priv->a_adj);
588
589   G_OBJECT_CLASS (gtk_color_editor_parent_class)->finalize (object);
590 }
591
592 static void
593 gtk_color_editor_class_init (GtkColorEditorClass *class)
594 {
595   GObjectClass *object_class = G_OBJECT_CLASS (class);
596
597   object_class->finalize = gtk_color_editor_finalize;
598   object_class->get_property = gtk_color_editor_get_property;
599   object_class->set_property = gtk_color_editor_set_property;
600
601   g_object_class_override_property (object_class, PROP_RGBA, "rgba");
602   g_object_class_override_property (object_class, PROP_USE_ALPHA, "use-alpha");
603
604   g_type_class_add_private (class, sizeof (GtkColorEditorPrivate));
605 }
606
607 static void
608 gtk_color_editor_get_rgba (GtkColorChooser *chooser,
609                            GdkRGBA         *color)
610 {
611   GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
612   gdouble h, s, v;
613
614   h = gtk_adjustment_get_value (editor->priv->h_adj);
615   s = gtk_adjustment_get_value (editor->priv->s_adj);
616   v = gtk_adjustment_get_value (editor->priv->v_adj);
617   gtk_hsv_to_rgb (h, s, v, &color->red, &color->green, &color->blue);
618   color->alpha = gtk_adjustment_get_value (editor->priv->a_adj);
619 }
620
621 static void
622 gtk_color_editor_set_rgba (GtkColorChooser *chooser,
623                            const GdkRGBA   *color)
624 {
625   GtkColorEditor *editor = GTK_COLOR_EDITOR (chooser);
626   gdouble h, s, v;
627
628   gtk_rgb_to_hsv (color->red, color->green, color->blue, &h, &s, &v);
629
630   gtk_adjustment_set_value (editor->priv->h_adj, h);
631   gtk_adjustment_set_value (editor->priv->s_adj, s);
632   gtk_adjustment_set_value (editor->priv->v_adj, v);
633   gtk_adjustment_set_value (editor->priv->a_adj, color->alpha);
634
635   gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (editor->priv->swatch), color);
636   gtk_color_scale_set_rgba (GTK_COLOR_SCALE (editor->priv->a_slider), color);
637   entry_set_rgba (editor, color);
638
639   g_object_notify (G_OBJECT (editor), "rgba");
640 }
641
642 static void
643 gtk_color_editor_iface_init (GtkColorChooserInterface *iface)
644 {
645   iface->get_rgba = gtk_color_editor_get_rgba;
646   iface->set_rgba = gtk_color_editor_set_rgba;
647 }
648
649 GtkWidget *
650 gtk_color_editor_new (void)
651 {
652   return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_EDITOR, NULL);
653 }