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