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