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