]> Pileus Git - ~andy/gtk/blob - gtk/gtkgradient.c
Change FSF Address
[~andy/gtk] / gtk / gtkgradient.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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 #include "gtkgradient.h"
20 #include "gtkstylecontextprivate.h"
21 #include "gtkstyleproperties.h"
22 #include "gtkintl.h"
23
24 /**
25  * SECTION:gtkgradient
26  * @Short_description: Gradients
27  * @Title: GtkGradient
28  *
29  * GtkGradient is a boxed type that represents a gradient.
30  * It is the result of parsing a
31  * <link linkend="gtkcssprovider-gradients">gradient expression</link>.
32  * To obtain the gradient represented by a GtkGradient, it has to
33  * be resolved with gtk_gradient_resolve(), which replaces all
34  * symbolic color references by the colors they refer to (in a given
35  * context) and constructs a #cairo_pattern_t value.
36  *
37  * It is not normally necessary to deal directly with #GtkGradients,
38  * since they are mostly used behind the scenes by #GtkStyleContext and
39  * #GtkCssProvider.
40  */
41
42 G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
43                      gtk_gradient_ref, gtk_gradient_unref)
44
45 typedef struct ColorStop ColorStop;
46
47 struct ColorStop
48 {
49   gdouble offset;
50   GtkSymbolicColor *color;
51 };
52
53 struct _GtkGradient
54 {
55   gdouble x0;
56   gdouble y0;
57   gdouble x1;
58   gdouble y1;
59   gdouble radius0;
60   gdouble radius1;
61
62   GArray *stops;
63
64   guint ref_count;
65 };
66
67 /**
68  * gtk_gradient_new_linear:
69  * @x0: X coordinate of the starting point
70  * @y0: Y coordinate of the starting point
71  * @x1: X coordinate of the end point
72  * @y1: Y coordinate of the end point
73  *
74  * Creates a new linear gradient along the line defined by (x0, y0) and (x1, y1). Before using the gradient
75  * a number of stop colors must be added through gtk_gradient_add_color_stop().
76  *
77  * Returns: A newly created #GtkGradient
78  *
79  * Since: 3.0
80  **/
81 GtkGradient *
82 gtk_gradient_new_linear (gdouble x0,
83                          gdouble y0,
84                          gdouble x1,
85                          gdouble y1)
86 {
87   GtkGradient *gradient;
88
89   gradient = g_slice_new (GtkGradient);
90   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
91
92   gradient->x0 = x0;
93   gradient->y0 = y0;
94   gradient->x1 = x1;
95   gradient->y1 = y1;
96   gradient->radius0 = 0;
97   gradient->radius1 = 0;
98
99   gradient->ref_count = 1;
100
101   return gradient;
102 }
103
104 /**
105  * gtk_gradient_new_radial:
106  * @x0: X coordinate of the start circle
107  * @y0: Y coordinate of the start circle
108  * @radius0: radius of the start circle
109  * @x1: X coordinate of the end circle
110  * @y1: Y coordinate of the end circle
111  * @radius1: radius of the end circle
112  *
113  * Creates a new radial gradient along the two circles defined by (x0, y0, radius0) and
114  * (x1, y1, radius1). Before using the gradient a number of stop colors must be added
115  * through gtk_gradient_add_color_stop().
116  *
117  * Returns: A newly created #GtkGradient
118  *
119  * Since: 3.0
120  **/
121 GtkGradient *
122 gtk_gradient_new_radial (gdouble x0,
123                          gdouble y0,
124                          gdouble radius0,
125                          gdouble x1,
126                          gdouble y1,
127                          gdouble radius1)
128 {
129   GtkGradient *gradient;
130
131   gradient = g_slice_new (GtkGradient);
132   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
133
134   gradient->x0 = x0;
135   gradient->y0 = y0;
136   gradient->x1 = x1;
137   gradient->y1 = y1;
138   gradient->radius0 = radius0;
139   gradient->radius1 = radius1;
140
141   gradient->ref_count = 1;
142
143   return gradient;
144 }
145
146 /**
147  * gtk_gradient_add_color_stop:
148  * @gradient: a #GtkGradient
149  * @offset: offset for the color stop
150  * @color: color to use
151  *
152  * Adds a stop color to @gradient.
153  *
154  * Since: 3.0
155  **/
156 void
157 gtk_gradient_add_color_stop (GtkGradient      *gradient,
158                              gdouble           offset,
159                              GtkSymbolicColor *color)
160 {
161   ColorStop stop;
162
163   g_return_if_fail (gradient != NULL);
164
165   stop.offset = offset;
166   stop.color = gtk_symbolic_color_ref (color);
167
168   g_array_append_val (gradient->stops, stop);
169 }
170
171 /**
172  * gtk_gradient_ref:
173  * @gradient: a #GtkGradient
174  *
175  * Increases the reference count of @gradient.
176  *
177  * Returns: The same @gradient
178  *
179  * Since: 3.0
180  **/
181 GtkGradient *
182 gtk_gradient_ref (GtkGradient *gradient)
183 {
184   g_return_val_if_fail (gradient != NULL, NULL);
185
186   gradient->ref_count++;
187
188   return gradient;
189 }
190
191 /**
192  * gtk_gradient_unref:
193  * @gradient: a #GtkGradient
194  *
195  * Decreases the reference count of @gradient, freeing its memory
196  * if the reference count reaches 0.
197  *
198  * Since: 3.0
199  **/
200 void
201 gtk_gradient_unref (GtkGradient *gradient)
202 {
203   g_return_if_fail (gradient != NULL);
204
205   gradient->ref_count--;
206
207   if (gradient->ref_count == 0)
208     {
209       guint i;
210
211       for (i = 0; i < gradient->stops->len; i++)
212         {
213           ColorStop *stop;
214
215           stop = &g_array_index (gradient->stops, ColorStop, i);
216           gtk_symbolic_color_unref (stop->color);
217         }
218
219       g_array_free (gradient->stops, TRUE);
220       g_slice_free (GtkGradient, gradient);
221     }
222 }
223
224 /**
225  * gtk_gradient_resolve:
226  * @gradient: a #GtkGradient
227  * @props: #GtkStyleProperties to use when resolving named colors
228  * @resolved_gradient: (out): return location for the resolved pattern
229  *
230  * If @gradient is resolvable, @resolved_gradient will be filled in
231  * with the resolved gradient as a cairo_pattern_t, and %TRUE will
232  * be returned. Generally, if @gradient can't be resolved, it is
233  * due to it being defined on top of a named color that doesn't
234  * exist in @props.
235  *
236  * Returns: %TRUE if the gradient has been resolved
237  *
238  * Since: 3.0
239  **/
240 gboolean
241 gtk_gradient_resolve (GtkGradient         *gradient,
242                       GtkStyleProperties  *props,
243                       cairo_pattern_t    **resolved_gradient)
244 {
245   cairo_pattern_t *pattern;
246   guint i;
247
248   g_return_val_if_fail (gradient != NULL, FALSE);
249   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
250   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
251
252   if (gradient->radius0 == 0 && gradient->radius1 == 0)
253     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
254                                            gradient->x1, gradient->y1);
255   else
256     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
257                                            gradient->radius0,
258                                            gradient->x1, gradient->y1,
259                                            gradient->radius1);
260
261   for (i = 0; i < gradient->stops->len; i++)
262     {
263       ColorStop *stop;
264       GdkRGBA color;
265
266       stop = &g_array_index (gradient->stops, ColorStop, i);
267
268       if (!gtk_symbolic_color_resolve (stop->color, props, &color))
269         {
270           cairo_pattern_destroy (pattern);
271           return FALSE;
272         }
273
274       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
275                                          color.red, color.green,
276                                          color.blue, color.alpha);
277     }
278
279   *resolved_gradient = pattern;
280   return TRUE;
281 }
282
283 cairo_pattern_t *
284 gtk_gradient_resolve_for_context (GtkGradient     *gradient,
285                                   GtkStyleContext *context)
286 {
287   cairo_pattern_t *pattern;
288   guint i;
289
290   g_return_val_if_fail (gradient != NULL, FALSE);
291   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
292
293   if (gradient->radius0 == 0 && gradient->radius1 == 0)
294     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
295                                            gradient->x1, gradient->y1);
296   else
297     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
298                                            gradient->radius0,
299                                            gradient->x1, gradient->y1,
300                                            gradient->radius1);
301
302   for (i = 0; i < gradient->stops->len; i++)
303     {
304       ColorStop *stop;
305       GdkRGBA rgba;
306
307       stop = &g_array_index (gradient->stops, ColorStop, i);
308
309       /* if color resolving fails, assume transparency */
310       if (!_gtk_style_context_resolve_color (context, stop->color, &rgba))
311         rgba.red = rgba.green = rgba.blue = rgba.alpha = 0.0;
312
313       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
314                                          rgba.red, rgba.green,
315                                          rgba.blue, rgba.alpha);
316     }
317
318   return pattern;
319 }
320
321 static void
322 append_number (GString    *str,
323                double      d,
324                const char *zero,
325                const char *half,
326                const char *one)
327 {
328   if (zero && d == 0.0)
329     g_string_append (str, zero);
330   else if (half && d == 0.5)
331     g_string_append (str, half);
332   else if (one && d == 1.0)
333     g_string_append (str, one);
334   else
335     {
336       char buf[G_ASCII_DTOSTR_BUF_SIZE];
337
338       g_ascii_dtostr (buf, sizeof (buf), d);
339       g_string_append (str, buf);
340     }
341 }
342
343 /**
344  * gtk_gradient_to_string:
345  * @gradient: the gradient to print
346  *
347  * Creates a string representation for @gradient that is suitable
348  * for using in GTK CSS files.
349  *
350  * Returns: A string representation for @gradient
351  **/
352 char *
353 gtk_gradient_to_string (GtkGradient *gradient)
354 {
355   GString *str;
356   guint i;
357
358   g_return_val_if_fail (gradient != NULL, NULL);
359
360   str = g_string_new ("-gtk-gradient (");
361
362   if (gradient->radius0 == 0 && gradient->radius1 == 0)
363     {
364       g_string_append (str, "linear, ");
365       append_number (str, gradient->x0, "left", "center", "right");
366       g_string_append_c (str, ' ');
367       append_number (str, gradient->y0, "top", "center", "bottom");
368       g_string_append (str, ", ");
369       append_number (str, gradient->x1, "left", "center", "right");
370       g_string_append_c (str, ' ');
371       append_number (str, gradient->y1, "top", "center", "bottom");
372     }
373   else
374     {
375       g_string_append (str, "radial, ");
376       append_number (str, gradient->x0, "left", "center", "right");
377       g_string_append_c (str, ' ');
378       append_number (str, gradient->y0, "top", "center", "bottom");
379       g_string_append (str, ", ");
380       append_number (str, gradient->radius0, NULL, NULL, NULL);
381       g_string_append (str, ", ");
382       append_number (str, gradient->x1, "left", "center", "right");
383       g_string_append_c (str, ' ');
384       append_number (str, gradient->y1, "top", "center", "bottom");
385       g_string_append (str, ", ");
386       append_number (str, gradient->radius1, NULL, NULL, NULL);
387     }
388   
389   for (i = 0; i < gradient->stops->len; i++)
390     {
391       ColorStop *stop;
392       char *s;
393
394       stop = &g_array_index (gradient->stops, ColorStop, i);
395
396       g_string_append (str, ", ");
397
398       if (stop->offset == 0.0)
399         g_string_append (str, "from (");
400       else if (stop->offset == 1.0)
401         g_string_append (str, "to (");
402       else
403         {
404           g_string_append (str, "color-stop (");
405           append_number (str, stop->offset, NULL, NULL, NULL);
406           g_string_append (str, ", ");
407         }
408
409       s = gtk_symbolic_color_to_string (stop->color);
410       g_string_append (str, s);
411       g_free (s);
412
413       g_string_append (str, ")");
414     }
415
416   g_string_append (str, ")");
417
418   return g_string_free (str, FALSE);
419 }