]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssimagegradient.c
Change FSF Address
[~andy/gtk] / gtk / gtkcssimagegradient.c
1 /*
2  * Copyright © 2011 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.1 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  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcssimagegradientprivate.h"
23
24 #include "gtkcssprovider.h"
25
26 G_DEFINE_TYPE (GtkCssImageGradient, _gtk_css_image_gradient, GTK_TYPE_CSS_IMAGE)
27
28 static GtkCssImage *
29 gtk_css_image_gradient_compute (GtkCssImage     *image,
30                                 GtkStyleContext *context)
31 {
32   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
33   GtkCssImageGradient *copy;
34
35   if (gradient->pattern)
36     return g_object_ref (gradient);
37
38   copy = g_object_new (GTK_TYPE_CSS_IMAGE_GRADIENT, NULL);
39   copy->gradient = gtk_gradient_ref (gradient->gradient);
40   copy->pattern = gtk_gradient_resolve_for_context (copy->gradient, context);
41
42   return GTK_CSS_IMAGE (copy);
43 }
44
45 static void
46 gtk_css_image_gradient_draw (GtkCssImage        *image,
47                              cairo_t            *cr,
48                              double              width,
49                              double              height)
50 {
51   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
52
53   if (!gradient->pattern)
54     {
55       g_warning ("trying to paint unresolved gradient");
56       return;
57     }
58
59   cairo_scale (cr, width, height);
60
61   cairo_rectangle (cr, 0, 0, 1, 1);
62   cairo_set_source (cr, gradient->pattern);
63   cairo_fill (cr);
64 }
65
66 static gboolean
67 gtk_css_image_gradient_parse (GtkCssImage  *image,
68                               GtkCssParser *parser,
69                               GFile        *base)
70 {
71   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
72
73   gradient->gradient = _gtk_gradient_parse (parser);
74
75   return gradient->gradient != NULL;
76 }
77
78 static void
79 gtk_css_image_gradient_print (GtkCssImage *image,
80                               GString     *string)
81 {
82   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (image);
83   char *s;
84
85   s = gtk_gradient_to_string (gradient->gradient);
86   g_string_append (string, s);
87   g_free (s);
88 }
89
90 static void
91 gtk_css_image_gradient_dispose (GObject *object)
92 {
93   GtkCssImageGradient *gradient = GTK_CSS_IMAGE_GRADIENT (object);
94
95   if (gradient->gradient)
96     {
97       gtk_gradient_unref (gradient->gradient);
98       gradient->gradient = NULL;
99     }
100   if (gradient->pattern)
101     {
102       cairo_pattern_destroy (gradient->pattern);
103       gradient->pattern = NULL;
104     }
105
106   G_OBJECT_CLASS (_gtk_css_image_gradient_parent_class)->dispose (object);
107 }
108
109 static void
110 _gtk_css_image_gradient_class_init (GtkCssImageGradientClass *klass)
111 {
112   GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
113   GObjectClass *object_class = G_OBJECT_CLASS (klass);
114
115   image_class->compute = gtk_css_image_gradient_compute;
116   image_class->draw = gtk_css_image_gradient_draw;
117   image_class->parse = gtk_css_image_gradient_parse;
118   image_class->print = gtk_css_image_gradient_print;
119
120   object_class->dispose = gtk_css_image_gradient_dispose;
121 }
122
123 static void
124 _gtk_css_image_gradient_init (GtkCssImageGradient *image_gradient)
125 {
126 }
127
128 GtkGradient *
129 _gtk_gradient_parse (GtkCssParser *parser)
130 {
131   GtkGradient *gradient;
132   cairo_pattern_type_t type;
133   gdouble coords[6];
134   guint i;
135
136   g_return_val_if_fail (parser != NULL, NULL);
137
138   if (!_gtk_css_parser_try (parser, "-gtk-gradient", TRUE))
139     {
140       _gtk_css_parser_error (parser,
141                              "Expected '-gtk-gradient'");
142       return NULL;
143     }
144
145   if (!_gtk_css_parser_try (parser, "(", TRUE))
146     {
147       _gtk_css_parser_error (parser,
148                              "Expected '(' after '-gtk-gradient'");
149       return NULL;
150     }
151
152   /* Parse gradient type */
153   if (_gtk_css_parser_try (parser, "linear", TRUE))
154     type = CAIRO_PATTERN_TYPE_LINEAR;
155   else if (_gtk_css_parser_try (parser, "radial", TRUE))
156     type = CAIRO_PATTERN_TYPE_RADIAL;
157   else
158     {
159       _gtk_css_parser_error (parser,
160                              "Gradient type must be 'radial' or 'linear'");
161       return NULL;
162     }
163
164   /* Parse start/stop position parameters */
165   for (i = 0; i < 2; i++)
166     {
167       if (! _gtk_css_parser_try (parser, ",", TRUE))
168         {
169           _gtk_css_parser_error (parser,
170                                  "Expected ','");
171           return NULL;
172         }
173
174       if (_gtk_css_parser_try (parser, "left", TRUE))
175         coords[i * 3] = 0;
176       else if (_gtk_css_parser_try (parser, "right", TRUE))
177         coords[i * 3] = 1;
178       else if (_gtk_css_parser_try (parser, "center", TRUE))
179         coords[i * 3] = 0.5;
180       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3]))
181         {
182           _gtk_css_parser_error (parser,
183                                  "Expected a valid X coordinate");
184           return NULL;
185         }
186
187       if (_gtk_css_parser_try (parser, "top", TRUE))
188         coords[i * 3 + 1] = 0;
189       else if (_gtk_css_parser_try (parser, "bottom", TRUE))
190         coords[i * 3 + 1] = 1;
191       else if (_gtk_css_parser_try (parser, "center", TRUE))
192         coords[i * 3 + 1] = 0.5;
193       else if (!_gtk_css_parser_try_double (parser, &coords[i * 3 + 1]))
194         {
195           _gtk_css_parser_error (parser,
196                                  "Expected a valid Y coordinate");
197           return NULL;
198         }
199
200       if (type == CAIRO_PATTERN_TYPE_RADIAL)
201         {
202           /* Parse radius */
203           if (! _gtk_css_parser_try (parser, ",", TRUE))
204             {
205               _gtk_css_parser_error (parser,
206                                      "Expected ','");
207               return NULL;
208             }
209
210           if (! _gtk_css_parser_try_double (parser, &coords[(i * 3) + 2]))
211             {
212               _gtk_css_parser_error (parser,
213                                      "Expected a numer for the radius");
214               return NULL;
215             }
216         }
217     }
218
219   if (type == CAIRO_PATTERN_TYPE_LINEAR)
220     gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
221   else
222     gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
223                                         coords[3], coords[4], coords[5]);
224
225   while (_gtk_css_parser_try (parser, ",", TRUE))
226     {
227       GtkSymbolicColor *color;
228       gdouble position;
229
230       if (_gtk_css_parser_try (parser, "from", TRUE))
231         {
232           position = 0;
233
234           if (!_gtk_css_parser_try (parser, "(", TRUE))
235             {
236               gtk_gradient_unref (gradient);
237               _gtk_css_parser_error (parser,
238                                      "Expected '('");
239               return NULL;
240             }
241
242         }
243       else if (_gtk_css_parser_try (parser, "to", TRUE))
244         {
245           position = 1;
246
247           if (!_gtk_css_parser_try (parser, "(", TRUE))
248             {
249               gtk_gradient_unref (gradient);
250               _gtk_css_parser_error (parser,
251                                      "Expected '('");
252               return NULL;
253             }
254
255         }
256       else if (_gtk_css_parser_try (parser, "color-stop", TRUE))
257         {
258           if (!_gtk_css_parser_try (parser, "(", TRUE))
259             {
260               gtk_gradient_unref (gradient);
261               _gtk_css_parser_error (parser,
262                                      "Expected '('");
263               return NULL;
264             }
265
266           if (!_gtk_css_parser_try_double (parser, &position))
267             {
268               gtk_gradient_unref (gradient);
269               _gtk_css_parser_error (parser,
270                                      "Expected a valid number");
271               return NULL;
272             }
273
274           if (!_gtk_css_parser_try (parser, ",", TRUE))
275             {
276               gtk_gradient_unref (gradient);
277               _gtk_css_parser_error (parser,
278                                      "Expected a comma");
279               return NULL;
280             }
281         }
282       else
283         {
284           gtk_gradient_unref (gradient);
285           _gtk_css_parser_error (parser,
286                                  "Not a valid color-stop definition");
287           return NULL;
288         }
289
290       color = _gtk_css_parser_read_symbolic_color (parser);
291       if (color == NULL)
292         {
293           gtk_gradient_unref (gradient);
294           return NULL;
295         }
296
297       gtk_gradient_add_color_stop (gradient, position, color);
298       gtk_symbolic_color_unref (color);
299
300       if (!_gtk_css_parser_try (parser, ")", TRUE))
301         {
302           gtk_gradient_unref (gradient);
303           _gtk_css_parser_error (parser,
304                                  "Expected ')'");
305           return NULL;
306         }
307     }
308
309   if (!_gtk_css_parser_try (parser, ")", TRUE))
310     {
311       gtk_gradient_unref (gradient);
312       _gtk_css_parser_error (parser,
313                              "Expected ')'");
314       return NULL;
315     }
316
317   return gradient;
318 }