]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorscale.c
d7fde5aa1d85c0a9960b61e5cd88caac13794966
[~andy/gtk] / gtk / gtkcolorscale.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 #include "config.h"
21
22 #include "gtkcolorscaleprivate.h"
23
24 #include "gtkcolorchooserprivate.h"
25 #include "gtkcolorutils.h"
26 #include "gtkorientable.h"
27 #include "gtkstylecontext.h"
28 #include "gtkaccessible.h"
29 #include "gtkprivate.h"
30 #include "gtkintl.h"
31
32 #include <math.h>
33
34 struct _GtkColorScalePrivate
35 {
36   cairo_surface_t *surface;
37   gint width, height;
38   GdkRGBA color;
39   GtkColorScaleType type;
40 };
41
42 enum
43 {
44   PROP_ZERO,
45   PROP_SCALE_TYPE
46 };
47
48 G_DEFINE_TYPE (GtkColorScale, gtk_color_scale, GTK_TYPE_SCALE)
49
50 static void
51 gtk_color_scale_get_trough_size (GtkColorScale *scale,
52                                  gint *x_offset_out,
53                                  gint *y_offset_out,
54                                  gint *width_out,
55                                  gint *height_out)
56 {
57   GtkWidget *widget = GTK_WIDGET (scale);
58   gint width, height, focus_line_width, focus_padding;
59   gint x_offset, y_offset;
60   gint slider_width, slider_height;
61
62   gtk_widget_style_get (widget,
63                         "focus-line-width", &focus_line_width,
64                         "focus-padding", &focus_padding,
65                         "slider-width", &slider_width,
66                         "slider-length", &slider_height,
67                         NULL);
68
69   width = gtk_widget_get_allocated_width (widget) - 2 * (focus_line_width + focus_padding);
70   height = gtk_widget_get_allocated_height (widget) - 2 * (focus_line_width + focus_padding);
71
72   x_offset = focus_line_width + focus_padding;
73   y_offset = focus_line_width + focus_padding;
74
75   /* if the slider has a vertical shape, draw the trough asymmetric */
76   if (slider_width > slider_height)
77     {
78       if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_VERTICAL)
79         {
80           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
81             x_offset += (gint) floor (slider_width / 2.0);
82
83           width = (gint) floor (slider_width / 2.0);
84         }
85       else
86         {
87           height = (gint) floor (slider_width / 2.0);
88         }
89     }
90
91   if (width_out)
92     *width_out = width;
93   if (height_out)
94     *height_out = height;
95   if (x_offset_out)
96     *x_offset_out = x_offset;
97   if (y_offset_out)
98     *y_offset_out = y_offset;
99 }
100
101 static void
102 create_surface (GtkColorScale *scale)
103 {
104   GtkWidget *widget = GTK_WIDGET (scale);
105   cairo_surface_t *surface;
106   gint width, height;
107
108   if (!gtk_widget_get_realized (widget))
109     return;
110
111   gtk_color_scale_get_trough_size (scale,
112                                    NULL, NULL,
113                                    &width, &height);
114
115   if (!scale->priv->surface ||
116       width != scale->priv->width ||
117       height != scale->priv->height)
118     {
119       surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
120                                                    CAIRO_CONTENT_COLOR,
121                                                    width, height);
122       if (scale->priv->surface)
123         cairo_surface_destroy (scale->priv->surface);
124       scale->priv->surface = surface;
125       scale->priv->width = width;
126       scale->priv->height= height;
127     }
128   else
129     surface = scale->priv->surface;
130
131   if (width == 1 || height == 1)
132     return;
133
134   if (scale->priv->type == GTK_COLOR_SCALE_HUE)
135     {
136       cairo_t *cr;
137       gint stride;
138       cairo_surface_t *tmp;
139       guint red, green, blue;
140       guint32 *data, *p;
141       gdouble h;
142       gdouble r, g, b;
143       gdouble f;
144       gint x, y;
145
146       stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
147
148       data = g_malloc (height * stride);
149
150       f = 1.0 / (height - 1);
151       for (y = 0; y < height; y++)
152         {
153           h = CLAMP (y * f, 0.0, 1.0);
154           p = data + y * (stride / 4);
155           for (x = 0; x < width; x++)
156             {
157               gtk_hsv_to_rgb (h, 1, 1, &r, &g, &b);
158               red = CLAMP (r * 255, 0, 255);
159               green = CLAMP (g * 255, 0, 255);
160               blue = CLAMP (b * 255, 0, 255);
161               p[x] = (red << 16) | (green << 8) | blue;
162             }
163         }
164
165       tmp = cairo_image_surface_create_for_data ((guchar *)data, CAIRO_FORMAT_RGB24,
166                                                  width, height, stride);
167       cr = cairo_create (surface);
168
169       cairo_set_source_surface (cr, tmp, 0, 0);
170       cairo_paint (cr);
171
172       cairo_destroy (cr);
173       cairo_surface_destroy (tmp);
174       g_free (data);
175     }
176   else if (scale->priv->type == GTK_COLOR_SCALE_ALPHA)
177     {
178       cairo_t *cr;
179       cairo_pattern_t *pattern;
180       cairo_matrix_t matrix;
181       GdkRGBA *color;
182
183       cr = cairo_create (surface);
184
185       cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
186       cairo_paint (cr);
187       cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
188
189       pattern = _gtk_color_chooser_get_checkered_pattern ();
190       cairo_matrix_init_scale (&matrix, 0.125, 0.125);
191       cairo_pattern_set_matrix (pattern, &matrix);
192       cairo_mask (cr, pattern);
193       cairo_pattern_destroy (pattern);
194
195       color = &scale->priv->color;
196
197       pattern = cairo_pattern_create_linear (0, 0, width, 0);
198       cairo_pattern_add_color_stop_rgba (pattern, 0, color->red, color->green, color->blue, 0);
199       cairo_pattern_add_color_stop_rgba (pattern, width, color->red, color->green, color->blue, 1);
200       cairo_set_source (cr, pattern);
201       cairo_paint (cr);
202       cairo_pattern_destroy (pattern);
203
204       cairo_destroy (cr);
205     }
206 }
207
208 static gboolean
209 scale_draw (GtkWidget *widget,
210             cairo_t   *cr)
211 {
212   GtkColorScale *scale = GTK_COLOR_SCALE (widget);
213   gint width, height, x_offset, y_offset;
214   cairo_pattern_t *pattern;
215
216   create_surface (scale);
217   gtk_color_scale_get_trough_size (scale,
218                                    &x_offset, &y_offset,
219                                    &width, &height);
220
221   cairo_save (cr);
222   cairo_translate (cr, x_offset, y_offset);
223   cairo_rectangle (cr, 0, 0, width, height);
224
225   pattern = cairo_pattern_create_for_surface (scale->priv->surface);
226   if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL &&
227       gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
228     {
229       cairo_matrix_t matrix;
230
231       cairo_matrix_init_scale (&matrix, -1, 1);
232       cairo_matrix_translate (&matrix, -width, 0);
233       cairo_pattern_set_matrix (pattern, &matrix);
234     }
235   cairo_set_source (cr, pattern);
236   cairo_fill (cr);
237
238   cairo_pattern_destroy (pattern);
239
240   cairo_restore (cr);
241
242   GTK_WIDGET_CLASS (gtk_color_scale_parent_class)->draw (widget, cr);
243
244   return FALSE;
245 }
246
247 static void
248 gtk_color_scale_init (GtkColorScale *scale)
249 {
250   scale->priv = G_TYPE_INSTANCE_GET_PRIVATE (scale,
251                                              GTK_TYPE_COLOR_SCALE,
252                                              GtkColorScalePrivate);
253 }
254
255 static void
256 scale_finalize (GObject *object)
257 {
258   GtkColorScale *scale = GTK_COLOR_SCALE (object);
259
260   if (scale->priv->surface)
261     cairo_surface_destroy (scale->priv->surface);
262
263   G_OBJECT_CLASS (gtk_color_scale_parent_class)->finalize (object);
264 }
265
266 static void
267 scale_get_property (GObject    *object,
268                     guint       prop_id,
269                     GValue     *value,
270                     GParamSpec *pspec)
271 {
272   GtkColorScale *scale = GTK_COLOR_SCALE (object);
273
274   switch (prop_id)
275     {
276     case PROP_SCALE_TYPE:
277       g_value_set_int (value, scale->priv->type);
278       break;
279     default:
280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281       break;
282     }
283 }
284
285 static void
286 scale_set_type (GtkColorScale     *scale,
287                 GtkColorScaleType  type)
288 {
289   AtkObject *atk_obj;
290
291   scale->priv->type = type;
292
293   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (scale));
294   if (GTK_IS_ACCESSIBLE (atk_obj))
295     {
296       if (type == GTK_COLOR_SCALE_HUE)
297         atk_object_set_name (atk_obj, C_("Color channel", "Hue"));
298       else if (type == GTK_COLOR_SCALE_ALPHA)
299         atk_object_set_name (atk_obj, C_("Color channel", "Alpha"));
300       atk_object_set_role (gtk_widget_get_accessible (GTK_WIDGET (scale)), ATK_ROLE_COLOR_CHOOSER);
301     }
302 }
303
304 static void
305 scale_set_property (GObject      *object,
306                     guint         prop_id,
307                     const GValue *value,
308                     GParamSpec   *pspec)
309 {
310   GtkColorScale *scale = GTK_COLOR_SCALE (object);
311
312   switch (prop_id)
313     {
314     case PROP_SCALE_TYPE:
315       scale_set_type (scale, (GtkColorScaleType)g_value_get_int (value));
316       break;
317     default:
318       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319       break;
320     }
321 }
322
323 static void
324 gtk_color_scale_class_init (GtkColorScaleClass *class)
325 {
326   GObjectClass *object_class = G_OBJECT_CLASS (class);
327   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
328
329   object_class->finalize = scale_finalize;
330   object_class->get_property = scale_get_property;
331   object_class->set_property = scale_set_property;
332
333   widget_class->draw = scale_draw;
334
335   g_object_class_install_property (object_class, PROP_SCALE_TYPE,
336       g_param_spec_int ("scale-type", P_("Scale type"), P_("Scale type"),
337                         0, 1, 0,
338                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
339
340   g_type_class_add_private (class, sizeof (GtkColorScalePrivate));
341 }
342
343 void
344 gtk_color_scale_set_rgba (GtkColorScale *scale,
345                           const GdkRGBA *color)
346 {
347   scale->priv->color = *color;
348   scale->priv->width = -1; /* force surface refresh */
349   create_surface (scale);
350   gtk_widget_queue_draw (GTK_WIDGET (scale));
351 }
352
353 GtkWidget *
354 gtk_color_scale_new (GtkAdjustment     *adjustment,
355                      GtkColorScaleType  type)
356 {
357   return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SCALE,
358                                      "adjustment", adjustment,
359                                      "draw-value", FALSE,
360                                      "scale-type", type,
361                                      NULL);
362 }