1 /* gstyle-color-plane.c
2  *
3  * based on : gtk-color-plane
4  *   GTK - The GIMP Toolkit
5  *   Copyright 2012 Red Hat, Inc.
6  *
7  * Copyright 2016 sebastien lafargue <slafargue@gnome.org>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * SPDX-License-Identifier: GPL-3.0-or-later
23  */
24 
25 #define G_LOG_DOMAIN "gstyle-color-plane"
26 
27 #include <math.h>
28 #include <cairo/cairo.h>
29 #include <glib/gi18n.h>
30 
31 #include "gstyle-cielab.h"
32 #include "gstyle-color-convert.h"
33 #include "gstyle-css-provider.h"
34 #include "gstyle-utils.h"
35 
36 #include "gstyle-color-plane.h"
37 
38 typedef struct _ComputeData
39 {
40   gint     width;
41   gint     height;
42   gint     stride;
43   guint32 *buffer;
44 
45   gdouble  x_factor;
46   gdouble  y_factor;
47   gdouble  lab_x_factor;
48   gdouble  lab_y_factor;
49   gdouble  lab_l_factor;
50 } ComputeData;
51 
52 typedef enum _ColorSpaceId
53 {
54   COLOR_SPACE_RGB,
55   COLOR_SPACE_CIELAB,
56   COLOR_SPACE_HSV,
57   COLOR_SPACE_NONE
58 } ColorSpaceId;
59 
60 typedef struct _Component
61 {
62   GtkAdjustment *adj;
63   gulong         handler;
64   gdouble        val;
65   gdouble        factor;
66   ColorSpaceId   color_space;
67 } Component;
68 
69 typedef struct
70 {
71   cairo_surface_t        *surface;
72 
73   GstyleCssProvider      *default_provider;
74 
75   GtkGesture             *drag_gesture;
76   GtkGesture             *long_press_gesture;
77 
78   GtkBorder               cached_margin;
79   GtkBorder               cached_border;
80   GdkRectangle            cached_margin_box;
81   GdkRectangle            cached_border_box;
82 
83   GstyleColorPlaneMode    mode;
84   GstyleXYZ               xyz;
85   gdouble                 cursor_x;
86   gdouble                 cursor_y;
87 
88   ComputeData             data;
89   GstyleColorFilterFunc   filter;
90   gpointer                filter_user_data;
91 
92   Component               comp [N_GSTYLE_COLOR_COMPONENT];
93   GstyleColorComponent    ref_comp;
94   GstyleColorUnit         preferred_unit;
95   gdouble                 hue_backup;
96 
97   guint                   hue_backup_set : 1;
98 } GstyleColorPlanePrivate;
99 
100 G_DEFINE_TYPE_WITH_PRIVATE (GstyleColorPlane, gstyle_color_plane, GTK_TYPE_DRAWING_AREA)
101 
102 enum {
103   PROP_0,
104   PROP_MODE,
105   PROP_RGBA,
106   PROP_XYZ,
107   N_PROPS
108 };
109 
110 static GParamSpec *properties [N_PROPS];
111 
112 /* We return %TRUE if there's no changes in border and margin, %FALSE otherwise.*/
113 static gboolean
update_css_boxes(GstyleColorPlane * self)114 update_css_boxes (GstyleColorPlane *self)
115 {
116   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
117   GtkWidget *widget = GTK_WIDGET (self);
118   GtkStyleContext *style_context;
119   GtkStateFlags state;
120   GdkRectangle margin_box;
121   GdkRectangle border_box;
122   GtkBorder margin;
123   GtkBorder border;
124   gboolean res;
125 
126   g_assert (GSTYLE_IS_COLOR_PLANE (self));
127 
128   style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
129   state = gtk_style_context_get_state (style_context);
130 
131   gtk_style_context_get_margin (style_context, state, &margin);
132   gtk_style_context_get_border (style_context, state, &border);
133   gtk_widget_get_allocation (widget, &margin_box);
134   margin_box.x = margin_box.y = 0;
135 
136   gstyle_utils_get_rect_resized_box (margin_box, &margin_box, &margin);
137   gstyle_utils_get_rect_resized_box (margin_box, &border_box, &border);
138 
139   res = (gstyle_utils_cmp_border (margin, priv->cached_margin) ||
140          gstyle_utils_cmp_border (border, priv->cached_border));
141 
142   priv->cached_margin_box = margin_box;
143   priv->cached_border_box = border_box;
144   priv->cached_margin = margin;
145   priv->cached_border = border;
146 
147   return res;
148 }
149 
150 static void
get_xyz_from_cursor(GstyleColorPlane * self,GstyleXYZ * xyz)151 get_xyz_from_cursor (GstyleColorPlane *self,
152                      GstyleXYZ        *xyz)
153 {
154   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
155   gdouble hsv_h, hsv_s, hsv_v;
156   GstyleCielab lab;
157   GdkRGBA rgba = {0};
158 
159   g_assert (GSTYLE_IS_COLOR_PLANE (self));
160   g_assert (xyz != NULL);
161 
162   switch (priv->mode)
163     {
164     case GSTYLE_COLOR_PLANE_MODE_HUE:
165       hsv_h = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor,
166       hsv_s = priv->cursor_x * priv->data.x_factor;
167       hsv_v = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
168       gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
169       break;
170 
171     case GSTYLE_COLOR_PLANE_MODE_SATURATION:
172       hsv_s = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].factor,
173       hsv_h = priv->cursor_x * priv->data.x_factor;
174       hsv_v = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
175       gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
176       break;
177 
178     case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
179       hsv_v = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].factor,
180       hsv_h = priv->cursor_x * priv->data.x_factor;
181       hsv_s = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
182       gstyle_color_convert_hsv_to_xyz (hsv_h, hsv_s, hsv_v, xyz);
183       break;
184 
185     case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
186       lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].factor,
187       lab.a = priv->cursor_x * priv->data.lab_x_factor - 128.0;
188       lab.b = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_y_factor - 128.0;
189       gstyle_color_convert_cielab_to_xyz (&lab, xyz);
190       break;
191 
192     case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
193       lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].factor,
194       lab.b = priv->cursor_x * priv->data.lab_x_factor - 128.0;
195       lab.l = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_l_factor;
196       gstyle_color_convert_cielab_to_xyz (&lab, xyz);
197       break;
198 
199     case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
200       lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].factor,
201       lab.a = priv->cursor_x * priv->data.lab_x_factor - 128.0;
202       lab.l = (priv->data.height - priv->cursor_y - 1) * priv->data.lab_l_factor;
203       gstyle_color_convert_cielab_to_xyz (&lab, xyz);
204       break;
205 
206     case GSTYLE_COLOR_PLANE_MODE_RED:
207       rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].factor,
208       rgba.blue = priv->cursor_x * priv->data.x_factor;
209       rgba.green = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
210       gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
211       break;
212 
213     case GSTYLE_COLOR_PLANE_MODE_GREEN:
214       rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor,
215       rgba.blue = priv->cursor_x * priv->data.x_factor;
216       rgba.red = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
217       gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
218       break;
219 
220     case GSTYLE_COLOR_PLANE_MODE_BLUE:
221       rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor,
222       rgba.red = priv->cursor_x * priv->data.x_factor;
223       rgba.green = (priv->data.height - priv->cursor_y - 1) * priv->data.y_factor;
224       gstyle_color_convert_rgb_to_xyz (&rgba, xyz);
225       break;
226 
227     case GSTYLE_COLOR_PLANE_MODE_NONE:
228     default:
229       g_assert_not_reached ();
230     }
231 }
232 
233 static void
set_cursor_from_xyz(GstyleColorPlane * self,GstyleXYZ * xyz)234 set_cursor_from_xyz (GstyleColorPlane *self,
235                      GstyleXYZ        *xyz)
236 {
237   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
238   gdouble hsv_h, hsv_s, hsv_v;
239   GstyleCielab lab;
240   GdkRGBA rgba = {0};
241   gdouble x = 0.0, y = 0.0;
242 
243   g_assert (GSTYLE_IS_COLOR_PLANE (self));
244   g_assert (xyz != NULL);
245 
246   switch (priv->mode)
247     {
248     case GSTYLE_COLOR_PLANE_MODE_HUE:
249       gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
250       x = hsv_s / priv->data.x_factor;
251       y = (1.0 - hsv_v) / priv->data.y_factor;
252       break;
253 
254     case GSTYLE_COLOR_PLANE_MODE_SATURATION:
255       gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
256       x = hsv_h / priv->data.x_factor;
257       y = (1.0 - hsv_v) / priv->data.y_factor;
258       break;
259 
260     case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
261       gstyle_color_convert_xyz_to_hsv (xyz, &hsv_h, &hsv_s, &hsv_v);
262       x = hsv_h / priv->data.x_factor;
263       y = (1.0 - hsv_s) / priv->data.y_factor;
264       break;
265 
266     case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
267       gstyle_color_convert_xyz_to_cielab (xyz, &lab);
268       x = (lab.a + 128.0) / priv->data.lab_x_factor;
269       y = (128.0 - lab.b) / priv->data.lab_y_factor;
270       break;
271 
272     case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
273       gstyle_color_convert_xyz_to_cielab (xyz, &lab);
274       x = (lab.b + 128.0) / priv->data.lab_x_factor;
275       y = (100.0 - lab.l) / priv->data.lab_l_factor;
276       break;
277 
278     case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
279       gstyle_color_convert_xyz_to_cielab (xyz, &lab);
280       x = (lab.a + 128.0) / priv->data.lab_x_factor;
281       y = (100.0 - lab.l) / priv->data.lab_y_factor;
282       break;
283 
284     case GSTYLE_COLOR_PLANE_MODE_RED:
285       gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
286       x = rgba.blue / priv->data.x_factor;
287       y = (1.0 - rgba.green) / priv->data.y_factor;
288       break;
289 
290     case GSTYLE_COLOR_PLANE_MODE_GREEN:
291       gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
292       x = rgba.blue / priv->data.x_factor;
293       y = (1.0 - rgba.red) / priv->data.y_factor;
294       break;
295 
296     case GSTYLE_COLOR_PLANE_MODE_BLUE:
297       gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
298       x = rgba.red / priv->data.x_factor;
299       y = (1.0 - rgba.green) / priv->data.y_factor;
300       break;
301 
302     case GSTYLE_COLOR_PLANE_MODE_NONE:
303     default:
304       g_assert_not_reached ();
305     }
306 
307   priv->cursor_x = CLAMP (x, 0.0, (gdouble)priv->data.width - 1.0);
308   priv->cursor_y = CLAMP (y, 0.0, (gdouble)priv->data.height - 1.0);
309 }
310 
311 static void
configure_component(GstyleColorPlane * self,GstyleColorComponent comp,gdouble upper,gdouble factor)312 configure_component (GstyleColorPlane     *self,
313                      GstyleColorComponent  comp,
314                      gdouble               upper,
315                      gdouble               factor)
316 {
317   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
318   gdouble new_value;
319 
320   g_assert (GSTYLE_IS_COLOR_PLANE (self));
321   g_assert (GTK_IS_ADJUSTMENT (priv->comp [comp].adj));
322 
323   new_value = priv->comp [comp].val / priv->comp [comp].factor * factor;
324   priv->comp [comp].factor = factor;
325 
326   g_object_freeze_notify (G_OBJECT (priv->comp [comp].adj));
327   gtk_adjustment_set_upper (priv->comp [comp].adj, upper);
328   gtk_adjustment_set_value (priv->comp [comp].adj, new_value);
329   g_object_thaw_notify (G_OBJECT (priv->comp [comp].adj));
330 }
331 
332 static void
setup_component(GstyleColorPlane * self,GstyleColorComponent comp,gdouble origin,gdouble lower,gdouble upper,gdouble step_increment,gdouble page_increment,gdouble factor,ColorSpaceId color_space)333 setup_component (GstyleColorPlane     *self,
334                  GstyleColorComponent  comp,
335                  gdouble               origin,
336                  gdouble               lower,
337                  gdouble               upper,
338                  gdouble               step_increment,
339                  gdouble               page_increment,
340                  gdouble               factor,
341                  ColorSpaceId          color_space)
342 {
343   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
344 
345   g_assert (GSTYLE_IS_COLOR_PLANE (self));
346 
347   priv->comp [comp].adj = g_object_ref (gtk_adjustment_new (origin, lower, upper, step_increment, page_increment, 0));
348   priv->comp [comp].factor = factor;
349   priv->comp [comp].color_space = color_space;
350 }
351 
352 /**
353  * gstyle_color_plane_set_preferred_unit:
354  * @self: a #GstyleColorPlane
355  * @preferred_unit: a #GstyleColorUnit enum value
356  *
357  * Set percent or value  as the preferred unit for rgb adjustment range.
358  * [0, 100] for percent unit or [0, 255] for value.
359  *
360  */
361 void
gstyle_color_plane_set_preferred_unit(GstyleColorPlane * self,GstyleColorUnit preferred_unit)362 gstyle_color_plane_set_preferred_unit (GstyleColorPlane *self,
363                                        GstyleColorUnit   preferred_unit)
364 {
365   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
366   gdouble max_range = 0.0;
367 
368   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
369 
370   if (priv->preferred_unit != preferred_unit)
371     {
372       priv->preferred_unit = preferred_unit;
373       if (preferred_unit == GSTYLE_COLOR_UNIT_PERCENT)
374         max_range = 100.0;
375       else if (preferred_unit == GSTYLE_COLOR_UNIT_VALUE)
376         max_range = 255.0;
377       else
378         g_assert_not_reached ();
379 
380       configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_RED, max_range, max_range);
381       configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_GREEN, max_range, max_range);
382       configure_component (self, GSTYLE_COLOR_COMPONENT_RGB_BLUE, max_range, max_range);
383     }
384 }
385 
386 /**
387  * gstyle_color_plane_get_filter_func: (skip):
388  * @self: a #GstyleColorPlane
389  *
390  * Get a pointer to the current filter function or %NULL
391  * if no filter is actually set.
392  *
393  * Returns: (nullable): A GstyleColorFilterFunc function pointer.
394  *
395  */
396 GstyleColorFilterFunc
gstyle_color_plane_get_filter_func(GstyleColorPlane * self)397 gstyle_color_plane_get_filter_func (GstyleColorPlane *self)
398 {
399   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
400 
401   g_return_val_if_fail (GSTYLE_IS_COLOR_PLANE (self), NULL);
402 
403   return priv->filter;
404 }
405 
406 /**
407  * gstyle_color_plane_set_filter_func:
408  * @self: a #GstyleColorPlane
409  * @filter_cb: (scope notified) (nullable): A GstyleColorFilterFunc filter function or
410  *   %NULL to unset the current filter. In this case, user_data is ignored
411  * @user_data: (closure) (nullable): user data to pass when calling the filter function
412  *
413  * Set a filter to be used to change the drawing of the color plane.
414  *
415  */
416 void
gstyle_color_plane_set_filter_func(GstyleColorPlane * self,GstyleColorFilterFunc filter_cb,gpointer user_data)417 gstyle_color_plane_set_filter_func (GstyleColorPlane      *self,
418                                     GstyleColorFilterFunc  filter_cb,
419                                     gpointer               user_data)
420 {
421   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
422 
423   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
424 
425   priv->filter = filter_cb;
426   priv->filter_user_data = (filter_cb == NULL) ? NULL : user_data;
427 
428   gtk_widget_queue_draw (GTK_WIDGET (self));
429 }
430 
431 static void
compute_plane_hue_mode(GstyleColorPlane * self,ComputeData data)432 compute_plane_hue_mode (GstyleColorPlane *self,
433                         ComputeData       data)
434 {
435   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
436   gdouble hue, saturation, value;
437   GdkRGBA rgba = {0};
438   guint32 *p;
439 
440   g_assert (GSTYLE_IS_COLOR_PLANE (self));
441 
442   hue = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor;
443   for (gint y = 0; y < data.height; ++y)
444     {
445       value = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
446       p = data.buffer + y * (data.stride / 4);
447       for (gint x = 0; x < data.width; ++x)
448         {
449           saturation = x * data.x_factor;
450           gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
451           if (priv->filter != NULL)
452             priv->filter (&rgba, &rgba, priv->filter_user_data);
453 
454           p[x] = pack_rgba24 (&rgba);
455         }
456     }
457 }
458 
459 static void
compute_plane_saturation_mode(GstyleColorPlane * self,ComputeData data)460 compute_plane_saturation_mode (GstyleColorPlane *self,
461                                ComputeData       data)
462 {
463   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
464   gdouble hue, saturation, value;
465   GdkRGBA rgba = {0};
466   guint32 *p;
467 
468   g_assert (GSTYLE_IS_COLOR_PLANE (self));
469 
470   saturation = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].factor;
471   for (gint y = 0; y < data.height; ++y)
472     {
473       value = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
474       p = data.buffer + y * (data.stride / 4);
475       for (gint x = 0; x < data.width; ++x)
476         {
477           hue = x * data.x_factor;
478           gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
479           if (priv->filter != NULL)
480             priv->filter (&rgba, &rgba, priv->filter_user_data);
481 
482           p[x] = pack_rgba24 (&rgba);
483         }
484     }
485 }
486 
487 static void
compute_plane_brightness_mode(GstyleColorPlane * self,ComputeData data)488 compute_plane_brightness_mode (GstyleColorPlane *self,
489                                ComputeData       data)
490 {
491   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
492   gdouble hue, saturation, value;
493   GdkRGBA rgba = {0};
494   guint32 *p;
495 
496   g_assert (GSTYLE_IS_COLOR_PLANE (self));
497 
498   value = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].factor;
499   for (gint y = 0; y < data.height - 1; ++y)
500     {
501       saturation = CLAMP ((data.height - y) * data.y_factor, 0.0, 1.0);
502       p = data.buffer + y * (data.stride / 4);
503       for (gint x = 0; x < data.width; ++x)
504         {
505           hue = x * data.x_factor;
506           gstyle_color_convert_hsv_to_rgb (hue, saturation, value, &rgba);
507           if (priv->filter != NULL)
508             priv->filter (&rgba, &rgba, priv->filter_user_data);
509 
510           p[x] = pack_rgba24 (&rgba);
511         }
512     }
513 }
514 
515 static void
compute_plane_cielab_l_mode(GstyleColorPlane * self,ComputeData data)516 compute_plane_cielab_l_mode (GstyleColorPlane *self,
517                              ComputeData       data)
518 {
519   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
520   GstyleCielab lab;
521   GdkRGBA rgba = {0};
522   guint32 *p;
523 
524   g_assert (GSTYLE_IS_COLOR_PLANE (self));
525 
526   lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].factor;
527   for (gint y = 0; y < data.height; ++y)
528     {
529       lab.b = (data.height - y) * data.lab_y_factor - 128.0;
530       p = data.buffer + y * (data.stride / 4);
531       for (gint x = 0; x < data.width; ++x)
532         {
533           lab.a = x * data.lab_x_factor - 128.0;
534           gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
535           if (priv->filter != NULL)
536             priv->filter (&rgba, &rgba, priv->filter_user_data);
537 
538           p[x] = pack_rgba24 (&rgba);
539         }
540     }
541 }
542 
543 static void
compute_plane_cielab_a_mode(GstyleColorPlane * self,ComputeData data)544 compute_plane_cielab_a_mode (GstyleColorPlane *self,
545                              ComputeData       data)
546 {
547   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
548   GstyleCielab lab;
549   GdkRGBA rgba = {0};
550   guint32 *p;
551 
552   g_assert (GSTYLE_IS_COLOR_PLANE (self));
553 
554   lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].factor;
555   for (gint y = 0; y < data.height; ++y)
556     {
557       lab.l = (data.height - y) * data.lab_l_factor;
558       p = data.buffer + y * (data.stride / 4);
559       for (gint x = 0; x < data.width; ++x)
560         {
561           lab.b = x * data.lab_x_factor - 128.0;
562           gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
563           if (priv->filter != NULL)
564             priv->filter (&rgba, &rgba, priv->filter_user_data);
565 
566           p[x] = pack_rgba24 (&rgba);
567         }
568     }
569 }
570 
571 static void
compute_plane_cielab_b_mode(GstyleColorPlane * self,ComputeData data)572 compute_plane_cielab_b_mode (GstyleColorPlane *self,
573                              ComputeData       data)
574 {
575   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
576   GstyleCielab lab;
577   GdkRGBA rgba = {0};
578   guint32 *p;
579 
580   g_assert (GSTYLE_IS_COLOR_PLANE (self));
581 
582   lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].factor;
583   for (gint y = 0; y < data.height; ++y)
584     {
585       lab.l = (data.height - y) * data.lab_l_factor;
586       p = data.buffer + y * (data.stride / 4);
587       for (gint x = 0; x < data.width; ++x)
588         {
589           lab.a = x * data.lab_x_factor - 128.0;
590           gstyle_color_convert_cielab_to_rgb (&lab, &rgba);
591           if (priv->filter != NULL)
592             priv->filter (&rgba, &rgba, priv->filter_user_data);
593 
594           p[x] = pack_rgba24 (&rgba);
595         }
596     }
597 }
598 
599 static void
compute_plane_red_mode(GstyleColorPlane * self,ComputeData data)600 compute_plane_red_mode (GstyleColorPlane *self,
601                         ComputeData       data)
602 {
603   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
604   GdkRGBA rgba = {0};
605   guint32 *p;
606 
607   g_assert (GSTYLE_IS_COLOR_PLANE (self));
608 
609   rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
610   for (gint y = 0; y < data.height; ++y)
611     {
612       rgba.green = (data.height - y) * data.y_factor;
613       p = data.buffer + y * (data.stride / 4);
614       for (gint x = 0; x < data.width; ++x)
615         {
616           rgba.blue = x * data.x_factor;
617           if (priv->filter != NULL)
618             priv->filter (&rgba, &rgba, priv->filter_user_data);
619 
620           p[x] = pack_rgba24 (&rgba);
621         }
622     }
623 }
624 
625 static void
compute_plane_green_mode(GstyleColorPlane * self,ComputeData data)626 compute_plane_green_mode (GstyleColorPlane *self,
627                           ComputeData       data)
628 {
629   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
630   GdkRGBA rgba = {0};
631   guint32 *p;
632 
633   g_assert (GSTYLE_IS_COLOR_PLANE (self));
634 
635   rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
636   for (gint y = 0; y < data.height; ++y)
637     {
638       rgba.red = (data.height - y) * data.y_factor;
639       p = data.buffer + y * (data.stride / 4);
640       for (gint x = 0; x < data.width; ++x)
641         {
642           rgba.blue = x * data.x_factor;
643           if (priv->filter != NULL)
644             priv->filter (&rgba, &rgba, priv->filter_user_data);
645 
646           p[x] = pack_rgba24 (&rgba);
647         }
648     }
649 }
650 
651 static void
compute_plane_blue_mode(GstyleColorPlane * self,ComputeData data)652 compute_plane_blue_mode (GstyleColorPlane *self,
653                          ComputeData       data)
654 {
655   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
656   GdkRGBA rgba = {0};
657   guint32 *p;
658 
659   g_assert (GSTYLE_IS_COLOR_PLANE (self));
660 
661   rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
662   for (gint y = 0; y < data.height; ++y)
663     {
664       rgba.green = (data.height - y) * data.y_factor;
665       p = data.buffer + y * (data.stride / 4);
666       for (gint x = 0; x < data.width; ++x)
667         {
668           rgba.red = x * data.x_factor;
669           if (priv->filter != NULL)
670             priv->filter (&rgba, &rgba, priv->filter_user_data);
671 
672           p[x] = pack_rgba24 (&rgba);
673         }
674     }
675 }
676 
677 static gboolean
create_surface(GstyleColorPlane * self)678 create_surface (GstyleColorPlane *self)
679 {
680   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
681   GtkWidget *widget = (GtkWidget *)self;
682   cairo_surface_t *surface;
683   cairo_surface_t *tmp;
684   cairo_t *cr;
685   gint adjusted_height;
686   gint adjusted_width;
687 
688   g_assert (GSTYLE_IS_COLOR_PLANE (self));
689 
690   if (!gtk_widget_get_realized (widget))
691     return FALSE;
692 
693   /* TODO: keep only one of priv->data.width or priv->cached_border_box.width */
694 
695   priv->data.width = priv->cached_border_box.width;
696   priv->data.height = priv->cached_border_box.height;
697   adjusted_height = priv->data.height - 1;
698   adjusted_width = priv->data.width - 1;
699 
700   priv->data.y_factor = 1.0 / adjusted_height;
701   priv->data.x_factor = 1.0 / adjusted_width;
702   priv->data.lab_y_factor = 255.0 / adjusted_height;
703   priv->data.lab_x_factor = 255.0 / adjusted_width;
704   priv->data.lab_l_factor = 100.0 / adjusted_height;
705 
706   surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
707                                                CAIRO_CONTENT_COLOR,
708                                                priv->data.width, priv->data.height);
709 
710   if (priv->surface)
711     cairo_surface_destroy (priv->surface);
712 
713   priv->surface = surface;
714 
715   if (priv->data.width <= 1 || priv->data.height <= 1)
716     return FALSE;
717 
718   priv->data.stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, priv->data.width);
719   priv->data.buffer = g_malloc (priv->data.height * priv->data.stride);
720 
721   switch (priv->mode)
722     {
723     case GSTYLE_COLOR_PLANE_MODE_HUE:
724       compute_plane_hue_mode (self, priv->data);
725       break;
726 
727     case GSTYLE_COLOR_PLANE_MODE_SATURATION:
728       compute_plane_saturation_mode (self, priv->data);
729       break;
730 
731     case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
732       compute_plane_brightness_mode (self, priv->data);
733       break;
734 
735     case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
736       compute_plane_cielab_l_mode (self, priv->data);
737       break;
738 
739     case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
740       compute_plane_cielab_a_mode (self, priv->data);
741       break;
742 
743     case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
744       compute_plane_cielab_b_mode (self, priv->data);
745       break;
746 
747     case GSTYLE_COLOR_PLANE_MODE_RED:
748       compute_plane_red_mode (self, priv->data);
749       break;
750 
751     case GSTYLE_COLOR_PLANE_MODE_GREEN:
752       compute_plane_green_mode (self, priv->data);
753       break;
754 
755     case GSTYLE_COLOR_PLANE_MODE_BLUE:
756       compute_plane_blue_mode (self, priv->data);
757       break;
758 
759     case GSTYLE_COLOR_PLANE_MODE_NONE:
760     default:
761       g_assert_not_reached ();
762     }
763 
764   tmp = cairo_image_surface_create_for_data ((guchar *)priv->data.buffer, CAIRO_FORMAT_RGB24,
765                                              priv->data.width, priv->data.height, priv->data.stride);
766   cr = cairo_create (surface);
767   cairo_set_source_surface (cr, tmp, 0, 0);
768   cairo_paint (cr);
769 
770   cairo_destroy (cr);
771   cairo_surface_destroy (tmp);
772   g_free (priv->data.buffer);
773 
774   return TRUE;
775 }
776 
777 static gboolean
gstyle_color_plane_draw(GtkWidget * widget,cairo_t * cr)778 gstyle_color_plane_draw (GtkWidget *widget,
779                          cairo_t   *cr)
780 {
781   GstyleColorPlane *self = (GstyleColorPlane *)widget;
782   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
783   gint left_spacing;
784   gint top_spacing;
785   gint x, y;
786 
787   g_assert (GSTYLE_IS_COLOR_PLANE (self));
788   g_assert (cr != NULL);
789 
790   if (!gtk_widget_get_visible (widget))
791     return GDK_EVENT_PROPAGATE;
792 
793   if (update_css_boxes (self) || priv->surface == NULL)
794     create_surface (self);
795 
796   left_spacing = priv->cached_margin.left + priv->cached_border.left;
797   top_spacing = priv->cached_margin.top + priv->cached_border.top;
798   x = round (priv->cursor_x) + left_spacing;
799   y = round (priv->cursor_y) + top_spacing;
800 
801   cairo_set_source_surface (cr, priv->surface, priv->cached_border_box.x, priv->cached_border_box.y);
802   cairo_paint (cr);
803 
804   gtk_render_frame (gtk_widget_get_style_context (widget), cr,
805                     priv->cached_margin_box.x, priv->cached_margin_box.y,
806                     priv->cached_margin_box.width, priv->cached_margin_box.height);
807 
808   cairo_move_to (cr, left_spacing, y + 0.5);
809   cairo_line_to (cr, left_spacing + priv->cached_border_box.width, y + 0.5);
810 
811   cairo_move_to (cr, x + 0.5, top_spacing);
812   cairo_line_to (cr, x + 0.5, top_spacing + priv->cached_border_box.height);
813 
814   if (gtk_widget_has_visible_focus (widget))
815     {
816       cairo_set_line_width (cr, 3.0);
817       cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
818       cairo_stroke_preserve (cr);
819 
820       cairo_set_line_width (cr, 1.0);
821       cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
822       cairo_stroke (cr);
823     }
824   else
825     {
826       cairo_set_line_width (cr, 1.0);
827       cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 0.8);
828       cairo_stroke (cr);
829     }
830 
831   return FALSE;
832 }
833 
834 static inline gboolean
compare_xyz(GstyleXYZ xyz1,GstyleXYZ xyz2)835 compare_xyz (GstyleXYZ xyz1,
836              GstyleXYZ xyz2)
837 {
838   return (xyz1.x == xyz2.x &&
839           xyz1.y == xyz2.y &&
840           xyz1.z == xyz2.z &&
841           xyz1.alpha == xyz2.alpha);
842 }
843 
844 /* Adjustments are updated from the stored xyz */
845 static void
update_adjustments(GstyleColorPlane * self,GstyleXYZ * xyz,GstyleColorComponent changed_comp)846 update_adjustments (GstyleColorPlane     *self,
847                     GstyleXYZ            *xyz,
848                     GstyleColorComponent  changed_comp)
849 {
850   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
851   gdouble hue, saturation, value;
852   gdouble current_hue;
853   GstyleCielab lab;
854   ColorSpaceId color_space;
855   GdkRGBA rgba = {0};
856 
857   g_assert (GSTYLE_IS_COLOR_PLANE (self));
858   g_assert (xyz != NULL);
859 
860   if (!compare_xyz (priv->xyz, *xyz))
861     {
862       color_space = (changed_comp == GSTYLE_COLOR_COMPONENT_NONE) ? COLOR_SPACE_NONE : priv->comp [changed_comp].color_space;
863       if (color_space != COLOR_SPACE_RGB)
864         {
865           gstyle_color_convert_xyz_to_rgb (xyz, &rgba);
866           priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val = rgba.red * priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
867           priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val = rgba.green * priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
868           priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val = rgba.blue * priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
869         }
870 
871       if (color_space != COLOR_SPACE_CIELAB)
872         {
873           gstyle_color_convert_xyz_to_cielab (xyz, &lab);
874           priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val = lab.l * priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].factor;
875           priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val = lab.a * priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].factor;
876           priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val = lab.b * priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].factor;
877         }
878 
879       if (color_space != COLOR_SPACE_HSV)
880         {
881           current_hue = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val;
882           gstyle_color_convert_xyz_to_hsv (xyz, &hue, &saturation, &value);
883           if (saturation > 1e-6)
884             {
885               if (priv->hue_backup_set)
886                 {
887                   priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val = priv->hue_backup;
888                   priv->hue_backup_set = FALSE;
889                 }
890               else
891                 priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val = hue * priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor;
892             }
893           else if (!priv->hue_backup_set)
894             {
895               priv->hue_backup = current_hue;
896               priv->hue_backup_set = TRUE;
897               priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val = hue;
898             }
899 
900           priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val = saturation * priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].factor;
901           priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val = value * priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].factor;
902         }
903 
904       for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
905         if (priv->comp [i].color_space != color_space)
906           {
907             g_signal_handler_block (priv->comp [i].adj, priv->comp [i].handler);
908             gtk_adjustment_set_value (priv->comp [i].adj, priv->comp [i].val);
909             g_signal_handler_unblock (priv->comp [i].adj, priv->comp [i].handler);
910           }
911     }
912 }
913 
914 static void
update_surface_and_cursor(GstyleColorPlane * self,gboolean update_surface)915 update_surface_and_cursor (GstyleColorPlane *self,
916                            gboolean          update_surface)
917 {
918   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
919 
920   g_assert (GSTYLE_IS_COLOR_PLANE (self));
921 
922   if (update_surface)
923     create_surface (self);
924 
925   set_cursor_from_xyz (self, &priv->xyz);
926 
927   if (gtk_widget_get_realized (GTK_WIDGET (self)))
928     gtk_widget_queue_draw (GTK_WIDGET (self));
929 }
930 
931 static void
update_cursor(GstyleColorPlane * self,gdouble x,gdouble y)932 update_cursor (GstyleColorPlane *self,
933                gdouble           x,
934                gdouble           y)
935 {
936   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
937   gint left_spacing;
938   gint top_spacing;
939   GstyleXYZ xyz = {0};
940 
941   g_assert (GSTYLE_IS_COLOR_PLANE (self));
942 
943   left_spacing = priv->cached_margin.left + priv->cached_border.left;
944   top_spacing = priv->cached_margin.top + priv->cached_border.top;
945   x = CLAMP (x - left_spacing, 0.0, priv->data.width - 1.0);
946   y = CLAMP (y - top_spacing, 0.0, priv->data.height - 1.0);
947 
948   if (priv->cursor_x != x || priv->cursor_y != y)
949     {
950       priv->cursor_x = x;
951       priv->cursor_y = y;
952 
953       get_xyz_from_cursor (self, &xyz);
954       update_adjustments (self, &xyz, GSTYLE_COLOR_COMPONENT_NONE);
955       priv->xyz = xyz;
956 
957       gtk_widget_queue_draw (GTK_WIDGET (self));
958       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
959       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XYZ]);
960     }
961 }
962 
963 static void
move_cursor(GstyleColorPlane * self,gdouble step_x,gdouble step_y)964 move_cursor (GstyleColorPlane *self,
965              gdouble           step_x,
966              gdouble           step_y)
967 {
968   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
969 
970   g_assert (GSTYLE_IS_COLOR_PLANE (self));
971 
972   if (!gtk_widget_get_realized (GTK_WIDGET (self)))
973     return;
974 
975   update_cursor (self, priv->cursor_x + step_x, priv->cursor_y - step_y);
976   /* TODO: ring when reaching the border */
977 }
978 
979 static GstyleColorComponent
get_adj_id(GstyleColorPlane * self,GtkAdjustment * adj)980 get_adj_id (GstyleColorPlane *self,
981             GtkAdjustment    *adj)
982 {
983   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
984 
985   g_assert (GSTYLE_IS_COLOR_PLANE (self));
986   g_assert (GTK_IS_ADJUSTMENT (adj));
987 
988   for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
989     if (adj == priv->comp [i].adj)
990       return i;
991 
992   g_return_val_if_reached (0);
993 }
994 
995 static void
adjustments_changed(GstyleColorPlane * self,GtkAdjustment * adj)996 adjustments_changed (GstyleColorPlane *self,
997                      GtkAdjustment    *adj)
998 {
999   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1000   gdouble hue, saturation, value;
1001   GdkRGBA rgba;
1002   GstyleXYZ xyz;
1003   GstyleCielab lab;
1004   GstyleColorComponent changed_comp;
1005   gdouble old_ref_val;
1006 
1007   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1008   g_assert (GTK_IS_ADJUSTMENT (adj));
1009 
1010   old_ref_val = priv->comp [priv->ref_comp].val;
1011   changed_comp = get_adj_id (self, adj);
1012   priv->comp [changed_comp].val = gtk_adjustment_get_value (priv->comp [changed_comp].adj);
1013 
1014   if (changed_comp == GSTYLE_COLOR_COMPONENT_HSV_H ||
1015       changed_comp == GSTYLE_COLOR_COMPONENT_HSV_S ||
1016       changed_comp == GSTYLE_COLOR_COMPONENT_HSV_V)
1017     {
1018       hue = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_H].factor;
1019       saturation = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_S].factor;
1020       value = priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].val / priv->comp [GSTYLE_COLOR_COMPONENT_HSV_V].factor;
1021 
1022       gstyle_color_convert_hsv_to_xyz (hue, saturation, value, &xyz);
1023     }
1024   else if (changed_comp == GSTYLE_COLOR_COMPONENT_LAB_L ||
1025            changed_comp == GSTYLE_COLOR_COMPONENT_LAB_A ||
1026            changed_comp == GSTYLE_COLOR_COMPONENT_LAB_B)
1027     {
1028       lab.l = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_L].factor;
1029       lab.a = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_A].factor;
1030       lab.b = priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].val / priv->comp [GSTYLE_COLOR_COMPONENT_LAB_B].factor;
1031 
1032       gstyle_color_convert_cielab_to_xyz (&lab, &xyz);
1033     }
1034   else
1035     {
1036       rgba.red = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_RED].factor;
1037       rgba.green = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_GREEN].factor;
1038       rgba.blue = priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].val / priv->comp [GSTYLE_COLOR_COMPONENT_RGB_BLUE].factor;
1039       gstyle_color_convert_rgb_to_xyz (&rgba, &xyz);
1040     }
1041 
1042   xyz.alpha = 1;
1043   update_adjustments (self, &xyz, changed_comp);
1044   priv->xyz = xyz;
1045   update_surface_and_cursor (self, old_ref_val != priv->comp [priv->ref_comp].val);
1046 
1047   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
1048   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_XYZ]);
1049 }
1050 
1051 /**
1052  * gstyle_color_plane_new:
1053  *
1054  * Returns: a new #GstyleColorPlane
1055  */
1056 GstyleColorPlane *
gstyle_color_plane_new(void)1057 gstyle_color_plane_new (void)
1058 {
1059   return g_object_new (GSTYLE_TYPE_COLOR_PLANE, NULL);
1060 }
1061 
1062 static void
gstyle_color_plane_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1063 gstyle_color_plane_size_allocate (GtkWidget     *widget,
1064                                   GtkAllocation *allocation)
1065 {
1066   GstyleColorPlane *self = GSTYLE_COLOR_PLANE (widget);
1067   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1068 
1069   GTK_WIDGET_CLASS (gstyle_color_plane_parent_class)->size_allocate (widget, allocation);
1070 
1071   update_css_boxes (self);
1072 
1073   if (create_surface (GSTYLE_COLOR_PLANE (widget)))
1074     set_cursor_from_xyz (self, &priv->xyz);
1075 }
1076 
1077 static gboolean
gstyle_color_plane_key_press(GtkWidget * widget,GdkEventKey * event)1078 gstyle_color_plane_key_press (GtkWidget   *widget,
1079                               GdkEventKey *event)
1080 {
1081   GstyleColorPlane *self = GSTYLE_COLOR_PLANE (widget);
1082   gdouble step;
1083 
1084   g_assert (event != NULL);
1085 
1086   if ((event->state & GDK_MOD1_MASK) != 0)
1087     step = 0.1;
1088   else
1089     step = 0.01;
1090 
1091   if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
1092     move_cursor (self, 0, step);
1093   else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
1094     move_cursor (self, 0, -step);
1095   else if (event->keyval == GDK_KEY_Left || event->keyval == GDK_KEY_KP_Left)
1096     move_cursor (self, -step, 0);
1097   else if (event->keyval == GDK_KEY_Right ||  event->keyval == GDK_KEY_KP_Right)
1098     move_cursor (self, step, 0);
1099   else
1100     return GTK_WIDGET_CLASS (gstyle_color_plane_parent_class)->key_press_event (widget, event);
1101 
1102   return TRUE;
1103 }
1104 
1105 /**
1106  * gstyle_color_plane_get_xyz:
1107  * @self: a #GstyleColorPlane
1108  * @xyz: (out): a #GstyleXYZ adress
1109  *
1110  * Fill @xyz with value at cursor position.
1111  * The alpha component is always equal to 1.
1112  *
1113  */
1114 void
gstyle_color_plane_get_xyz(GstyleColorPlane * self,GstyleXYZ * xyz)1115 gstyle_color_plane_get_xyz (GstyleColorPlane *self,
1116                             GstyleXYZ        *xyz)
1117 {
1118   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1119 
1120   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1121   g_return_if_fail (xyz != NULL);
1122 
1123   *xyz = priv->xyz;
1124 }
1125 
1126 /**
1127  * gstyle_color_plane_get_rgba:
1128  * @self: a #GstyleColorPlane
1129  * @rgba: (out): a #GdkRGBA adress
1130  *
1131  * Fill @rgba with value at cursor position.
1132  * The alpha component is always equal to 1.
1133  *
1134  */
1135 void
gstyle_color_plane_get_rgba(GstyleColorPlane * self,GdkRGBA * rgba)1136 gstyle_color_plane_get_rgba (GstyleColorPlane *self,
1137                              GdkRGBA          *rgba)
1138 {
1139   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1140 
1141   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1142   g_return_if_fail (rgba != NULL);
1143 
1144   gstyle_color_convert_xyz_to_rgb (&priv->xyz, rgba);
1145 }
1146 
1147 /**
1148  * gstyle_color_plane_get_filtered_rgba:
1149  * @self: a #GstyleColorPlane
1150  * @rgba: (out): a #GdkRGBA adress
1151  *
1152  * Fill @rgba with filtered value at cursor position.
1153  *
1154  */
1155 void
gstyle_color_plane_get_filtered_rgba(GstyleColorPlane * self,GdkRGBA * rgba)1156 gstyle_color_plane_get_filtered_rgba (GstyleColorPlane *self,
1157                                       GdkRGBA          *rgba)
1158 {
1159   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1160 
1161   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1162   g_return_if_fail (rgba != NULL);
1163 
1164   gstyle_color_convert_xyz_to_rgb (&priv->xyz, rgba);
1165   priv->filter (rgba, rgba, priv->filter_user_data);
1166 }
1167 
1168 /**
1169  * gstyle_color_plane_get_component_adjustment:
1170  * @self: a #GstyleColorPlane
1171  * @comp: a #GstyleColorComponent enum value
1172  *
1173  * Return the color component adjustment designated by
1174  * the #GstyleColorComponent value.
1175  *
1176  * Returns: (transfer none): #GtkAdjustment.
1177  *
1178  */
1179 GtkAdjustment *
gstyle_color_plane_get_component_adjustment(GstyleColorPlane * self,GstyleColorComponent comp)1180 gstyle_color_plane_get_component_adjustment (GstyleColorPlane     *self,
1181                                              GstyleColorComponent  comp)
1182 {
1183   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1184 
1185   g_return_val_if_fail (GSTYLE_IS_COLOR_PLANE (self), NULL);
1186   g_return_val_if_fail (comp !=  GSTYLE_COLOR_COMPONENT_NONE, NULL);
1187 
1188   return priv->comp [comp].adj;
1189 }
1190 
1191 /**
1192  * gstyle_color_plane_set_rgba:
1193  * @self: a #GstyleColorPlane
1194  * @rgba: a #GdkRGBA
1195  *
1196  * Set cursor position from @rgba value.
1197  *
1198  */
1199 void
gstyle_color_plane_set_rgba(GstyleColorPlane * self,const GdkRGBA * rgba)1200 gstyle_color_plane_set_rgba (GstyleColorPlane *self,
1201                              const GdkRGBA    *rgba)
1202 {
1203   GstyleColorPlanePrivate *priv;
1204   GstyleXYZ xyz = {0};
1205 
1206   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1207   g_return_if_fail (rgba != NULL);
1208 
1209   priv = gstyle_color_plane_get_instance_private (self);
1210 
1211   gstyle_color_convert_rgb_to_xyz ((GdkRGBA *)rgba, &xyz);
1212   if (compare_xyz (xyz, priv->xyz))
1213     return;
1214 
1215   update_adjustments (self, &xyz, GSTYLE_COLOR_COMPONENT_NONE);
1216   priv->xyz = xyz;
1217   update_surface_and_cursor (self, TRUE);
1218 
1219   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
1220   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
1221 }
1222 
1223 /**
1224  * gstyle_color_plane_set_xyz:
1225  * @self: a #GstyleColorPlane.
1226  * @xyz: a #GstyleXYZ struct.
1227  *
1228  * Set cursor position from @rgba value.
1229  *
1230  */
1231 void
gstyle_color_plane_set_xyz(GstyleColorPlane * self,const GstyleXYZ * xyz)1232 gstyle_color_plane_set_xyz (GstyleColorPlane *self,
1233                             const GstyleXYZ  *xyz)
1234 {
1235   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1236 
1237   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1238   g_return_if_fail (xyz != NULL);
1239 
1240   if (compare_xyz (*xyz, priv->xyz))
1241     return;
1242 
1243   update_adjustments (self, (GstyleXYZ *)xyz, GSTYLE_COLOR_COMPONENT_NONE);
1244   priv->xyz = *xyz;
1245   update_surface_and_cursor (self, TRUE);
1246 
1247   /* TODO: add xyz props */
1248   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RGBA]);
1249   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_XYZ]);
1250 }
1251 
1252 /**
1253  * gstyle_color_plane_set_mode:
1254  * @self: a #GstyleColorPlane.
1255  * @mode: a #GstylewColorPlaneMode.
1256  *
1257  * Set the displayed mode to use.
1258  *
1259  */
1260 void
gstyle_color_plane_set_mode(GstyleColorPlane * self,GstyleColorPlaneMode mode)1261 gstyle_color_plane_set_mode (GstyleColorPlane     *self,
1262                              GstyleColorPlaneMode  mode)
1263 {
1264   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1265   gdouble hsv_h, hsv_s, hsv_v;
1266   gdouble ref_val = 0.0;
1267   GstyleCielab lab;
1268   GdkRGBA rgba = {0};
1269 
1270   g_return_if_fail (GSTYLE_IS_COLOR_PLANE (self));
1271 
1272   if (priv->mode != mode)
1273     {
1274       priv->mode = mode;
1275 
1276       switch (priv->mode)
1277       {
1278       case GSTYLE_COLOR_PLANE_MODE_HUE:
1279         gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
1280         priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_H;
1281         ref_val = hsv_h;
1282         break;
1283 
1284       case GSTYLE_COLOR_PLANE_MODE_SATURATION:
1285         gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
1286         priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_S;
1287         ref_val = hsv_s;
1288         break;
1289 
1290       case GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS:
1291         gstyle_color_convert_xyz_to_hsv (&priv->xyz, &hsv_h, &hsv_s, &hsv_v);
1292         priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_V;
1293         ref_val = hsv_v;
1294         break;
1295 
1296       case GSTYLE_COLOR_PLANE_MODE_CIELAB_L:
1297         gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
1298         priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_L;
1299         ref_val = lab.l;
1300         break;
1301 
1302       case GSTYLE_COLOR_PLANE_MODE_CIELAB_A:
1303         gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
1304         priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_A;
1305         ref_val = lab.a;
1306         break;
1307 
1308       case GSTYLE_COLOR_PLANE_MODE_CIELAB_B:
1309         gstyle_color_convert_xyz_to_cielab (&priv->xyz, &lab);
1310         priv->ref_comp = GSTYLE_COLOR_COMPONENT_LAB_B;
1311         ref_val = lab.b;
1312         break;
1313 
1314       case GSTYLE_COLOR_PLANE_MODE_RED:
1315         gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
1316         priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_RED;
1317         ref_val = rgba.red;
1318         break;
1319 
1320       case GSTYLE_COLOR_PLANE_MODE_GREEN:
1321         gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
1322         priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_GREEN;
1323         ref_val = rgba.green;
1324         break;
1325 
1326       case GSTYLE_COLOR_PLANE_MODE_BLUE:
1327         gstyle_color_convert_xyz_to_rgb (&priv->xyz, &rgba);
1328         priv->ref_comp = GSTYLE_COLOR_COMPONENT_RGB_BLUE;
1329         ref_val = rgba.blue;
1330         break;
1331 
1332       case GSTYLE_COLOR_PLANE_MODE_NONE:
1333       default:
1334         g_assert_not_reached ();
1335       }
1336 
1337       g_signal_handler_block (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].handler);
1338 
1339       priv->comp [priv->ref_comp].val = ref_val * priv->comp [priv->ref_comp].factor;
1340       gtk_adjustment_set_value (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].val);
1341 
1342       g_signal_handler_unblock (priv->comp [priv->ref_comp].adj, priv->comp [priv->ref_comp].handler);
1343 
1344       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE]);
1345       update_surface_and_cursor (self, TRUE);
1346     }
1347 }
1348 
1349 static void
gstyle_color_plane_destroy(GtkWidget * widget)1350 gstyle_color_plane_destroy (GtkWidget *widget)
1351 {
1352   GstyleColorPlane *self = (GstyleColorPlane *)widget;
1353   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1354 
1355   gstyle_clear_pointer (&priv->surface, cairo_surface_destroy);
1356 
1357   GTK_WIDGET_CLASS (gstyle_color_plane_parent_class)->destroy (widget);
1358 }
1359 
1360 static void
gstyle_color_plane_finalize(GObject * object)1361 gstyle_color_plane_finalize (GObject *object)
1362 {
1363   GstyleColorPlane *self = (GstyleColorPlane *)object;
1364   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1365 
1366   g_clear_object (&priv->drag_gesture);
1367   g_clear_object (&priv->long_press_gesture);
1368   g_clear_object (&priv->default_provider);
1369 
1370   for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
1371     g_clear_object (&priv->comp [i].adj);
1372 
1373   G_OBJECT_CLASS (gstyle_color_plane_parent_class)->finalize (object);
1374 }
1375 
1376 static void
gstyle_color_plane_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1377 gstyle_color_plane_get_property (GObject    *object,
1378                                  guint       prop_id,
1379                                  GValue     *value,
1380                                  GParamSpec *pspec)
1381 {
1382   GstyleColorPlane *self = GSTYLE_COLOR_PLANE (object);
1383   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1384   GdkRGBA rgba = {0};
1385   GstyleXYZ xyz;
1386 
1387   switch (prop_id)
1388     {
1389     case PROP_MODE:
1390       g_value_set_enum (value, priv->mode);
1391       break;
1392 
1393     case PROP_RGBA:
1394       gstyle_color_plane_get_rgba (self, &rgba);
1395       g_value_set_boxed (value, &rgba);
1396       break;
1397 
1398     case PROP_XYZ:
1399       gstyle_color_plane_get_xyz (self, &xyz);
1400       g_value_set_boxed (value, &xyz);
1401       break;
1402 
1403     default:
1404       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1405     }
1406 }
1407 
1408 static void
gstyle_color_plane_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1409 gstyle_color_plane_set_property (GObject      *object,
1410                                  guint         prop_id,
1411                                  const GValue *value,
1412                                  GParamSpec   *pspec)
1413 {
1414   GstyleColorPlane *self = GSTYLE_COLOR_PLANE (object);
1415   GdkRGBA *rgba_p;
1416   GstyleXYZ *xyz_p;
1417   GstyleXYZ xyz = {0};
1418   GdkRGBA rgba = {0.5, 0.3, 0.3, 0.0};
1419 
1420   switch (prop_id)
1421     {
1422     case PROP_MODE:
1423       gstyle_color_plane_set_mode (self, g_value_get_enum (value));
1424       break;
1425 
1426     case PROP_RGBA:
1427       rgba_p = (GdkRGBA *)g_value_get_boxed (value);
1428       if (rgba_p == NULL)
1429         rgba_p = &rgba;
1430 
1431       gstyle_color_plane_set_rgba (self, rgba_p);
1432       break;
1433 
1434     case PROP_XYZ:
1435       xyz_p = (GstyleXYZ *)g_value_get_boxed (value);
1436       if (xyz_p == NULL)
1437         {
1438           gstyle_color_convert_rgb_to_xyz (&rgba, &xyz);
1439           gstyle_color_plane_set_xyz (self, &xyz);
1440         }
1441       else
1442         gstyle_color_plane_set_xyz (self, xyz_p);
1443       break;
1444 
1445     default:
1446       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1447     }
1448 }
1449 
1450 static void
gstyle_color_plane_class_init(GstyleColorPlaneClass * klass)1451 gstyle_color_plane_class_init (GstyleColorPlaneClass *klass)
1452 {
1453   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1454   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1455 
1456   object_class->finalize = gstyle_color_plane_finalize;
1457   object_class->get_property = gstyle_color_plane_get_property;
1458   object_class->set_property = gstyle_color_plane_set_property;
1459 
1460   widget_class->draw = gstyle_color_plane_draw;
1461   widget_class->size_allocate = gstyle_color_plane_size_allocate;
1462   widget_class->key_press_event = gstyle_color_plane_key_press;
1463   widget_class->destroy = gstyle_color_plane_destroy;
1464 
1465   properties [PROP_MODE] =
1466     g_param_spec_enum ("mode",
1467                        "Mode",
1468                        "The mode displayed",
1469                        GSTYLE_TYPE_COLOR_PLANE_MODE,
1470                        GSTYLE_COLOR_PLANE_MODE_HUE,
1471                        (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1472 
1473   properties [PROP_RGBA] =
1474     g_param_spec_boxed ("rgba",
1475                         "rgba",
1476                         "Color pointed by the cursor",
1477                         GDK_TYPE_RGBA,
1478                         (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1479 
1480   properties [PROP_XYZ] =
1481     g_param_spec_boxed ("xyz",
1482                         "xyz",
1483                         "Color pointed by the cursor",
1484                         GSTYLE_TYPE_XYZ,
1485                         (G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1486 
1487   /* TODO: add *_adjustment properties */
1488 
1489   g_object_class_install_properties (object_class, N_PROPS, properties);
1490 
1491   gtk_widget_class_set_css_name (widget_class, "gstylecolorplane");
1492 }
1493 
1494 static void
set_cross_cursor(GtkWidget * widget,gboolean enabled)1495 set_cross_cursor (GtkWidget *widget,
1496                   gboolean   enabled)
1497 {
1498   GstyleColorPlane *self = (GstyleColorPlane *)widget;
1499   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1500   GdkCursor *cursor = NULL;
1501   GdkWindow *window;
1502   GdkDevice *device;
1503 
1504   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1505 
1506   window = gtk_widget_get_window (widget);
1507   device = gtk_gesture_get_device (priv->drag_gesture);
1508 
1509   if (!window || !device)
1510     return;
1511 
1512   if (enabled)
1513     cursor = gdk_cursor_new_from_name (gtk_widget_get_display (GTK_WIDGET (widget)), "crosshair");
1514 
1515   gdk_window_set_device_cursor (window, device, cursor);
1516 
1517   if (cursor)
1518     g_object_unref (cursor);
1519 }
1520 
1521 static void
hold_action(GtkGestureLongPress * gesture,gdouble x,gdouble y,GstyleColorPlane * self)1522 hold_action (GtkGestureLongPress *gesture,
1523              gdouble              x,
1524              gdouble              y,
1525              GstyleColorPlane    *self)
1526 {
1527   gboolean handled;
1528 
1529   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1530 
1531   g_signal_emit_by_name (self, "popup-menu", &handled);
1532 }
1533 
1534 static void
drag_gesture_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,GstyleColorPlane * self)1535 drag_gesture_begin (GtkGestureDrag   *gesture,
1536                     gdouble           start_x,
1537                     gdouble           start_y,
1538                     GstyleColorPlane *self)
1539 {
1540   /* GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self); */
1541   guint button;
1542 
1543   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1544 
1545   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
1546   if (button == GDK_BUTTON_SECONDARY)
1547     {
1548       gboolean handled;
1549       g_signal_emit_by_name (self, "popup-menu", &handled);
1550     }
1551 
1552   if (button != GDK_BUTTON_PRIMARY)
1553     {
1554       gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
1555       return;
1556     }
1557 
1558   set_cross_cursor (GTK_WIDGET (self), TRUE);
1559   update_cursor (self, start_x, start_y);
1560 
1561   gtk_widget_grab_focus (GTK_WIDGET (self));
1562   gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
1563 }
1564 
1565 static void
drag_gesture_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GstyleColorPlane * self)1566 drag_gesture_update (GtkGestureDrag   *gesture,
1567                      gdouble           offset_x,
1568                      gdouble           offset_y,
1569                      GstyleColorPlane *self)
1570 {
1571   /* GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self); */
1572   gdouble start_x, start_y;
1573 
1574   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1575 
1576   gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (gesture),
1577                                     &start_x, &start_y);
1578 
1579   update_cursor (self, start_x + offset_x, start_y + offset_y);
1580 }
1581 
1582 static void
drag_gesture_end(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,GstyleColorPlane * self)1583 drag_gesture_end (GtkGestureDrag   *gesture,
1584                   gdouble           offset_x,
1585                   gdouble           offset_y,
1586                   GstyleColorPlane *self)
1587 {
1588   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1589 
1590   set_cross_cursor (GTK_WIDGET (self), FALSE);
1591 }
1592 
1593 static void
gstyle_color_plane_init(GstyleColorPlane * self)1594 gstyle_color_plane_init (GstyleColorPlane *self)
1595 {
1596   GstyleColorPlanePrivate *priv = gstyle_color_plane_get_instance_private (self);
1597   GtkStyleContext *context;
1598   AtkObject *atk_obj;
1599 
1600   g_assert (GSTYLE_IS_COLOR_PLANE (self));
1601 
1602   gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
1603   gtk_widget_set_events (GTK_WIDGET (self), GDK_KEY_PRESS_MASK |
1604                                             GDK_TOUCH_MASK |
1605                                             GDK_BUTTON_PRESS_MASK |
1606                                             GDK_BUTTON_RELEASE_MASK |
1607                                             GDK_POINTER_MOTION_MASK);
1608 
1609   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (self));
1610   if (GTK_IS_ACCESSIBLE (atk_obj))
1611     {
1612       atk_object_set_name (atk_obj, _("Color Plane"));
1613       atk_object_set_role (atk_obj, ATK_ROLE_COLOR_CHOOSER);
1614     }
1615 
1616   setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_H, 0.0, 0.0, 360.0, 1.0, 1.0, 360.0, COLOR_SPACE_HSV);
1617   setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_S, 0.0, 0.0, 100.0, 1.0, 1.0, 100.0, COLOR_SPACE_HSV);
1618   setup_component (self, GSTYLE_COLOR_COMPONENT_HSV_V, 0.0, 0.0, 100.0, 1.0, 1.0, 100.0, COLOR_SPACE_HSV);
1619 
1620   setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_L, 0.0, 0.0, 100.0, 1.0, 1.0, 1.0, COLOR_SPACE_CIELAB);
1621   setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_A, 0.0, -128.0, 128.0, 1.0, 1.0, 1.0, COLOR_SPACE_CIELAB);
1622   setup_component (self, GSTYLE_COLOR_COMPONENT_LAB_B, 0.0, -128.0, 128.0, 1.0, 1.0, 1.0, COLOR_SPACE_CIELAB);
1623 
1624   setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_RED, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0, COLOR_SPACE_RGB);
1625   setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_GREEN, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0, COLOR_SPACE_RGB);
1626   setup_component (self, GSTYLE_COLOR_COMPONENT_RGB_BLUE, 0.0, 0.0, 255.0, 1.0, 1.0, 255.0, COLOR_SPACE_RGB);
1627 
1628   priv->preferred_unit = GSTYLE_COLOR_UNIT_VALUE;
1629 
1630   for (gint i = 0; i < N_GSTYLE_COLOR_COMPONENT; ++i)
1631     priv->comp [i].handler = g_signal_connect_swapped (priv->comp [i].adj,
1632                                                        "value-changed",
1633                                                        G_CALLBACK (adjustments_changed), self);
1634 
1635   priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (self));
1636   g_signal_connect (priv->drag_gesture, "drag-begin", G_CALLBACK (drag_gesture_begin), self);
1637   g_signal_connect (priv->drag_gesture, "drag-update", G_CALLBACK (drag_gesture_update), self);
1638   g_signal_connect (priv->drag_gesture, "drag-end", G_CALLBACK (drag_gesture_end), self);
1639 
1640   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), 0);
1641 
1642   priv->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (self));
1643   g_signal_connect (priv->long_press_gesture, "pressed", G_CALLBACK (hold_action), self);
1644 
1645   gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
1646 
1647   priv->mode = GSTYLE_COLOR_PLANE_MODE_HUE;
1648   priv->ref_comp = GSTYLE_COLOR_COMPONENT_HSV_H;
1649   priv->xyz.alpha = 1;
1650 
1651   context = gtk_widget_get_style_context (GTK_WIDGET (self));
1652   priv->default_provider = gstyle_css_provider_init_default (gtk_style_context_get_screen (context));
1653 }
1654 
1655 GType
gstyle_color_plane_mode_get_type(void)1656 gstyle_color_plane_mode_get_type (void)
1657 {
1658   static GType type_id;
1659   static const GEnumValue values[] = {
1660     { GSTYLE_COLOR_PLANE_MODE_HUE, "GSTYLE_COLOR_PLANE_MODE_HUE", "hue" },
1661     { GSTYLE_COLOR_PLANE_MODE_SATURATION, "GSTYLE_COLOR_PLANE_MODE_SATURATION", "saturation" },
1662     { GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS, "GSTYLE_COLOR_PLANE_MODE_BRIGHTNESS", "brightness" },
1663     { GSTYLE_COLOR_PLANE_MODE_CIELAB_L, "GSTYLE_COLOR_PLANE_MODE_CIELAB_L", "cielab-l" },
1664     { GSTYLE_COLOR_PLANE_MODE_CIELAB_A, "GSTYLE_COLOR_PLANE_MODE_CIELAB_A", "cielab-a" },
1665     { GSTYLE_COLOR_PLANE_MODE_CIELAB_B, "GSTYLE_COLOR_PLANE_MODE_CIELAB_B", "cielab-b" },
1666     { GSTYLE_COLOR_PLANE_MODE_RED, "GSTYLE_COLOR_PLANE_MODE_RED", "red" },
1667     { GSTYLE_COLOR_PLANE_MODE_GREEN, "GSTYLE_COLOR_PLANE_MODE_GREEN", "green" },
1668     { GSTYLE_COLOR_PLANE_MODE_BLUE, "GSTYLE_COLOR_PLANE_MODE_BLUE", "blue" },
1669     { 0 }
1670   };
1671 
1672   if (g_once_init_enter (&type_id))
1673     {
1674       GType _type_id;
1675 
1676       _type_id = g_enum_register_static ("GstyleColorPlaneMode", values);
1677       g_once_init_leave (&type_id, _type_id);
1678     }
1679 
1680   return type_id;
1681 }
1682