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