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