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