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