]> Pileus Git - ~andy/gtk/blob - gtk/gtkthemingengine.c
fa65ab645813319d6de880b679c55fb6f9aeea2a
[~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 "gtkalias.h"
30
31 typedef struct GtkThemingEnginePrivate GtkThemingEnginePrivate;
32
33 enum {
34   SIDE_LEFT   = 1,
35   SIDE_BOTTOM = 1 << 1,
36   SIDE_RIGHT  = 1 << 2,
37   SIDE_TOP    = 1 << 3
38 };
39
40 struct GtkThemingEnginePrivate
41 {
42   GtkStyleContext *context;
43 };
44
45 #define GTK_THEMING_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_THEMING_ENGINE, GtkThemingEnginePrivate))
46
47 static void gtk_theming_engine_render_check (GtkThemingEngine *engine,
48                                              cairo_t          *cr,
49                                              gdouble           x,
50                                              gdouble           y,
51                                              gdouble           width,
52                                              gdouble           height);
53 static void gtk_theming_engine_render_option (GtkThemingEngine *engine,
54                                               cairo_t          *cr,
55                                               gdouble           x,
56                                               gdouble           y,
57                                               gdouble           width,
58                                               gdouble           height);
59 static void gtk_theming_engine_render_arrow  (GtkThemingEngine *engine,
60                                               cairo_t          *cr,
61                                               gdouble           angle,
62                                               gdouble           x,
63                                               gdouble           y,
64                                               gdouble           size);
65 static void gtk_theming_engine_render_background (GtkThemingEngine *engine,
66                                                   cairo_t          *cr,
67                                                   gdouble           x,
68                                                   gdouble           y,
69                                                   gdouble           width,
70                                                   gdouble           height);
71 static void gtk_theming_engine_render_frame  (GtkThemingEngine *engine,
72                                               cairo_t          *cr,
73                                               gdouble           x,
74                                               gdouble           y,
75                                               gdouble           width,
76                                               gdouble           height);
77 static void gtk_theming_engine_render_expander (GtkThemingEngine *engine,
78                                                 cairo_t          *cr,
79                                                 gdouble           x,
80                                                 gdouble           y,
81                                                 gdouble           width,
82                                                 gdouble           height);
83 static void gtk_theming_engine_render_focus    (GtkThemingEngine *engine,
84                                                 cairo_t          *cr,
85                                                 gdouble           x,
86                                                 gdouble           y,
87                                                 gdouble           width,
88                                                 gdouble           height);
89 static void gtk_theming_engine_render_layout   (GtkThemingEngine *engine,
90                                                 cairo_t          *cr,
91                                                 gdouble           x,
92                                                 gdouble           y,
93                                                 PangoLayout      *layout);
94 static void gtk_theming_engine_render_line     (GtkThemingEngine *engine,
95                                                 cairo_t          *cr,
96                                                 gdouble           x0,
97                                                 gdouble           y0,
98                                                 gdouble           x1,
99                                                 gdouble           y1);
100 static void gtk_theming_engine_render_slider   (GtkThemingEngine *engine,
101                                                 cairo_t          *cr,
102                                                 gdouble           x,
103                                                 gdouble           y,
104                                                 gdouble           width,
105                                                 gdouble           height,
106                                                 GtkOrientation    orientation);
107 static void gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
108                                                  cairo_t          *cr,
109                                                  gdouble           x,
110                                                  gdouble           y,
111                                                  gdouble           width,
112                                                  gdouble           height,
113                                                  GtkPositionType   gap_side,
114                                                  gdouble           xy0_gap,
115                                                  gdouble           xy1_gap);
116 static void gtk_theming_engine_render_extension (GtkThemingEngine *engine,
117                                                  cairo_t          *cr,
118                                                  gdouble           x,
119                                                  gdouble           y,
120                                                  gdouble           width,
121                                                  gdouble           height,
122                                                  GtkPositionType   gap_side);
123 static void gtk_theming_engine_render_handle    (GtkThemingEngine *engine,
124                                                  cairo_t          *cr,
125                                                  gdouble           x,
126                                                  gdouble           y,
127                                                  gdouble           width,
128                                                  gdouble           height,
129                                                  GtkOrientation    orientation);
130
131 G_DEFINE_TYPE (GtkThemingEngine, gtk_theming_engine, G_TYPE_OBJECT)
132
133
134 typedef struct GtkThemingModule GtkThemingModule;
135 typedef struct GtkThemingModuleClass GtkThemingModuleClass;
136
137 struct GtkThemingModule
138 {
139   GTypeModule parent_instance;
140   gchar *name;
141
142   GtkThemingEngine * (*create_engine) (void);
143 };
144
145 struct GtkThemingModuleClass
146 {
147   GTypeModuleClass parent_class;
148 };
149
150 #define GTK_TYPE_THEMING_MODULE  (gtk_theming_module_get_type ())
151 #define GTK_THEMING_MODULE(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_THEMING_MODULE, GtkThemingModule))
152 #define GTK_IS_THEMING_MODULE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_THEMING_MODULE))
153
154 G_DEFINE_TYPE (GtkThemingModule, gtk_theming_module, G_TYPE_TYPE_MODULE);
155
156 static void
157 gtk_theming_engine_class_init (GtkThemingEngineClass *klass)
158 {
159   GObjectClass *object_class = G_OBJECT_CLASS (klass);
160
161   klass->render_check = gtk_theming_engine_render_check;
162   klass->render_option = gtk_theming_engine_render_option;
163   klass->render_arrow = gtk_theming_engine_render_arrow;
164   klass->render_background = gtk_theming_engine_render_background;
165   klass->render_frame = gtk_theming_engine_render_frame;
166   klass->render_expander = gtk_theming_engine_render_expander;
167   klass->render_focus = gtk_theming_engine_render_focus;
168   klass->render_layout = gtk_theming_engine_render_layout;
169   klass->render_line = gtk_theming_engine_render_line;
170   klass->render_slider = gtk_theming_engine_render_slider;
171   klass->render_frame_gap = gtk_theming_engine_render_frame_gap;
172   klass->render_extension = gtk_theming_engine_render_extension;
173   klass->render_handle = gtk_theming_engine_render_handle;
174
175   g_type_class_add_private (object_class, sizeof (GtkThemingEnginePrivate));
176 }
177
178 static void
179 gtk_theming_engine_init (GtkThemingEngine *engine)
180 {
181   engine->priv = GTK_THEMING_ENGINE_GET_PRIVATE (engine);
182 }
183
184 void
185 _gtk_theming_engine_set_context (GtkThemingEngine *engine,
186                                  GtkStyleContext  *context)
187 {
188   GtkThemingEnginePrivate *priv;
189
190   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
191   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
192
193   priv = engine->priv;
194   priv->context = context;
195 }
196
197 void
198 gtk_theming_engine_register_property (GtkThemingEngine       *engine,
199                                       const gchar            *property_name,
200                                       GType                   type,
201                                       const GValue           *default_value,
202                                       GtkStylePropertyParser  parse_func)
203 {
204   gchar *name;
205
206   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
207   g_return_if_fail (property_name != NULL);
208   g_return_if_fail (type != G_TYPE_INVALID);
209   g_return_if_fail (default_value != NULL && G_IS_VALUE (default_value));
210
211   name = g_strdup_printf ("-%s-%s", G_OBJECT_TYPE_NAME (engine), property_name);
212   gtk_style_set_register_property (name, type, default_value, parse_func);
213   g_free (name);
214 }
215
216 void
217 gtk_theming_engine_get_property (GtkThemingEngine *engine,
218                                  const gchar      *property,
219                                  GtkStateType      state,
220                                  GValue           *value)
221 {
222   GtkThemingEnginePrivate *priv;
223
224   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
225   g_return_if_fail (property != NULL);
226   g_return_if_fail (state < GTK_STATE_LAST);
227   g_return_if_fail (value != NULL);
228
229   priv = engine->priv;
230   gtk_style_context_get_property (priv->context, property, state, value);
231 }
232
233 void
234 gtk_theming_engine_get_valist (GtkThemingEngine *engine,
235                                GtkStateType      state,
236                                va_list           args)
237 {
238   GtkThemingEnginePrivate *priv;
239
240   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
241   g_return_if_fail (state < GTK_STATE_LAST);
242
243   priv = engine->priv;
244   gtk_style_context_get_valist (priv->context, state, args);
245 }
246
247 void
248 gtk_theming_engine_get (GtkThemingEngine *engine,
249                         GtkStateType      state,
250                         ...)
251 {
252   GtkThemingEnginePrivate *priv;
253   va_list args;
254
255   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
256   g_return_if_fail (state < GTK_STATE_LAST);
257
258   priv = engine->priv;
259
260   va_start (args, state);
261   gtk_style_context_get_valist (priv->context, state, args);
262   va_end (args);
263 }
264
265 void
266 gtk_theming_engine_get_style_property (GtkThemingEngine *engine,
267                                        const gchar      *property_name,
268                                        GValue           *value)
269 {
270   GtkThemingEnginePrivate *priv;
271
272   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
273   g_return_if_fail (property_name != NULL);
274
275   priv = engine->priv;
276   gtk_style_context_get_style_property (priv->context, property_name, value);
277 }
278
279 void
280 gtk_theming_engine_get_style_valist (GtkThemingEngine *engine,
281                                      va_list           args)
282 {
283   GtkThemingEnginePrivate *priv;
284
285   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
286
287   priv = engine->priv;
288   gtk_style_context_get_style_valist (priv->context, args);
289 }
290
291 void
292 gtk_theming_engine_get_style (GtkThemingEngine *engine,
293                               ...)
294 {
295   GtkThemingEnginePrivate *priv;
296   va_list args;
297
298   g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
299
300   priv = engine->priv;
301
302   va_start (args, engine);
303   gtk_style_context_get_style_valist (priv->context, args);
304   va_end (args);
305 }
306
307 GtkStateFlags
308 gtk_theming_engine_get_state (GtkThemingEngine *engine)
309 {
310   GtkThemingEnginePrivate *priv;
311
312   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
313
314   priv = engine->priv;
315   return gtk_style_context_get_state (priv->context);
316 }
317
318 gboolean
319 gtk_theming_engine_is_state_set (GtkThemingEngine *engine,
320                                  GtkStateType      state)
321 {
322   GtkThemingEnginePrivate *priv;
323
324   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
325
326   priv = engine->priv;
327   return gtk_style_context_is_state_set (priv->context, state);
328 }
329
330 G_CONST_RETURN GtkWidgetPath *
331 gtk_theming_engine_get_path (GtkThemingEngine *engine)
332 {
333   GtkThemingEnginePrivate *priv;
334
335   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
336
337   priv = engine->priv;
338   return gtk_style_context_get_path (priv->context);
339 }
340
341 gboolean
342 gtk_theming_engine_has_class (GtkThemingEngine *engine,
343                               const gchar      *style_class)
344 {
345   GtkThemingEnginePrivate *priv;
346
347   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
348
349   priv = engine->priv;
350   return gtk_style_context_has_class (priv->context, style_class);
351 }
352
353 gboolean
354 gtk_theming_engine_has_child_class (GtkThemingEngine   *engine,
355                                     const gchar        *style_class,
356                                     GtkChildClassFlags *flags)
357 {
358   GtkThemingEnginePrivate *priv;
359
360   if (flags)
361     *flags = 0;
362
363   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
364
365   priv = engine->priv;
366   return gtk_style_context_has_child_class (priv->context, style_class, flags);
367 }
368
369 GtkTextDirection
370 gtk_theming_engine_get_direction (GtkThemingEngine *engine)
371 {
372   GtkThemingEnginePrivate *priv;
373
374   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), GTK_TEXT_DIR_LTR);
375
376   priv = engine->priv;
377   return gtk_style_context_get_direction (priv->context);
378 }
379
380 GtkJunctionSides
381 gtk_theming_engine_get_junction_sides (GtkThemingEngine *engine)
382 {
383   GtkThemingEnginePrivate *priv;
384
385   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
386
387   priv = engine->priv;
388   return gtk_style_context_get_junction_sides (priv->context);
389 }
390
391 /* GtkThemingModule */
392
393 static gboolean
394 gtk_theming_module_load (GTypeModule *type_module)
395 {
396   GtkThemingModule *theming_module;
397   GModule *module;
398   gchar *name, *module_path;
399
400   theming_module = GTK_THEMING_MODULE (type_module);
401   name = theming_module->name;
402   module_path = _gtk_find_module (name, "theming-engines");
403
404   if (!module_path)
405     {
406       g_warning (_("Unable to locate theme engine in module path: \"%s\","), name);
407       return FALSE;
408     }
409
410   module = g_module_open (module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
411   g_free (module_path);
412
413   if (!module)
414     {
415       g_warning ("%s", g_module_error ());
416       return FALSE;
417     }
418
419   if (!g_module_symbol (module, "create_engine",
420                         (gpointer *) &theming_module->create_engine))
421     {
422       g_warning ("%s", g_module_error());
423       g_module_close (module);
424
425       return FALSE;
426     }
427
428   g_module_make_resident (module);
429
430   return TRUE;
431 }
432
433 static void
434 gtk_theming_module_class_init (GtkThemingModuleClass *klass)
435 {
436   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
437
438   module_class->load = gtk_theming_module_load;
439 }
440
441 static void
442 gtk_theming_module_init (GtkThemingModule *module)
443 {
444 }
445
446 G_CONST_RETURN GtkThemingEngine *
447 gtk_theming_engine_load (const gchar *name)
448 {
449   static GHashTable *engines = NULL;
450   static GtkThemingEngine *default_engine;
451   GtkThemingEngine *engine = NULL;
452
453   if (name)
454     {
455       if (!engines)
456         engines = g_hash_table_new (g_str_hash, g_str_equal);
457
458       engine = g_hash_table_lookup (engines, name);
459
460       if (!engine)
461         {
462           GtkThemingModule *module;
463
464           module = g_object_new (GTK_TYPE_THEMING_MODULE, NULL);
465           g_type_module_set_name (G_TYPE_MODULE (module), name);
466           module->name = g_strdup (name);
467
468           if (module && g_type_module_use (G_TYPE_MODULE (module)))
469             {
470               engine = (module->create_engine) ();
471
472               if (engine)
473                 g_hash_table_insert (engines, module->name, engine);
474             }
475         }
476     }
477
478   if (!engine)
479     {
480       if (G_UNLIKELY (!default_engine))
481         default_engine = g_object_new (GTK_TYPE_THEMING_ENGINE, NULL);
482
483       engine = default_engine;
484     }
485
486   return engine;
487 }
488
489 GdkScreen *
490 gtk_theming_engine_get_screen (GtkThemingEngine *engine)
491 {
492   GtkThemingEnginePrivate *priv;
493
494   g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
495
496   priv = engine->priv;
497   return gtk_style_context_get_screen (priv->context);
498 }
499
500 /* Paint method implementations */
501 static void
502 gtk_theming_engine_render_check (GtkThemingEngine *engine,
503                                  cairo_t          *cr,
504                                  gdouble           x,
505                                  gdouble           y,
506                                  gdouble           width,
507                                  gdouble           height)
508 {
509   GdkColor *fg_color, *base_color, *text_color;
510   const GtkWidgetPath *path;
511   GtkStateFlags flags;
512   GtkStateType state;
513   gint exterior_size, interior_size, thickness, pad;
514
515   flags = gtk_theming_engine_get_state (engine);
516   path = gtk_theming_engine_get_path (engine);
517   cairo_save (cr);
518
519   if (flags & GTK_STATE_FLAG_PRELIGHT)
520     state = GTK_STATE_PRELIGHT;
521   else
522     state = GTK_STATE_NORMAL;
523
524   gtk_theming_engine_get (engine, state,
525                           "foreground-color", &fg_color,
526                           "base-color", &base_color,
527                           "text-color", &text_color,
528                           NULL);
529
530   exterior_size = MIN (width, height);
531
532   if (exterior_size % 2 == 0) /* Ensure odd */
533     exterior_size -= 1;
534
535   /* FIXME: thickness */
536   thickness = 1;
537   pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
538   interior_size = MAX (1, exterior_size - 2 * pad);
539
540   if (interior_size < 7)
541     {
542       interior_size = 7;
543       pad = MAX (0, (exterior_size - interior_size) / 2);
544     }
545
546   x -= (1 + exterior_size - (gint) width) / 2;
547   y -= (1 + exterior_size - (gint) height) / 2;
548
549   if (!gtk_theming_engine_has_class (engine, "menu"))
550     {
551       cairo_set_line_width (cr, 1.0);
552
553       cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
554       gdk_cairo_set_source_color (cr, base_color);
555       cairo_fill_preserve (cr);
556
557       if (gtk_theming_engine_has_class (engine, "cell"))
558         gdk_cairo_set_source_color (cr, text_color);
559       else
560         gdk_cairo_set_source_color (cr, fg_color);
561
562       cairo_stroke (cr);
563     }
564
565   if (gtk_theming_engine_has_class (engine, "menu"))
566     gdk_cairo_set_source_color (cr, fg_color);
567   else
568     gdk_cairo_set_source_color (cr, text_color);
569
570   if (gtk_theming_engine_is_state_set (engine, GTK_STATE_INCONSISTENT))
571     {
572       int line_thickness = MAX (1, (3 + interior_size * 2) / 7);
573
574       cairo_rectangle (cr,
575                        x + pad,
576                        y + pad + (1 + interior_size - line_thickness) / 2,
577                        interior_size,
578                        line_thickness);
579       cairo_fill (cr);
580     }
581   else if (gtk_theming_engine_is_state_set (engine, GTK_STATE_ACTIVE))
582     {
583       cairo_translate (cr,
584                        x + pad, y + pad);
585
586       cairo_scale (cr, interior_size / 7., interior_size / 7.);
587
588       cairo_move_to  (cr, 7.0, 0.0);
589       cairo_line_to  (cr, 7.5, 1.0);
590       cairo_curve_to (cr, 5.3, 2.0,
591                       4.3, 4.0,
592                       3.5, 7.0);
593       cairo_curve_to (cr, 3.0, 5.7,
594                       1.3, 4.7,
595                       0.0, 4.7);
596       cairo_line_to  (cr, 0.2, 3.5);
597       cairo_curve_to (cr, 1.1, 3.5,
598                       2.3, 4.3,
599                       3.0, 5.0);
600       cairo_curve_to (cr, 1.0, 3.9,
601                       2.4, 4.1,
602                       3.2, 4.9);
603       cairo_curve_to (cr, 3.5, 3.1,
604                       5.2, 2.0,
605                       7.0, 0.0);
606
607       cairo_fill (cr);
608     }
609
610   cairo_restore (cr);
611
612   gdk_color_free (fg_color);
613   gdk_color_free (base_color);
614   gdk_color_free (text_color);
615 }
616
617 static void
618 gtk_theming_engine_render_option (GtkThemingEngine *engine,
619                                   cairo_t          *cr,
620                                   gdouble           x,
621                                   gdouble           y,
622                                   gdouble           width,
623                                   gdouble           height)
624 {
625   GtkStateFlags flags;
626   GdkColor *base_color, *fg_color, *text_color;
627   const GtkWidgetPath *path;
628   GtkStateType state;
629   gint exterior_size, interior_size, pad, thickness;
630   gdouble radius;
631
632   /* FIXME: set clipping */
633
634   flags = gtk_theming_engine_get_state (engine);
635   path = gtk_theming_engine_get_path (engine);
636   radius = MIN (width, height) / 2 - 0.5;
637
638   cairo_save (cr);
639
640   if (flags & GTK_STATE_FLAG_PRELIGHT)
641     state = GTK_STATE_PRELIGHT;
642   else
643     state = GTK_STATE_NORMAL;
644
645   gtk_theming_engine_get (engine, state,
646                           "foreground-color", &fg_color,
647                           "base-color", &base_color,
648                           "text-color", &text_color,
649                           NULL);
650
651   exterior_size = MIN (width, height);
652
653   if (exterior_size % 2 == 0) /* Ensure odd */
654     exterior_size -= 1;
655
656   x -= (1 + exterior_size - width) / 2;
657   y -= (1 + exterior_size - height) / 2;
658
659   if (!gtk_theming_engine_has_class (engine, "menu"))
660     {
661       gdk_cairo_set_source_color (cr, base_color);
662
663       cairo_arc (cr,
664                  x + exterior_size / 2.,
665                  y + exterior_size / 2.,
666                  (exterior_size - 1) / 2.,
667                  0, 2 * G_PI);
668
669       cairo_fill_preserve (cr);
670
671       if (gtk_theming_engine_has_class (engine, "cell"))
672         gdk_cairo_set_source_color (cr, text_color);
673       else
674         gdk_cairo_set_source_color (cr, fg_color);
675
676       cairo_set_line_width (cr, 1.);
677       cairo_stroke (cr);
678     }
679
680   if (gtk_theming_engine_has_class (engine, "menu"))
681     gdk_cairo_set_source_color (cr, fg_color);
682   else
683     gdk_cairo_set_source_color (cr, text_color);
684
685   /* FIXME: thickness */
686   thickness = 1;
687
688   if (gtk_theming_engine_is_state_set (engine, GTK_STATE_INCONSISTENT))
689     {
690       gint line_thickness;
691
692       pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
693       interior_size = MAX (1, exterior_size - 2 * pad);
694
695       if (interior_size < 7)
696         {
697           interior_size = 7;
698           pad = MAX (0, (exterior_size - interior_size) / 2);
699         }
700
701       line_thickness = MAX (1, (3 + interior_size * 2) / 7);
702
703       cairo_rectangle (cr,
704                        x + pad,
705                        y + pad + (interior_size - line_thickness) / 2.,
706                        interior_size,
707                        line_thickness);
708       cairo_fill (cr);
709     }
710   if (gtk_theming_engine_is_state_set (engine, GTK_STATE_ACTIVE))
711     {
712       pad = thickness + MAX (1, 2 * (exterior_size - 2 * thickness) / 9);
713       interior_size = MAX (1, exterior_size - 2 * pad);
714
715       if (interior_size < 5)
716         {
717           interior_size = 7;
718           pad = MAX (0, (exterior_size - interior_size) / 2);
719         }
720
721       cairo_arc (cr,
722                  x + pad + interior_size / 2.,
723                  y + pad + interior_size / 2.,
724                  interior_size / 2.,
725                  0, 2 * G_PI);
726       cairo_fill (cr);
727     }
728
729   cairo_restore (cr);
730 }
731
732 static void
733 add_path_arrow (cairo_t *cr,
734                 gdouble  angle,
735                 gdouble  x,
736                 gdouble  y,
737                 gdouble  size)
738 {
739   cairo_save (cr);
740
741   cairo_translate (cr, x + (size / 2), y + (size / 2));
742   cairo_rotate (cr, angle);
743
744   cairo_move_to (cr, 0, - (size / 4));
745   cairo_line_to (cr, - (size / 2), (size / 4));
746   cairo_line_to (cr, (size / 2), (size / 4));
747   cairo_close_path (cr);
748
749   cairo_restore (cr);
750 }
751
752 static void
753 gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
754                                  cairo_t          *cr,
755                                  gdouble           angle,
756                                  gdouble           x,
757                                  gdouble           y,
758                                  gdouble           size)
759 {
760   GtkStateFlags flags;
761   GtkStateType state;
762   GdkColor *fg_color;
763
764   cairo_save (cr);
765
766   flags = gtk_theming_engine_get_state (engine);
767
768   if (flags & GTK_STATE_FLAG_PRELIGHT)
769     state = GTK_STATE_PRELIGHT;
770   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
771     state = GTK_STATE_INSENSITIVE;
772   else
773     state = GTK_STATE_NORMAL;
774
775   gtk_theming_engine_get (engine, state,
776                           "foreground-color", &fg_color,
777                           NULL);
778
779   if (flags & GTK_STATE_FLAG_INSENSITIVE)
780     {
781       add_path_arrow (cr, angle, x + 1, y + 1, size);
782       cairo_set_source_rgb (cr, 1, 1, 1);
783       cairo_fill (cr);
784     }
785
786   add_path_arrow (cr, angle, x, y, size);
787   gdk_cairo_set_source_color (cr, fg_color);
788   cairo_fill (cr);
789
790   cairo_restore (cr);
791
792   gdk_color_free (fg_color);
793 }
794
795 static void
796 add_path_line (cairo_t        *cr,
797                gdouble         x1,
798                gdouble         y1,
799                gdouble         x2,
800                gdouble         y2)
801 {
802   /* Adjust endpoints */
803   if (y1 == y2)
804     {
805       y1 += 0.5;
806       y2 += 0.5;
807       x2 += 1;
808     }
809   else if (x1 == x2)
810     {
811       x1 += 0.5;
812       x2 += 0.5;
813       y2 += 1;
814     }
815
816   cairo_move_to (cr, x1, y1);
817   cairo_line_to (cr, x2, y2);
818 }
819
820 static void
821 add_path_rectangle_sides (cairo_t  *cr,
822                           gdouble   x,
823                           gdouble   y,
824                           gdouble   width,
825                           gdouble   height,
826                           guint     sides)
827 {
828   if (sides & SIDE_TOP)
829     {
830       cairo_move_to (cr, x, y + 0.5);
831       cairo_line_to (cr, x + width, y + 0.5);
832     }
833
834   if (sides & SIDE_RIGHT)
835     {
836       cairo_move_to (cr, x + width - 0.5, y);
837       cairo_line_to (cr, x + width - 0.5, y + height);
838     }
839
840   if (sides & SIDE_BOTTOM)
841     {
842       cairo_move_to (cr, x, y + height - 0.5);
843       cairo_line_to (cr, x + width, y + height - 0.5);
844     }
845
846   if (sides & SIDE_LEFT)
847     {
848       cairo_move_to (cr, x + 0.5, y + height);
849       cairo_line_to (cr, x + 0.5, y);
850     }
851 }
852
853 static void
854 add_path_gap_side (cairo_t           *cr,
855                    GtkPositionType    gap_side,
856                    gdouble            x,
857                    gdouble            y,
858                    gdouble            width,
859                    gdouble            height,
860                    gdouble            xy0_gap,
861                    gdouble            xy1_gap)
862 {
863   switch (gap_side)
864     {
865     case GTK_POS_TOP:
866       add_path_line (cr, x, y, x + xy0_gap, y);
867       add_path_line (cr, x + xy1_gap, y, x + width, y);
868       break;
869     case GTK_POS_BOTTOM:
870       add_path_line (cr, x, y + height, x + xy0_gap, y + height);
871       add_path_line (cr, x + xy1_gap, y + height, x + width, y + height);
872       break;
873     case GTK_POS_LEFT:
874       add_path_line (cr, x, y, x, y + xy0_gap);
875       add_path_line (cr, x, y + xy1_gap, x, y + height);
876       break;
877     case GTK_POS_RIGHT:
878       add_path_line (cr, x + width, y, x + width, y + xy0_gap);
879       add_path_line (cr, x + width, y + xy1_gap, x + width, y + height);
880       break;
881     }
882 }
883
884 static void
885 color_shade (const GdkColor *color,
886              gdouble         factor,
887              GdkColor       *color_return)
888 {
889   color_return->red = CLAMP (color->red * factor, 0, 65535);
890   color_return->green = CLAMP (color->green * factor, 0, 65535);
891   color_return->blue = CLAMP (color->blue * factor, 0, 65535);
892 }
893
894 static void
895 gtk_theming_engine_render_background (GtkThemingEngine *engine,
896                                       cairo_t          *cr,
897                                       gdouble           x,
898                                       gdouble           y,
899                                       gdouble           width,
900                                       gdouble           height)
901 {
902   GtkStateFlags flags;
903   GtkStateType state;
904   GdkColor *color;
905
906   cairo_save (cr);
907   flags = gtk_theming_engine_get_state (engine);
908
909   if (flags & GTK_STATE_FLAG_ACTIVE)
910     state = GTK_STATE_ACTIVE;
911   else if (flags & GTK_STATE_FLAG_SELECTED)
912     state = GTK_STATE_SELECTED;
913   else if (flags & GTK_STATE_FLAG_PRELIGHT)
914     state = GTK_STATE_PRELIGHT;
915   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
916     state = GTK_STATE_INSENSITIVE;
917   else
918     state = GTK_STATE_NORMAL;
919
920   if (gtk_theming_engine_has_class (engine, "entry"))
921     gtk_theming_engine_get (engine, state,
922                             "base-color", &color,
923                             NULL);
924   else
925     gtk_theming_engine_get (engine, state,
926                             "background-color", &color,
927                             NULL);
928
929   gdk_cairo_set_source_color (cr, color);
930
931   if (gtk_theming_engine_has_class (engine, "spinbutton") &&
932       gtk_theming_engine_has_class (engine, "button"))
933     cairo_rectangle (cr, x + 2, y + 2, width - 4, height - 4);
934   else
935     cairo_rectangle (cr, x, y, width, height);
936
937   if (gtk_theming_engine_has_class (engine, "tooltip"))
938     {
939       cairo_fill_preserve (cr);
940
941       cairo_set_source_rgb (cr, 0, 0, 0);
942       cairo_stroke (cr);
943     }
944   else
945     cairo_fill (cr);
946
947   cairo_restore (cr);
948
949   gdk_color_free (color);
950 }
951
952 static void
953 gtk_theming_engine_render_frame (GtkThemingEngine *engine,
954                                  cairo_t          *cr,
955                                  gdouble           x,
956                                  gdouble           y,
957                                  gdouble           width,
958                                  gdouble           height)
959 {
960   GtkStateFlags flags;
961   GtkStateType state;
962   GdkColor lighter, darker;
963   GdkColor *bg_color;
964
965   cairo_save (cr);
966   flags = gtk_theming_engine_get_state (engine);
967
968   if (flags & GTK_STATE_FLAG_PRELIGHT)
969     state = GTK_STATE_PRELIGHT;
970   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
971     state = GTK_STATE_INSENSITIVE;
972   else
973     state = GTK_STATE_NORMAL;
974
975   cairo_set_line_width (cr, 1);
976
977   gtk_theming_engine_get (engine, state,
978                           "background-color", &bg_color,
979                           NULL);
980   color_shade (bg_color, 0.7, &darker);
981   color_shade (bg_color, 1.3, &lighter);
982
983   if (gtk_theming_engine_has_class (engine, "entry") ||
984       gtk_theming_engine_has_class (engine, "scrolled-window") ||
985       gtk_theming_engine_has_class (engine, "viewport"))
986     {
987       gdk_cairo_set_source_color (cr, bg_color);
988       add_path_rectangle_sides (cr, x + 1, y + 1, width - 2, height - 2,
989                                 SIDE_BOTTOM | SIDE_RIGHT);
990       cairo_stroke (cr);
991
992       cairo_set_source_rgb (cr, 0, 0, 0);
993       add_path_rectangle_sides (cr, x + 1, y + 1, width - 2, height - 2,
994                                 SIDE_TOP | SIDE_LEFT);
995       cairo_stroke (cr);
996
997       cairo_set_source_rgb (cr, 1, 1, 1);
998       add_path_rectangle_sides (cr, x, y, width, height,
999                                 SIDE_BOTTOM | SIDE_RIGHT);
1000       cairo_stroke (cr);
1001
1002       gdk_cairo_set_source_color (cr, &darker);
1003       add_path_rectangle_sides (cr, x, y, width, height,
1004                                 SIDE_TOP | SIDE_LEFT);
1005       cairo_stroke (cr);
1006     }
1007   else if (gtk_theming_engine_has_class (engine, "button") &&
1008            gtk_theming_engine_has_class (engine, "default"))
1009     {
1010       cairo_set_source_rgb (cr, 0, 0, 0);
1011       cairo_rectangle (cr, x + 0.5, x + 0.5, width - 1, height - 1);
1012       cairo_stroke (cr);
1013     }
1014   else if (gtk_theming_engine_has_class (engine, "scrollbar") &&
1015            gtk_theming_engine_has_class (engine, "trough"))
1016     {
1017       gdk_cairo_set_source_color (cr, &darker);
1018       add_path_rectangle_sides (cr, x, y, width, height,
1019                                 SIDE_TOP | SIDE_LEFT);
1020       cairo_stroke (cr);
1021
1022       gdk_cairo_set_source_color (cr, &lighter);
1023       add_path_rectangle_sides (cr, x, y, width, height,
1024                                 SIDE_BOTTOM | SIDE_RIGHT);
1025       cairo_stroke (cr);
1026     }
1027   else if (gtk_theming_engine_has_class (engine, "spinbutton"))
1028     {
1029       if (gtk_theming_engine_has_class (engine, "button"))
1030         {
1031           GtkJunctionSides sides;
1032
1033           sides = gtk_theming_engine_get_junction_sides (engine);
1034
1035           if (sides & GTK_JUNCTION_BOTTOM)
1036             y += 2;
1037
1038           width -= 3;
1039           height -= 2;
1040
1041           if (gtk_theming_engine_get_direction (engine) == GTK_TEXT_DIR_RTL)
1042             x += 2;
1043           else
1044             x += 1;
1045
1046           gdk_cairo_set_source_color (cr, &lighter);
1047           add_path_rectangle_sides (cr, x, y, width, height, SIDE_TOP);
1048           cairo_stroke (cr);
1049
1050           gdk_cairo_set_source_color (cr, &darker);
1051           add_path_rectangle_sides (cr, x, y, width, height, SIDE_BOTTOM);
1052           cairo_stroke (cr);
1053         }
1054       else
1055         {
1056           gdk_cairo_set_source_color (cr, &lighter);
1057           add_path_rectangle_sides (cr, x, y, width, height,
1058                                     SIDE_BOTTOM | SIDE_RIGHT);
1059           cairo_stroke (cr);
1060
1061           gdk_cairo_set_source_color (cr, &darker);
1062           add_path_rectangle_sides (cr, x, y, width, height, SIDE_TOP);
1063           cairo_stroke (cr);
1064
1065           gdk_cairo_set_source_color (cr, bg_color);
1066           add_path_rectangle_sides (cr, x, y, width - 1, height - 1, SIDE_BOTTOM);
1067           cairo_stroke (cr);
1068
1069           cairo_set_source_rgb (cr, 0, 0, 0);
1070           add_path_rectangle_sides (cr, x, y + 1, width - 1, height - 3,
1071                                     SIDE_TOP | SIDE_LEFT | SIDE_RIGHT);
1072           cairo_stroke (cr);
1073         }
1074     }
1075   else
1076     {
1077       if (flags & GTK_STATE_FLAG_ACTIVE)
1078         {
1079           cairo_set_source_rgb (cr, 0, 0, 0);
1080           add_path_rectangle_sides (cr, x + 1, y + 1, width - 2, height - 2,
1081                                     SIDE_TOP | SIDE_LEFT);
1082           cairo_stroke (cr);
1083
1084           gdk_cairo_set_source_color (cr, &lighter);
1085           add_path_rectangle_sides (cr, x, y, width, height,
1086                                     SIDE_BOTTOM | SIDE_RIGHT);
1087           cairo_stroke (cr);
1088
1089           gdk_cairo_set_source_color (cr, &darker);
1090           add_path_rectangle_sides (cr, x, y, width, height,
1091                                     SIDE_TOP | SIDE_LEFT);
1092           cairo_stroke (cr);
1093         }
1094       else
1095         {
1096           gdk_cairo_set_source_color (cr, &darker);
1097           add_path_rectangle_sides (cr, x, y, width - 1, height - 1,
1098                                     SIDE_BOTTOM | SIDE_RIGHT);
1099           cairo_stroke (cr);
1100
1101           gdk_cairo_set_source_color (cr, &lighter);
1102           add_path_rectangle_sides (cr, x, y, width, height,
1103                                     SIDE_TOP | SIDE_LEFT);
1104           cairo_stroke (cr);
1105
1106           cairo_set_source_rgb (cr, 0, 0, 0);
1107           add_path_rectangle_sides (cr, x, y, width, height,
1108                                     SIDE_BOTTOM | SIDE_RIGHT);
1109           cairo_stroke (cr);
1110         }
1111     }
1112
1113   cairo_restore (cr);
1114
1115   gdk_color_free (bg_color);
1116 }
1117
1118 static void
1119 gtk_theming_engine_render_expander (GtkThemingEngine *engine,
1120                                     cairo_t          *cr,
1121                                     gdouble           x,
1122                                     gdouble           y,
1123                                     gdouble           width,
1124                                     gdouble           height)
1125 {
1126   GtkStateFlags flags;
1127   GdkColor *bg_color, *fg_color, *base_color;
1128   GtkStateType state;
1129   double vertical_overshoot;
1130   int diameter;
1131   double radius;
1132   double interp;                /* interpolation factor for center position */
1133   double x_double_horz, y_double_horz;
1134   double x_double_vert, y_double_vert;
1135   double x_double, y_double;
1136   gdouble angle;
1137   gint line_width;
1138
1139   cairo_save (cr);
1140   flags = gtk_theming_engine_get_state (engine);
1141
1142   if (flags & GTK_STATE_FLAG_PRELIGHT)
1143     state = GTK_STATE_PRELIGHT;
1144   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1145     state = GTK_STATE_INSENSITIVE;
1146   else
1147     state = GTK_STATE_NORMAL;
1148
1149   gtk_theming_engine_get (engine, state,
1150                           "foreground-color", &fg_color,
1151                           "background-color", &bg_color,
1152                           "base-color", &base_color,
1153                           NULL);
1154
1155   line_width = 1;
1156
1157   /* FIXME: LTR/RTL */
1158   if (flags & GTK_STATE_FLAG_ACTIVE)
1159     {
1160       angle = G_PI / 2;
1161       interp = 1.0;
1162     }
1163   else
1164     {
1165       angle = 0;
1166       interp = 0;
1167     }
1168
1169   /* Compute distance that the stroke extends beyonds the end
1170    * of the triangle we draw.
1171    */
1172   vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
1173
1174   /* For odd line widths, we end the vertical line of the triangle
1175    * at a half pixel, so we round differently.
1176    */
1177   if (line_width % 2 == 1)
1178     vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
1179   else
1180     vertical_overshoot = ceil (vertical_overshoot);
1181
1182   /* Adjust the size of the triangle we draw so that the entire stroke fits
1183    */
1184   diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
1185
1186   /* If the line width is odd, we want the diameter to be even,
1187    * and vice versa, so force the sum to be odd. This relationship
1188    * makes the point of the triangle look right.
1189    */
1190   diameter -= (1 - (diameter + line_width) % 2);
1191
1192   radius = diameter / 2.;
1193
1194   /* Adjust the center so that the stroke is properly aligned with
1195    * the pixel grid. The center adjustment is different for the
1196    * horizontal and vertical orientations. For intermediate positions
1197    * we interpolate between the two.
1198    */
1199   x_double_vert = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
1200   y_double_vert = (y + height / 2) - 0.5;
1201
1202   x_double_horz = (x + width / 2) - 0.5;
1203   y_double_horz = floor ((y + height / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
1204
1205   x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
1206   y_double = y_double_vert * (1 - interp) + y_double_horz * interp;
1207
1208   cairo_translate (cr, x_double, y_double);
1209   cairo_rotate (cr, angle);
1210
1211   cairo_move_to (cr, - radius / 2., - radius);
1212   cairo_line_to (cr,   radius / 2.,   0);
1213   cairo_line_to (cr, - radius / 2.,   radius);
1214   cairo_close_path (cr);
1215
1216   cairo_set_line_width (cr, line_width);
1217
1218   if (flags & GTK_STATE_FLAG_PRELIGHT)
1219     gdk_cairo_set_source_color (cr, fg_color);
1220   else
1221     gdk_cairo_set_source_color (cr, base_color);
1222
1223   cairo_fill_preserve (cr);
1224
1225   gdk_cairo_set_source_color (cr, fg_color);
1226   cairo_stroke (cr);
1227
1228   cairo_restore (cr);
1229
1230   gdk_color_free (base_color);
1231   gdk_color_free (fg_color);
1232   gdk_color_free (bg_color);
1233 }
1234
1235 static void
1236 gtk_theming_engine_render_focus (GtkThemingEngine *engine,
1237                                  cairo_t          *cr,
1238                                  gdouble           x,
1239                                  gdouble           y,
1240                                  gdouble           width,
1241                                  gdouble           height)
1242 {
1243   GtkStateFlags flags;
1244   GtkStateType state;
1245   GdkColor *color;
1246   gint line_width;
1247   gint8 *dash_list;
1248
1249   cairo_save (cr);
1250   flags = gtk_theming_engine_get_state (engine);
1251
1252   if (flags & GTK_STATE_FLAG_PRELIGHT)
1253     state = GTK_STATE_PRELIGHT;
1254   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1255     state = GTK_STATE_INSENSITIVE;
1256   else
1257     state = GTK_STATE_NORMAL;
1258
1259   gtk_theming_engine_get (engine, state,
1260                           "foreground-color", &color,
1261                           NULL);
1262
1263   gtk_theming_engine_get_style (engine,
1264                                 "focus-line-width", &line_width,
1265                                 "focus-line-pattern", (gchar *) &dash_list,
1266                                 NULL);
1267
1268   cairo_set_line_width (cr, (gdouble) line_width);
1269
1270   if (dash_list[0])
1271     {
1272       gint n_dashes = strlen ((const gchar *) dash_list);
1273       gdouble *dashes = g_new (gdouble, n_dashes);
1274       gdouble total_length = 0;
1275       gdouble dash_offset;
1276       gint i;
1277
1278       for (i = 0; i < n_dashes; i++)
1279         {
1280           dashes[i] = dash_list[i];
1281           total_length += dash_list[i];
1282         }
1283
1284       /* The dash offset here aligns the pattern to integer pixels
1285        * by starting the dash at the right side of the left border
1286        * Negative dash offsets in cairo don't work
1287        * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
1288        */
1289       dash_offset = - line_width / 2.;
1290
1291       while (dash_offset < 0)
1292         dash_offset += total_length;
1293
1294       cairo_set_dash (cr, dashes, n_dashes, dash_offset);
1295       g_free (dashes);
1296     }
1297
1298   cairo_rectangle (cr,
1299                    x + line_width / 2.,
1300                    y + line_width / 2.,
1301                    width - line_width,
1302                    height - line_width);
1303
1304   gdk_cairo_set_source_color (cr, color);
1305   cairo_stroke (cr);
1306
1307   cairo_restore (cr);
1308
1309   gdk_color_free (color);
1310   g_free (dash_list);
1311 }
1312
1313 static void
1314 gtk_theming_engine_render_line (GtkThemingEngine *engine,
1315                                 cairo_t          *cr,
1316                                 gdouble           x0,
1317                                 gdouble           y0,
1318                                 gdouble           x1,
1319                                 gdouble           y1)
1320 {
1321   GdkColor *bg_color, darker, lighter;
1322   GtkStateFlags flags;
1323   GtkStateType state;
1324   gint i, thickness, thickness_dark, thickness_light, len;
1325   cairo_matrix_t matrix;
1326   gdouble angle;
1327
1328   /* FIXME: thickness */
1329   thickness = 2;
1330   thickness_dark = thickness / 2;
1331   thickness_light = thickness - thickness_dark;
1332
1333   flags = gtk_theming_engine_get_state (engine);
1334   cairo_save (cr);
1335
1336   if (flags & GTK_STATE_FLAG_PRELIGHT)
1337     state = GTK_STATE_PRELIGHT;
1338   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1339     state = GTK_STATE_INSENSITIVE;
1340   else
1341     state = GTK_STATE_NORMAL;
1342
1343   gtk_theming_engine_get (engine, state,
1344                           "background-color", &bg_color,
1345                           NULL);
1346   color_shade (bg_color, 0.7, &darker);
1347   color_shade (bg_color, 1.3, &lighter);
1348
1349   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1350   cairo_set_line_width (cr, 1);
1351
1352   angle = atan2 (x1 - x0, y1 - y0);
1353   angle = (2 * G_PI) - angle;
1354   angle += G_PI / 2;
1355
1356   cairo_get_matrix (cr, &matrix);
1357   cairo_matrix_translate (&matrix, x0, y0);
1358   cairo_matrix_rotate (&matrix, angle);
1359   cairo_set_matrix (cr, &matrix);
1360
1361   x1 -= x0;
1362   y1 -= y0;
1363
1364   len = (gint) sqrt ((x1 * x1) + (y1 * y1));
1365
1366   y0 = -thickness_dark;
1367
1368   for (i = 0; i < thickness_dark; i++)
1369     {
1370       gdk_cairo_set_source_color (cr, &lighter);
1371       add_path_line (cr, len - i - 1.5, y0, len - 0.5, y0);
1372       cairo_stroke (cr);
1373
1374       gdk_cairo_set_source_color (cr, &darker);
1375       add_path_line (cr, 0.5, y0, len - i - 1.5, y0);
1376       cairo_stroke (cr);
1377
1378       y0++;
1379     }
1380
1381   for (i = 0; i < thickness_light; i++)
1382     {
1383       gdk_cairo_set_source_color (cr, &darker);
1384       add_path_line (cr, 0.5, y0, thickness_light - i + 0.5, y0);
1385       cairo_stroke (cr);
1386
1387       gdk_cairo_set_source_color (cr, &lighter);
1388       add_path_line (cr, thickness_light - i + 0.5, y0, len - 0.5, y0);
1389       cairo_stroke (cr);
1390
1391       y0++;
1392     }
1393
1394   cairo_restore (cr);
1395 }
1396
1397 typedef struct _ByteRange ByteRange;
1398
1399 struct _ByteRange
1400 {
1401   guint start;
1402   guint end;
1403 };
1404
1405 static ByteRange*
1406 range_new (guint start,
1407            guint end)
1408 {
1409   ByteRange *br = g_new (ByteRange, 1);
1410
1411   br->start = start;
1412   br->end = end;
1413
1414   return br;
1415 }
1416
1417 static PangoLayout*
1418 get_insensitive_layout (PangoLayout *layout)
1419 {
1420   GSList *embossed_ranges = NULL;
1421   GSList *shaded_ranges = NULL;
1422   PangoLayoutIter *iter;
1423   GSList *tmp_list = NULL;
1424   PangoLayout *new_layout;
1425   PangoAttrList *attrs;
1426
1427   iter = pango_layout_get_iter (layout);
1428
1429   do
1430     {
1431       PangoLayoutRun *run;
1432       PangoAttribute *attr;
1433       gboolean need_shade = FALSE;
1434       ByteRange *br;
1435
1436       run = pango_layout_iter_get_run_readonly (iter);
1437
1438       if (run)
1439         {
1440           tmp_list = run->item->analysis.extra_attrs;
1441
1442           while (tmp_list != NULL)
1443             {
1444               attr = tmp_list->data;
1445               switch (attr->klass->type)
1446                 {
1447                 case PANGO_ATTR_FOREGROUND:
1448                 case PANGO_ATTR_BACKGROUND:
1449                   need_shade = TRUE;
1450                   break;
1451
1452                 default:
1453                   break;
1454                 }
1455
1456               if (need_shade)
1457                 break;
1458
1459               tmp_list = g_slist_next (tmp_list);
1460             }
1461
1462           br = range_new (run->item->offset, run->item->offset + run->item->length);
1463
1464           if (need_shade)
1465             shaded_ranges = g_slist_prepend (shaded_ranges, br);
1466           else
1467             embossed_ranges = g_slist_prepend (embossed_ranges, br);
1468         }
1469     }
1470   while (pango_layout_iter_next_run (iter));
1471
1472   pango_layout_iter_free (iter);
1473
1474   new_layout = pango_layout_copy (layout);
1475
1476   attrs = pango_layout_get_attributes (new_layout);
1477
1478   if (attrs == NULL)
1479     {
1480       /* Create attr list if there wasn't one */
1481       attrs = pango_attr_list_new ();
1482       pango_layout_set_attributes (new_layout, attrs);
1483       pango_attr_list_unref (attrs);
1484     }
1485
1486   tmp_list = embossed_ranges;
1487   while (tmp_list != NULL)
1488     {
1489       PangoAttribute *attr;
1490       ByteRange *br = tmp_list->data;
1491
1492       attr = gdk_pango_attr_embossed_new (TRUE);
1493
1494       attr->start_index = br->start;
1495       attr->end_index = br->end;
1496
1497       pango_attr_list_change (attrs, attr);
1498
1499       g_free (br);
1500
1501       tmp_list = g_slist_next (tmp_list);
1502     }
1503
1504   g_slist_free (embossed_ranges);
1505
1506   tmp_list = shaded_ranges;
1507   while (tmp_list != NULL)
1508     {
1509       PangoAttribute *attr;
1510       ByteRange *br = tmp_list->data;
1511
1512       attr = gdk_pango_attr_shade_new (0.7);
1513
1514       attr->start_index = br->start;
1515       attr->end_index = br->end;
1516
1517       pango_attr_list_change (attrs, attr);
1518
1519       g_free (br);
1520
1521       tmp_list = g_slist_next (tmp_list);
1522     }
1523
1524   g_slist_free (shaded_ranges);
1525
1526   return new_layout;
1527 }
1528
1529 static void
1530 gtk_theming_engine_render_layout (GtkThemingEngine *engine,
1531                                   cairo_t          *cr,
1532                                   gdouble           x,
1533                                   gdouble           y,
1534                                   PangoLayout      *layout)
1535 {
1536   GdkColor *fg_color;
1537   GtkStateFlags flags;
1538   GtkStateType state;
1539   GdkScreen *screen;
1540
1541   cairo_save (cr);
1542   flags = gtk_theming_engine_get_state (engine);
1543
1544   /* FIXME: Set clipping */
1545
1546   if (flags & GTK_STATE_FLAG_ACTIVE)
1547     state = GTK_STATE_ACTIVE;
1548   else if (flags & GTK_STATE_FLAG_SELECTED)
1549     state = GTK_STATE_SELECTED;
1550   else if (flags & GTK_STATE_FLAG_PRELIGHT)
1551     state = GTK_STATE_PRELIGHT;
1552   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1553     state = GTK_STATE_INSENSITIVE;
1554   else
1555     state = GTK_STATE_NORMAL;
1556
1557   gtk_theming_engine_get (engine, state,
1558                           "foreground-color", &fg_color,
1559                           NULL);
1560
1561   screen = gtk_theming_engine_get_screen (engine);
1562
1563   if (state == GTK_STATE_INSENSITIVE)
1564     {
1565       PangoLayout *insensitive_layout;
1566
1567       insensitive_layout = get_insensitive_layout (layout);
1568       gdk_pango_show_layout (screen,
1569                              cr, x, y,
1570                              insensitive_layout,
1571                              fg_color, NULL);
1572       g_object_unref (insensitive_layout);
1573     }
1574   else
1575     gdk_pango_show_layout (screen, cr, x, y, layout,
1576                            fg_color, NULL);
1577
1578   cairo_restore (cr);
1579
1580   gdk_color_free (fg_color);
1581 }
1582
1583 static void
1584 gtk_theming_engine_render_slider (GtkThemingEngine *engine,
1585                                   cairo_t          *cr,
1586                                   gdouble           x,
1587                                   gdouble           y,
1588                                   gdouble           width,
1589                                   gdouble           height,
1590                                   GtkOrientation    orientation)
1591 {
1592   const GtkWidgetPath *path;
1593   gint thickness;
1594
1595   path = gtk_theming_engine_get_path (engine);
1596
1597   gtk_theming_engine_render_background (engine, cr, x, y, width, height);
1598   gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
1599
1600   /* FIXME: thickness */
1601   thickness = 2;
1602
1603   if (gtk_widget_path_is_type (path, GTK_TYPE_SCALE))
1604     {
1605       if (orientation == GTK_ORIENTATION_VERTICAL)
1606         gtk_theming_engine_render_line (engine, cr,
1607                                         x + thickness,
1608                                         y + (gint) height / 2,
1609                                         x + width - thickness - 1,
1610                                         y + (gint) height / 2);
1611       else
1612         gtk_theming_engine_render_line (engine, cr,
1613                                         x + (gint) width / 2,
1614                                         y + thickness,
1615                                         x + (gint) width / 2,
1616                                         y + height - thickness - 1);
1617     }
1618 }
1619
1620 static void
1621 gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
1622                                      cairo_t          *cr,
1623                                      gdouble           x,
1624                                      gdouble           y,
1625                                      gdouble           width,
1626                                      gdouble           height,
1627                                      GtkPositionType   gap_side,
1628                                      gdouble           xy0_gap,
1629                                      gdouble           xy1_gap)
1630 {
1631   GtkStateFlags flags;
1632   GtkStateType state;
1633   GdkColor *bg_color;
1634   GdkColor lighter, darker;
1635   guint sides;
1636
1637   cairo_save (cr);
1638   flags = gtk_theming_engine_get_state (engine);
1639
1640   if (flags & GTK_STATE_FLAG_PRELIGHT)
1641     state = GTK_STATE_PRELIGHT;
1642   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1643     state = GTK_STATE_INSENSITIVE;
1644   else
1645     state = GTK_STATE_NORMAL;
1646
1647   cairo_set_line_width (cr, 1);
1648
1649   gtk_theming_engine_get (engine, state,
1650                           "background-color", &bg_color,
1651                           NULL);
1652   color_shade (bg_color, 0.7, &darker);
1653   color_shade (bg_color, 1.3, &lighter);
1654
1655   if (gtk_theming_engine_has_class (engine, "frame"))
1656     {
1657       if (gap_side == GTK_POS_RIGHT)
1658         sides = SIDE_BOTTOM;
1659       else if (gap_side == GTK_POS_BOTTOM)
1660         sides = SIDE_RIGHT;
1661       else
1662         sides = SIDE_BOTTOM | SIDE_RIGHT;
1663
1664       gdk_cairo_set_source_color (cr, &lighter);
1665       add_path_rectangle_sides (cr, x , y, width , height, sides);
1666       cairo_stroke (cr);
1667
1668       gdk_cairo_set_source_color (cr, &darker);
1669       add_path_rectangle_sides (cr, x, y, width - 1, height - 1, sides);
1670       cairo_stroke (cr);
1671
1672       if (gap_side == GTK_POS_RIGHT ||
1673           gap_side == GTK_POS_BOTTOM)
1674         {
1675           gdk_cairo_set_source_color (cr, &darker);
1676           add_path_gap_side (cr, gap_side,
1677                              x + 1, y + 1, width - 4, height - 4,
1678                              xy0_gap, xy1_gap);
1679           cairo_stroke (cr);
1680
1681           gdk_cairo_set_source_color (cr, &lighter);
1682           add_path_gap_side (cr, gap_side,
1683                              x, y, width, height,
1684                              xy0_gap, xy1_gap);
1685           cairo_stroke (cr);
1686         }
1687
1688       if (gap_side == GTK_POS_LEFT)
1689         sides = SIDE_TOP;
1690       else if (gap_side == GTK_POS_TOP)
1691         sides = SIDE_LEFT;
1692       else
1693         sides = SIDE_TOP | SIDE_LEFT;
1694
1695       gdk_cairo_set_source_color (cr, &lighter);
1696       add_path_rectangle_sides (cr, x + 1, y + 1, width - 2, height - 3, sides);
1697       cairo_stroke (cr);
1698
1699       gdk_cairo_set_source_color (cr, &darker);
1700       add_path_rectangle_sides (cr, x, y, width - 1, height - 1, sides);
1701       cairo_stroke (cr);
1702
1703       if (gap_side == GTK_POS_LEFT ||
1704           gap_side == GTK_POS_TOP)
1705         {
1706           gdk_cairo_set_source_color (cr, &lighter);
1707           add_path_gap_side (cr, gap_side,
1708                              x + 1, y + 1, width - 4, height - 4,
1709                              xy0_gap, xy1_gap);
1710           cairo_stroke (cr);
1711
1712           gdk_cairo_set_source_color (cr, &darker);
1713           add_path_gap_side (cr, gap_side,
1714                              x, y, width - 2, height - 2,
1715                              xy0_gap, xy1_gap);
1716           cairo_stroke (cr);
1717         }
1718     }
1719   else
1720     {
1721       if (gap_side == GTK_POS_RIGHT)
1722         sides = SIDE_BOTTOM;
1723       else if (gap_side == GTK_POS_BOTTOM)
1724         sides = SIDE_RIGHT;
1725       else
1726         sides = SIDE_BOTTOM | SIDE_RIGHT;
1727
1728       gdk_cairo_set_source_color (cr, &darker);
1729       add_path_rectangle_sides (cr, x + 1, y, width - 2, height, sides);
1730       add_path_rectangle_sides (cr, x, y + 1, width, height - 2, sides);
1731       cairo_stroke (cr);
1732
1733       cairo_set_source_rgb (cr, 0, 0, 0);
1734       add_path_rectangle_sides (cr, x, y, width, height, sides);
1735       cairo_stroke (cr);
1736
1737       if (gap_side == GTK_POS_RIGHT ||
1738           gap_side == GTK_POS_BOTTOM)
1739         {
1740           gdk_cairo_set_source_color (cr, &darker);
1741           add_path_gap_side (cr, gap_side,
1742                              x, y, width - 2, height - 2,
1743                              xy0_gap, xy1_gap);
1744           cairo_stroke (cr);
1745
1746           cairo_set_source_rgb (cr, 0, 0, 0);
1747           add_path_gap_side (cr, gap_side,
1748                              x, y, width - 1, height - 1,
1749                              xy0_gap, xy1_gap);
1750           cairo_stroke (cr);
1751         }
1752
1753       if (gap_side == GTK_POS_LEFT)
1754         sides = SIDE_TOP;
1755       else if (gap_side == GTK_POS_TOP)
1756         sides = SIDE_LEFT;
1757       else
1758         sides = SIDE_TOP | SIDE_LEFT;
1759
1760       gdk_cairo_set_source_color (cr, &lighter);
1761       add_path_rectangle_sides (cr, x, y, width, height, sides);
1762       cairo_stroke (cr);
1763
1764       if (gap_side == GTK_POS_LEFT ||
1765           gap_side == GTK_POS_TOP)
1766         {
1767           add_path_gap_side (cr, gap_side,
1768                              x, y, width, height,
1769                              xy0_gap, xy1_gap);
1770           cairo_stroke (cr);
1771         }
1772     }
1773
1774   cairo_restore (cr);
1775
1776   gdk_color_free (bg_color);
1777 }
1778
1779 static void
1780 gtk_theming_engine_render_extension (GtkThemingEngine *engine,
1781                                      cairo_t          *cr,
1782                                      gdouble           x,
1783                                      gdouble           y,
1784                                      gdouble           width,
1785                                      gdouble           height,
1786                                      GtkPositionType   gap_side)
1787 {
1788   GtkStateFlags flags;
1789   GtkStateType state;
1790   GdkColor *bg_color;
1791   GdkColor lighter, darker;
1792
1793   cairo_save (cr);
1794   flags = gtk_theming_engine_get_state (engine);
1795
1796   if (flags & GTK_STATE_FLAG_ACTIVE)
1797     state = GTK_STATE_ACTIVE;
1798   else if (flags & GTK_STATE_FLAG_PRELIGHT)
1799     state = GTK_STATE_PRELIGHT;
1800   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1801     state = GTK_STATE_INSENSITIVE;
1802   else
1803     state = GTK_STATE_NORMAL;
1804
1805   cairo_set_line_width (cr, 1);
1806
1807   gtk_theming_engine_get (engine, state,
1808                           "background-color", &bg_color,
1809                           NULL);
1810   color_shade (bg_color, 0.7, &darker);
1811   color_shade (bg_color, 1.3, &lighter);
1812
1813   switch (gap_side)
1814     {
1815     case GTK_POS_TOP:
1816       gdk_cairo_set_source_color (cr, bg_color);
1817       cairo_rectangle (cr, x + 1, y, width - 2, height);
1818       cairo_fill (cr);
1819
1820       gdk_cairo_set_source_color (cr, &lighter);
1821       add_path_line (cr, x, y, x, y + height - 2);
1822       cairo_stroke (cr);
1823
1824       gdk_cairo_set_source_color (cr, bg_color);
1825       add_path_line (cr, x + 1, y, x + 1, y + height - 2);
1826       cairo_stroke (cr);
1827
1828       gdk_cairo_set_source_color (cr, &darker);
1829       add_path_line (cr, x + 2, y + height - 2, x + width - 2, y + height - 2);
1830       add_path_line (cr, x + width - 2, y, x + width - 2, y + height - 2);
1831       cairo_stroke (cr);
1832
1833       cairo_set_source_rgb (cr, 0, 0, 0);
1834       add_path_line (cr, x + 1, y + height - 1, x + width - 2, y + height - 1);
1835       add_path_line (cr, x + width - 1, y, x + width - 1, y + height - 2);
1836       cairo_stroke (cr);
1837
1838       break;
1839     case GTK_POS_BOTTOM:
1840       gdk_cairo_set_source_color (cr, bg_color);
1841       cairo_rectangle (cr, x + 1, y, width - 2, height);
1842       cairo_fill (cr);
1843
1844       gdk_cairo_set_source_color (cr, &lighter);
1845       add_path_line (cr, x + 1, y, x + width - 2, y);
1846       add_path_line (cr, x, y + 1, x, y + height - 1);
1847       cairo_stroke (cr);
1848
1849       gdk_cairo_set_source_color (cr, bg_color);
1850       add_path_line (cr, x + 1, y + 1, x + width - 2, y + 1);
1851       add_path_line (cr, x + 1, y + 1, x + 1, y + height - 1);
1852       cairo_stroke (cr);
1853
1854       gdk_cairo_set_source_color (cr, &darker);
1855       add_path_line (cr, x + width - 2, y + 2, x + width - 2, y + height - 1);
1856       cairo_stroke (cr);
1857
1858       cairo_set_source_rgb (cr, 0, 0, 0);
1859       add_path_line (cr, x + width - 1, y + 1, x + width - 1, y + height - 1);
1860       cairo_stroke (cr);
1861
1862       break;
1863     case GTK_POS_LEFT:
1864       gdk_cairo_set_source_color (cr, bg_color);
1865       cairo_rectangle (cr, x, y + 1, width, height - 2);
1866       cairo_fill (cr);
1867
1868       gdk_cairo_set_source_color (cr, &lighter);
1869       add_path_line (cr, x, y, x + width - 2, y);
1870       cairo_stroke (cr);
1871
1872       gdk_cairo_set_source_color (cr, bg_color);
1873       add_path_line (cr, x + 1, y + 1, x + width - 2, y + 1);
1874       cairo_stroke (cr);
1875
1876       gdk_cairo_set_source_color (cr, &darker);
1877       add_path_line (cr, x, y + height - 2, x + width - 2, y + height - 2);
1878       add_path_line (cr, x + width - 2, y + 2, x + width - 2, y + height - 2);
1879       cairo_stroke (cr);
1880
1881       cairo_set_source_rgb (cr, 0, 0, 0);
1882       add_path_line (cr, x, y + height - 1, x + width - 2, y + height - 1);
1883       add_path_line (cr, x + width - 1, y + 1, x + width - 1, y + height - 2);
1884       cairo_stroke (cr);
1885
1886       break;
1887     case GTK_POS_RIGHT:
1888       gdk_cairo_set_source_color (cr, bg_color);
1889       cairo_rectangle (cr, x, y + 1, width, height - 2);
1890       cairo_fill (cr);
1891
1892       gdk_cairo_set_source_color (cr, &lighter);
1893       add_path_line (cr, x + 1, y, x + width - 1, y);
1894       add_path_line (cr, x, y + 1, x, y + height - 2);
1895       cairo_stroke (cr);
1896
1897       gdk_cairo_set_source_color (cr, bg_color);
1898       add_path_line (cr, x + 1, y + 1, x + width - 1, y + 1);
1899       add_path_line (cr, x + 1, y + 1, x + 1, y + height - 2);
1900       cairo_stroke (cr);
1901
1902       gdk_cairo_set_source_color (cr, &darker);
1903       add_path_line (cr, x + 2, y + height - 2, x + width - 1, y + height - 2);
1904       cairo_stroke (cr);
1905
1906       cairo_set_source_rgb (cr, 0, 0, 0);
1907       add_path_line (cr, x + 1, y + height - 1, x + width - 1, y + height - 1);
1908       cairo_stroke (cr);
1909
1910       break;
1911     }
1912
1913   cairo_restore (cr);
1914
1915   gdk_color_free (bg_color);
1916 }
1917
1918 static void
1919 render_dot (cairo_t        *cr,
1920             const GdkColor *lighter,
1921             const GdkColor *darker,
1922             gdouble         x,
1923             gdouble         y,
1924             gdouble         size)
1925 {
1926   size = CLAMP ((gint) size, 2, 3);
1927
1928   if (size == 2)
1929     {
1930       gdk_cairo_set_source_color (cr, lighter);
1931       cairo_rectangle (cr, x, y, 1, 1);
1932       cairo_rectangle (cr, x + 1, y + 1, 1, 1);
1933       cairo_fill (cr);
1934     }
1935   else if (size == 3)
1936     {
1937       gdk_cairo_set_source_color (cr, lighter);
1938       cairo_rectangle (cr, x, y, 2, 1);
1939       cairo_rectangle (cr, x, y, 1, 2);
1940       cairo_fill (cr);
1941
1942       gdk_cairo_set_source_color (cr, darker);
1943       cairo_rectangle (cr, x + 1, y + 1, 2, 1);
1944       cairo_rectangle (cr, x + 2, y, 1, 2);
1945       cairo_fill (cr);
1946     }
1947 }
1948
1949 static void
1950 gtk_theming_engine_render_handle (GtkThemingEngine *engine,
1951                                   cairo_t          *cr,
1952                                   gdouble           x,
1953                                   gdouble           y,
1954                                   gdouble           width,
1955                                   gdouble           height,
1956                                   GtkOrientation    orientation)
1957 {
1958   GtkStateFlags flags;
1959   GtkStateType state;
1960   GdkColor *bg_color;
1961   GdkColor lighter, darker;
1962   gint xx, yy;
1963
1964   cairo_save (cr);
1965   flags = gtk_theming_engine_get_state (engine);
1966
1967   if (flags & GTK_STATE_FLAG_ACTIVE)
1968     state = GTK_STATE_ACTIVE;
1969   else if (flags & GTK_STATE_FLAG_PRELIGHT)
1970     state = GTK_STATE_PRELIGHT;
1971   else if (flags & GTK_STATE_FLAG_INSENSITIVE)
1972     state = GTK_STATE_INSENSITIVE;
1973   else
1974     state = GTK_STATE_NORMAL;
1975
1976   cairo_set_line_width (cr, 1);
1977
1978   gtk_theming_engine_get (engine, state,
1979                           "background-color", &bg_color,
1980                           NULL);
1981   color_shade (bg_color, 0.7, &darker);
1982   color_shade (bg_color, 1.3, &lighter);
1983
1984   gdk_cairo_set_source_color (cr, bg_color);
1985   cairo_rectangle (cr, x, y, width, height);
1986   cairo_fill (cr);
1987
1988   if (gtk_theming_engine_has_class (engine, "paned"))
1989     {
1990       if (orientation == GTK_ORIENTATION_HORIZONTAL)
1991         for (xx = x + width / 2 - 15; xx <= x + width / 2 + 15; xx += 5)
1992           render_dot (cr, &lighter, &darker, xx, y + height / 2 - 1, 3);
1993       else
1994         for (yy = y + height / 2 - 15; yy <= y + height / 2 + 15; yy += 5)
1995           render_dot (cr, &lighter, &darker, x + width / 2 - 1, yy, 3);
1996     }
1997   else
1998     {
1999       for (yy = y; yy < y + height; yy += 3)
2000         for (xx = x; xx < x + width; xx += 6)
2001           {
2002             render_dot (cr, &lighter, &darker, xx, yy, 2);
2003             render_dot (cr, &lighter, &darker, xx + 3, yy + 1, 2);
2004           }
2005     }
2006
2007   cairo_restore (cr);
2008
2009   gdk_color_free (bg_color);
2010 }
2011
2012 #define __GTK_THEMING_ENGINE_C__
2013 #include "gtkaliasdef.c"