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