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