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