]> Pileus Git - ~andy/gtk/blob - gtk/gtkthemingengine.c
Fix rendering glitch in menu radiobuttons
[~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
1215       cairo_new_sub_path (cr);
1216       cairo_arc (cr,
1217                  x + exterior_size / 2.,
1218                  y + exterior_size / 2.,
1219                  (exterior_size - 1) / 2.,
1220                  0, 2 * G_PI);
1221
1222       gdk_cairo_set_source_rgba (cr, bg_color);
1223       cairo_fill_preserve (cr);
1224
1225       if (border_color)
1226         gdk_cairo_set_source_rgba (cr, border_color);
1227       else
1228         gdk_cairo_set_source_rgba (cr, fg_color);
1229
1230       cairo_stroke (cr);
1231     }
1232
1233   gdk_cairo_set_source_rgba (cr, fg_color);
1234
1235   /* FIXME: thickness */
1236   thickness = 1;
1237
1238   if (flags & GTK_STATE_FLAG_INCONSISTENT)
1239     {
1240       gint line_thickness;
1241
1242       pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
1243       interior_size = MAX (1, exterior_size - 2 * pad);
1244
1245       if (interior_size < 7)
1246         {
1247           interior_size = 7;
1248           pad = MAX (0, (exterior_size - interior_size) / 2);
1249         }
1250
1251       line_thickness = MAX (1, (3 + interior_size * 2) / 7);
1252
1253       cairo_rectangle (cr,
1254                        x + pad,
1255                        y + pad + (interior_size - line_thickness) / 2.,
1256                        interior_size,
1257                        line_thickness);
1258       cairo_fill (cr);
1259     }
1260   if (flags & GTK_STATE_FLAG_ACTIVE)
1261     {
1262       pad = thickness + MAX (1, 2 * (exterior_size - 2 * thickness) / 9);
1263       interior_size = MAX (1, exterior_size - 2 * pad);
1264
1265       if (interior_size < 5)
1266         {
1267           interior_size = 7;
1268           pad = MAX (0, (exterior_size - interior_size) / 2);
1269         }
1270
1271       cairo_new_sub_path (cr);
1272       cairo_arc (cr,
1273                  x + pad + interior_size / 2.,
1274                  y + pad + interior_size / 2.,
1275                  interior_size / 2.,
1276                  0, 2 * G_PI);
1277       cairo_fill (cr);
1278     }
1279
1280   cairo_restore (cr);
1281
1282   gdk_rgba_free (fg_color);
1283   gdk_rgba_free (bg_color);
1284   gdk_rgba_free (border_color);
1285   gtk_border_free (border);
1286 }
1287
1288 static void
1289 add_path_arrow (cairo_t *cr,
1290                 gdouble  angle,
1291                 gdouble  x,
1292                 gdouble  y,
1293                 gdouble  size)
1294 {
1295   cairo_save (cr);
1296
1297   cairo_translate (cr, x + (size / 2), y + (size / 2));
1298   cairo_rotate (cr, angle);
1299
1300   cairo_move_to (cr, 0, - (size / 4));
1301   cairo_line_to (cr, - (size / 2), (size / 4));
1302   cairo_line_to (cr, (size / 2), (size / 4));
1303   cairo_close_path (cr);
1304
1305   cairo_restore (cr);
1306 }
1307
1308 static void
1309 gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
1310                                  cairo_t          *cr,
1311                                  gdouble           angle,
1312                                  gdouble           x,
1313                                  gdouble           y,
1314                                  gdouble           size)
1315 {
1316   GtkStateFlags flags;
1317   GdkRGBA *fg_color;
1318
1319   cairo_save (cr);
1320
1321   flags = gtk_theming_engine_get_state (engine);
1322
1323   gtk_theming_engine_get (engine, flags,
1324                           "color", &fg_color,
1325                           NULL);
1326
1327   if (flags & GTK_STATE_FLAG_INSENSITIVE)
1328     {
1329       add_path_arrow (cr, angle, x + 1, y + 1, size);
1330       cairo_set_source_rgb (cr, 1, 1, 1);
1331       cairo_fill (cr);
1332     }
1333
1334   add_path_arrow (cr, angle, x, y, size);
1335   gdk_cairo_set_source_rgba (cr, fg_color);
1336   cairo_fill (cr);
1337
1338   cairo_restore (cr);
1339
1340   gdk_rgba_free (fg_color);
1341 }
1342
1343 static void
1344 add_path_line (cairo_t        *cr,
1345                gdouble         x1,
1346                gdouble         y1,
1347                gdouble         x2,
1348                gdouble         y2)
1349 {
1350   /* Adjust endpoints */
1351   if (y1 == y2)
1352     {
1353       y1 += 0.5;
1354       y2 += 0.5;
1355       x2 += 1;
1356     }
1357   else if (x1 == x2)
1358     {
1359       x1 += 0.5;
1360       x2 += 0.5;
1361       y2 += 1;
1362     }
1363
1364   cairo_move_to (cr, x1, y1);
1365   cairo_line_to (cr, x2, y2);
1366 }
1367
1368 static void
1369 color_shade (const GdkRGBA *color,
1370              gdouble        factor,
1371              GdkRGBA       *color_return)
1372 {
1373   GtkSymbolicColor *literal, *shade;
1374
1375   literal = gtk_symbolic_color_new_literal (color);
1376   shade = gtk_symbolic_color_new_shade (literal, factor);
1377   gtk_symbolic_color_unref (literal);
1378
1379   gtk_symbolic_color_resolve (shade, NULL, color_return);
1380   gtk_symbolic_color_unref (shade);
1381 }
1382
1383 static void
1384 _cairo_round_rectangle_sides (cairo_t          *cr,
1385                               gdouble           radius,
1386                               gdouble           x,
1387                               gdouble           y,
1388                               gdouble           width,
1389                               gdouble           height,
1390                               guint             sides,
1391                               GtkJunctionSides  junction)
1392 {
1393   radius = CLAMP (radius, 0, MIN (width / 2, height / 2));
1394
1395   if (sides & SIDE_RIGHT)
1396     {
1397       if (radius == 0 ||
1398           (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
1399         cairo_move_to (cr, x + width, y);
1400       else
1401         {
1402           cairo_new_sub_path (cr);
1403           cairo_arc (cr, x + width - radius, y + radius, radius, - G_PI / 4, 0);
1404         }
1405
1406       if (radius == 0 ||
1407           (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
1408         cairo_line_to (cr, x + width, y + height);
1409       else
1410         cairo_arc (cr, x + width - radius, y + height - radius, radius, 0, G_PI / 4);
1411     }
1412
1413   if (sides & SIDE_BOTTOM)
1414     {
1415       if (radius != 0 &&
1416           ! (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
1417         {
1418           if ((sides & SIDE_RIGHT) == 0)
1419             cairo_new_sub_path (cr);
1420
1421           cairo_arc (cr, x + width - radius, y + height - radius, radius, G_PI / 4, G_PI / 2);
1422         }
1423       else if ((sides & SIDE_RIGHT) == 0)
1424         cairo_move_to (cr, x + width, y + height);
1425
1426       if (radius == 0 ||
1427           (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
1428         cairo_line_to (cr, x, y + height);
1429       else
1430         cairo_arc (cr, x + radius, y + height - radius, radius, G_PI / 2, 3 * (G_PI / 4));
1431     }
1432   else
1433     cairo_move_to (cr, x, y + height);
1434
1435   if (sides & SIDE_LEFT)
1436     {
1437       if (radius != 0 &&
1438           ! (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
1439         {
1440           if ((sides & SIDE_BOTTOM) == 0)
1441             cairo_new_sub_path (cr);
1442
1443           cairo_arc (cr, x + radius, y + height - radius, radius, 3 * (G_PI / 4), G_PI);
1444         }
1445       else if ((sides & SIDE_BOTTOM) == 0)
1446         cairo_move_to (cr, x, y + height);
1447
1448       if (radius == 0 ||
1449           (junction & GTK_JUNCTION_CORNER_TOPLEFT))
1450         cairo_line_to (cr, x, y);
1451       else
1452         cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI + G_PI / 4);
1453     }
1454
1455   if (sides & SIDE_TOP)
1456     {
1457       if (radius != 0 &&
1458           ! (junction & GTK_JUNCTION_CORNER_TOPLEFT))
1459         {
1460           if ((sides & SIDE_LEFT) == 0)
1461             cairo_new_sub_path (cr);
1462
1463           cairo_arc (cr, x + radius, y + radius, radius, 5 * (G_PI / 4), 3 * (G_PI / 2));
1464         }
1465       else if ((sides & SIDE_LEFT) == 0)
1466         cairo_move_to (cr, x, y);
1467
1468       if (radius == 0 ||
1469           (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
1470         cairo_line_to (cr, x + width, y);
1471       else
1472         cairo_arc (cr, x + width - radius, y + radius, radius, 3 * (G_PI / 2), - G_PI / 4);
1473     }
1474 }
1475
1476 static void
1477 render_background_internal (GtkThemingEngine *engine,
1478                             cairo_t          *cr,
1479                             gdouble           x,
1480                             gdouble           y,
1481                             gdouble           width,
1482                             gdouble           height,
1483                             GtkJunctionSides  junction)
1484 {
1485   GdkRGBA *bg_color;
1486   cairo_pattern_t *pattern;
1487   GtkStateFlags flags;
1488   gboolean running;
1489   gdouble progress, alpha = 1;
1490   gint radius;
1491
1492   flags = gtk_theming_engine_get_state (engine);
1493   cairo_save (cr);
1494
1495   gtk_theming_engine_get (engine, flags,
1496                           "background-image", &pattern,
1497                           "background-color", &bg_color,
1498                           "border-radius", &radius,
1499                           NULL);
1500
1501   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
1502   _cairo_round_rectangle_sides (cr, (gdouble) radius,
1503                                 x, y, width, height,
1504                                 SIDE_ALL, junction);
1505   cairo_clip (cr);
1506
1507   if (gtk_theming_engine_has_class (engine, "background"))
1508     {
1509       cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
1510       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1511       cairo_paint (cr);
1512     }
1513
1514   cairo_translate (cr, x, y);
1515   cairo_scale (cr, width, height);
1516
1517   if (running)
1518     {
1519       cairo_pattern_t *other_pattern;
1520       GtkStateFlags other_flags;
1521       GdkRGBA *other_bg;
1522       cairo_pattern_t *new_pattern = NULL;
1523
1524       if (flags & GTK_STATE_FLAG_PRELIGHT)
1525         {
1526           other_flags = flags & ~(GTK_STATE_FLAG_PRELIGHT);
1527           progress = 1 - progress;
1528         }
1529       else
1530         other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
1531
1532       gtk_theming_engine_get (engine, other_flags,
1533                               "background-image", &other_pattern,
1534                               "background-color", &other_bg,
1535                               NULL);
1536
1537       if (pattern && other_pattern)
1538         {
1539           cairo_pattern_type_t type, other_type;
1540           gint n0, n1;
1541
1542           cairo_pattern_get_color_stop_count (pattern, &n0);
1543           cairo_pattern_get_color_stop_count (other_pattern, &n1);
1544           type = cairo_pattern_get_type (pattern);
1545           other_type = cairo_pattern_get_type (other_pattern);
1546
1547           if (type == other_type && n0 == n1)
1548             {
1549               gdouble offset0, red0, green0, blue0, alpha0;
1550               gdouble offset1, red1, green1, blue1, alpha1;
1551               gdouble x00, x01, y00, y01, x10, x11, y10, y11;
1552               gdouble r00, r01, r10, r11;
1553               guint i;
1554
1555               if (type == CAIRO_PATTERN_TYPE_LINEAR)
1556                 {
1557                   cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01);
1558                   cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11);
1559
1560                   new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
1561                                                              y00 + (y10 - y00) * progress,
1562                                                              x01 + (x11 - x01) * progress,
1563                                                              y01 + (y11 - y01) * progress);
1564                 }
1565               else
1566                 {
1567                   cairo_pattern_get_radial_circles (pattern, &x00, &y00, &r00, &x01, &y01, &r01);
1568                   cairo_pattern_get_radial_circles (other_pattern, &x10, &y10, &r10, &x11, &y11, &r11);
1569
1570                   new_pattern = cairo_pattern_create_radial (x00 + (x10 - x00) * progress,
1571                                                              y00 + (y10 - y00) * progress,
1572                                                              r00 + (r10 - r00) * progress,
1573                                                              x01 + (x11 - x01) * progress,
1574                                                              y01 + (y11 - y01) * progress,
1575                                                              r01 + (r11 - r01) * progress);
1576                 }
1577
1578               cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST);
1579               i = 0;
1580
1581               /* Blend both gradients into one */
1582               while (i < n0 && i < n1)
1583                 {
1584                   cairo_pattern_get_color_stop_rgba (pattern, i,
1585                                                      &offset0,
1586                                                      &red0, &green0, &blue0,
1587                                                      &alpha0);
1588                   cairo_pattern_get_color_stop_rgba (other_pattern, i,
1589                                                      &offset1,
1590                                                      &red1, &green1, &blue1,
1591                                                      &alpha1);
1592
1593                   cairo_pattern_add_color_stop_rgba (new_pattern,
1594                                                      offset0 + ((offset1 - offset0) * progress),
1595                                                      red0 + ((red1 - red0) * progress),
1596                                                      green0 + ((green1 - green0) * progress),
1597                                                      blue0 + ((blue1 - blue0) * progress),
1598                                                      alpha0 + ((alpha1 - alpha0) * progress));
1599                   i++;
1600                 }
1601             }
1602           else
1603             {
1604               /* Different pattern types, or different color
1605                * stop counts, alpha blend both patterns.
1606                */
1607               cairo_rectangle (cr, 0, 0, 1, 1);
1608               cairo_set_source (cr, other_pattern);
1609               cairo_fill_preserve (cr);
1610
1611               /* Set alpha for posterior drawing
1612                * of the target pattern
1613                */
1614               alpha = 1 - progress;
1615             }
1616         }
1617       else if (pattern || other_pattern)
1618         {
1619           cairo_pattern_t *p;
1620           const GdkRGBA *c;
1621           gdouble x0, y0, x1, y1, r0, r1;
1622           gint n, i;
1623
1624           /* Blend a pattern with a color */
1625           if (pattern)
1626             {
1627               p = pattern;
1628               c = other_bg;
1629               progress = 1 - progress;
1630             }
1631           else
1632             {
1633               p = other_pattern;
1634               c = bg_color;
1635             }
1636
1637           if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
1638             {
1639               cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
1640               new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1);
1641             }
1642           else
1643             {
1644               cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1);
1645               new_pattern = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1);
1646             }
1647
1648           cairo_pattern_get_color_stop_count (p, &n);
1649
1650           for (i = 0; i < n; i++)
1651             {
1652               gdouble red1, green1, blue1, alpha1;
1653               gdouble offset;
1654
1655               cairo_pattern_get_color_stop_rgba (p, i,
1656                                                  &offset,
1657                                                  &red1, &green1, &blue1,
1658                                                  &alpha1);
1659               cairo_pattern_add_color_stop_rgba (new_pattern, offset,
1660                                                  c->red + ((red1 - c->red) * progress),
1661                                                  c->green + ((green1 - c->green) * progress),
1662                                                  c->blue + ((blue1 - c->blue) * progress),
1663                                                  c->alpha + ((alpha1 - c->alpha) * progress));
1664             }
1665         }
1666       else
1667         {
1668           const GdkRGBA *color, *other_color;
1669
1670           /* Merge just colors */
1671           color = bg_color;
1672           other_color = other_bg;
1673
1674           new_pattern = cairo_pattern_create_rgba (CLAMP (color->red + ((other_color->red - color->red) * progress), 0, 1),
1675                                                    CLAMP (color->green + ((other_color->green - color->green) * progress), 0, 1),
1676                                                    CLAMP (color->blue + ((other_color->blue - color->blue) * progress), 0, 1),
1677                                                    CLAMP (color->alpha + ((other_color->alpha - color->alpha) * progress), 0, 1));
1678         }
1679
1680       if (new_pattern)
1681         {
1682           /* Replace pattern to use */
1683           cairo_pattern_destroy (pattern);
1684           pattern = new_pattern;
1685         }
1686
1687       if (other_pattern)
1688         cairo_pattern_destroy (other_pattern);
1689
1690       if (other_bg)
1691         gdk_rgba_free (other_bg);
1692     }
1693
1694   cairo_rectangle (cr, 0, 0, 1, 1);
1695
1696   if (pattern)
1697     {
1698       cairo_set_source (cr, pattern);
1699       cairo_pattern_destroy (pattern);
1700     }
1701   else
1702     gdk_cairo_set_source_rgba (cr, bg_color);
1703
1704   if (alpha == 1)
1705     cairo_fill (cr);
1706   else
1707     {
1708       cairo_pattern_t *mask;
1709
1710       mask = cairo_pattern_create_rgba (1, 1, 1, alpha);
1711       cairo_mask (cr, mask);
1712       cairo_pattern_destroy (mask);
1713     }
1714
1715   cairo_restore (cr);
1716
1717   gdk_rgba_free (bg_color);
1718 }
1719
1720 static void
1721 gtk_theming_engine_render_background (GtkThemingEngine *engine,
1722                                       cairo_t          *cr,
1723                                       gdouble           x,
1724                                       gdouble           y,
1725                                       gdouble           width,
1726                                       gdouble           height)
1727 {
1728   GtkJunctionSides junction;
1729   GtkStateFlags flags;
1730   GtkBorder *border;
1731
1732   junction = gtk_theming_engine_get_junction_sides (engine);
1733
1734   if (gtk_theming_engine_has_class (engine, "spinbutton") &&
1735       gtk_theming_engine_has_class (engine, "button"))
1736     {
1737       x += 2;
1738       y += 2;
1739       width -= 4;
1740       height -= 4;
1741     }
1742
1743   flags = gtk_theming_engine_get_state (engine);
1744   gtk_theming_engine_get (engine, flags,
1745                           "border-width", &border,
1746                           NULL);
1747
1748   x += border->left;
1749   y += border->top;
1750   width -= border->left + border->right;
1751   height -= border->top + border->bottom;
1752
1753   render_background_internal (engine, cr,
1754                               x, y, width, height,
1755                               junction);
1756
1757   gtk_border_free (border);
1758 }
1759
1760 /* Renders the small triangle on corners so
1761  * frames with 0 radius have a 3D-like effect
1762  */
1763 static void
1764 _cairo_corner_triangle (cairo_t *cr,
1765                         gdouble  x,
1766                         gdouble  y,
1767                         gint     size)
1768 {
1769   gint i;
1770
1771   cairo_move_to (cr, x + 0.5, y + size - 0.5);
1772   cairo_line_to (cr, x + size - 0.5, y + size - 0.5);
1773   cairo_line_to (cr, x + size - 0.5, y + 0.5);
1774
1775   for (i = 1; i < size - 1; i++)
1776     {
1777       cairo_move_to (cr, x + size - 0.5, y + i + 0.5);
1778       cairo_line_to (cr, x + (size - i) - 0.5, y + i + 0.5);
1779     }
1780 }
1781
1782 static void
1783 render_frame_internal (GtkThemingEngine *engine,
1784                        cairo_t          *cr,
1785                        gdouble           x,
1786                        gdouble           y,
1787                        gdouble           width,
1788                        gdouble           height,
1789                        guint             hidden_side,
1790                        GtkJunctionSides  junction)
1791 {
1792   GtkStateFlags state;
1793   GdkRGBA lighter;
1794   GdkRGBA *border_color;
1795   GtkBorderStyle border_style;
1796   gint border_width, radius;
1797   gdouble progress, d1, d2, m;
1798   gboolean running;
1799   GtkBorder *border;
1800
1801   state = gtk_theming_engine_get_state (engine);
1802   gtk_theming_engine_get (engine, state,
1803                           "border-color", &border_color,
1804                           "border-style", &border_style,
1805                           "border-width", &border,
1806                           "border-radius", &radius,
1807                           NULL);
1808
1809   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
1810   border_width = MIN (MIN (border->top, border->bottom),
1811                       MIN (border->left, border->right));
1812
1813   if (running)
1814     {
1815       GtkStateFlags other_state;
1816       GdkRGBA *other_color;
1817
1818       if (state & GTK_STATE_FLAG_PRELIGHT)
1819         {
1820           other_state = state & ~(GTK_STATE_FLAG_PRELIGHT);
1821           progress = 1 - progress;
1822         }
1823       else
1824         other_state = state | GTK_STATE_FLAG_PRELIGHT;
1825
1826       gtk_theming_engine_get (engine, other_state,
1827                               "border-color", &other_color,
1828                               NULL);
1829
1830       border_color->red = CLAMP (border_color->red + ((other_color->red - border_color->red) * progress), 0, 1);
1831       border_color->green = CLAMP (border_color->green + ((other_color->green - border_color->green) * progress), 0, 1);
1832       border_color->blue = CLAMP (border_color->blue + ((other_color->blue - border_color->blue) * progress), 0, 1);
1833       border_color->alpha = CLAMP (border_color->alpha + ((other_color->alpha - border_color->alpha) * progress), 0, 1);
1834
1835       gdk_rgba_free (other_color);
1836     }
1837
1838   cairo_save (cr);
1839
1840   color_shade (border_color, 1.8, &lighter);
1841
1842   switch (border_style)
1843     {
1844     case GTK_BORDER_STYLE_NONE:
1845       break;
1846     case GTK_BORDER_STYLE_SOLID:
1847       cairo_set_line_width (cr, border_width);
1848       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1849
1850       if (border_width > 1)
1851         {
1852           x += (gdouble) border_width / 2;
1853           y += (gdouble) border_width / 2;
1854           width -= border_width;
1855           height -= border_width;
1856         }
1857       else if (border_width == 1)
1858         {
1859           x += 0.5;
1860           y += 0.5;
1861           width -= 1;
1862           height -= 1;
1863         }
1864
1865       _cairo_round_rectangle_sides (cr, (gdouble) radius,
1866                                     x, y, width, height,
1867                                     SIDE_ALL & ~(hidden_side),
1868                                     junction);
1869       gdk_cairo_set_source_rgba (cr, border_color);
1870       cairo_stroke (cr);
1871
1872       break;
1873     case GTK_BORDER_STYLE_INSET:
1874     case GTK_BORDER_STYLE_OUTSET:
1875       cairo_set_line_width (cr, border_width);
1876
1877       if (radius == 0)
1878         cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1879       else
1880         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
1881
1882       if (border_width > 1)
1883         {
1884           d1 = (gdouble) border_width / 2;
1885           d2 = border_width;
1886         }
1887       else
1888         {
1889           d1 = 0.5;
1890           d2 = 1;
1891         }
1892
1893       cairo_save (cr);
1894
1895       m = MIN (width, height);
1896       m /= 2;
1897
1898       if (border_style == GTK_BORDER_STYLE_INSET)
1899         gdk_cairo_set_source_rgba (cr, &lighter);
1900       else
1901         gdk_cairo_set_source_rgba (cr, border_color);
1902
1903       _cairo_round_rectangle_sides (cr, (gdouble) radius,
1904                                     x + d1, y + d1,
1905                                     width - d2, height - d2,
1906                                     (SIDE_BOTTOM | SIDE_RIGHT) & ~(hidden_side),
1907                                     junction);
1908       cairo_stroke (cr);
1909
1910       if (border_style == GTK_BORDER_STYLE_INSET)
1911         gdk_cairo_set_source_rgba (cr, border_color);
1912       else
1913         gdk_cairo_set_source_rgba (cr, &lighter);
1914
1915       _cairo_round_rectangle_sides (cr, (gdouble) radius,
1916                                     x + d1, y + d1,
1917                                     width - d2, height - d2,
1918                                     (SIDE_TOP | SIDE_LEFT) & ~(hidden_side),
1919                                     junction);
1920       cairo_stroke (cr);
1921
1922       if (border_width > 1)
1923         {
1924           /* overprint top/right and bottom/left corner
1925            * triangles if there are square corners there,
1926            * to give the box a 3D-like appearance.
1927            */
1928           cairo_save (cr);
1929
1930           if (border_style == GTK_BORDER_STYLE_INSET)
1931             gdk_cairo_set_source_rgba (cr, &lighter);
1932           else
1933             gdk_cairo_set_source_rgba (cr, border_color);
1934
1935           cairo_set_line_width (cr, 1);
1936
1937           if (radius == 0 ||
1938               (junction & GTK_JUNCTION_CORNER_TOPRIGHT) != 0)
1939             _cairo_corner_triangle (cr,
1940                                     x + width - border_width, y,
1941                                     border_width);
1942
1943           if (radius == 0 ||
1944               (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) != 0)
1945             _cairo_corner_triangle (cr,
1946                                     x, y + height - border_width,
1947                                     border_width);
1948           cairo_stroke (cr);
1949           cairo_restore (cr);
1950         }
1951
1952       cairo_restore (cr);
1953       break;
1954     }
1955
1956   cairo_restore (cr);
1957
1958   if (border_color)
1959     gdk_rgba_free (border_color);
1960
1961   gtk_border_free (border);
1962 }
1963
1964 static void
1965 gtk_theming_engine_render_frame (GtkThemingEngine *engine,
1966                                  cairo_t          *cr,
1967                                  gdouble           x,
1968                                  gdouble           y,
1969                                  gdouble           width,
1970                                  gdouble           height)
1971 {
1972   GtkStateFlags flags;
1973   Gtk9Slice *slice;
1974   GtkBorderStyle border_style;
1975   GtkJunctionSides junction;
1976
1977   flags = gtk_theming_engine_get_state (engine);
1978   junction = gtk_theming_engine_get_junction_sides (engine);
1979
1980   gtk_theming_engine_get (engine, flags,
1981                           "border-image", &slice,
1982                           "border-style", &border_style,
1983                           NULL);
1984
1985   if (slice)
1986     {
1987       _gtk_9slice_render (slice, cr, x, y, width, height);
1988       _gtk_9slice_unref (slice);
1989     }
1990   else if (border_style != GTK_BORDER_STYLE_NONE)
1991     render_frame_internal (engine, cr,
1992                            x, y, width, height,
1993                            0, junction);
1994 }
1995
1996 static void
1997 gtk_theming_engine_render_expander (GtkThemingEngine *engine,
1998                                     cairo_t          *cr,
1999                                     gdouble           x,
2000                                     gdouble           y,
2001                                     gdouble           width,
2002                                     gdouble           height)
2003 {
2004   GtkStateFlags flags;
2005   GdkRGBA *outline_color, *fg_color;
2006   double vertical_overshoot;
2007   int diameter;
2008   double radius;
2009   double interp;                /* interpolation factor for center position */
2010   double x_double_horz, y_double_horz;
2011   double x_double_vert, y_double_vert;
2012   double x_double, y_double;
2013   gdouble angle;
2014   gint line_width;
2015   gboolean running, is_rtl;
2016   gdouble progress;
2017
2018   cairo_save (cr);
2019   flags = gtk_theming_engine_get_state (engine);
2020
2021   gtk_theming_engine_get (engine, flags,
2022                           "color", &fg_color,
2023                           NULL);
2024   gtk_theming_engine_get (engine, flags,
2025                           "border-color", &outline_color,
2026                           NULL);
2027
2028   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress);
2029   is_rtl = (gtk_theming_engine_get_direction (engine) == GTK_TEXT_DIR_RTL);
2030   line_width = 1;
2031
2032   if (!running)
2033     progress = (flags & GTK_STATE_FLAG_ACTIVE) ? 1 : 0;
2034
2035   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_VERTICAL))
2036     {
2037       if (is_rtl)
2038         angle = (G_PI) - ((G_PI / 2) * progress);
2039       else
2040         angle = (G_PI / 2) * progress;
2041     }
2042   else
2043     {
2044       if (is_rtl)
2045         angle = (G_PI / 2) + ((G_PI / 2) * progress);
2046       else
2047         angle = (G_PI / 2) - ((G_PI / 2) * progress);
2048     }
2049
2050   interp = progress;
2051
2052   /* Compute distance that the stroke extends beyonds the end
2053    * of the triangle we draw.
2054    */
2055   vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
2056
2057   /* For odd line widths, we end the vertical line of the triangle
2058    * at a half pixel, so we round differently.
2059    */
2060   if (line_width % 2 == 1)
2061     vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
2062   else
2063     vertical_overshoot = ceil (vertical_overshoot);
2064
2065   /* Adjust the size of the triangle we draw so that the entire stroke fits
2066    */
2067   diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
2068
2069   /* If the line width is odd, we want the diameter to be even,
2070    * and vice versa, so force the sum to be odd. This relationship
2071    * makes the point of the triangle look right.
2072    */
2073   diameter -= (1 - (diameter + line_width) % 2);
2074
2075   radius = diameter / 2.;
2076
2077   /* Adjust the center so that the stroke is properly aligned with
2078    * the pixel grid. The center adjustment is different for the
2079    * horizontal and vertical orientations. For intermediate positions
2080    * we interpolate between the two.
2081    */
2082   x_double_vert = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
2083   y_double_vert = (y + height / 2) - 0.5;
2084
2085   x_double_horz = (x + width / 2) - 0.5;
2086   y_double_horz = floor ((y + height / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
2087
2088   x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
2089   y_double = y_double_vert * (1 - interp) + y_double_horz * interp;
2090
2091   cairo_translate (cr, x_double, y_double);
2092   cairo_rotate (cr, angle);
2093
2094   cairo_move_to (cr, - radius / 2., - radius);
2095   cairo_line_to (cr,   radius / 2.,   0);
2096   cairo_line_to (cr, - radius / 2.,   radius);
2097   cairo_close_path (cr);
2098
2099   cairo_set_line_width (cr, line_width);
2100
2101   gdk_cairo_set_source_rgba (cr, fg_color);
2102
2103   cairo_fill_preserve (cr);
2104
2105   gdk_cairo_set_source_rgba (cr, outline_color);
2106   cairo_stroke (cr);
2107
2108   cairo_restore (cr);
2109
2110   gdk_rgba_free (fg_color);
2111   gdk_rgba_free (outline_color);
2112 }
2113
2114 static void
2115 gtk_theming_engine_render_focus (GtkThemingEngine *engine,
2116                                  cairo_t          *cr,
2117                                  gdouble           x,
2118                                  gdouble           y,
2119                                  gdouble           width,
2120                                  gdouble           height)
2121 {
2122   GtkStateFlags flags;
2123   GdkRGBA *color;
2124   gint line_width;
2125   gint8 *dash_list;
2126
2127   cairo_save (cr);
2128   flags = gtk_theming_engine_get_state (engine);
2129
2130   gtk_theming_engine_get (engine, flags,
2131                           "color", &color,
2132                           NULL);
2133
2134   gtk_theming_engine_get_style (engine,
2135                                 "focus-line-width", &line_width,
2136                                 "focus-line-pattern", (gchar *) &dash_list,
2137                                 NULL);
2138
2139   cairo_set_line_width (cr, (gdouble) line_width);
2140
2141   if (dash_list[0])
2142     {
2143       gint n_dashes = strlen ((const gchar *) dash_list);
2144       gdouble *dashes = g_new (gdouble, n_dashes);
2145       gdouble total_length = 0;
2146       gdouble dash_offset;
2147       gint i;
2148
2149       for (i = 0; i < n_dashes; i++)
2150         {
2151           dashes[i] = dash_list[i];
2152           total_length += dash_list[i];
2153         }
2154
2155       /* The dash offset here aligns the pattern to integer pixels
2156        * by starting the dash at the right side of the left border
2157        * Negative dash offsets in cairo don't work
2158        * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
2159        */
2160       dash_offset = - line_width / 2.;
2161
2162       while (dash_offset < 0)
2163         dash_offset += total_length;
2164
2165       cairo_set_dash (cr, dashes, n_dashes, dash_offset);
2166       g_free (dashes);
2167     }
2168
2169   cairo_rectangle (cr,
2170                    x + line_width / 2.,
2171                    y + line_width / 2.,
2172                    width - line_width,
2173                    height - line_width);
2174
2175   gdk_cairo_set_source_rgba (cr, color);
2176   cairo_stroke (cr);
2177
2178   cairo_restore (cr);
2179
2180   gdk_rgba_free (color);
2181   g_free (dash_list);
2182 }
2183
2184 static void
2185 gtk_theming_engine_render_line (GtkThemingEngine *engine,
2186                                 cairo_t          *cr,
2187                                 gdouble           x0,
2188                                 gdouble           y0,
2189                                 gdouble           x1,
2190                                 gdouble           y1)
2191 {
2192   GdkRGBA *bg_color, darker, lighter;
2193   GtkStateFlags flags;
2194   gint i, thickness, thickness_dark, thickness_light, len;
2195   cairo_matrix_t matrix;
2196   gdouble angle;
2197
2198   /* FIXME: thickness */
2199   thickness = 2;
2200   thickness_dark = thickness / 2;
2201   thickness_light = thickness - thickness_dark;
2202
2203   flags = gtk_theming_engine_get_state (engine);
2204   cairo_save (cr);
2205
2206   gtk_theming_engine_get (engine, flags,
2207                           "background-color", &bg_color,
2208                           NULL);
2209   color_shade (bg_color, 0.7, &darker);
2210   color_shade (bg_color, 1.3, &lighter);
2211
2212   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
2213   cairo_set_line_width (cr, 1);
2214
2215   angle = atan2 (x1 - x0, y1 - y0);
2216   angle = (2 * G_PI) - angle;
2217   angle += G_PI / 2;
2218
2219   cairo_get_matrix (cr, &matrix);
2220   cairo_matrix_translate (&matrix, x0, y0);
2221   cairo_matrix_rotate (&matrix, angle);
2222   cairo_set_matrix (cr, &matrix);
2223
2224   x1 -= x0;
2225   y1 -= y0;
2226
2227   len = (gint) sqrt ((x1 * x1) + (y1 * y1));
2228
2229   y0 = -thickness_dark;
2230
2231   for (i = 0; i < thickness_dark; i++)
2232     {
2233       gdk_cairo_set_source_rgba (cr, &lighter);
2234       add_path_line (cr, len - i - 1.5, y0, len - 0.5, y0);
2235       cairo_stroke (cr);
2236
2237       gdk_cairo_set_source_rgba (cr, &darker);
2238       add_path_line (cr, 0.5, y0, len - i - 1.5, y0);
2239       cairo_stroke (cr);
2240
2241       y0++;
2242     }
2243
2244   for (i = 0; i < thickness_light; i++)
2245     {
2246       gdk_cairo_set_source_rgba (cr, &darker);
2247       add_path_line (cr, 0.5, y0, thickness_light - i + 0.5, y0);
2248       cairo_stroke (cr);
2249
2250       gdk_cairo_set_source_rgba (cr, &lighter);
2251       add_path_line (cr, thickness_light - i + 0.5, y0, len - 0.5, y0);
2252       cairo_stroke (cr);
2253
2254       y0++;
2255     }
2256
2257   cairo_restore (cr);
2258
2259   gdk_rgba_free (bg_color);
2260 }
2261
2262 static void
2263 gtk_theming_engine_render_layout (GtkThemingEngine *engine,
2264                                   cairo_t          *cr,
2265                                   gdouble           x,
2266                                   gdouble           y,
2267                                   PangoLayout      *layout)
2268 {
2269   const PangoMatrix *matrix;
2270   GdkRGBA *fg_color;
2271   GtkStateFlags flags;
2272   GdkScreen *screen;
2273   gdouble progress;
2274   gboolean running;
2275
2276   cairo_save (cr);
2277   flags = gtk_theming_engine_get_state (engine);
2278
2279   gtk_theming_engine_get (engine, flags,
2280                           "color", &fg_color,
2281                           NULL);
2282
2283   screen = gtk_theming_engine_get_screen (engine);
2284   matrix = pango_context_get_matrix (pango_layout_get_context (layout));
2285
2286   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
2287
2288   if (running)
2289     {
2290       GtkStateFlags other_flags;
2291       GdkRGBA *other_fg;
2292
2293       if (flags & GTK_STATE_FLAG_PRELIGHT)
2294         {
2295           other_flags = flags & ~(GTK_STATE_FLAG_PRELIGHT);
2296           progress = 1 - progress;
2297         }
2298       else
2299         other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
2300
2301       gtk_theming_engine_get (engine, other_flags,
2302                               "color", &other_fg,
2303                               NULL);
2304
2305       if (fg_color && other_fg)
2306         {
2307           fg_color->red = CLAMP (fg_color->red + ((other_fg->red - fg_color->red) * progress), 0, 1);
2308           fg_color->green = CLAMP (fg_color->green + ((other_fg->green - fg_color->green) * progress), 0, 1);
2309           fg_color->blue = CLAMP (fg_color->blue + ((other_fg->blue - fg_color->blue) * progress), 0, 1);
2310           fg_color->alpha = CLAMP (fg_color->alpha + ((other_fg->alpha - fg_color->alpha) * progress), 0, 1);
2311         }
2312
2313       if (other_fg)
2314         gdk_rgba_free (other_fg);
2315     }
2316
2317   if (matrix)
2318     {
2319       cairo_matrix_t cairo_matrix;
2320       PangoMatrix tmp_matrix;
2321       PangoRectangle rect;
2322
2323       cairo_matrix_init (&cairo_matrix,
2324                          matrix->xx, matrix->yx,
2325                          matrix->xy, matrix->yy,
2326                          matrix->x0, matrix->y0);
2327
2328       pango_layout_get_extents (layout, NULL, &rect);
2329       pango_matrix_transform_rectangle (matrix, &rect);
2330       pango_extents_to_pixels (&rect, NULL);
2331
2332       tmp_matrix = *matrix;
2333       cairo_matrix.x0 += x - rect.x;
2334       cairo_matrix.y0 += y - rect.y;
2335
2336       cairo_set_matrix (cr, &cairo_matrix);
2337     }
2338   else
2339     cairo_move_to (cr, x, y);
2340
2341   if (flags & GTK_STATE_FLAG_INSENSITIVE)
2342     {
2343       cairo_save (cr);
2344       cairo_set_source_rgb (cr, 1, 1, 1);
2345       cairo_move_to (cr, x + 1, y + 1);
2346       _gtk_pango_fill_layout (cr, layout);
2347       cairo_restore (cr);
2348     }
2349
2350   gdk_cairo_set_source_rgba (cr, fg_color);
2351   pango_cairo_show_layout (cr, layout);
2352
2353   cairo_restore (cr);
2354
2355   gdk_rgba_free (fg_color);
2356 }
2357
2358 static void
2359 gtk_theming_engine_render_slider (GtkThemingEngine *engine,
2360                                   cairo_t          *cr,
2361                                   gdouble           x,
2362                                   gdouble           y,
2363                                   gdouble           width,
2364                                   gdouble           height,
2365                                   GtkOrientation    orientation)
2366 {
2367   const GtkWidgetPath *path;
2368   gint thickness;
2369
2370   path = gtk_theming_engine_get_path (engine);
2371
2372   gtk_theming_engine_render_background (engine, cr, x, y, width, height);
2373   gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
2374
2375   /* FIXME: thickness */
2376   thickness = 2;
2377
2378   if (gtk_widget_path_is_type (path, GTK_TYPE_SCALE))
2379     {
2380       if (orientation == GTK_ORIENTATION_VERTICAL)
2381         gtk_theming_engine_render_line (engine, cr,
2382                                         x + thickness,
2383                                         y + (gint) height / 2,
2384                                         x + width - thickness - 1,
2385                                         y + (gint) height / 2);
2386       else
2387         gtk_theming_engine_render_line (engine, cr,
2388                                         x + (gint) width / 2,
2389                                         y + thickness,
2390                                         x + (gint) width / 2,
2391                                         y + height - thickness - 1);
2392     }
2393 }
2394
2395 static void
2396 gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
2397                                      cairo_t          *cr,
2398                                      gdouble           x,
2399                                      gdouble           y,
2400                                      gdouble           width,
2401                                      gdouble           height,
2402                                      GtkPositionType   gap_side,
2403                                      gdouble           xy0_gap,
2404                                      gdouble           xy1_gap)
2405 {
2406   GtkJunctionSides junction;
2407   GtkStateFlags state;
2408   gint border_width, radius;
2409   gdouble x0, y0, x1, y1, xc, yc, wc, hc;
2410   GtkBorder *border;
2411
2412   xc = yc = wc = hc = 0;
2413   state = gtk_theming_engine_get_state (engine);
2414   junction = gtk_theming_engine_get_junction_sides (engine);
2415   gtk_theming_engine_get (engine, state,
2416                           "border-width", &border,
2417                           "border-radius", &radius,
2418                           NULL);
2419
2420   border_width = MIN (MIN (border->top, border->bottom),
2421                       MIN (border->left, border->right));
2422
2423   cairo_save (cr);
2424
2425   switch (gap_side)
2426     {
2427     case GTK_POS_TOP:
2428       xc = x + xy0_gap + border_width;
2429       yc = y;
2430       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2431       hc = border_width;
2432
2433       if (xy0_gap < radius)
2434         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
2435
2436       if (xy1_gap > width - radius)
2437         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
2438       break;
2439     case GTK_POS_BOTTOM:
2440       xc = x + xy0_gap + border_width;
2441       yc = y + height - border_width;
2442       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2443       hc = border_width;
2444
2445       if (xy0_gap < radius)
2446         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
2447
2448       if (xy1_gap > width - radius)
2449         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2450
2451       break;
2452     case GTK_POS_LEFT:
2453       xc = x;
2454       yc = y + xy0_gap + border_width;
2455       wc = border_width;
2456       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2457
2458       if (xy0_gap < radius)
2459         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
2460
2461       if (xy1_gap > height - radius)
2462         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
2463
2464       break;
2465     case GTK_POS_RIGHT:
2466       xc = x + width - border_width;
2467       yc = y + xy0_gap + border_width;
2468       wc = border_width;
2469       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
2470
2471       if (xy0_gap < radius)
2472         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
2473
2474       if (xy1_gap > height - radius)
2475         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2476
2477       break;
2478     }
2479
2480   cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
2481   cairo_rectangle (cr, x0, y0, x1 - x0, yc - y0);
2482   cairo_rectangle (cr, x0, yc, xc - x0, hc);
2483   cairo_rectangle (cr, xc + wc, yc, x1 - (xc + wc), hc);
2484   cairo_rectangle (cr, x0, yc + hc, x1 - x0, y1 - (yc + hc));
2485   cairo_clip (cr);
2486
2487   render_frame_internal (engine, cr,
2488                          x, y, width, height,
2489                          0, junction);
2490
2491   cairo_restore (cr);
2492
2493   gtk_border_free (border);
2494 }
2495
2496 static void
2497 gtk_theming_engine_render_extension (GtkThemingEngine *engine,
2498                                      cairo_t          *cr,
2499                                      gdouble           x,
2500                                      gdouble           y,
2501                                      gdouble           width,
2502                                      gdouble           height,
2503                                      GtkPositionType   gap_side)
2504 {
2505   GtkJunctionSides junction = 0;
2506   guint hidden_side = 0;
2507
2508   cairo_save (cr);
2509
2510   switch (gap_side)
2511     {
2512     case GTK_POS_LEFT:
2513       junction = GTK_JUNCTION_LEFT;
2514       hidden_side = SIDE_LEFT;
2515
2516       cairo_translate (cr, x + width, y);
2517       cairo_rotate (cr, G_PI / 2);
2518       break;
2519     case GTK_POS_RIGHT:
2520       junction = GTK_JUNCTION_RIGHT;
2521       hidden_side = SIDE_RIGHT;
2522
2523       cairo_translate (cr, x, y + height);
2524       cairo_rotate (cr, - G_PI / 2);
2525       break;
2526     case GTK_POS_TOP:
2527       junction = GTK_JUNCTION_TOP;
2528       hidden_side = SIDE_TOP;
2529
2530       cairo_translate (cr, x + width, y + height);
2531       cairo_rotate (cr, G_PI);
2532       break;
2533     case GTK_POS_BOTTOM:
2534       junction = GTK_JUNCTION_BOTTOM;
2535       hidden_side = SIDE_BOTTOM;
2536
2537       cairo_translate (cr, x, y);
2538       break;
2539     }
2540
2541   if (gap_side == GTK_POS_TOP ||
2542       gap_side == GTK_POS_BOTTOM)
2543     render_background_internal (engine, cr,
2544                                 0, 0, width, height,
2545                                 GTK_JUNCTION_BOTTOM);
2546   else
2547     render_background_internal (engine, cr,
2548                                 0, 0, height, width,
2549                                 GTK_JUNCTION_BOTTOM);
2550   cairo_restore (cr);
2551
2552   cairo_save (cr);
2553
2554   render_frame_internal (engine, cr,
2555                          x, y, width, height,
2556                          hidden_side, junction);
2557
2558   cairo_restore (cr);
2559 }
2560
2561 static void
2562 render_dot (cairo_t       *cr,
2563             const GdkRGBA *lighter,
2564             const GdkRGBA *darker,
2565             gdouble        x,
2566             gdouble        y,
2567             gdouble        size)
2568 {
2569   size = CLAMP ((gint) size, 2, 3);
2570
2571   if (size == 2)
2572     {
2573       gdk_cairo_set_source_rgba (cr, lighter);
2574       cairo_rectangle (cr, x, y, 1, 1);
2575       cairo_rectangle (cr, x + 1, y + 1, 1, 1);
2576       cairo_fill (cr);
2577     }
2578   else if (size == 3)
2579     {
2580       gdk_cairo_set_source_rgba (cr, lighter);
2581       cairo_rectangle (cr, x, y, 2, 1);
2582       cairo_rectangle (cr, x, y, 1, 2);
2583       cairo_fill (cr);
2584
2585       gdk_cairo_set_source_rgba (cr, darker);
2586       cairo_rectangle (cr, x + 1, y + 1, 2, 1);
2587       cairo_rectangle (cr, x + 2, y, 1, 2);
2588       cairo_fill (cr);
2589     }
2590 }
2591
2592 static void
2593 gtk_theming_engine_render_handle (GtkThemingEngine *engine,
2594                                   cairo_t          *cr,
2595                                   gdouble           x,
2596                                   gdouble           y,
2597                                   gdouble           width,
2598                                   gdouble           height)
2599 {
2600   GtkStateFlags flags;
2601   GdkRGBA *bg_color;
2602   GdkRGBA lighter, darker;
2603   gint xx, yy;
2604
2605   cairo_save (cr);
2606   flags = gtk_theming_engine_get_state (engine);
2607
2608   cairo_set_line_width (cr, 1);
2609
2610   gtk_theming_engine_get (engine, flags,
2611                           "background-color", &bg_color,
2612                           NULL);
2613   color_shade (bg_color, 0.7, &darker);
2614   color_shade (bg_color, 1.3, &lighter);
2615
2616   gdk_cairo_set_source_rgba (cr, bg_color);
2617   cairo_rectangle (cr, x, y, width, height);
2618   cairo_fill (cr);
2619
2620   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_GRIP))
2621     {
2622       GtkJunctionSides sides;
2623       gint skip = -1;
2624
2625       cairo_save (cr);
2626
2627       cairo_set_line_width (cr, 1.0);
2628       sides = gtk_theming_engine_get_junction_sides (engine);
2629
2630       /* reduce confusing values to a meaningful state */
2631       if ((sides & (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)) == (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT))
2632         sides &= ~GTK_JUNCTION_CORNER_TOPLEFT;
2633
2634       if ((sides & (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT)) == (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT))
2635         sides &= ~GTK_JUNCTION_CORNER_TOPRIGHT;
2636
2637       if (sides == 0)
2638         sides = GTK_JUNCTION_CORNER_BOTTOMRIGHT;
2639
2640       /* align drawing area to the connected side */
2641       if (sides == GTK_JUNCTION_LEFT)
2642         {
2643           if (height < width)
2644             width = height;
2645         }
2646       else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
2647         {
2648           if (width < height)
2649             height = width;
2650           else if (height < width)
2651             width = height;
2652
2653           skip = 2;
2654         }
2655       else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
2656         {
2657           /* make it square, aligning to bottom left */
2658           if (width < height)
2659             {
2660               y += (height - width);
2661               height = width;
2662             }
2663           else if (height < width)
2664             width = height;
2665
2666           skip = 1;
2667         }
2668       else if (sides == GTK_JUNCTION_RIGHT)
2669         {
2670           /* aligning to right */
2671           if (height < width)
2672             {
2673               x += (width - height);
2674               width = height;
2675             }
2676         }
2677       else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
2678         {
2679           if (width < height)
2680             height = width;
2681           else if (height < width)
2682             {
2683               x += (width - height);
2684               width = height;
2685             }
2686
2687           skip = 3;
2688         }
2689       else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
2690         {
2691           /* make it square, aligning to bottom right */
2692           if (width < height)
2693             {
2694               y += (height - width);
2695               height = width;
2696             }
2697           else if (height < width)
2698             {
2699               x += (width - height);
2700               width = height;
2701             }
2702
2703           skip = 0;
2704         }
2705       else if (sides == GTK_JUNCTION_TOP)
2706         {
2707           if (width < height)
2708             height = width;
2709         }
2710       else if (sides == GTK_JUNCTION_BOTTOM)
2711         {
2712           /* align to bottom */
2713           if (width < height)
2714             {
2715               y += (height - width);
2716               height = width;
2717             }
2718         }
2719       else
2720         g_assert_not_reached ();
2721
2722       if (sides == GTK_JUNCTION_LEFT ||
2723           sides == GTK_JUNCTION_RIGHT)
2724         {
2725           gint xi;
2726
2727           xi = x;
2728
2729           while (xi < x + width)
2730             {
2731               gdk_cairo_set_source_rgba (cr, &lighter);
2732               add_path_line (cr, x, y, x, y + height);
2733               cairo_stroke (cr);
2734               xi++;
2735
2736               gdk_cairo_set_source_rgba (cr, &darker);
2737               add_path_line (cr, xi, y, xi, y + height);
2738               cairo_stroke (cr);
2739               xi += 2;
2740             }
2741         }
2742       else if (sides == GTK_JUNCTION_TOP ||
2743                sides == GTK_JUNCTION_BOTTOM)
2744         {
2745           gint yi;
2746
2747           yi = y;
2748
2749           while (yi < y + height)
2750             {
2751               gdk_cairo_set_source_rgba (cr, &lighter);
2752               add_path_line (cr, x, yi, x + width, yi);
2753               cairo_stroke (cr);
2754               yi++;
2755
2756               gdk_cairo_set_source_rgba (cr, &darker);
2757               add_path_line (cr, x, yi, x + width, yi);
2758               cairo_stroke (cr);
2759               yi+= 2;
2760             }
2761         }
2762       else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
2763         {
2764           gint xi, yi;
2765
2766           xi = x + width;
2767           yi = y + height;
2768
2769           while (xi > x + 3)
2770             {
2771               gdk_cairo_set_source_rgba (cr, &darker);
2772               add_path_line (cr, xi, y, x, yi);
2773               cairo_stroke (cr);
2774
2775               --xi;
2776               --yi;
2777
2778               add_path_line (cr, xi, y, x, yi);
2779               cairo_stroke (cr);
2780
2781               --xi;
2782               --yi;
2783
2784               gdk_cairo_set_source_rgba (cr, &lighter);
2785               add_path_line (cr, xi, y, x, yi);
2786               cairo_stroke (cr);
2787
2788               xi -= 3;
2789               yi -= 3;
2790             }
2791         }
2792       else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
2793         {
2794           gint xi, yi;
2795
2796           xi = x;
2797           yi = y + height;
2798
2799           while (xi < (x + width - 3))
2800             {
2801               gdk_cairo_set_source_rgba (cr, &lighter);
2802               add_path_line (cr, xi, y, x + width, yi);
2803               cairo_stroke (cr);
2804
2805               ++xi;
2806               --yi;
2807
2808               gdk_cairo_set_source_rgba (cr, &darker);
2809               add_path_line (cr, xi, y, x + width, yi);
2810               cairo_stroke (cr);
2811
2812               ++xi;
2813               --yi;
2814
2815               add_path_line (cr, xi, y, x + width, yi);
2816               cairo_stroke (cr);
2817
2818               xi += 3;
2819               yi -= 3;
2820             }
2821         }
2822       else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
2823         {
2824           gint xi, yi;
2825
2826           xi = x + width;
2827           yi = y;
2828
2829           while (xi > x + 3)
2830             {
2831               gdk_cairo_set_source_rgba (cr, &darker);
2832               add_path_line (cr, x, yi, xi, y + height);
2833               cairo_stroke (cr);
2834
2835               --xi;
2836               ++yi;
2837
2838               add_path_line (cr, x, yi, xi, y + height);
2839               cairo_stroke (cr);
2840
2841               --xi;
2842               ++yi;
2843
2844               gdk_cairo_set_source_rgba (cr, &lighter);
2845               add_path_line (cr, x, yi, xi, y + height);
2846               cairo_stroke (cr);
2847
2848               xi -= 3;
2849               yi += 3;
2850             }
2851         }
2852       else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
2853         {
2854           gint xi, yi;
2855
2856           xi = x;
2857           yi = y;
2858
2859           while (xi < (x + width - 3))
2860             {
2861               gdk_cairo_set_source_rgba (cr, &lighter);
2862               add_path_line (cr, xi, y + height, x + width, yi);
2863               cairo_stroke (cr);
2864
2865               ++xi;
2866               ++yi;
2867
2868               gdk_cairo_set_source_rgba (cr, &darker);
2869               add_path_line (cr, xi, y + height, x + width, yi);
2870               cairo_stroke (cr);
2871
2872               ++xi;
2873               ++yi;
2874
2875               add_path_line (cr, xi, y + height, x + width, yi);
2876               cairo_stroke (cr);
2877
2878               xi += 3;
2879               yi += 3;
2880             }
2881         }
2882
2883       cairo_restore (cr);
2884     }
2885   else if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_PANE_SEPARATOR))
2886     {
2887       if (width > height)
2888         for (xx = x + width / 2 - 15; xx <= x + width / 2 + 15; xx += 5)
2889           render_dot (cr, &lighter, &darker, xx, y + height / 2 - 1, 3);
2890       else
2891         for (yy = y + height / 2 - 15; yy <= y + height / 2 + 15; yy += 5)
2892           render_dot (cr, &lighter, &darker, x + width / 2 - 1, yy, 3);
2893     }
2894   else
2895     {
2896       for (yy = y; yy < y + height; yy += 3)
2897         for (xx = x; xx < x + width; xx += 6)
2898           {
2899             render_dot (cr, &lighter, &darker, xx, yy, 2);
2900             render_dot (cr, &lighter, &darker, xx + 3, yy + 1, 2);
2901           }
2902     }
2903
2904   cairo_restore (cr);
2905
2906   gdk_rgba_free (bg_color);
2907 }
2908
2909 static void
2910 gtk_theming_engine_render_activity (GtkThemingEngine *engine,
2911                                     cairo_t          *cr,
2912                                     gdouble           x,
2913                                     gdouble           y,
2914                                     gdouble           width,
2915                                     gdouble           height)
2916 {
2917   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_SPINNER))
2918     {
2919       GtkStateFlags state;
2920       guint num_steps, step;
2921       GdkRGBA *color;
2922       gdouble dx, dy;
2923       gdouble progress;
2924       gdouble radius;
2925       gdouble half;
2926       gint i;
2927
2928       num_steps = 12;
2929
2930       state = gtk_theming_engine_get_state (engine);
2931       gtk_theming_engine_get (engine, state,
2932                               "color", &color,
2933                               NULL);
2934
2935       if (gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress))
2936         step = (guint) (progress * num_steps);
2937       else
2938         step = 0;
2939
2940       cairo_save (cr);
2941
2942       cairo_translate (cr, x, y);
2943
2944       /* draw clip region */
2945       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
2946
2947       dx = width / 2;
2948       dy = height / 2;
2949       radius = MIN (width / 2, height / 2);
2950       half = num_steps / 2;
2951
2952       for (i = 0; i < num_steps; i++)
2953         {
2954           gint inset = 0.7 * radius;
2955
2956           /* transparency is a function of time and intial value */
2957           gdouble t = (gdouble) ((i + num_steps - step)
2958                                  % num_steps) / num_steps;
2959
2960           cairo_save (cr);
2961
2962           cairo_set_source_rgba (cr,
2963                                  color->red,
2964                                  color->green,
2965                                  color->blue,
2966                                  color->alpha * t);
2967
2968           cairo_set_line_width (cr, 2.0);
2969           cairo_move_to (cr,
2970                          dx + (radius - inset) * cos (i * G_PI / half),
2971                          dy + (radius - inset) * sin (i * G_PI / half));
2972           cairo_line_to (cr,
2973                          dx + radius * cos (i * G_PI / half),
2974                          dy + radius * sin (i * G_PI / half));
2975           cairo_stroke (cr);
2976
2977           cairo_restore (cr);
2978         }
2979
2980       cairo_restore (cr);
2981
2982       gdk_rgba_free (color);
2983     }
2984   else
2985     {
2986       gtk_theming_engine_render_background (engine, cr, x, y, width, height);
2987       gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
2988     }
2989 }
2990
2991 static GdkPixbuf *
2992 scale_or_ref (GdkPixbuf *src,
2993               gint       width,
2994               gint       height)
2995 {
2996   if (width == gdk_pixbuf_get_width (src) &&
2997       height == gdk_pixbuf_get_height (src))
2998     return g_object_ref (src);
2999   else
3000     return gdk_pixbuf_scale_simple (src,
3001                                     width, height,
3002                                     GDK_INTERP_BILINEAR);
3003 }
3004
3005 static gboolean
3006 lookup_icon_size (GtkThemingEngine *engine,
3007                   GtkIconSize       size,
3008                   gint             *width,
3009                   gint             *height)
3010 {
3011   GdkScreen *screen;
3012   GtkSettings *settings;
3013
3014   screen = gtk_theming_engine_get_screen (engine);
3015   settings = gtk_settings_get_for_screen (screen);
3016
3017   return gtk_icon_size_lookup_for_settings (settings, size, width, height);
3018 }
3019
3020 static GdkPixbuf *
3021 gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
3022                                        const GtkIconSource *source,
3023                                        GtkIconSize          size)
3024 {
3025   GdkPixbuf *scaled;
3026   GdkPixbuf *stated;
3027   GdkPixbuf *base_pixbuf;
3028   GtkStateFlags state;
3029   gint width = 1;
3030   gint height = 1;
3031
3032   base_pixbuf = gtk_icon_source_get_pixbuf (source);
3033   state = gtk_theming_engine_get_state (engine);
3034
3035   g_return_val_if_fail (base_pixbuf != NULL, NULL);
3036
3037   if (size != (GtkIconSize) -1 &&
3038       !lookup_icon_size (engine, size, &width, &height))
3039     {
3040       g_warning (G_STRLOC ": invalid icon size '%d'", size);
3041       return NULL;
3042     }
3043
3044   /* If the size was wildcarded, and we're allowed to scale, then scale; otherwise,
3045    * leave it alone.
3046    */
3047   if (size != (GtkIconSize) -1 &&
3048       gtk_icon_source_get_size_wildcarded (source))
3049     scaled = scale_or_ref (base_pixbuf, width, height);
3050   else
3051     scaled = g_object_ref (base_pixbuf);
3052
3053   /* If the state was wildcarded, then generate a state. */
3054   if (gtk_icon_source_get_state_wildcarded (source))
3055     {
3056       if (state & GTK_STATE_FLAG_INSENSITIVE)
3057         {
3058           stated = gdk_pixbuf_copy (scaled);
3059           gdk_pixbuf_saturate_and_pixelate (scaled, stated,
3060                                             0.8, TRUE);
3061           g_object_unref (scaled);
3062         }
3063       else if (state & GTK_STATE_FLAG_PRELIGHT)
3064         {
3065           stated = gdk_pixbuf_copy (scaled);
3066           gdk_pixbuf_saturate_and_pixelate (scaled, stated,
3067                                             1.2, FALSE);
3068           g_object_unref (scaled);
3069         }
3070       else
3071         stated = scaled;
3072     }
3073   else
3074     stated = scaled;
3075
3076   return stated;
3077 }