1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gtkcolorscaleprivate.h"
21 
22 #include "gtkcolorchooserprivate.h"
23 #include "gtkgesturelongpress.h"
24 #include "gtkcolorutils.h"
25 #include "gtkorientable.h"
26 #include "gtkrangeprivate.h"
27 #include "gtkstylecontext.h"
28 #include "gtkaccessible.h"
29 #include "gtkprivate.h"
30 #include "gtkintl.h"
31 #include "gtkrenderprivate.h"
32 
33 #include <math.h>
34 
35 struct _GtkColorScalePrivate
36 {
37   GdkRGBA color;
38   GtkColorScaleType type;
39 
40   GtkGesture *long_press_gesture;
41 };
42 
43 enum
44 {
45   PROP_ZERO,
46   PROP_SCALE_TYPE
47 };
48 
49 static void hold_action (GtkGestureLongPress *gesture,
50                          gdouble              x,
51                          gdouble              y,
52                          GtkColorScale       *scale);
53 
G_DEFINE_TYPE_WITH_PRIVATE(GtkColorScale,gtk_color_scale,GTK_TYPE_SCALE)54 G_DEFINE_TYPE_WITH_PRIVATE (GtkColorScale, gtk_color_scale, GTK_TYPE_SCALE)
55 
56 void
57 gtk_color_scale_draw_trough (GtkColorScale  *scale,
58                              cairo_t        *cr,
59                              int             x,
60                              int             y,
61                              int             width,
62                              int             height)
63 {
64   GtkWidget *widget;
65 
66   if (width <= 1 || height <= 1)
67     return;
68 
69   cairo_save (cr);
70   cairo_translate (cr, x, y);
71 
72   widget = GTK_WIDGET (scale);
73   cairo_rectangle (cr, 0, 0, width, height);
74   cairo_clip (cr);
75 
76   if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL &&
77       gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
78     {
79       cairo_translate (cr, width, 0);
80       cairo_scale (cr, -1, 1);
81     }
82 
83   if (scale->priv->type == GTK_COLOR_SCALE_HUE)
84     {
85       gint stride;
86       cairo_surface_t *tmp;
87       guint red, green, blue;
88       guint32 *data, *p;
89       gdouble h;
90       gdouble r, g, b;
91       gdouble f;
92       gint x, y;
93 
94       stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
95 
96       data = g_malloc (height * stride);
97 
98       f = 1.0 / (height - 1);
99       for (y = 0; y < height; y++)
100         {
101           h = CLAMP (y * f, 0.0, 1.0);
102           p = data + y * (stride / 4);
103           for (x = 0; x < width; x++)
104             {
105               gtk_hsv_to_rgb (h, 1, 1, &r, &g, &b);
106               red = CLAMP (r * 255, 0, 255);
107               green = CLAMP (g * 255, 0, 255);
108               blue = CLAMP (b * 255, 0, 255);
109               p[x] = (red << 16) | (green << 8) | blue;
110             }
111         }
112 
113       tmp = cairo_image_surface_create_for_data ((guchar *)data, CAIRO_FORMAT_RGB24,
114                                                  width, height, stride);
115 
116       cairo_set_source_surface (cr, tmp, 0, 0);
117       cairo_paint (cr);
118 
119       cairo_surface_destroy (tmp);
120       g_free (data);
121     }
122   else if (scale->priv->type == GTK_COLOR_SCALE_ALPHA)
123     {
124       cairo_pattern_t *pattern;
125       cairo_matrix_t matrix;
126       GdkRGBA *color;
127 
128       cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
129       cairo_paint (cr);
130       cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
131 
132       pattern = _gtk_color_chooser_get_checkered_pattern ();
133       cairo_matrix_init_scale (&matrix, 0.125, 0.125);
134       cairo_pattern_set_matrix (pattern, &matrix);
135       cairo_mask (cr, pattern);
136       cairo_pattern_destroy (pattern);
137 
138       color = &scale->priv->color;
139 
140       pattern = cairo_pattern_create_linear (0, 0, width, 0);
141       cairo_pattern_add_color_stop_rgba (pattern, 0, color->red, color->green, color->blue, 0);
142       cairo_pattern_add_color_stop_rgba (pattern, width, color->red, color->green, color->blue, 1);
143       cairo_set_source (cr, pattern);
144       cairo_paint (cr);
145       cairo_pattern_destroy (pattern);
146     }
147 
148   cairo_restore (cr);
149 }
150 
151 static void
gtk_color_scale_init(GtkColorScale * scale)152 gtk_color_scale_init (GtkColorScale *scale)
153 {
154   GtkStyleContext *context;
155 
156   scale->priv = gtk_color_scale_get_instance_private (scale);
157 
158   gtk_widget_add_events (GTK_WIDGET (scale), GDK_TOUCH_MASK);
159 
160   scale->priv->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (scale));
161   g_signal_connect (scale->priv->long_press_gesture, "pressed",
162                     G_CALLBACK (hold_action), scale);
163   gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (scale->priv->long_press_gesture),
164                                               GTK_PHASE_TARGET);
165 
166   context = gtk_widget_get_style_context (GTK_WIDGET (scale));
167   gtk_style_context_add_class (context, "color");
168 }
169 
170 static void
scale_finalize(GObject * object)171 scale_finalize (GObject *object)
172 {
173   GtkColorScale *scale = GTK_COLOR_SCALE (object);
174 
175   g_clear_object (&scale->priv->long_press_gesture);
176 
177   G_OBJECT_CLASS (gtk_color_scale_parent_class)->finalize (object);
178 }
179 
180 static void
scale_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)181 scale_get_property (GObject    *object,
182                     guint       prop_id,
183                     GValue     *value,
184                     GParamSpec *pspec)
185 {
186   GtkColorScale *scale = GTK_COLOR_SCALE (object);
187 
188   switch (prop_id)
189     {
190     case PROP_SCALE_TYPE:
191       g_value_set_int (value, scale->priv->type);
192       break;
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195       break;
196     }
197 }
198 
199 static void
scale_set_type(GtkColorScale * scale,GtkColorScaleType type)200 scale_set_type (GtkColorScale     *scale,
201                 GtkColorScaleType  type)
202 {
203   AtkObject *atk_obj;
204 
205   scale->priv->type = type;
206 
207   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (scale));
208   if (GTK_IS_ACCESSIBLE (atk_obj))
209     {
210       if (type == GTK_COLOR_SCALE_HUE)
211         atk_object_set_name (atk_obj, C_("Color channel", "Hue"));
212       else if (type == GTK_COLOR_SCALE_ALPHA)
213         atk_object_set_name (atk_obj, C_("Color channel", "Alpha"));
214       atk_object_set_role (atk_obj, ATK_ROLE_COLOR_CHOOSER);
215     }
216 }
217 
218 static void
scale_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)219 scale_set_property (GObject      *object,
220                     guint         prop_id,
221                     const GValue *value,
222                     GParamSpec   *pspec)
223 {
224   GtkColorScale *scale = GTK_COLOR_SCALE (object);
225 
226   switch (prop_id)
227     {
228     case PROP_SCALE_TYPE:
229       scale_set_type (scale, (GtkColorScaleType)g_value_get_int (value));
230       break;
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233       break;
234     }
235 }
236 
237 static void
hold_action(GtkGestureLongPress * gesture,gdouble x,gdouble y,GtkColorScale * scale)238 hold_action (GtkGestureLongPress *gesture,
239              gdouble              x,
240              gdouble              y,
241              GtkColorScale       *scale)
242 {
243   gboolean handled;
244 
245   g_signal_emit_by_name (scale, "popup-menu", &handled);
246 }
247 
248 static void
gtk_color_scale_class_init(GtkColorScaleClass * class)249 gtk_color_scale_class_init (GtkColorScaleClass *class)
250 {
251   GObjectClass *object_class = G_OBJECT_CLASS (class);
252 
253   object_class->finalize = scale_finalize;
254   object_class->get_property = scale_get_property;
255   object_class->set_property = scale_set_property;
256 
257   g_object_class_install_property (object_class, PROP_SCALE_TYPE,
258       g_param_spec_int ("scale-type", P_("Scale type"), P_("Scale type"),
259                         0, 1, 0,
260                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
261 }
262 
263 void
gtk_color_scale_set_rgba(GtkColorScale * scale,const GdkRGBA * color)264 gtk_color_scale_set_rgba (GtkColorScale *scale,
265                           const GdkRGBA *color)
266 {
267   scale->priv->color = *color;
268   gtk_widget_queue_draw (GTK_WIDGET (scale));
269 }
270 
271 GtkWidget *
gtk_color_scale_new(GtkAdjustment * adjustment,GtkColorScaleType type)272 gtk_color_scale_new (GtkAdjustment     *adjustment,
273                      GtkColorScaleType  type)
274 {
275   return g_object_new (GTK_TYPE_COLOR_SCALE,
276                        "adjustment", adjustment,
277                        "draw-value", FALSE,
278                        "scale-type", type,
279                        NULL);
280 }
281