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