]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshadowvalue.c
css: Fold color value computation into gtksymboliccolor.c
[~andy/gtk] / gtk / gtkcssshadowvalue.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * Author: Cosimo Cecchi <cosimoc@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21
22 #include "gtkcssshadowvalueprivate.h"
23
24 #include "gtkcssnumbervalueprivate.h"
25 #include "gtkcssrgbavalueprivate.h"
26 #include "gtkstylecontextprivate.h"
27 #include "gtksymboliccolorprivate.h"
28 #include "gtkthemingengineprivate.h"
29 #include "gtkpango.h"
30
31 struct _GtkCssValue {
32   GTK_CSS_VALUE_BASE
33   guint inset :1;
34
35   GtkCssValue *hoffset;
36   GtkCssValue *voffset;
37   GtkCssValue *radius;
38   GtkCssValue *spread;
39
40   GtkCssValue *color;
41 };
42
43 static GtkCssValue *    gtk_css_shadow_value_new (GtkCssValue *hoffset,
44                                                   GtkCssValue *voffset,
45                                                   GtkCssValue *radius,
46                                                   GtkCssValue *spread,
47                                                   gboolean     inset,
48                                                   GtkCssValue *color);
49
50 static void
51 gtk_css_value_shadow_free (GtkCssValue *shadow)
52 {
53   _gtk_css_value_unref (shadow->hoffset);
54   _gtk_css_value_unref (shadow->voffset);
55   _gtk_css_value_unref (shadow->radius);
56   _gtk_css_value_unref (shadow->spread);
57   _gtk_css_value_unref (shadow->color);
58
59   g_slice_free (GtkCssValue, shadow);
60 }
61
62 static GtkCssValue *
63 gtk_css_value_shadow_compute (GtkCssValue     *shadow,
64                               guint            property_id,
65                               GtkStyleContext *context)
66 {
67   return gtk_css_shadow_value_new (_gtk_css_value_compute (shadow->hoffset, property_id, context),
68                                    _gtk_css_value_compute (shadow->voffset, property_id, context),
69                                    _gtk_css_value_compute (shadow->radius, property_id, context),
70                                    _gtk_css_value_compute (shadow->spread, property_id, context),
71                                    shadow->inset,
72                                    _gtk_css_value_compute (shadow->color, property_id, context));
73 }
74
75 static gboolean
76 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
77                             const GtkCssValue *shadow2)
78 {
79   return shadow1->inset == shadow2->inset
80       && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset)
81       && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset)
82       && _gtk_css_value_equal (shadow1->radius, shadow2->radius)
83       && _gtk_css_value_equal (shadow1->spread, shadow2->spread)
84       && _gtk_css_value_equal (shadow1->color, shadow2->color);
85 }
86
87 static GtkCssValue *
88 gtk_css_value_shadow_transition (GtkCssValue *start,
89                                  GtkCssValue *end,
90                                  double       progress)
91 {
92   if (start->inset != end->inset)
93     return NULL;
94
95   return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, progress),
96                                    _gtk_css_value_transition (start->voffset, end->voffset, progress),
97                                    _gtk_css_value_transition (start->radius, end->radius, progress),
98                                    _gtk_css_value_transition (start->spread, end->spread, progress),
99                                    start->inset,
100                                    _gtk_css_value_transition (start->color, end->color, progress));
101 }
102
103 static void
104 gtk_css_value_shadow_print (const GtkCssValue *shadow,
105                             GString           *string)
106 {
107   _gtk_css_value_print (shadow->hoffset, string);
108   g_string_append_c (string, ' ');
109   _gtk_css_value_print (shadow->voffset, string);
110   g_string_append_c (string, ' ');
111   if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
112       _gtk_css_number_value_get (shadow->spread, 100) != 0)
113     {
114       _gtk_css_value_print (shadow->radius, string);
115       g_string_append_c (string, ' ');
116     }
117
118   if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
119     {
120       _gtk_css_value_print (shadow->spread, string);
121       g_string_append_c (string, ' ');
122     }
123
124   _gtk_css_value_print (shadow->color, string);
125
126   if (shadow->inset)
127     g_string_append (string, " inset");
128
129 }
130
131 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
132   gtk_css_value_shadow_free,
133   gtk_css_value_shadow_compute,
134   gtk_css_value_shadow_equal,
135   gtk_css_value_shadow_transition,
136   gtk_css_value_shadow_print
137 };
138
139 static GtkCssValue *
140 gtk_css_shadow_value_new (GtkCssValue *hoffset,
141                           GtkCssValue *voffset,
142                           GtkCssValue *radius,
143                           GtkCssValue *spread,
144                           gboolean     inset,
145                           GtkCssValue *color)
146 {
147   GtkCssValue *retval;
148
149   retval = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
150
151   retval->hoffset = hoffset;
152   retval->voffset = voffset;
153   retval->radius = radius;
154   retval->spread = spread;
155   retval->inset = inset;
156   retval->color = color;
157
158   return retval;
159 }                  
160
161 GtkCssValue *
162 _gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
163 {
164   GdkRGBA transparent = { 0, 0, 0, 0 };
165
166   g_return_val_if_fail (target->class == &GTK_CSS_VALUE_SHADOW, NULL);
167
168   return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX),
169                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
170                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
171                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
172                                    target->inset,
173                                    _gtk_css_rgba_value_new_from_rgba (&transparent));
174 }
175
176 static gboolean
177 value_is_done_parsing (GtkCssParser *parser)
178 {
179   return _gtk_css_parser_is_eof (parser) ||
180          _gtk_css_parser_begins_with (parser, ',') ||
181          _gtk_css_parser_begins_with (parser, ';') ||
182          _gtk_css_parser_begins_with (parser, '}');
183 }
184
185 GtkCssValue *
186 _gtk_css_shadow_value_parse (GtkCssParser *parser)
187 {
188   enum {
189     HOFFSET,
190     VOFFSET,
191     RADIUS,
192     SPREAD,
193     COLOR,
194     N_VALUES
195   };
196   GtkCssValue *values[N_VALUES] = { NULL, };
197   gboolean inset;
198   guint i;
199
200   inset = _gtk_css_parser_try (parser, "inset", TRUE);
201
202   do
203   {
204     if (values[HOFFSET] == NULL &&
205          _gtk_css_parser_has_number (parser))
206       {
207         values[HOFFSET] = _gtk_css_number_value_parse (parser,
208                                                        GTK_CSS_PARSE_LENGTH
209                                                        | GTK_CSS_NUMBER_AS_PIXELS);
210         if (values[HOFFSET] == NULL)
211           goto fail;
212
213         values[VOFFSET] = _gtk_css_number_value_parse (parser,
214                                                        GTK_CSS_PARSE_LENGTH
215                                                        | GTK_CSS_NUMBER_AS_PIXELS);
216         if (values[VOFFSET] == NULL)
217           goto fail;
218
219         if (_gtk_css_parser_has_number (parser))
220           {
221             values[RADIUS] = _gtk_css_number_value_parse (parser,
222                                                           GTK_CSS_PARSE_LENGTH
223                                                           | GTK_CSS_POSITIVE_ONLY
224                                                           | GTK_CSS_NUMBER_AS_PIXELS);
225             if (values[RADIUS] == NULL)
226               goto fail;
227           }
228         else
229           values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
230                                                         
231         if (_gtk_css_parser_has_number (parser))
232           {
233             values[SPREAD] = _gtk_css_number_value_parse (parser,
234                                                           GTK_CSS_PARSE_LENGTH
235                                                           | GTK_CSS_NUMBER_AS_PIXELS);
236             if (values[SPREAD] == NULL)
237               goto fail;
238           }
239         else
240           values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
241       }
242     else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE))
243       {
244         if (values[HOFFSET] == NULL)
245           goto fail;
246         inset = TRUE;
247         break;
248       }
249     else if (values[COLOR] == NULL)
250       {
251         values[COLOR] = _gtk_css_symbolic_value_new (parser);
252
253         if (values[COLOR] == NULL)
254           goto fail;
255       }
256     else
257       {
258         /* We parsed everything and there's still stuff left?
259          * Pretend we didn't notice and let the normal code produce
260          * a 'junk at end of value' error */
261         goto fail;
262       }
263   }
264   while (values[HOFFSET] == NULL || !value_is_done_parsing (parser));
265
266   if (values[COLOR] == NULL)
267     values[COLOR] = _gtk_css_symbolic_value_new_take_symbolic_color (
268                       gtk_symbolic_color_ref (
269                         _gtk_symbolic_color_get_current_color ()));
270
271   return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET],
272                                    values[RADIUS], values[SPREAD],
273                                    inset, values[COLOR]);
274
275 fail:
276   for (i = 0; i < N_VALUES; i++)
277     {
278       if (values[i])
279         _gtk_css_value_unref (values[i]);
280     }
281
282   return NULL;
283 }
284
285 void
286 _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
287                                     cairo_t           *cr,
288                                     PangoLayout       *layout)
289 {
290   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
291
292   if (!cairo_has_current_point (cr))
293     cairo_move_to (cr, 0, 0);
294
295   cairo_save (cr);
296
297   cairo_rel_move_to (cr, 
298                      _gtk_css_number_value_get (shadow->hoffset, 0),
299                      _gtk_css_number_value_get (shadow->voffset, 0));
300   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
301   _gtk_pango_fill_layout (cr, layout);
302
303   cairo_rel_move_to (cr,
304                      - _gtk_css_number_value_get (shadow->hoffset, 0),
305                      - _gtk_css_number_value_get (shadow->voffset, 0));
306   cairo_restore (cr);
307 }
308
309 void
310 _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
311                                   cairo_t           *cr)
312 {
313   cairo_pattern_t *pattern;
314
315   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
316
317   cairo_save (cr);
318   pattern = cairo_pattern_reference (cairo_get_source (cr));
319   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
320
321   cairo_translate (cr,
322                    _gtk_css_number_value_get (shadow->hoffset, 0),
323                    _gtk_css_number_value_get (shadow->voffset, 0));
324   cairo_mask (cr, pattern);
325
326   cairo_restore (cr);
327   cairo_pattern_destroy (pattern);
328 }
329
330 void
331 _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
332                                      cairo_t           *cr,
333                                      gdouble            radius,
334                                      gdouble            progress)
335 {
336   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
337
338   cairo_save (cr);
339
340   cairo_translate (cr,
341                    _gtk_css_number_value_get (shadow->hoffset, 0),
342                    _gtk_css_number_value_get (shadow->voffset, 0));
343   _gtk_theming_engine_paint_spinner (cr,
344                                      radius, progress,
345                                      _gtk_css_rgba_value_get_rgba (shadow->color));
346
347   cairo_restore (cr);
348 }
349
350 void
351 _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
352                                  cairo_t             *cr,
353                                  const GtkRoundedBox *padding_box)
354 {
355   GtkRoundedBox box;
356   double spread;
357
358   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
359
360   cairo_save (cr);
361   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
362
363   _gtk_rounded_box_path (padding_box, cr);
364   cairo_clip (cr);
365
366   box = *padding_box;
367   _gtk_rounded_box_move (&box,
368                          _gtk_css_number_value_get (shadow->hoffset, 0),
369                          _gtk_css_number_value_get (shadow->voffset, 0));
370   spread = _gtk_css_number_value_get (shadow->spread, 0);
371   _gtk_rounded_box_shrink (&box, spread, spread, spread, spread);
372
373   _gtk_rounded_box_path (&box, cr);
374   _gtk_rounded_box_clip_path (padding_box, cr);
375
376   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
377   cairo_fill (cr);
378
379   cairo_restore (cr);
380 }