]> Pileus Git - ~andy/gtk/blob - gtk/gtkshadow.c
Change FSF Address
[~andy/gtk] / gtk / gtkshadow.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 "gtkshadowprivate.h"
23
24 #include "gtkstylecontextprivate.h"
25 #include "gtkthemingengineprivate.h"
26 #include "gtkpango.h"
27
28 typedef struct _GtkShadowElement GtkShadowElement;
29
30 struct _GtkShadowElement {
31   gint16 hoffset;
32   gint16 voffset;
33   gint16 radius;
34   gint16 spread;
35
36   gboolean inset;
37
38   GdkRGBA color;
39   GtkSymbolicColor *symbolic_color;
40 };
41
42 static void
43 shadow_element_print (GtkShadowElement *element,
44                       GString          *str)
45 {
46   gchar *color_str;
47
48   if (element->inset)
49     g_string_append (str, "inset ");
50
51   g_string_append_printf (str, "%d %d ",
52                           (gint) element->hoffset,
53                           (gint) element->voffset);
54
55   if (element->radius != 0)
56     g_string_append_printf (str, "%d ", (gint) element->radius);
57
58   if (element->spread != 0)
59     g_string_append_printf (str, "%d ", (gint) element->spread);
60
61   if (element->symbolic_color != NULL)
62     color_str = gtk_symbolic_color_to_string (element->symbolic_color);
63   else
64     color_str = gdk_rgba_to_string (&element->color);
65
66   g_string_append (str, color_str);
67   g_free (color_str);
68 }
69
70 static void
71 shadow_element_free (GtkShadowElement *element)
72 {
73   if (element->symbolic_color != NULL)
74     gtk_symbolic_color_unref (element->symbolic_color);
75
76   g_slice_free (GtkShadowElement, element);
77 }
78
79 static GtkShadowElement *
80 shadow_element_new (gdouble hoffset,
81                     gdouble voffset,
82                     gdouble radius,
83                     gdouble spread,
84                     gboolean inset,
85                     GdkRGBA *color,
86                     GtkSymbolicColor *symbolic_color)
87 {
88   GtkShadowElement *retval;
89
90   retval = g_slice_new0 (GtkShadowElement);
91
92   retval->hoffset = hoffset;
93   retval->voffset = voffset;
94   retval->radius = radius;
95   retval->spread = spread;
96   retval->inset = inset;
97
98   if (symbolic_color != NULL)
99     retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color);
100
101   if (color != NULL)
102     retval->color = *color;
103
104   return retval;
105 }                  
106
107 /****************
108  * GtkShadow *
109  ****************/
110
111 G_DEFINE_BOXED_TYPE (GtkShadow, _gtk_shadow,
112                      _gtk_shadow_ref, _gtk_shadow_unref)
113
114 struct _GtkShadow {
115   GList *elements;
116
117   guint ref_count;
118   gboolean resolved;
119 };
120
121 GtkShadow *
122 _gtk_shadow_new (void)
123 {
124   GtkShadow *retval;
125
126   retval = g_slice_new0 (GtkShadow);
127   retval->ref_count = 1;
128   retval->resolved = FALSE;
129
130   return retval;
131 }
132
133 GtkShadow *
134 _gtk_shadow_ref (GtkShadow *shadow)
135 {
136   g_return_val_if_fail (shadow != NULL, NULL);
137
138   shadow->ref_count++;
139
140   return shadow;
141 }
142
143 gboolean
144 _gtk_shadow_get_resolved (GtkShadow *shadow)
145 {
146   return shadow->resolved;
147 }
148
149 void
150 _gtk_shadow_unref (GtkShadow *shadow)
151 {
152   g_return_if_fail (shadow != NULL);
153
154   shadow->ref_count--;
155
156   if (shadow->ref_count == 0)
157     {
158       g_list_free_full (shadow->elements,
159                         (GDestroyNotify) shadow_element_free);
160       g_slice_free (GtkShadow, shadow);
161     }
162 }
163
164 void
165 _gtk_shadow_append (GtkShadow        *shadow,
166                     gdouble           hoffset,
167                     gdouble           voffset,
168                     gdouble           radius,
169                     gdouble           spread,
170                     gboolean          inset,
171                     GtkSymbolicColor *color)
172 {
173   GtkShadowElement *element;
174
175   g_return_if_fail (shadow != NULL);
176   g_return_if_fail (color != NULL);
177
178   element = shadow_element_new (hoffset, voffset,
179                                 radius, spread, inset,
180                                 NULL, color);
181
182   shadow->elements = g_list_append (shadow->elements, element);
183 }
184
185 GtkShadow *
186 _gtk_shadow_resolve (GtkShadow       *shadow,
187                      GtkStyleContext *context)
188 {
189   GtkShadow *resolved_shadow;
190   GtkShadowElement *element, *resolved_element;
191   GdkRGBA color;
192   GList *l;
193
194   if (shadow->resolved)
195     return _gtk_shadow_ref (shadow);
196
197   resolved_shadow = _gtk_shadow_new ();
198
199   for (l = shadow->elements; l != NULL; l = l->next)
200     {
201       element = l->data;
202
203       if (!_gtk_style_context_resolve_color (context,
204                                              element->symbolic_color,
205                                              &color))
206         {
207           _gtk_shadow_unref (resolved_shadow);
208           return NULL;
209         }
210
211       resolved_element =
212         shadow_element_new (element->hoffset, element->voffset,
213                             element->radius, element->spread, element->inset,
214                             &color, NULL);
215
216       resolved_shadow->elements =
217         g_list_append (resolved_shadow->elements, resolved_element);
218     }
219
220   resolved_shadow->resolved = TRUE;
221
222   return resolved_shadow;
223 }
224
225 void
226 _gtk_shadow_print (GtkShadow *shadow,
227                    GString   *str)
228 {
229   gint length;
230   GList *l;
231
232   length = g_list_length (shadow->elements);
233
234   if (length == 0)
235     return;
236
237   shadow_element_print (shadow->elements->data, str);
238
239   if (length == 1)
240     return;
241
242   for (l = g_list_next (shadow->elements); l != NULL; l = l->next)
243     {
244       g_string_append (str, ", ");
245       shadow_element_print (l->data, str);
246     }
247 }
248
249 void
250 _gtk_text_shadow_paint_layout (GtkShadow       *shadow,
251                                cairo_t         *cr,
252                                PangoLayout     *layout)
253 {
254   GList *l;
255   GtkShadowElement *element;
256
257   if (!cairo_has_current_point (cr))
258     cairo_move_to (cr, 0, 0);
259
260   /* render shadows starting from the last one,
261    * and the others on top.
262    */
263   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
264     {
265       element = l->data;
266
267       cairo_save (cr);
268
269       cairo_rel_move_to (cr, element->hoffset, element->voffset);
270       gdk_cairo_set_source_rgba (cr, &element->color);
271       _gtk_pango_fill_layout (cr, layout);
272
273       cairo_rel_move_to (cr, -element->hoffset, -element->voffset);
274       cairo_restore (cr);
275   }
276 }
277
278 void
279 _gtk_icon_shadow_paint (GtkShadow *shadow,
280                         cairo_t *cr)
281 {
282   GList *l;
283   GtkShadowElement *element;
284   cairo_pattern_t *pattern;
285
286   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
287     {
288       element = l->data;
289
290       cairo_save (cr);
291       pattern = cairo_pattern_reference (cairo_get_source (cr));
292       gdk_cairo_set_source_rgba (cr, &element->color);
293
294       cairo_translate (cr, element->hoffset, element->voffset);
295       cairo_mask (cr, pattern);
296
297       cairo_restore (cr);
298       cairo_pattern_destroy (pattern);
299     }
300 }
301
302 void
303 _gtk_icon_shadow_paint_spinner (GtkShadow *shadow,
304                                 cairo_t   *cr,
305                                 gdouble    radius,
306                                 gdouble    progress)
307 {
308   GtkShadowElement *element;
309   GList *l;
310
311   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
312     {
313       element = l->data;
314
315       cairo_save (cr);
316
317       cairo_translate (cr, element->hoffset, element->voffset);
318       _gtk_theming_engine_paint_spinner (cr,
319                                          radius, progress,
320                                          &element->color);
321
322       cairo_restore (cr);
323     }
324 }
325
326 void
327 _gtk_box_shadow_render (GtkShadow           *shadow,
328                         cairo_t             *cr,
329                         const GtkRoundedBox *padding_box)
330 {
331   GtkShadowElement *element;
332   GtkRoundedBox box;
333   GList *l;
334
335   cairo_save (cr);
336   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
337
338   _gtk_rounded_box_path (padding_box, cr);
339   cairo_clip (cr);
340
341   /* render shadows starting from the last one,
342    * and the others on top.
343    */
344   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
345     {
346       element = l->data;
347
348       if (!element->inset)
349         continue;
350
351       box = *padding_box;
352       _gtk_rounded_box_move (&box, element->hoffset, element->voffset);
353       _gtk_rounded_box_shrink (&box,
354                                element->spread, element->spread,
355                                element->spread, element->spread);
356
357       _gtk_rounded_box_path (&box, cr);
358       _gtk_rounded_box_clip_path (padding_box, cr);
359
360       gdk_cairo_set_source_rgba (cr, &element->color);
361       cairo_fill (cr);
362   }
363
364   cairo_restore (cr);
365 }