]> Pileus Git - ~andy/gtk/blob - gtk/gtkthemingengine.c
Merge branch 'bgo687196-filesystemmodel-crash'
[~andy/gtk] / gtk / gtkthemingengine.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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 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
18 #include "config.h"
19
20 #include <math.h>
21 #include <gtk/gtk.h>
22
23 #include <gtk/gtkthemingengine.h>
24 #include <gtk/gtkstylecontext.h>
25 #include <gtk/gtkintl.h>
26
27 #include "gtkprivate.h"
28 #include "gtkmodulesprivate.h"
29 #include "gtkborderimageprivate.h"
30 #include "gtkpango.h"
31 #include "gtkcssarrayvalueprivate.h"
32 #include "gtkcsscornervalueprivate.h"
33 #include "gtkcssenumvalueprivate.h"
34 #include "gtkcssnumbervalueprivate.h"
35 #include "gtkcssrgbavalueprivate.h"
36 #include "gtkcssshadowsvalueprivate.h"
37 #include "gtkcsstypesprivate.h"
38 #include "gtkthemingengineprivate.h"
39 #include "gtkroundedboxprivate.h"
40 #include "gtkthemingbackgroundprivate.h"
41
42 #include "fallback-c89.c"
43
44 /**
45  * SECTION:gtkthemingengine
46  * @Short_description: Theming renderers
47  * @Title: GtkThemingEngine
48  * @See_also: #GtkStyleContext
49  *
50  * #GtkThemingEngine is the object used for rendering themed content
51  * in GTK+ widgets. Even though GTK+ has a default implementation,
52  * it can be overridden in CSS files by enforcing a #GtkThemingEngine
53  * object to be loaded as a module.
54  *
55  * In order to implement a theming engine, a #GtkThemingEngine subclass
56  * must be created, alongside the CSS file that will reference it, the
57  * theming engine would be created as an .so library, and installed in
58  * $(gtk-modules-dir)/theming-engines/.
59  *
60  * #GtkThemingEngine<!-- -->s have limited access to the object they are
61  * rendering, the #GtkThemingEngine API has read-only accessors to the
62  * style information contained in the rendered object's #GtkStyleContext.
63  */
64
65 enum {
66   PROP_0,
67   PROP_NAME
68 };
69
70 struct GtkThemingEnginePrivate
71 {
72   GtkStyleContext *context;
73   gchar *name;
74 };
75
76 #define GTK_THEMING_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_THEMING_ENGINE, GtkThemingEnginePrivate))
77
78 static void gtk_theming_engine_finalize          (GObject      *object);
79 static void gtk_theming_engine_impl_set_property (GObject      *object,
80                                                   guint         prop_id,
81                                                   const GValue *value,
82                                                   GParamSpec   *pspec);
83 static void gtk_theming_engine_impl_get_property (GObject      *object,
84                                                   guint         prop_id,
85                                                   GValue       *value,
86                                                   GParamSpec   *pspec);
87
88 static void gtk_theming_engine_render_check (GtkThemingEngine *engine,
89                                              cairo_t          *cr,
90                                              gdouble           x,
91                                              gdouble           y,
92                                              gdouble           width,
93                                              gdouble           height);
94 static void gtk_theming_engine_render_option (GtkThemingEngine *engine,
95                                               cairo_t          *cr,
96                                               gdouble           x,
97                                               gdouble           y,
98                                               gdouble           width,
99                                               gdouble           height);
100 static void gtk_theming_engine_render_arrow  (GtkThemingEngine *engine,
101                                               cairo_t          *cr,
102                                               gdouble           angle,
103                                               gdouble           x,
104                                               gdouble           y,
105                                               gdouble           size);
106 static void gtk_theming_engine_render_background (GtkThemingEngine *engine,
107                                                   cairo_t          *cr,
108                                                   gdouble           x,
109                                                   gdouble           y,
110                                                   gdouble           width,
111                                                   gdouble           height);
112 static void gtk_theming_engine_render_frame  (GtkThemingEngine *engine,
113                                               cairo_t          *cr,
114                                               gdouble           x,
115                                               gdouble           y,
116                                               gdouble           width,
117                                               gdouble           height);
118 static void gtk_theming_engine_render_expander (GtkThemingEngine *engine,
119                                                 cairo_t          *cr,
120                                                 gdouble           x,
121                                                 gdouble           y,
122                                                 gdouble           width,
123                                                 gdouble           height);
124 static void gtk_theming_engine_render_focus    (GtkThemingEngine *engine,
125                                                 cairo_t          *cr,
126                                                 gdouble           x,
127                                                 gdouble           y,
128                                                 gdouble           width,
129                                                 gdouble           height);
130 static void gtk_theming_engine_render_layout   (GtkThemingEngine *engine,
131                                                 cairo_t          *cr,
132                                                 gdouble           x,
133                                                 gdouble           y,
134                                                 PangoLayout      *layout);
135 static void gtk_theming_engine_render_line     (GtkThemingEngine *engine,
136                                                 cairo_t          *cr,
137                                                 gdouble           x0,
138                                                 gdouble           y0,
139                                                 gdouble           x1,
140                                                 gdouble           y1);
141 static void gtk_theming_engine_render_slider   (GtkThemingEngine *engine,
142                                                 cairo_t          *cr,
143                                                 gdouble           x,
144                                                 gdouble           y,
145                                                 gdouble           width,
146                                                 gdouble           height,
147                                                 GtkOrientation    orientation);
148 static void gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
149                                                  cairo_t          *cr,
150                                                  gdouble           x,
151                                                  gdouble           y,
152                                                  gdouble           width,
153                                                  gdouble           height,
154                                                  GtkPositionType   gap_side,
155                                                  gdouble           xy0_gap,
156                                                  gdouble           xy1_gap);
157 static void gtk_theming_engine_render_extension (GtkThemingEngine *engine,
158                                                  cairo_t          *cr,
159                                                  gdouble           x,
160                                                  gdouble           y,
161                                                  gdouble           width,
162                                                  gdouble           height,
163                                                  GtkPositionType   gap_side);
164 static void gtk_theming_engine_render_handle    (GtkThemingEngine *engine,
165                                                  cairo_t          *cr,
166                                                  gdouble           x,
167                                                  gdouble           y,
168                                                  gdouble           width,
169                                                  gdouble           height);
170 static void gtk_theming_engine_render_activity  (GtkThemingEngine *engine,
171                                                  cairo_t          *cr,
172                                                  gdouble           x,
173                                                  gdouble           y,
174                                                  gdouble           width,
175                                                  gdouble           height);
176 static GdkPixbuf * gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
177                                                           const GtkIconSource *source,
178                                                           GtkIconSize          size);
179 static void gtk_theming_engine_render_icon (GtkThemingEngine *engine,
180                                             cairo_t *cr,
181                                             GdkPixbuf *pixbuf,
182                                             gdouble x,
183                                             gdouble y);
184
185 G_DEFINE_TYPE (GtkThemingEngine, gtk_theming_engine, G_TYPE_OBJECT)
186
187
188 typedef struct GtkThemingModule GtkThemingModule;
189 typedef struct GtkThemingModuleClass GtkThemingModuleClass;
190
191 struct GtkThemingModule
192 {
193   GTypeModule parent_instance;
194   GModule *module;
195   gchar *name;
196
197   void (*init) (GTypeModule *module);
198   void (*exit) (void);
199   GtkThemingEngine * (*create_engine) (void);
200 };
201
202 struct GtkThemingModuleClass
203 {
204   GTypeModuleClass parent_class;
205 };
206
207 #define GTK_TYPE_THEMING_MODULE  (gtk_theming_module_get_type ())
208 #define GTK_THEMING_MODULE(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_THEMING_MODULE, GtkThemingModule))
209 #define GTK_IS_THEMING_MODULE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_THEMING_MODULE))
210
211 GType gtk_theming_module_get_type (void);
212
213 G_DEFINE_TYPE (GtkThemingModule, gtk_theming_module, G_TYPE_TYPE_MODULE);
214
215 static void
216 gtk_theming_engine_class_init (GtkThemingEngineClass *klass)
217 {
218   GObjectClass *object_class = G_OBJECT_CLASS (klass);
219
220   object_class->finalize = gtk_theming_engine_finalize;
221   object_class->set_property = gtk_theming_engine_impl_set_property;
222   object_class->get_property = gtk_theming_engine_impl_get_property;
223
224   klass->render_icon = gtk_theming_engine_render_icon;
225   klass->render_check = gtk_theming_engine_render_check;
226   klass->render_option = gtk_theming_engine_render_option;
227   klass->render_arrow = gtk_theming_engine_render_arrow;
228   klass->render_background = gtk_theming_engine_render_background;
229   klass->render_frame = gtk_theming_engine_render_frame;
230   klass->render_expander = gtk_theming_engine_render_expander;
231   klass->render_focus = gtk_theming_engine_render_focus;
232   klass->render_layout = gtk_theming_engine_render_layout;
233   klass->render_line = gtk_theming_engine_render_line;
234   klass->render_slider = gtk_theming_engine_render_slider;
235   klass->render_frame_gap = gtk_theming_engine_render_frame_gap;
236   klass->render_extension = gtk_theming_engine_render_extension;
237   klass->render_handle = gtk_theming_engine_render_handle;
238   klass->render_activity = gtk_theming_engine_render_activity;
239   klass->render_icon_pixbuf = gtk_theming_engine_render_icon_pixbuf;
240
241   /**
242    * GtkThemingEngine:name:
243    *
244    * The theming engine name, this name will be used when registering
245    * custom properties, for a theming engine named "Clearlooks" registering
246    * a "glossy" custom property, it could be referenced in the CSS file as
247    *
248    * <programlisting>
249    * -Clearlooks-glossy: true;
250    * </programlisting>
251    *
252    * Since: 3.0
253    */
254   g_object_class_install_property (object_class,
255                                    PROP_NAME,
256                                    g_param_spec_string ("name",
257                                                         P_("Name"),
258                                                         P_("Theming engine name"),
259                                                         NULL,
260                                                         G_PARAM_CONSTRUCT_ONLY | GTK_PARAM_READWRITE));
261
262   g_type_class_add_private (object_class, sizeof (GtkThemingEnginePrivate));
263 }
264
265 static void
266 gtk_theming_engine_init (GtkThemingEngine *engine)
267 {
268   engine->priv = GTK_THEMING_ENGINE_GET_PRIVATE (engine);
269 }
270
271 static void
272 gtk_theming_engine_finalize (GObject *object)
273 {
274   GtkThemingEnginePrivate *priv;
275
276   priv = GTK_THEMING_ENGINE (object)->priv;
277   g_free (priv->name);
278
279   G_OBJECT_GET_CLASS (gtk_theming_engine_parent_class)->finalize (object);
280 }
281
282 static void
283 gtk_theming_engine_impl_set_property (GObject      *object,
284                                       guint         prop_id,
285                                       const GValue *value,
286                                       GParamSpec   *pspec)
287 {
288   GtkThemingEnginePrivate *priv;
289
290   priv = GTK_THEMING_ENGINE (object)->priv;
291
292   switch (prop_id)
293     {
294     case PROP_NAME:
295       if (priv->name)
296         g_free (priv->name);
297
298       priv->name = g_value_dup_string (value);
299       break;
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302       break;
303     }
304 }
305
306 static void
307 gtk_theming_engine_impl_get_property (GObject    *object,
308                                       guint       prop_id,
309                                       GValue     *value,
310                                       GParamSpec *pspec)
311 {
312   GtkThemingEnginePrivate *priv;
313
314   priv = GTK_THEMING_ENGINE (object)->priv;
315
316   switch (prop_id)
317     {
318     case PROP_NAME:
319       g_value_set_string (value, priv->name);
320       break;
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
323       break;
324     }
325 }
326
327 void
328 _gtk_theming_engine_set_context (GtkThemingEngine *engine,
329                                  GtkStyleContext  *context)
330 {
331   GtkThemingEnginePrivate *priv;
332
333   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
334   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
335
336   priv = engine->priv;
337   priv->context = context;
338 }
339
340 GtkStyleContext *
341 _gtk_theming_engine_get_context (GtkThemingEngine *engine)
342 {
343   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
344
345   return engine->priv->context;
346 }
347
348 GtkCssValue *
349 _gtk_theming_engine_peek_property (GtkThemingEngine *engine,
350                                    guint             property_id)
351 {
352   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
353
354   return _gtk_style_context_peek_property (engine->priv->context, property_id);
355 }
356
357 double
358 _gtk_theming_engine_get_number (GtkThemingEngine *engine,
359                                 guint             property_id,
360                                 double            one_hundred_percent)
361 {
362   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0.0);
363
364   return _gtk_style_context_get_number (engine->priv->context, property_id, one_hundred_percent);
365 }
366
367 /**
368  * gtk_theming_engine_get_property:
369  * @engine: a #GtkThemingEngine
370  * @property: the property name
371  * @state: state to retrieve the value for
372  * @value: (out) (transfer full): return location for the property value,
373  *         you must free this memory using g_value_unset() once you are
374  *         done with it.
375  *
376  * Gets a property value as retrieved from the style settings that apply
377  * to the currently rendered element.
378  *
379  * Since: 3.0
380  **/
381 void
382 gtk_theming_engine_get_property (GtkThemingEngine *engine,
383                                  const gchar      *property,
384                                  GtkStateFlags     state,
385                                  GValue           *value)
386 {
387   GtkThemingEnginePrivate *priv;
388
389   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
390   g_return_if_fail (property != NULL);
391   g_return_if_fail (value != NULL);
392
393   priv = engine->priv;
394   gtk_style_context_get_property (priv->context, property, state, value);
395 }
396
397 /**
398  * gtk_theming_engine_get_valist:
399  * @engine: a #GtkThemingEngine
400  * @state: state to retrieve values for
401  * @args: va_list of property name/return location pairs, followed by %NULL
402  *
403  * Retrieves several style property values that apply to the currently
404  * rendered element.
405  *
406  * Since: 3.0
407  **/
408 void
409 gtk_theming_engine_get_valist (GtkThemingEngine *engine,
410                                GtkStateFlags     state,
411                                va_list           args)
412 {
413   GtkThemingEnginePrivate *priv;
414
415   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
416
417   priv = engine->priv;
418   gtk_style_context_get_valist (priv->context, state, args);
419 }
420
421 /**
422  * gtk_theming_engine_get:
423  * @engine: a #GtkThemingEngine
424  * @state: state to retrieve values for
425  * @...: property name /return value pairs, followed by %NULL
426  *
427  * Retrieves several style property values that apply to the currently
428  * rendered element.
429  *
430  * Since: 3.0
431  **/
432 void
433 gtk_theming_engine_get (GtkThemingEngine *engine,
434                         GtkStateFlags     state,
435                         ...)
436 {
437   GtkThemingEnginePrivate *priv;
438   va_list args;
439
440   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
441
442   priv = engine->priv;
443
444   va_start (args, state);
445   gtk_style_context_get_valist (priv->context, state, args);
446   va_end (args);
447 }
448
449 /**
450  * gtk_theming_engine_get_style_property:
451  * @engine: a #GtkThemingEngine
452  * @property_name: the name of the widget style property
453  * @value: Return location for the property value, free with
454  *         g_value_unset() after use.
455  *
456  * Gets the value for a widget style property.
457  *
458  * Since: 3.0
459  **/
460 void
461 gtk_theming_engine_get_style_property (GtkThemingEngine *engine,
462                                        const gchar      *property_name,
463                                        GValue           *value)
464 {
465   GtkThemingEnginePrivate *priv;
466
467   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
468   g_return_if_fail (property_name != NULL);
469
470   priv = engine->priv;
471   gtk_style_context_get_style_property (priv->context, property_name, value);
472 }
473
474 /**
475  * gtk_theming_engine_get_style_valist:
476  * @engine: a #GtkThemingEngine
477  * @args: va_list of property name/return location pairs, followed by %NULL
478  *
479  * Retrieves several widget style properties from @engine according to the
480  * currently rendered content's style.
481  *
482  * Since: 3.0
483  **/
484 void
485 gtk_theming_engine_get_style_valist (GtkThemingEngine *engine,
486                                      va_list           args)
487 {
488   GtkThemingEnginePrivate *priv;
489
490   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
491
492   priv = engine->priv;
493   gtk_style_context_get_style_valist (priv->context, args);
494 }
495
496 /**
497  * gtk_theming_engine_get_style:
498  * @engine: a #GtkThemingEngine
499  * @...: property name /return value pairs, followed by %NULL
500  *
501  * Retrieves several widget style properties from @engine according
502  * to the currently rendered content's style.
503  *
504  * Since: 3.0
505  **/
506 void
507 gtk_theming_engine_get_style (GtkThemingEngine *engine,
508                               ...)
509 {
510   GtkThemingEnginePrivate *priv;
511   va_list args;
512
513   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
514
515   priv = engine->priv;
516
517   va_start (args, engine);
518   gtk_style_context_get_style_valist (priv->context, args);
519   va_end (args);
520 }
521
522 /**
523  * gtk_theming_engine_lookup_color:
524  * @engine: a #GtkThemingEngine
525  * @color_name: color name to lookup
526  * @color: (out): Return location for the looked up color
527  *
528  * Looks up and resolves a color name in the current style's color map.
529  *
530  * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
531  **/
532 gboolean
533 gtk_theming_engine_lookup_color (GtkThemingEngine *engine,
534                                  const gchar      *color_name,
535                                  GdkRGBA          *color)
536 {
537   GtkThemingEnginePrivate *priv;
538
539   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
540   g_return_val_if_fail (color_name != NULL, FALSE);
541
542   priv = engine->priv;
543   return gtk_style_context_lookup_color (priv->context, color_name, color);
544 }
545
546 /**
547  * gtk_theming_engine_get_state:
548  * @engine: a #GtkThemingEngine
549  *
550  * returns the state used when rendering.
551  *
552  * Returns: the state flags
553  *
554  * Since: 3.0
555  **/
556 GtkStateFlags
557 gtk_theming_engine_get_state (GtkThemingEngine *engine)
558 {
559   GtkThemingEnginePrivate *priv;
560
561   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
562
563   priv = engine->priv;
564   return gtk_style_context_get_state (priv->context);
565 }
566
567 /**
568  * gtk_theming_engine_state_is_running:
569  * @engine: a #GtkThemingEngine
570  * @state: a widget state
571  * @progress: (out): return location for the transition progress
572  *
573  * Returns %TRUE if there is a transition animation running for the
574  * current region (see gtk_style_context_push_animatable_region()).
575  *
576  * If @progress is not %NULL, the animation progress will be returned
577  * there, 0.0 means the state is closest to being %FALSE, while 1.0 means
578  * it's closest to being %TRUE. This means transition animations will
579  * run from 0 to 1 when @state is being set to %TRUE and from 1 to 0 when
580  * it's being set to %FALSE.
581  *
582  * Returns: %TRUE if there is a running transition animation for @state.
583  *
584  * Since: 3.0
585  *
586  * Deprecated: 3.6: Always returns %FALSE
587  **/
588 gboolean
589 gtk_theming_engine_state_is_running (GtkThemingEngine *engine,
590                                      GtkStateType      state,
591                                      gdouble          *progress)
592 {
593   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
594
595   return FALSE;
596 }
597
598 /**
599  * gtk_theming_engine_get_path:
600  * @engine: a #GtkThemingEngine
601  *
602  * Returns the widget path used for style matching.
603  *
604  * Returns: (transfer none): A #GtkWidgetPath
605  *
606  * Since: 3.0
607  **/
608 const GtkWidgetPath *
609 gtk_theming_engine_get_path (GtkThemingEngine *engine)
610 {
611   GtkThemingEnginePrivate *priv;
612
613   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
614
615   priv = engine->priv;
616   return gtk_style_context_get_path (priv->context);
617 }
618
619 /**
620  * gtk_theming_engine_has_class:
621  * @engine: a #GtkThemingEngine
622  * @style_class: class name to look up
623  *
624  * Returns %TRUE if the currently rendered contents have
625  * defined the given class name.
626  *
627  * Returns: %TRUE if @engine has @class_name defined
628  *
629  * Since: 3.0
630  **/
631 gboolean
632 gtk_theming_engine_has_class (GtkThemingEngine *engine,
633                               const gchar      *style_class)
634 {
635   GtkThemingEnginePrivate *priv;
636
637   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
638
639   priv = engine->priv;
640   return gtk_style_context_has_class (priv->context, style_class);
641 }
642
643 /**
644  * gtk_theming_engine_has_region:
645  * @engine: a #GtkThemingEngine
646  * @style_region: a region name
647  * @flags: (out) (allow-none): return location for region flags
648  *
649  * Returns %TRUE if the currently rendered contents have the
650  * region defined. If @flags_return is not %NULL, it is set
651  * to the flags affecting the region.
652  *
653  * Returns: %TRUE if region is defined
654  *
655  * Since: 3.0
656  **/
657 gboolean
658 gtk_theming_engine_has_region (GtkThemingEngine *engine,
659                                const gchar      *style_region,
660                                GtkRegionFlags   *flags)
661 {
662   GtkThemingEnginePrivate *priv;
663
664   if (flags)
665     *flags = 0;
666
667   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
668
669   priv = engine->priv;
670   return gtk_style_context_has_region (priv->context, style_region, flags);
671 }
672
673 /**
674  * gtk_theming_engine_get_direction:
675  * @engine: a #GtkThemingEngine
676  *
677  * Returns the widget direction used for rendering.
678  *
679  * Returns: the widget direction
680  *
681  * Since: 3.0
682  **/
683 GtkTextDirection
684 gtk_theming_engine_get_direction (GtkThemingEngine *engine)
685 {
686   GtkThemingEnginePrivate *priv;
687
688   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), GTK_TEXT_DIR_LTR);
689
690   priv = engine->priv;
691   return gtk_style_context_get_direction (priv->context);
692 }
693
694 /**
695  * gtk_theming_engine_get_junction_sides:
696  * @engine: a #GtkThemingEngine
697  *
698  * Returns the widget direction used for rendering.
699  *
700  * Returns: the widget direction
701  *
702  * Since: 3.0
703  **/
704 GtkJunctionSides
705 gtk_theming_engine_get_junction_sides (GtkThemingEngine *engine)
706 {
707   GtkThemingEnginePrivate *priv;
708
709   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
710
711   priv = engine->priv;
712   return gtk_style_context_get_junction_sides (priv->context);
713 }
714
715 /**
716  * gtk_theming_engine_get_color:
717  * @engine: a #GtkThemingEngine
718  * @state: state to retrieve the color for
719  * @color: (out): return value for the foreground color
720  *
721  * Gets the foreground color for a given state.
722  *
723  * Since: 3.0
724  **/
725 void
726 gtk_theming_engine_get_color (GtkThemingEngine *engine,
727                               GtkStateFlags     state,
728                               GdkRGBA          *color)
729 {
730   GtkThemingEnginePrivate *priv;
731
732   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
733
734   priv = engine->priv;
735   gtk_style_context_get_color (priv->context, state, color);
736 }
737
738 /**
739  * gtk_theming_engine_get_background_color:
740  * @engine: a #GtkThemingEngine
741  * @state: state to retrieve the color for
742  * @color: (out): return value for the background color
743  *
744  * Gets the background color for a given state.
745  *
746  * Since: 3.0
747  **/
748 void
749 gtk_theming_engine_get_background_color (GtkThemingEngine *engine,
750                                          GtkStateFlags     state,
751                                          GdkRGBA          *color)
752 {
753   GtkThemingEnginePrivate *priv;
754
755   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
756
757   priv = engine->priv;
758   gtk_style_context_get_background_color (priv->context, state, color);
759 }
760
761 /**
762  * gtk_theming_engine_get_border_color:
763  * @engine: a #GtkThemingEngine
764  * @state: state to retrieve the color for
765  * @color: (out): return value for the border color
766  *
767  * Gets the border color for a given state.
768  *
769  * Since: 3.0
770  **/
771 void
772 gtk_theming_engine_get_border_color (GtkThemingEngine *engine,
773                                      GtkStateFlags     state,
774                                      GdkRGBA          *color)
775 {
776   GtkThemingEnginePrivate *priv;
777
778   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
779
780   priv = engine->priv;
781   gtk_style_context_get_border_color (priv->context, state, color);
782 }
783
784 /**
785  * gtk_theming_engine_get_border:
786  * @engine: a #GtkThemingEngine
787  * @state: state to retrieve the border for
788  * @border: (out): return value for the border settings
789  *
790  * Gets the border for a given state as a #GtkBorder.
791  *
792  * Since: 3.0
793  **/
794 void
795 gtk_theming_engine_get_border (GtkThemingEngine *engine,
796                                GtkStateFlags     state,
797                                GtkBorder        *border)
798 {
799   GtkThemingEnginePrivate *priv;
800
801   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
802
803   priv = engine->priv;
804   gtk_style_context_get_border (priv->context, state, border);
805 }
806
807 /**
808  * gtk_theming_engine_get_padding:
809  * @engine: a #GtkThemingEngine
810  * @state: state to retrieve the padding for
811  * @padding: (out): return value for the padding settings
812  *
813  * Gets the padding for a given state as a #GtkBorder.
814  *
815  * Since: 3.0
816  **/
817 void
818 gtk_theming_engine_get_padding (GtkThemingEngine *engine,
819                                 GtkStateFlags     state,
820                                 GtkBorder        *padding)
821 {
822   GtkThemingEnginePrivate *priv;
823
824   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
825
826   priv = engine->priv;
827   gtk_style_context_get_padding (priv->context, state, padding);
828 }
829
830 /**
831  * gtk_theming_engine_get_margin:
832  * @engine: a #GtkThemingEngine
833  * @state: state to retrieve the border for
834  * @margin: (out): return value for the margin settings
835  *
836  * Gets the margin for a given state as a #GtkBorder.
837  *
838  * Since: 3.0
839  **/
840 void
841 gtk_theming_engine_get_margin (GtkThemingEngine *engine,
842                                GtkStateFlags     state,
843                                GtkBorder        *margin)
844 {
845   GtkThemingEnginePrivate *priv;
846
847   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
848
849   priv = engine->priv;
850   gtk_style_context_get_margin (priv->context, state, margin);
851 }
852
853 /**
854  * gtk_theming_engine_get_font:
855  * @engine: a #GtkThemingEngine
856  * @state: state to retrieve the font for
857  *
858  * Returns the font description for a given state.
859  *
860  * Returns: (transfer none): the #PangoFontDescription for the given
861  *          state. This object is owned by GTK+ and should not be
862  *          freed.
863  *
864  * Since: 3.0
865  **/
866 const PangoFontDescription *
867 gtk_theming_engine_get_font (GtkThemingEngine *engine,
868                              GtkStateFlags     state)
869 {
870   GtkThemingEnginePrivate *priv;
871
872   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
873
874   priv = engine->priv;
875   return gtk_style_context_get_font (priv->context, state);
876 }
877
878 /* GtkThemingModule */
879
880 static gboolean
881 gtk_theming_module_load (GTypeModule *type_module)
882 {
883   GtkThemingModule *theming_module;
884   GModule *module;
885   gchar *name, *module_path;
886
887   theming_module = GTK_THEMING_MODULE (type_module);
888   name = theming_module->name;
889   module_path = _gtk_find_module (name, "theming-engines");
890
891   if (!module_path)
892     return FALSE;
893
894   module = g_module_open (module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
895   g_free (module_path);
896
897   if (!module)
898     return FALSE;
899
900   if (!g_module_symbol (module, "theme_init",
901                         (gpointer *) &theming_module->init) ||
902       !g_module_symbol (module, "theme_exit",
903                         (gpointer *) &theming_module->exit) ||
904       !g_module_symbol (module, "create_engine",
905                         (gpointer *) &theming_module->create_engine))
906     {
907       g_module_close (module);
908
909       return FALSE;
910     }
911
912   theming_module->module = module;
913
914   theming_module->init (G_TYPE_MODULE (theming_module));
915
916   return TRUE;
917 }
918
919 static void
920 gtk_theming_module_unload (GTypeModule *type_module)
921 {
922   GtkThemingModule *theming_module;
923
924   theming_module = GTK_THEMING_MODULE (type_module);
925
926   theming_module->exit ();
927
928   g_module_close (theming_module->module);
929
930   theming_module->module = NULL;
931   theming_module->init = NULL;
932   theming_module->exit = NULL;
933   theming_module->create_engine = NULL;
934 }
935
936 static void
937 gtk_theming_module_class_init (GtkThemingModuleClass *klass)
938 {
939   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
940
941   module_class->load = gtk_theming_module_load;
942   module_class->unload = gtk_theming_module_unload;
943 }
944
945 static void
946 gtk_theming_module_init (GtkThemingModule *module)
947 {
948 }
949
950 /**
951  * gtk_theming_engine_load:
952  * @name: Theme engine name to load
953  *
954  * Loads and initializes a theming engine module from the
955  * standard directories.
956  *
957  * Returns: (transfer none): A theming engine, or %NULL if
958  * the engine @name doesn't exist.
959  **/
960 GtkThemingEngine *
961 gtk_theming_engine_load (const gchar *name)
962 {
963   static GHashTable *engines = NULL;
964   static GtkThemingEngine *default_engine;
965   GtkThemingEngine *engine = NULL;
966
967   if (name)
968     {
969       if (!engines)
970         engines = g_hash_table_new (g_str_hash, g_str_equal);
971
972       engine = g_hash_table_lookup (engines, name);
973
974       if (!engine)
975         {
976           GtkThemingModule *module;
977
978           module = g_object_new (GTK_TYPE_THEMING_MODULE, NULL);
979           g_type_module_set_name (G_TYPE_MODULE (module), name);
980           module->name = g_strdup (name);
981
982           if (module && g_type_module_use (G_TYPE_MODULE (module)))
983             {
984               engine = (module->create_engine) ();
985
986               if (engine)
987                 g_hash_table_insert (engines, module->name, engine);
988             }
989         }
990     }
991   else
992     {
993       if (G_UNLIKELY (!default_engine))
994         default_engine = g_object_new (GTK_TYPE_THEMING_ENGINE, NULL);
995
996       engine = default_engine;
997     }
998
999   return engine;
1000 }
1001
1002 /**
1003  * gtk_theming_engine_get_screen:
1004  * @engine: a #GtkThemingEngine
1005  *
1006  * Returns the #GdkScreen to which @engine currently rendering to.
1007  *
1008  * Returns: (transfer none): a #GdkScreen, or %NULL.
1009  **/
1010 GdkScreen *
1011 gtk_theming_engine_get_screen (GtkThemingEngine *engine)
1012 {
1013   GtkThemingEnginePrivate *priv;
1014
1015   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
1016
1017   priv = engine->priv;
1018   return gtk_style_context_get_screen (priv->context);
1019 }
1020
1021 /* Paint method implementations */
1022 static void
1023 gtk_theming_engine_render_check (GtkThemingEngine *engine,
1024                                  cairo_t          *cr,
1025                                  gdouble           x,
1026                                  gdouble           y,
1027                                  gdouble           width,
1028                                  gdouble           height)
1029 {
1030   GdkRGBA fg_color, bg_color;
1031   GtkStateFlags flags;
1032   gint exterior_size, interior_size, thickness, pad;
1033   GtkBorderStyle border_style;
1034   GtkBorder border;
1035   gint border_width;
1036   GtkThemingBackground bg;
1037
1038   _gtk_theming_background_init (&bg, engine, 
1039                                 x, y,
1040                                 width, height,
1041                                 gtk_theming_engine_get_junction_sides (engine));
1042
1043   if (_gtk_theming_background_has_background_image (&bg))
1044     {
1045       _gtk_theming_background_render (&bg, cr);
1046       return;
1047     }
1048
1049   flags = gtk_theming_engine_get_state (engine);
1050   cairo_save (cr);
1051
1052   gtk_theming_engine_get_color (engine, flags, &fg_color);
1053   gtk_theming_engine_get_background_color (engine, flags, &bg_color);
1054   gtk_theming_engine_get_border (engine, flags, &border);
1055   border_style = _gtk_css_border_style_value_get 
1056     (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
1057
1058   border_width = MIN (MIN (border.top, border.bottom),
1059                       MIN (border.left, border.right));
1060   exterior_size = MIN (width, height);
1061
1062   if (exterior_size % 2 == 0) /* Ensure odd */
1063     exterior_size -= 1;
1064
1065   /* FIXME: thickness */
1066   thickness = 1;
1067   pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
1068   interior_size = MAX (1, exterior_size - 2 * pad);
1069
1070   if (interior_size < 7)
1071     {
1072       interior_size = 7;
1073       pad = MAX (0, (exterior_size - interior_size) / 2);
1074     }
1075
1076   x -= (1 + exterior_size - (gint) width) / 2;
1077   y -= (1 + exterior_size - (gint) height) / 2;
1078
1079   if (border_style == GTK_BORDER_STYLE_SOLID)
1080     {
1081       GdkRGBA border_color;
1082
1083       cairo_set_line_width (cr, border_width);
1084       gtk_theming_engine_get_border_color (engine, flags, &border_color);
1085
1086       cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
1087       gdk_cairo_set_source_rgba (cr, &bg_color);
1088       cairo_fill_preserve (cr);
1089
1090       gdk_cairo_set_source_rgba (cr, &border_color);
1091       cairo_stroke (cr);
1092     }
1093
1094   gdk_cairo_set_source_rgba (cr, &fg_color);
1095
1096   if (flags & GTK_STATE_FLAG_INCONSISTENT)
1097     {
1098       int line_thickness = MAX (1, (3 + interior_size * 2) / 7);
1099
1100       cairo_rectangle (cr,
1101                        x + pad,
1102                        y + pad + (1 + interior_size - line_thickness) / 2,
1103                        interior_size,
1104                        line_thickness);
1105       cairo_fill (cr);
1106     }
1107   else
1108     {
1109       if (flags & GTK_STATE_FLAG_ACTIVE)
1110         {
1111           cairo_translate (cr,
1112                            x + pad, y + pad);
1113
1114           cairo_scale (cr, interior_size / 7., interior_size / 7.);
1115
1116           cairo_rectangle (cr, 0, 0, 7, 7);
1117           cairo_clip (cr);
1118
1119           cairo_move_to  (cr, 7.0, 0.0);
1120           cairo_line_to  (cr, 7.5, 1.0);
1121           cairo_curve_to (cr, 5.3, 2.0,
1122                           4.3, 4.0,
1123                           3.5, 7.0);
1124           cairo_curve_to (cr, 3.0, 5.7,
1125                           1.3, 4.7,
1126                           0.0, 4.7);
1127           cairo_line_to  (cr, 0.2, 3.5);
1128           cairo_curve_to (cr, 1.1, 3.5,
1129                           2.3, 4.3,
1130                           3.0, 5.0);
1131           cairo_curve_to (cr, 1.0, 3.9,
1132                           2.4, 4.1,
1133                           3.2, 4.9);
1134           cairo_curve_to (cr, 3.5, 3.1,
1135                           5.2, 2.0,
1136                           7.0, 0.0);
1137
1138           cairo_fill (cr);
1139         }
1140     }
1141
1142   cairo_restore (cr);
1143 }
1144
1145 static void
1146 gtk_theming_engine_render_option (GtkThemingEngine *engine,
1147                                   cairo_t          *cr,
1148                                   gdouble           x,
1149                                   gdouble           y,
1150                                   gdouble           width,
1151                                   gdouble           height)
1152 {
1153   GtkStateFlags flags;
1154   GdkRGBA fg_color, bg_color;
1155   gint exterior_size, interior_size, pad, thickness, border_width;
1156   GtkBorderStyle border_style;
1157   GtkBorder border;
1158   GtkThemingBackground bg;
1159
1160   _gtk_theming_background_init (&bg, engine, 
1161                                 x, y,
1162                                 width, height,
1163                                 gtk_theming_engine_get_junction_sides (engine));
1164
1165   if (_gtk_theming_background_has_background_image (&bg))
1166     {
1167       _gtk_theming_background_render (&bg, cr);
1168       return;
1169     }
1170
1171   flags = gtk_theming_engine_get_state (engine);
1172
1173   cairo_save (cr);
1174
1175   gtk_theming_engine_get_color (engine, flags, &fg_color);
1176   gtk_theming_engine_get_background_color (engine, flags, &bg_color);
1177   gtk_theming_engine_get_border (engine, flags, &border);
1178   border_style = _gtk_css_border_style_value_get 
1179     (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
1180
1181   exterior_size = MIN (width, height);
1182   border_width = MIN (MIN (border.top, border.bottom),
1183                       MIN (border.left, border.right));
1184
1185   if (exterior_size % 2 == 0) /* Ensure odd */
1186     exterior_size -= 1;
1187
1188   x -= (1 + exterior_size - width) / 2;
1189   y -= (1 + exterior_size - height) / 2;
1190
1191   if (border_style == GTK_BORDER_STYLE_SOLID)
1192     {
1193       GdkRGBA border_color;
1194
1195       cairo_set_line_width (cr, border_width);
1196       gtk_theming_engine_get_border_color (engine, flags, &border_color);
1197
1198       cairo_new_sub_path (cr);
1199       cairo_arc (cr,
1200                  x + exterior_size / 2.,
1201                  y + exterior_size / 2.,
1202                  (exterior_size - 1) / 2.,
1203                  0, 2 * G_PI);
1204
1205       gdk_cairo_set_source_rgba (cr, &bg_color);
1206       cairo_fill_preserve (cr);
1207
1208       gdk_cairo_set_source_rgba (cr, &border_color);
1209       cairo_stroke (cr);
1210     }
1211
1212   gdk_cairo_set_source_rgba (cr, &fg_color);
1213
1214   /* FIXME: thickness */
1215   thickness = 1;
1216
1217   if (flags & GTK_STATE_FLAG_INCONSISTENT)
1218     {
1219       gint line_thickness;
1220
1221       pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
1222       interior_size = MAX (1, exterior_size - 2 * pad);
1223
1224       if (interior_size < 7)
1225         {
1226           interior_size = 7;
1227           pad = MAX (0, (exterior_size - interior_size) / 2);
1228         }
1229
1230       line_thickness = MAX (1, (3 + interior_size * 2) / 7);
1231
1232       cairo_rectangle (cr,
1233                        x + pad,
1234                        y + pad + (interior_size - line_thickness) / 2.,
1235                        interior_size,
1236                        line_thickness);
1237       cairo_fill (cr);
1238     }
1239   if (flags & GTK_STATE_FLAG_ACTIVE)
1240     {
1241       pad = thickness + MAX (1, 2 * (exterior_size - 2 * thickness) / 9);
1242       interior_size = MAX (1, exterior_size - 2 * pad);
1243
1244       if (interior_size < 5)
1245         {
1246           interior_size = 7;
1247           pad = MAX (0, (exterior_size - interior_size) / 2);
1248         }
1249
1250       cairo_new_sub_path (cr);
1251       cairo_arc (cr,
1252                  x + pad + interior_size / 2.,
1253                  y + pad + interior_size / 2.,
1254                  interior_size / 2.,
1255                  0, 2 * G_PI);
1256       cairo_fill (cr);
1257     }
1258
1259   cairo_restore (cr);
1260 }
1261
1262 static void
1263 add_path_arrow (cairo_t *cr,
1264                 gdouble  angle,
1265                 gdouble  x,
1266                 gdouble  y,
1267                 gdouble  size)
1268 {
1269   cairo_save (cr);
1270
1271   cairo_translate (cr, x + (size / 2), y + (size / 2));
1272   cairo_rotate (cr, angle);
1273
1274   cairo_move_to (cr, 0, - (size / 4));
1275   cairo_line_to (cr, - (size / 2), (size / 4));
1276   cairo_line_to (cr, (size / 2), (size / 4));
1277   cairo_close_path (cr);
1278
1279   cairo_restore (cr);
1280 }
1281
1282 static void
1283 gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
1284                                  cairo_t          *cr,
1285                                  gdouble           angle,
1286                                  gdouble           x,
1287                                  gdouble           y,
1288                                  gdouble           size)
1289 {
1290   GtkStateFlags flags;
1291   GdkRGBA fg_color;
1292
1293   cairo_save (cr);
1294
1295   flags = gtk_theming_engine_get_state (engine);
1296   gtk_theming_engine_get_color (engine, flags, &fg_color);
1297
1298   if (flags & GTK_STATE_FLAG_INSENSITIVE)
1299     {
1300       add_path_arrow (cr, angle, x + 1, y + 1, size);
1301       cairo_set_source_rgb (cr, 1, 1, 1);
1302       cairo_fill (cr);
1303     }
1304
1305   add_path_arrow (cr, angle, x, y, size);
1306   gdk_cairo_set_source_rgba (cr, &fg_color);
1307   cairo_fill (cr);
1308
1309   cairo_restore (cr);
1310 }
1311
1312 static void
1313 add_path_line (cairo_t        *cr,
1314                gdouble         x1,
1315                gdouble         y1,
1316                gdouble         x2,
1317                gdouble         y2)
1318 {
1319   /* Adjust endpoints */
1320   if (y1 == y2)
1321     {
1322       y1 += 0.5;
1323       y2 += 0.5;
1324       x2 += 1;
1325     }
1326   else if (x1 == x2)
1327     {
1328       x1 += 0.5;
1329       x2 += 0.5;
1330       y2 += 1;
1331     }
1332
1333   cairo_move_to (cr, x1, y1);
1334   cairo_line_to (cr, x2, y2);
1335 }
1336
1337 static void
1338 color_shade (const GdkRGBA *color,
1339              gdouble        factor,
1340              GdkRGBA       *color_return)
1341 {
1342   GtkSymbolicColor *literal, *shade;
1343
1344   literal = gtk_symbolic_color_new_literal (color);
1345   shade = gtk_symbolic_color_new_shade (literal, factor);
1346   gtk_symbolic_color_unref (literal);
1347
1348   gtk_symbolic_color_resolve (shade, NULL, color_return);
1349   gtk_symbolic_color_unref (shade);
1350 }
1351
1352 static void
1353 gtk_theming_engine_render_background (GtkThemingEngine *engine,
1354                                       cairo_t          *cr,
1355                                       gdouble           x,
1356                                       gdouble           y,
1357                                       gdouble           width,
1358                                       gdouble           height)
1359 {
1360   GtkThemingBackground bg;
1361
1362   _gtk_theming_background_init (&bg, engine,
1363                                 x, y,
1364                                 width, height,
1365                                 gtk_theming_engine_get_junction_sides (engine));
1366
1367   _gtk_theming_background_render (&bg, cr);
1368 }
1369
1370 static void
1371 gtk_theming_engine_hide_border_sides (double         border[4],
1372                                       GtkBorderStyle border_style[4],
1373                                       guint          hidden_side)
1374 {
1375   guint i;
1376
1377   for (i = 0; i < 4; i++)
1378     {
1379       if (hidden_side & (1 << i) ||
1380           border_style[i] == GTK_BORDER_STYLE_NONE ||
1381           border_style[i] == GTK_BORDER_STYLE_HIDDEN)
1382         border[i] = 0;
1383     }
1384 }
1385
1386 static void
1387 render_frame_fill (cairo_t       *cr,
1388                    GtkRoundedBox *border_box,
1389                    const double   border_width[4],
1390                    GdkRGBA        colors[4],
1391                    guint          hidden_side)
1392 {
1393   GtkRoundedBox padding_box;
1394   guint i, j;
1395
1396   padding_box = *border_box;
1397   _gtk_rounded_box_shrink (&padding_box,
1398                            border_width[GTK_CSS_TOP],
1399                            border_width[GTK_CSS_RIGHT],
1400                            border_width[GTK_CSS_BOTTOM],
1401                            border_width[GTK_CSS_LEFT]);
1402
1403   if (hidden_side == 0 &&
1404       gdk_rgba_equal (&colors[0], &colors[1]) &&
1405       gdk_rgba_equal (&colors[0], &colors[2]) &&
1406       gdk_rgba_equal (&colors[0], &colors[3]))
1407     {
1408       gdk_cairo_set_source_rgba (cr, &colors[0]);
1409
1410       _gtk_rounded_box_path (border_box, cr);
1411       _gtk_rounded_box_path (&padding_box, cr);
1412       cairo_fill (cr);
1413     }
1414   else
1415     {
1416       for (i = 0; i < 4; i++) 
1417         {
1418           if (hidden_side & (1 << i))
1419             continue;
1420
1421           for (j = 0; j < 4; j++)
1422             { 
1423               if (hidden_side & (1 << j))
1424                 continue;
1425
1426               if (i == j || 
1427                   (gdk_rgba_equal (&colors[i], &colors[j])))
1428                 {
1429                   /* We were already painted when i == j */
1430                   if (i > j)
1431                     break;
1432
1433                   if (j == 0)
1434                     _gtk_rounded_box_path_top (border_box, &padding_box, cr);
1435                   else if (j == 1)
1436                     _gtk_rounded_box_path_right (border_box, &padding_box, cr);
1437                   else if (j == 2)
1438                     _gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
1439                   else if (j == 3)
1440                     _gtk_rounded_box_path_left (border_box, &padding_box, cr);
1441                 }
1442             }
1443           /* We were already painted when i == j */
1444           if (i > j)
1445             continue;
1446
1447           gdk_cairo_set_source_rgba (cr, &colors[i]);
1448
1449           cairo_fill (cr);
1450         }
1451     }
1452 }
1453
1454 static void
1455 set_stroke_style (cairo_t        *cr,
1456                   double          line_width,
1457                   GtkBorderStyle  style,
1458                   double          length)
1459 {
1460   double segments[2];
1461   double n;
1462
1463   cairo_set_line_width (cr, line_width);
1464
1465   if (style == GTK_BORDER_STYLE_DOTTED)
1466     {
1467       n = round (0.5 * length / line_width);
1468
1469       segments[0] = 0;
1470       segments[1] = n ? length / n : 2;
1471       cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
1472
1473       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1474       cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1475     }
1476   else
1477     {
1478       n = length / line_width;
1479       /* Optimize the common case of an integer-sized rectangle
1480        * Again, we care about focus rectangles.
1481        */
1482       if (n == nearbyint (n))
1483         {
1484           segments[0] = 1;
1485           segments[1] = 2;
1486         }
1487       else
1488         {
1489           n = round ((1. / 3) * n);
1490
1491           segments[0] = n ? (1. / 3) * length / n : 1;
1492           segments[1] = 2 * segments[1];
1493         }
1494       cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
1495
1496       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1497       cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
1498     }
1499 }
1500
1501 static void
1502 render_frame_stroke (cairo_t       *cr,
1503                      GtkRoundedBox *border_box,
1504                      const double   border_width[4],
1505                      GdkRGBA        colors[4],
1506                      guint          hidden_side,
1507                      GtkBorderStyle stroke_style)
1508 {
1509   gboolean different_colors, different_borders;
1510   GtkRoundedBox stroke_box;
1511   guint i;
1512
1513   different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
1514                      !gdk_rgba_equal (&colors[0], &colors[2]) ||
1515                      !gdk_rgba_equal (&colors[0], &colors[3]);
1516   different_borders = border_width[0] != border_width[1] ||
1517                       border_width[0] != border_width[2] ||
1518                       border_width[0] != border_width[3] ;
1519
1520   stroke_box = *border_box;
1521   _gtk_rounded_box_shrink (&stroke_box,
1522                            border_width[GTK_CSS_TOP] / 2.0,
1523                            border_width[GTK_CSS_RIGHT] / 2.0,
1524                            border_width[GTK_CSS_BOTTOM] / 2.0,
1525                            border_width[GTK_CSS_LEFT] / 2.0);
1526
1527   if (!different_colors && !different_borders && hidden_side == 0)
1528     {
1529       double length = 0;
1530
1531       /* FAST PATH:
1532        * Mostly expected to trigger for focus rectangles */
1533       for (i = 0; i < 4; i++) 
1534         {
1535           length += _gtk_rounded_box_guess_length (&stroke_box, i);
1536           _gtk_rounded_box_path_side (&stroke_box, cr, i);
1537         }
1538
1539       gdk_cairo_set_source_rgba (cr, &colors[0]);
1540       set_stroke_style (cr, border_width[0], stroke_style, length);
1541       cairo_stroke (cr);
1542     }
1543   else
1544     {
1545       GtkRoundedBox padding_box;
1546
1547       padding_box = *border_box;
1548       _gtk_rounded_box_path (&padding_box, cr);
1549       _gtk_rounded_box_shrink (&padding_box,
1550                                border_width[GTK_CSS_TOP],
1551                                border_width[GTK_CSS_RIGHT],
1552                                border_width[GTK_CSS_BOTTOM],
1553                                border_width[GTK_CSS_LEFT]);
1554
1555       for (i = 0; i < 4; i++) 
1556         {
1557           if (hidden_side & (1 << i))
1558             continue;
1559
1560           cairo_save (cr);
1561
1562           if (i == 0)
1563             _gtk_rounded_box_path_top (border_box, &padding_box, cr);
1564           else if (i == 1)
1565             _gtk_rounded_box_path_right (border_box, &padding_box, cr);
1566           else if (i == 2)
1567             _gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
1568           else if (i == 3)
1569             _gtk_rounded_box_path_left (border_box, &padding_box, cr);
1570           cairo_clip (cr);
1571
1572           _gtk_rounded_box_path_side (&stroke_box, cr, i);
1573
1574           gdk_cairo_set_source_rgba (cr, &colors[i]);
1575           set_stroke_style (cr,
1576                             border_width[i],
1577                             stroke_style,
1578                             _gtk_rounded_box_guess_length (&stroke_box, i));
1579           cairo_stroke (cr);
1580
1581           cairo_restore (cr);
1582         }
1583     }
1584 }
1585
1586 static void
1587 render_border (cairo_t       *cr,
1588                GtkRoundedBox *border_box,
1589                const double   border_width[4],
1590                guint          hidden_side,
1591                GdkRGBA        colors[4],
1592                GtkBorderStyle border_style[4])
1593 {
1594   guint i, j;
1595
1596   cairo_save (cr);
1597
1598   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
1599
1600   for (i = 0; i < 4; i++)
1601     {
1602       if (hidden_side & (1 << i))
1603         continue;
1604
1605       switch (border_style[i])
1606         {
1607         case GTK_BORDER_STYLE_NONE:
1608         case GTK_BORDER_STYLE_HIDDEN:
1609         case GTK_BORDER_STYLE_SOLID:
1610           break;
1611         case GTK_BORDER_STYLE_INSET:
1612           if (i == 1 || i == 2)
1613             color_shade (&colors[i], 1.8, &colors[i]);
1614           break;
1615         case GTK_BORDER_STYLE_OUTSET:
1616           if (i == 0 || i == 3)
1617             color_shade (&colors[i], 1.8, &colors[i]);
1618           break;
1619         case GTK_BORDER_STYLE_DOTTED:
1620         case GTK_BORDER_STYLE_DASHED:
1621           {
1622             guint dont_draw = hidden_side;
1623
1624             for (j = 0; j < 4; j++)
1625               {
1626                 if (border_style[j] == border_style[i])
1627                   hidden_side |= (1 << j);
1628                 else
1629                   dont_draw |= (1 << j);
1630               }
1631             
1632             render_frame_stroke (cr, border_box, border_width, colors, dont_draw, border_style[i]);
1633           }
1634           break;
1635         case GTK_BORDER_STYLE_DOUBLE:
1636           {
1637             GtkRoundedBox other_box;
1638             double other_border[4];
1639             guint dont_draw = hidden_side;
1640
1641             for (j = 0; j < 4; j++)
1642               {
1643                 if (border_style[j] == GTK_BORDER_STYLE_DOUBLE)
1644                   hidden_side |= (1 << j);
1645                 else
1646                   dont_draw |= (1 << j);
1647                 
1648                 other_border[i] = border_width[i] / 3;
1649               }
1650             
1651             render_frame_fill (cr, border_box, other_border, colors, dont_draw);
1652             
1653             other_box = *border_box;
1654             _gtk_rounded_box_shrink (&other_box,
1655                                      2 * other_border[GTK_CSS_TOP],
1656                                      2 * other_border[GTK_CSS_RIGHT],
1657                                      2 * other_border[GTK_CSS_BOTTOM],
1658                                      2 * other_border[GTK_CSS_LEFT]);
1659             render_frame_fill (cr, &other_box, other_border, colors, dont_draw);
1660           }
1661         case GTK_BORDER_STYLE_GROOVE:
1662         case GTK_BORDER_STYLE_RIDGE:
1663           {
1664             GtkRoundedBox other_box;
1665             GdkRGBA other_colors[4];
1666             guint dont_draw = hidden_side;
1667             double other_border[4];
1668
1669             for (j = 0; j < 4; j++)
1670               {
1671                 other_colors[j] = colors[j];
1672                 if ((j == 0 || j == 3) ^ (border_style[j] == GTK_BORDER_STYLE_RIDGE))
1673                   color_shade (&other_colors[j], 1.8, &other_colors[j]);
1674                 else
1675                   color_shade (&colors[j], 1.8, &colors[j]);
1676                 if (border_style[j] == GTK_BORDER_STYLE_GROOVE ||
1677                     border_style[j] == GTK_BORDER_STYLE_RIDGE)
1678                   hidden_side |= (1 << j);
1679                 else
1680                   dont_draw |= (1 << j);
1681                 other_border[i] = border_width[i] / 2;
1682               }
1683             
1684             render_frame_fill (cr, border_box, other_border, colors, dont_draw);
1685             
1686             other_box = *border_box;
1687             _gtk_rounded_box_shrink (&other_box,
1688                                      other_border[GTK_CSS_TOP],
1689                                      other_border[GTK_CSS_RIGHT],
1690                                      other_border[GTK_CSS_BOTTOM],
1691                                      other_border[GTK_CSS_LEFT]);
1692             render_frame_fill (cr, &other_box, other_border, other_colors, dont_draw);
1693           }
1694           break;
1695         default:
1696           g_assert_not_reached ();
1697           break;
1698         }
1699     }
1700   
1701   render_frame_fill (cr, border_box, border_width, colors, hidden_side);
1702
1703   cairo_restore (cr);
1704 }
1705
1706 static void
1707 render_frame_internal (GtkThemingEngine *engine,
1708                        cairo_t          *cr,
1709                        gdouble           x,
1710                        gdouble           y,
1711                        gdouble           width,
1712                        gdouble           height,
1713                        guint             hidden_side,
1714                        GtkJunctionSides  junction)
1715 {
1716   GtkBorderImage border_image;
1717   GtkBorderStyle border_style[4];
1718   GtkRoundedBox border_box;
1719   double border_width[4];
1720   GdkRGBA colors[4];
1721
1722   border_width[0] = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
1723   border_width[1] = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
1724   border_width[2] = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
1725   border_width[3] = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
1726
1727   border_style[0] = _gtk_css_border_style_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
1728   border_style[1] = _gtk_css_border_style_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE));
1729   border_style[2] = _gtk_css_border_style_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE));
1730   border_style[3] = _gtk_css_border_style_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_LEFT_STYLE));
1731
1732   gtk_theming_engine_hide_border_sides (border_width, border_style, hidden_side);
1733
1734   if (_gtk_border_image_init (&border_image, engine))
1735     _gtk_border_image_render (&border_image, border_width, cr, x, y, width, height);
1736   else
1737     {
1738       colors[0] = *_gtk_css_rgba_value_get_rgba (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_COLOR));
1739       colors[1] = *_gtk_css_rgba_value_get_rgba (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_RIGHT_COLOR));
1740       colors[2] = *_gtk_css_rgba_value_get_rgba (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_BOTTOM_COLOR));
1741       colors[3] = *_gtk_css_rgba_value_get_rgba (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_LEFT_COLOR));
1742
1743       _gtk_rounded_box_init_rect (&border_box, x, y, width, height);
1744       _gtk_rounded_box_apply_border_radius_for_engine (&border_box, engine, junction);
1745
1746       render_border (cr, &border_box, border_width, hidden_side, colors, border_style);
1747     }
1748
1749   border_style[0] = _gtk_css_border_style_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_OUTLINE_STYLE));
1750   if (border_style[0] != GTK_BORDER_STYLE_NONE)
1751     {
1752       int offset;
1753
1754       border_style[1] = border_style[2] = border_style[3] = border_style[0];
1755       border_width[0] = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_OUTLINE_WIDTH), 100);
1756       border_width[3] = border_width[2] = border_width[1] = border_width[0];
1757       colors[0] = *_gtk_css_rgba_value_get_rgba (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_OUTLINE_COLOR));
1758       colors[3] = colors[2] = colors[1] = colors[0];
1759       offset = _gtk_css_number_value_get (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_OUTLINE_OFFSET), 100);
1760       
1761       /* reinit box here - outlines don't have a border radius */
1762       _gtk_rounded_box_init_rect (&border_box, x, y, width, height);
1763       _gtk_rounded_box_shrink (&border_box,
1764                                - border_width[GTK_CSS_TOP] - offset,
1765                                - border_width[GTK_CSS_RIGHT] - offset,
1766                                - border_width[GTK_CSS_LEFT] - offset,
1767                                - border_width[GTK_CSS_BOTTOM] - offset);
1768       
1769       render_border (cr, &border_box, border_width, hidden_side, colors, border_style);
1770     }
1771 }
1772
1773 static void
1774 gtk_theming_engine_render_frame (GtkThemingEngine *engine,
1775                                  cairo_t          *cr,
1776                                  gdouble           x,
1777                                  gdouble           y,
1778                                  gdouble           width,
1779                                  gdouble           height)
1780 {
1781   GtkJunctionSides junction;
1782
1783   junction = gtk_theming_engine_get_junction_sides (engine);
1784
1785   render_frame_internal (engine, cr,
1786                          x, y, width, height,
1787                          0, junction);
1788 }
1789
1790 static void
1791 gtk_theming_engine_render_expander (GtkThemingEngine *engine,
1792                                     cairo_t          *cr,
1793                                     gdouble           x,
1794                                     gdouble           y,
1795                                     gdouble           width,
1796                                     gdouble           height)
1797 {
1798   GtkStateFlags flags;
1799   GdkRGBA outline_color, fg_color;
1800   double vertical_overshoot;
1801   int diameter;
1802   double radius;
1803   double interp;                /* interpolation factor for center position */
1804   double x_double_horz, y_double_horz;
1805   double x_double_vert, y_double_vert;
1806   double x_double, y_double;
1807   gdouble angle;
1808   gint line_width;
1809   gboolean is_rtl;
1810   gdouble progress;
1811
1812   cairo_save (cr);
1813   flags = gtk_theming_engine_get_state (engine);
1814
1815   gtk_theming_engine_get_color (engine, flags, &fg_color);
1816   gtk_theming_engine_get_border_color (engine, flags, &outline_color);
1817
1818   is_rtl = (gtk_theming_engine_get_direction (engine) == GTK_TEXT_DIR_RTL);
1819   line_width = 1;
1820   progress = (flags & GTK_STATE_FLAG_ACTIVE) ? 1 : 0;
1821
1822   if (!gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_HORIZONTAL))
1823     {
1824       if (is_rtl)
1825         angle = (G_PI) - ((G_PI / 2) * progress);
1826       else
1827         angle = (G_PI / 2) * progress;
1828     }
1829   else
1830     {
1831       if (is_rtl)
1832         angle = (G_PI / 2) + ((G_PI / 2) * progress);
1833       else
1834         angle = (G_PI / 2) - ((G_PI / 2) * progress);
1835     }
1836
1837   interp = progress;
1838
1839   /* Compute distance that the stroke extends beyonds the end
1840    * of the triangle we draw.
1841    */
1842   vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
1843
1844   /* For odd line widths, we end the vertical line of the triangle
1845    * at a half pixel, so we round differently.
1846    */
1847   if (line_width % 2 == 1)
1848     vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
1849   else
1850     vertical_overshoot = ceil (vertical_overshoot);
1851
1852   /* Adjust the size of the triangle we draw so that the entire stroke fits
1853    */
1854   diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
1855
1856   /* If the line width is odd, we want the diameter to be even,
1857    * and vice versa, so force the sum to be odd. This relationship
1858    * makes the point of the triangle look right.
1859    */
1860   diameter -= (1 - (diameter + line_width) % 2);
1861
1862   radius = diameter / 2.;
1863
1864   /* Adjust the center so that the stroke is properly aligned with
1865    * the pixel grid. The center adjustment is different for the
1866    * horizontal and vertical orientations. For intermediate positions
1867    * we interpolate between the two.
1868    */
1869   x_double_vert = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
1870   y_double_vert = (y + height / 2) - 0.5;
1871
1872   x_double_horz = (x + width / 2) - 0.5;
1873   y_double_horz = floor ((y + height / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
1874
1875   x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
1876   y_double = y_double_vert * (1 - interp) + y_double_horz * interp;
1877
1878   cairo_translate (cr, x_double, y_double);
1879   cairo_rotate (cr, angle);
1880
1881   cairo_move_to (cr, - radius / 2., - radius);
1882   cairo_line_to (cr,   radius / 2.,   0);
1883   cairo_line_to (cr, - radius / 2.,   radius);
1884   cairo_close_path (cr);
1885
1886   cairo_set_line_width (cr, line_width);
1887
1888   gdk_cairo_set_source_rgba (cr, &fg_color);
1889
1890   cairo_fill_preserve (cr);
1891
1892   gdk_cairo_set_source_rgba (cr, &outline_color);
1893   cairo_stroke (cr);
1894
1895   cairo_restore (cr);
1896 }
1897
1898 static void
1899 gtk_theming_engine_render_focus (GtkThemingEngine *engine,
1900                                  cairo_t          *cr,
1901                                  gdouble           x,
1902                                  gdouble           y,
1903                                  gdouble           width,
1904                                  gdouble           height)
1905 {
1906   GtkStateFlags flags;
1907   GdkRGBA color;
1908   gint line_width;
1909   gint8 *dash_list;
1910
1911   cairo_save (cr);
1912   flags = gtk_theming_engine_get_state (engine);
1913
1914   gtk_theming_engine_get_color (engine, flags, &color);
1915
1916   gtk_theming_engine_get_style (engine,
1917                                 "focus-line-width", &line_width,
1918                                 "focus-line-pattern", (gchar *) &dash_list,
1919                                 NULL);
1920
1921   cairo_set_line_width (cr, (gdouble) line_width);
1922
1923   if (dash_list[0])
1924     {
1925       gint n_dashes = strlen ((const gchar *) dash_list);
1926       gdouble *dashes = g_new (gdouble, n_dashes);
1927       gdouble total_length = 0;
1928       gdouble dash_offset;
1929       gint i;
1930
1931       for (i = 0; i < n_dashes; i++)
1932         {
1933           dashes[i] = dash_list[i];
1934           total_length += dash_list[i];
1935         }
1936
1937       /* The dash offset here aligns the pattern to integer pixels
1938        * by starting the dash at the right side of the left border
1939        * Negative dash offsets in cairo don't work
1940        * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
1941        */
1942       dash_offset = - line_width / 2.;
1943
1944       while (dash_offset < 0)
1945         dash_offset += total_length;
1946
1947       cairo_set_dash (cr, dashes, n_dashes, dash_offset);
1948       g_free (dashes);
1949     }
1950
1951   cairo_rectangle (cr,
1952                    x + line_width / 2.,
1953                    y + line_width / 2.,
1954                    width - line_width,
1955                    height - line_width);
1956
1957   gdk_cairo_set_source_rgba (cr, &color);
1958   cairo_stroke (cr);
1959
1960   cairo_restore (cr);
1961
1962   g_free (dash_list);
1963 }
1964
1965 static void
1966 gtk_theming_engine_render_line (GtkThemingEngine *engine,
1967                                 cairo_t          *cr,
1968                                 gdouble           x0,
1969                                 gdouble           y0,
1970                                 gdouble           x1,
1971                                 gdouble           y1)
1972 {
1973   GdkRGBA color;
1974   GtkStateFlags flags;
1975
1976   flags = gtk_theming_engine_get_state (engine);
1977   cairo_save (cr);
1978
1979   gtk_theming_engine_get_color (engine, flags, &color);
1980
1981   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1982   cairo_set_line_width (cr, 1);
1983
1984   cairo_move_to (cr, x0 + 0.5, y0 + 0.5);
1985   cairo_line_to (cr, x1 + 0.5, y1 + 0.5);
1986
1987   gdk_cairo_set_source_rgba (cr, &color);
1988   cairo_stroke (cr);
1989
1990   cairo_restore (cr);
1991 }
1992
1993 static void
1994 prepare_context_for_layout (cairo_t *cr,
1995                             gdouble x,
1996                             gdouble y,
1997                             PangoLayout *layout)
1998 {
1999   const PangoMatrix *matrix;
2000
2001   matrix = pango_context_get_matrix (pango_layout_get_context (layout));
2002
2003   cairo_move_to (cr, x, y);
2004
2005   if (matrix)
2006     {
2007       cairo_matrix_t cairo_matrix;
2008
2009       cairo_matrix_init (&cairo_matrix,
2010                          matrix->xx, matrix->yx,
2011                          matrix->xy, matrix->yy,
2012                          matrix->x0, matrix->y0);
2013
2014       cairo_transform (cr, &cairo_matrix);
2015     }
2016 }
2017
2018 static void
2019 gtk_theming_engine_render_layout (GtkThemingEngine *engine,
2020                                   cairo_t          *cr,
2021                                   gdouble           x,
2022                                   gdouble           y,
2023                                   PangoLayout      *layout)
2024 {
2025   GdkRGBA fg_color;
2026   GtkStateFlags flags;
2027
2028   cairo_save (cr);
2029   flags = gtk_theming_engine_get_state (engine);
2030   gtk_theming_engine_get_color (engine, flags, &fg_color);
2031
2032   prepare_context_for_layout (cr, x, y, layout);
2033
2034   _gtk_css_shadows_value_paint_layout (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_TEXT_SHADOW),
2035                                        cr, layout);
2036
2037   gdk_cairo_set_source_rgba (cr, &fg_color);
2038   pango_cairo_show_layout (cr, layout);
2039
2040   cairo_restore (cr);
2041 }
2042
2043 static void
2044 gtk_theming_engine_render_slider (GtkThemingEngine *engine,
2045                                   cairo_t          *cr,
2046                                   gdouble           x,
2047                                   gdouble           y,
2048                                   gdouble           width,
2049                                   gdouble           height,
2050                                   GtkOrientation    orientation)
2051 {
2052   gtk_theming_engine_render_background (engine, cr, x, y, width, height);
2053   gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
2054 }
2055
2056 static void
2057 gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
2058                                      cairo_t          *cr,
2059                                      gdouble           x,
2060                                      gdouble           y,
2061                                      gdouble           width,
2062                                      gdouble           height,
2063                                      GtkPositionType   gap_side,
2064                                      gdouble           xy0_gap,
2065                                      gdouble           xy1_gap)
2066 {
2067   GtkJunctionSides junction;
2068   GtkStateFlags state;
2069   gint border_width;
2070   GtkCssValue *corner[4];
2071   gdouble x0, y0, x1, y1, xc, yc, wc, hc;
2072   GtkBorder border;
2073
2074   xc = yc = wc = hc = 0;
2075   state = gtk_theming_engine_get_state (engine);
2076   junction = gtk_theming_engine_get_junction_sides (engine);
2077
2078   gtk_theming_engine_get_border (engine, state, &border);
2079   corner[GTK_CSS_TOP_LEFT] = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS);
2080   corner[GTK_CSS_TOP_RIGHT] = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS);
2081   corner[GTK_CSS_BOTTOM_LEFT] = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS);
2082   corner[GTK_CSS_BOTTOM_RIGHT] = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS);
2083
2084   border_width = MIN (MIN (border.top, border.bottom),
2085                       MIN (border.left, border.right));
2086
2087   cairo_save (cr);
2088
2089   switch (gap_side)
2090     {
2091     case GTK_POS_TOP:
2092       xc = x + xy0_gap + border_width;
2093       yc = y;
2094       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2095       hc = border_width;
2096
2097       if (xy0_gap < _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_LEFT], width))
2098         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
2099
2100       if (xy1_gap > width - _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_RIGHT], width))
2101         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
2102       break;
2103     case GTK_POS_BOTTOM:
2104       xc = x + xy0_gap + border_width;
2105       yc = y + height - border_width;
2106       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2107       hc = border_width;
2108
2109       if (xy0_gap < _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_LEFT], width))
2110         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
2111
2112       if (xy1_gap > width - _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_RIGHT], width))
2113         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2114
2115       break;
2116     case GTK_POS_LEFT:
2117       xc = x;
2118       yc = y + xy0_gap + border_width;
2119       wc = border_width;
2120       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2121
2122       if (xy0_gap < _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_LEFT], height))
2123         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
2124
2125       if (xy1_gap > height - _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_LEFT], height))
2126         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
2127
2128       break;
2129     case GTK_POS_RIGHT:
2130       xc = x + width - border_width;
2131       yc = y + xy0_gap + border_width;
2132       wc = border_width;
2133       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2134
2135       if (xy0_gap < _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_RIGHT], height))
2136         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
2137
2138       if (xy1_gap > height - _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_RIGHT], height))
2139         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2140
2141       break;
2142     }
2143
2144   cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
2145   cairo_rectangle (cr, x0, y0, x1 - x0, yc - y0);
2146   cairo_rectangle (cr, x0, yc, xc - x0, hc);
2147   cairo_rectangle (cr, xc + wc, yc, x1 - (xc + wc), hc);
2148   cairo_rectangle (cr, x0, yc + hc, x1 - x0, y1 - (yc + hc));
2149   cairo_clip (cr);
2150
2151   render_frame_internal (engine, cr,
2152                          x, y, width, height,
2153                          0, junction);
2154
2155   cairo_restore (cr);
2156 }
2157
2158 static void
2159 gtk_theming_engine_render_extension (GtkThemingEngine *engine,
2160                                      cairo_t          *cr,
2161                                      gdouble           x,
2162                                      gdouble           y,
2163                                      gdouble           width,
2164                                      gdouble           height,
2165                                      GtkPositionType   gap_side)
2166 {
2167   GtkThemingBackground bg;
2168   GtkJunctionSides junction = 0;
2169   guint hidden_side = 0;
2170
2171   switch (gap_side)
2172     {
2173     case GTK_POS_LEFT:
2174       junction = GTK_JUNCTION_LEFT;
2175       hidden_side = (1 << GTK_CSS_LEFT);
2176       break;
2177     case GTK_POS_RIGHT:
2178       junction = GTK_JUNCTION_RIGHT;
2179       hidden_side = (1 << GTK_CSS_RIGHT);
2180       break;
2181     case GTK_POS_TOP:
2182       junction = GTK_JUNCTION_TOP;
2183       hidden_side = (1 << GTK_CSS_TOP);
2184       break;
2185     case GTK_POS_BOTTOM:
2186       junction = GTK_JUNCTION_BOTTOM;
2187       hidden_side = (1 << GTK_CSS_BOTTOM);
2188       break;
2189     }
2190
2191   _gtk_theming_background_init (&bg, engine, 
2192                                 x, y,
2193                                 width, height,
2194                                 junction);
2195   _gtk_theming_background_render (&bg, cr);
2196
2197   render_frame_internal (engine, cr,
2198                          x, y, width, height,
2199                          hidden_side, junction);
2200 }
2201
2202 static void
2203 render_dot (cairo_t       *cr,
2204             const GdkRGBA *lighter,
2205             const GdkRGBA *darker,
2206             gdouble        x,
2207             gdouble        y,
2208             gdouble        size)
2209 {
2210   size = CLAMP ((gint) size, 2, 3);
2211
2212   if (size == 2)
2213     {
2214       gdk_cairo_set_source_rgba (cr, lighter);
2215       cairo_rectangle (cr, x, y, 1, 1);
2216       cairo_rectangle (cr, x + 1, y + 1, 1, 1);
2217       cairo_fill (cr);
2218     }
2219   else if (size == 3)
2220     {
2221       gdk_cairo_set_source_rgba (cr, lighter);
2222       cairo_rectangle (cr, x, y, 2, 1);
2223       cairo_rectangle (cr, x, y, 1, 2);
2224       cairo_fill (cr);
2225
2226       gdk_cairo_set_source_rgba (cr, darker);
2227       cairo_rectangle (cr, x + 1, y + 1, 2, 1);
2228       cairo_rectangle (cr, x + 2, y, 1, 2);
2229       cairo_fill (cr);
2230     }
2231 }
2232
2233 static void
2234 gtk_theming_engine_render_handle (GtkThemingEngine *engine,
2235                                   cairo_t          *cr,
2236                                   gdouble           x,
2237                                   gdouble           y,
2238                                   gdouble           width,
2239                                   gdouble           height)
2240 {
2241   GtkStateFlags flags;
2242   GdkRGBA bg_color, lighter, darker;
2243   GtkJunctionSides sides;
2244   GtkThemingBackground bg;
2245   gint xx, yy;
2246   gboolean has_image;
2247
2248   cairo_save (cr);
2249   flags = gtk_theming_engine_get_state (engine);
2250
2251   cairo_set_line_width (cr, 1.0);
2252   sides = gtk_theming_engine_get_junction_sides (engine);
2253   gtk_theming_engine_get_background_color (engine, flags, &bg_color);
2254
2255   color_shade (&bg_color, 0.7, &darker);
2256   color_shade (&bg_color, 1.3, &lighter);
2257
2258   _gtk_theming_background_init (&bg, engine, x, y, width, height, sides);
2259   has_image = _gtk_theming_background_has_background_image (&bg);
2260   _gtk_theming_background_render (&bg, cr);
2261
2262   gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
2263
2264   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_GRIP))
2265     {
2266       /* reduce confusing values to a meaningful state */
2267       if ((sides & (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)) == (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT))
2268         sides &= ~GTK_JUNCTION_CORNER_TOPLEFT;
2269
2270       if ((sides & (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT)) == (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT))
2271         sides &= ~GTK_JUNCTION_CORNER_TOPRIGHT;
2272
2273       if (sides == 0)
2274         sides = GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2275
2276       /* align drawing area to the connected side */
2277       if (sides == GTK_JUNCTION_LEFT)
2278         {
2279           if (height < width)
2280             width = height;
2281         }
2282       else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
2283         {
2284           if (width < height)
2285             height = width;
2286           else if (height < width)
2287             width = height;
2288         }
2289       else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
2290         {
2291           /* make it square, aligning to bottom left */
2292           if (width < height)
2293             {
2294               y += (height - width);
2295               height = width;
2296             }
2297           else if (height < width)
2298             width = height;
2299         }
2300       else if (sides == GTK_JUNCTION_RIGHT)
2301         {
2302           /* aligning to right */
2303           if (height < width)
2304             {
2305               x += (width - height);
2306               width = height;
2307             }
2308         }
2309       else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
2310         {
2311           if (width < height)
2312             height = width;
2313           else if (height < width)
2314             {
2315               x += (width - height);
2316               width = height;
2317             }
2318         }
2319       else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
2320         {
2321           /* make it square, aligning to bottom right */
2322           if (width < height)
2323             {
2324               y += (height - width);
2325               height = width;
2326             }
2327           else if (height < width)
2328             {
2329               x += (width - height);
2330               width = height;
2331             }
2332         }
2333       else if (sides == GTK_JUNCTION_TOP)
2334         {
2335           if (width < height)
2336             height = width;
2337         }
2338       else if (sides == GTK_JUNCTION_BOTTOM)
2339         {
2340           /* align to bottom */
2341           if (width < height)
2342             {
2343               y += (height - width);
2344               height = width;
2345             }
2346         }
2347       else
2348         g_assert_not_reached ();
2349
2350       if (sides == GTK_JUNCTION_LEFT ||
2351           sides == GTK_JUNCTION_RIGHT)
2352         {
2353           gint xi;
2354
2355           xi = x;
2356
2357           while (xi < x + width)
2358             {
2359               gdk_cairo_set_source_rgba (cr, &lighter);
2360               add_path_line (cr, x, y, x, y + height);
2361               cairo_stroke (cr);
2362               xi++;
2363
2364               gdk_cairo_set_source_rgba (cr, &darker);
2365               add_path_line (cr, xi, y, xi, y + height);
2366               cairo_stroke (cr);
2367               xi += 2;
2368             }
2369         }
2370       else if (sides == GTK_JUNCTION_TOP ||
2371                sides == GTK_JUNCTION_BOTTOM)
2372         {
2373           gint yi;
2374
2375           yi = y;
2376
2377           while (yi < y + height)
2378             {
2379               gdk_cairo_set_source_rgba (cr, &lighter);
2380               add_path_line (cr, x, yi, x + width, yi);
2381               cairo_stroke (cr);
2382               yi++;
2383
2384               gdk_cairo_set_source_rgba (cr, &darker);
2385               add_path_line (cr, x, yi, x + width, yi);
2386               cairo_stroke (cr);
2387               yi += 2;
2388             }
2389         }
2390       else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
2391         {
2392           gint xi, yi;
2393
2394           xi = x + width;
2395           yi = y + height;
2396
2397           while (xi > x + 3)
2398             {
2399               gdk_cairo_set_source_rgba (cr, &darker);
2400               add_path_line (cr, xi, y, x, yi);
2401               cairo_stroke (cr);
2402
2403               --xi;
2404               --yi;
2405
2406               add_path_line (cr, xi, y, x, yi);
2407               cairo_stroke (cr);
2408
2409               --xi;
2410               --yi;
2411
2412               gdk_cairo_set_source_rgba (cr, &lighter);
2413               add_path_line (cr, xi, y, x, yi);
2414               cairo_stroke (cr);
2415
2416               xi -= 3;
2417               yi -= 3;
2418             }
2419         }
2420       else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
2421         {
2422           gint xi, yi;
2423
2424           xi = x;
2425           yi = y + height;
2426
2427           while (xi < (x + width - 3))
2428             {
2429               gdk_cairo_set_source_rgba (cr, &lighter);
2430               add_path_line (cr, xi, y, x + width, yi);
2431               cairo_stroke (cr);
2432
2433               ++xi;
2434               --yi;
2435
2436               gdk_cairo_set_source_rgba (cr, &darker);
2437               add_path_line (cr, xi, y, x + width, yi);
2438               cairo_stroke (cr);
2439
2440               ++xi;
2441               --yi;
2442
2443               add_path_line (cr, xi, y, x + width, yi);
2444               cairo_stroke (cr);
2445
2446               xi += 3;
2447               yi -= 3;
2448             }
2449         }
2450       else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
2451         {
2452           gint xi, yi;
2453
2454           xi = x + width;
2455           yi = y;
2456
2457           while (xi > x + 3)
2458             {
2459               gdk_cairo_set_source_rgba (cr, &darker);
2460               add_path_line (cr, x, yi, xi, y + height);
2461               cairo_stroke (cr);
2462
2463               --xi;
2464               ++yi;
2465
2466               add_path_line (cr, x, yi, xi, y + height);
2467               cairo_stroke (cr);
2468
2469               --xi;
2470               ++yi;
2471
2472               gdk_cairo_set_source_rgba (cr, &lighter);
2473               add_path_line (cr, x, yi, xi, y + height);
2474               cairo_stroke (cr);
2475
2476               xi -= 3;
2477               yi += 3;
2478             }
2479         }
2480       else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
2481         {
2482           gint xi, yi;
2483
2484           xi = x;
2485           yi = y;
2486
2487           while (xi < (x + width - 3))
2488             {
2489               gdk_cairo_set_source_rgba (cr, &lighter);
2490               add_path_line (cr, xi, y + height, x + width, yi);
2491               cairo_stroke (cr);
2492
2493               ++xi;
2494               ++yi;
2495
2496               gdk_cairo_set_source_rgba (cr, &darker);
2497               add_path_line (cr, xi, y + height, x + width, yi);
2498               cairo_stroke (cr);
2499
2500               ++xi;
2501               ++yi;
2502
2503               add_path_line (cr, xi, y + height, x + width, yi);
2504               cairo_stroke (cr);
2505
2506               xi += 3;
2507               yi += 3;
2508             }
2509         }
2510     }
2511   else if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_PANE_SEPARATOR))
2512     {
2513       if (!has_image)
2514         {
2515           if (width > height)
2516             for (xx = x + width / 2 - 15; xx <= x + width / 2 + 15; xx += 5)
2517               render_dot (cr, &lighter, &darker, xx, y + height / 2 - 1, 3);
2518           else
2519             for (yy = y + height / 2 - 15; yy <= y + height / 2 + 15; yy += 5)
2520               render_dot (cr, &lighter, &darker, x + width / 2 - 1, yy, 3);
2521         }
2522     }
2523   else
2524     {
2525       for (yy = y; yy < y + height; yy += 3)
2526         for (xx = x; xx < x + width; xx += 6)
2527           {
2528             render_dot (cr, &lighter, &darker, xx, yy, 2);
2529             render_dot (cr, &lighter, &darker, xx + 3, yy + 1, 2);
2530           }
2531     }
2532
2533   cairo_restore (cr);
2534 }
2535
2536 void
2537 _gtk_theming_engine_paint_spinner (cairo_t       *cr,
2538                                    gdouble        radius,
2539                                    gdouble        progress,
2540                                    const GdkRGBA *color)
2541 {
2542   guint num_steps, step;
2543   gdouble half;
2544   gint i;
2545
2546   num_steps = 12;
2547
2548   if (progress >= 0)
2549     step = (guint) (progress * num_steps);
2550   else
2551     step = 0;
2552
2553   cairo_save (cr);
2554
2555   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
2556   cairo_set_line_width (cr, 2.0);
2557
2558   half = num_steps / 2;
2559
2560   for (i = 0; i < num_steps; i++)
2561     {
2562       gint inset = 0.7 * radius;
2563
2564       /* transparency is a function of time and intial value */
2565       gdouble t = 1.0 - (gdouble) ((i + step) % num_steps) / num_steps;
2566       gdouble xscale = - sin (i * G_PI / half);
2567       gdouble yscale = - cos (i * G_PI / half);
2568
2569       cairo_set_source_rgba (cr,
2570                              color->red,
2571                              color->green,
2572                              color->blue,
2573                              color->alpha * t);
2574
2575       cairo_move_to (cr,
2576                      (radius - inset) * xscale,
2577                      (radius - inset) * yscale);
2578       cairo_line_to (cr,
2579                      radius * xscale,
2580                      radius * yscale);
2581
2582       cairo_stroke (cr);
2583     }
2584
2585   cairo_restore (cr);
2586 }
2587
2588 static void
2589 render_spinner (GtkThemingEngine *engine,
2590                 cairo_t          *cr,
2591                 gdouble           x,
2592                 gdouble           y,
2593                 gdouble           width,
2594                 gdouble           height)
2595 {
2596   GtkStateFlags state;
2597   GdkRGBA color;
2598   gdouble radius;
2599
2600   state = gtk_theming_engine_get_state (engine);
2601   radius = MIN (width / 2, height / 2);
2602
2603   gtk_theming_engine_get_color (engine, state, &color);
2604
2605   cairo_save (cr);
2606   cairo_translate (cr, x + width / 2, y + height / 2);
2607
2608   _gtk_css_shadows_value_paint_spinner (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_ICON_SHADOW),
2609                                         cr,
2610                                         radius,
2611                                         -1);
2612
2613   _gtk_theming_engine_paint_spinner (cr,
2614                                      radius,
2615                                      -1,
2616                                      &color);
2617
2618   cairo_restore (cr);
2619 }
2620
2621 static void
2622 gtk_theming_engine_render_activity (GtkThemingEngine *engine,
2623                                     cairo_t          *cr,
2624                                     gdouble           x,
2625                                     gdouble           y,
2626                                     gdouble           width,
2627                                     gdouble           height)
2628 {
2629   GtkThemingBackground bg;
2630   
2631   _gtk_theming_background_init (&bg, engine, x, y, width, height, 0);
2632   
2633   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_SPINNER) &&
2634       !_gtk_theming_background_has_background_image (&bg))
2635     {
2636       render_spinner (engine, cr, x, y, width, height);
2637     }
2638   else
2639     {
2640       _gtk_theming_background_render (&bg, cr);
2641       gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
2642     }
2643 }
2644
2645 static GdkPixbuf *
2646 scale_or_ref (GdkPixbuf *src,
2647               gint       width,
2648               gint       height)
2649 {
2650   if (width == gdk_pixbuf_get_width (src) &&
2651       height == gdk_pixbuf_get_height (src))
2652     return g_object_ref (src);
2653   else
2654     return gdk_pixbuf_scale_simple (src,
2655                                     width, height,
2656                                     GDK_INTERP_BILINEAR);
2657 }
2658
2659 static gboolean
2660 lookup_icon_size (GtkThemingEngine *engine,
2661                   GtkIconSize       size,
2662                   gint             *width,
2663                   gint             *height)
2664 {
2665   GdkScreen *screen;
2666   GtkSettings *settings;
2667
2668   screen = gtk_theming_engine_get_screen (engine);
2669   settings = gtk_settings_get_for_screen (screen);
2670
2671   return gtk_icon_size_lookup_for_settings (settings, size, width, height);
2672 }
2673
2674 static void
2675 colorshift_source (cairo_t *cr,
2676                    gdouble shift)
2677 {
2678   cairo_pattern_t *source;
2679
2680   cairo_save (cr);
2681   cairo_paint (cr);
2682
2683   source = cairo_pattern_reference (cairo_get_source (cr));
2684
2685   cairo_set_source_rgb (cr, shift, shift, shift);
2686   cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
2687
2688   cairo_mask (cr, source);
2689
2690   cairo_pattern_destroy (source);
2691   cairo_restore (cr);
2692 }
2693
2694 static GdkPixbuf *
2695 gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
2696                                        const GtkIconSource *source,
2697                                        GtkIconSize          size)
2698 {
2699   GdkPixbuf *scaled;
2700   GdkPixbuf *stated;
2701   GdkPixbuf *base_pixbuf;
2702   GtkStateFlags state;
2703   gint width = 1;
2704   gint height = 1;
2705   cairo_t *cr;
2706   cairo_surface_t *surface;
2707
2708   base_pixbuf = gtk_icon_source_get_pixbuf (source);
2709   state = gtk_theming_engine_get_state (engine);
2710
2711   g_return_val_if_fail (base_pixbuf != NULL, NULL);
2712
2713   if (size != (GtkIconSize) -1 &&
2714       !lookup_icon_size (engine, size, &width, &height))
2715     {
2716       g_warning (G_STRLOC ": invalid icon size '%d'", size);
2717       return NULL;
2718     }
2719
2720   /* If the size was wildcarded, and we're allowed to scale, then scale; otherwise,
2721    * leave it alone.
2722    */
2723   if (size != (GtkIconSize) -1 &&
2724       gtk_icon_source_get_size_wildcarded (source))
2725     scaled = scale_or_ref (base_pixbuf, width, height);
2726   else
2727     scaled = g_object_ref (base_pixbuf);
2728
2729   /* If the state was wildcarded, then generate a state. */
2730   if (gtk_icon_source_get_state_wildcarded (source))
2731     {
2732       if (state & GTK_STATE_FLAG_INSENSITIVE)
2733         {
2734           surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2735                                                 gdk_pixbuf_get_width (scaled),
2736                                                 gdk_pixbuf_get_height (scaled));
2737           cr = cairo_create (surface);
2738           gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
2739           cairo_paint_with_alpha (cr, 0.5);
2740
2741           cairo_destroy (cr);
2742
2743           g_object_unref (scaled);
2744           stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
2745                                                 cairo_image_surface_get_width (surface),
2746                                                 cairo_image_surface_get_height (surface));
2747           cairo_surface_destroy (surface);
2748         }
2749       else if (state & GTK_STATE_FLAG_PRELIGHT)
2750         {
2751           surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2752                                                 gdk_pixbuf_get_width (scaled),
2753                                                 gdk_pixbuf_get_height (scaled));
2754
2755           cr = cairo_create (surface);
2756           gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
2757           colorshift_source (cr, 0.10);
2758
2759           cairo_destroy (cr);
2760
2761           g_object_unref (scaled);
2762           stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
2763                                                 cairo_image_surface_get_width (surface),
2764                                                 cairo_image_surface_get_height (surface));
2765           cairo_surface_destroy (surface);
2766         }
2767       else
2768         stated = scaled;
2769     }
2770   else
2771     stated = scaled;
2772
2773   return stated;
2774 }
2775
2776 static void
2777 gtk_theming_engine_render_icon (GtkThemingEngine *engine,
2778                                 cairo_t *cr,
2779                                 GdkPixbuf *pixbuf,
2780                                 gdouble x,
2781                                 gdouble y)
2782 {
2783   cairo_save (cr);
2784
2785   gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
2786
2787   _gtk_css_shadows_value_paint_icon (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_ICON_SHADOW), cr);
2788
2789   cairo_paint (cr);
2790
2791   cairo_restore (cr);
2792 }
2793