]> Pileus Git - ~andy/gtk/blob - gtk/gtksymboliccolor.c
win32-theme: Fix up docs for gtk_symbolic_color_new_win32
[~andy/gtk] / gtk / gtksymboliccolor.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 #include "gtksymboliccolor.h"
22 #include "gtkstyleproperties.h"
23 #include "gtkintl.h"
24 #include "gtkwin32themeprivate.h"
25
26 /**
27  * SECTION:gtksymboliccolor
28  * @Short_description: Symbolic colors
29  * @Title: GtkSymbolicColor
30  *
31  * GtkSymbolicColor is a boxed type that represents a symbolic color.
32  * It is the result of parsing a
33  * <link linkend="gtkcssprovider-symbolic-colors">color expression</link>.
34  * To obtain the color represented by a GtkSymbolicColor, it has to
35  * be resolved with gtk_symbolic_color_resolve(), which replaces all
36  * symbolic color references by the colors they refer to (in a given
37  * context) and evaluates mix, shade and other expressions, resulting
38  * in a #GdkRGBA value.
39  *
40  * It is not normally necessary to deal directly with #GtkSymbolicColors,
41  * since they are mostly used behind the scenes by #GtkStyleContext and
42  * #GtkCssProvider.
43  */
44
45 G_DEFINE_BOXED_TYPE (GtkSymbolicColor, gtk_symbolic_color,
46                      gtk_symbolic_color_ref, gtk_symbolic_color_unref)
47
48 /* Symbolic colors */
49 typedef enum {
50   COLOR_TYPE_LITERAL,
51   COLOR_TYPE_NAME,
52   COLOR_TYPE_SHADE,
53   COLOR_TYPE_ALPHA,
54   COLOR_TYPE_MIX,
55   COLOR_TYPE_WIN32
56 } ColorType;
57
58 struct _GtkSymbolicColor
59 {
60   ColorType type;
61   guint ref_count;
62
63   union
64   {
65     GdkRGBA color;
66     gchar *name;
67
68     struct
69     {
70       GtkSymbolicColor *color;
71       gdouble factor;
72     } shade, alpha;
73
74     struct
75     {
76       GtkSymbolicColor *color1;
77       GtkSymbolicColor *color2;
78       gdouble factor;
79     } mix;
80
81     struct
82     {
83       gchar *theme_class;
84       gint id;
85     } win32;
86   };
87 };
88
89 /**
90  * gtk_symbolic_color_new_literal:
91  * @color: a #GdkRGBA
92  *
93  * Creates a symbolic color pointing to a literal color.
94  *
95  * Returns: a newly created #GtkSymbolicColor
96  *
97  * Since: 3.0
98  **/
99 GtkSymbolicColor *
100 gtk_symbolic_color_new_literal (const GdkRGBA *color)
101 {
102   GtkSymbolicColor *symbolic_color;
103
104   g_return_val_if_fail (color != NULL, NULL);
105
106   symbolic_color = g_slice_new0 (GtkSymbolicColor);
107   symbolic_color->type = COLOR_TYPE_LITERAL;
108   symbolic_color->color = *color;
109   symbolic_color->ref_count = 1;
110
111   return symbolic_color;
112 }
113
114 /**
115  * gtk_symbolic_color_new_name:
116  * @name: color name
117  *
118  * Creates a symbolic color pointing to an unresolved named
119  * color. See gtk_style_context_lookup_color() and
120  * gtk_style_properties_lookup_color().
121  *
122  * Returns: a newly created #GtkSymbolicColor
123  *
124  * Since: 3.0
125  **/
126 GtkSymbolicColor *
127 gtk_symbolic_color_new_name (const gchar *name)
128 {
129   GtkSymbolicColor *symbolic_color;
130
131   g_return_val_if_fail (name != NULL, NULL);
132
133   symbolic_color = g_slice_new0 (GtkSymbolicColor);
134   symbolic_color->type = COLOR_TYPE_NAME;
135   symbolic_color->name = g_strdup (name);
136   symbolic_color->ref_count = 1;
137
138   return symbolic_color;
139 }
140
141 /**
142  * gtk_symbolic_color_new_shade: (constructor)
143  * @color: another #GtkSymbolicColor
144  * @factor: shading factor to apply to @color
145  *
146  * Creates a symbolic color defined as a shade of
147  * another color. A factor > 1.0 would resolve to
148  * a brighter color, while < 1.0 would resolve to
149  * a darker color.
150  *
151  * Returns: A newly created #GtkSymbolicColor
152  *
153  * Since: 3.0
154  **/
155 GtkSymbolicColor *
156 gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
157                               gdouble           factor)
158 {
159   GtkSymbolicColor *symbolic_color;
160
161   g_return_val_if_fail (color != NULL, NULL);
162
163   symbolic_color = g_slice_new0 (GtkSymbolicColor);
164   symbolic_color->type = COLOR_TYPE_SHADE;
165   symbolic_color->shade.color = gtk_symbolic_color_ref (color);
166   symbolic_color->shade.factor = factor;
167   symbolic_color->ref_count = 1;
168
169   return symbolic_color;
170 }
171
172 /**
173  * gtk_symbolic_color_new_alpha: (constructor)
174  * @color: another #GtkSymbolicColor
175  * @factor: factor to apply to @color alpha
176  *
177  * Creates a symbolic color by modifying the relative alpha
178  * value of @color. A factor < 1.0 would resolve to a more
179  * transparent color, while > 1.0 would resolve to a more
180  * opaque color.
181  *
182  * Returns: A newly created #GtkSymbolicColor
183  *
184  * Since: 3.0
185  **/
186 GtkSymbolicColor *
187 gtk_symbolic_color_new_alpha (GtkSymbolicColor *color,
188                               gdouble           factor)
189 {
190   GtkSymbolicColor *symbolic_color;
191
192   g_return_val_if_fail (color != NULL, NULL);
193
194   symbolic_color = g_slice_new0 (GtkSymbolicColor);
195   symbolic_color->type = COLOR_TYPE_ALPHA;
196   symbolic_color->alpha.color = gtk_symbolic_color_ref (color);
197   symbolic_color->alpha.factor = factor;
198   symbolic_color->ref_count = 1;
199
200   return symbolic_color;
201 }
202
203 /**
204  * gtk_symbolic_color_new_mix: (constructor)
205  * @color1: color to mix
206  * @color2: another color to mix
207  * @factor: mix factor
208  *
209  * Creates a symbolic color defined as a mix of another
210  * two colors. a mix factor of 0 would resolve to @color1,
211  * while a factor of 1 would resolve to @color2.
212  *
213  * Returns: A newly created #GtkSymbolicColor
214  *
215  * Since: 3.0
216  **/
217 GtkSymbolicColor *
218 gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
219                             GtkSymbolicColor *color2,
220                             gdouble           factor)
221 {
222   GtkSymbolicColor *symbolic_color;
223
224   g_return_val_if_fail (color1 != NULL, NULL);
225   g_return_val_if_fail (color1 != NULL, NULL);
226
227   symbolic_color = g_slice_new0 (GtkSymbolicColor);
228   symbolic_color->type = COLOR_TYPE_MIX;
229   symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
230   symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
231   symbolic_color->mix.factor = factor;
232   symbolic_color->ref_count = 1;
233
234   return symbolic_color;
235 }
236
237 /**
238  * gtk_symbolic_color_new_win32: (constructor)
239  * @theme_class: The theme class to pull color from
240  * @id: The color id
241  *
242  * Creates a symbolic color based on the current win32
243  * theme.
244  *
245  * Note that while this call is availible on all platforms
246  * the actual value returned is not reliable on non-win32
247  * platforms.
248  *
249  * Returns: A newly created #GtkSymbolicColor
250  *
251  * Since: 3.4
252  **/
253 GtkSymbolicColor *
254 gtk_symbolic_color_new_win32 (const gchar        *theme_class,
255                               gint                id)
256 {
257   GtkSymbolicColor *symbolic_color;
258
259   g_return_val_if_fail (theme_class != NULL, NULL);
260
261   symbolic_color = g_slice_new0 (GtkSymbolicColor);
262   symbolic_color->type = COLOR_TYPE_WIN32;
263   symbolic_color->win32.theme_class = g_strdup (theme_class);
264   symbolic_color->win32.id = id;
265   symbolic_color->ref_count = 1;
266
267   return symbolic_color;
268 }
269
270 /**
271  * gtk_symbolic_color_ref:
272  * @color: a #GtkSymbolicColor
273  *
274  * Increases the reference count of @color
275  *
276  * Returns: the same @color
277  *
278  * Since: 3.0
279  **/
280 GtkSymbolicColor *
281 gtk_symbolic_color_ref (GtkSymbolicColor *color)
282 {
283   g_return_val_if_fail (color != NULL, NULL);
284
285   color->ref_count++;
286
287   return color;
288 }
289
290 /**
291  * gtk_symbolic_color_unref:
292  * @color: a #GtkSymbolicColor
293  *
294  * Decreases the reference count of @color, freeing its memory if the
295  * reference count reaches 0.
296  *
297  * Since: 3.0
298  **/
299 void
300 gtk_symbolic_color_unref (GtkSymbolicColor *color)
301 {
302   g_return_if_fail (color != NULL);
303
304   color->ref_count--;
305
306   if (color->ref_count == 0)
307     {
308       switch (color->type)
309         {
310         case COLOR_TYPE_NAME:
311           g_free (color->name);
312           break;
313         case COLOR_TYPE_SHADE:
314           gtk_symbolic_color_unref (color->shade.color);
315           break;
316         case COLOR_TYPE_ALPHA:
317           gtk_symbolic_color_unref (color->alpha.color);
318           break;
319         case COLOR_TYPE_MIX:
320           gtk_symbolic_color_unref (color->mix.color1);
321           gtk_symbolic_color_unref (color->mix.color2);
322           break;
323         case COLOR_TYPE_WIN32:
324           g_free (color->win32.theme_class);
325           break;
326         default:
327           break;
328         }
329
330       g_slice_free (GtkSymbolicColor, color);
331     }
332 }
333
334 static void
335 rgb_to_hls (gdouble *r,
336             gdouble *g,
337             gdouble *b)
338 {
339   gdouble min;
340   gdouble max;
341   gdouble red;
342   gdouble green;
343   gdouble blue;
344   gdouble h, l, s;
345   gdouble delta;
346   
347   red = *r;
348   green = *g;
349   blue = *b;
350   
351   if (red > green)
352     {
353       if (red > blue)
354         max = red;
355       else
356         max = blue;
357       
358       if (green < blue)
359         min = green;
360       else
361         min = blue;
362     }
363   else
364     {
365       if (green > blue)
366         max = green;
367       else
368         max = blue;
369       
370       if (red < blue)
371         min = red;
372       else
373         min = blue;
374     }
375   
376   l = (max + min) / 2;
377   s = 0;
378   h = 0;
379   
380   if (max != min)
381     {
382       if (l <= 0.5)
383         s = (max - min) / (max + min);
384       else
385         s = (max - min) / (2 - max - min);
386       
387       delta = max -min;
388       if (red == max)
389         h = (green - blue) / delta;
390       else if (green == max)
391         h = 2 + (blue - red) / delta;
392       else if (blue == max)
393         h = 4 + (red - green) / delta;
394       
395       h *= 60;
396       if (h < 0.0)
397         h += 360;
398     }
399   
400   *r = h;
401   *g = l;
402   *b = s;
403 }
404
405 static void
406 hls_to_rgb (gdouble *h,
407             gdouble *l,
408             gdouble *s)
409 {
410   gdouble hue;
411   gdouble lightness;
412   gdouble saturation;
413   gdouble m1, m2;
414   gdouble r, g, b;
415   
416   lightness = *l;
417   saturation = *s;
418   
419   if (lightness <= 0.5)
420     m2 = lightness * (1 + saturation);
421   else
422     m2 = lightness + saturation - lightness * saturation;
423   m1 = 2 * lightness - m2;
424   
425   if (saturation == 0)
426     {
427       *h = lightness;
428       *l = lightness;
429       *s = lightness;
430     }
431   else
432     {
433       hue = *h + 120;
434       while (hue > 360)
435         hue -= 360;
436       while (hue < 0)
437         hue += 360;
438       
439       if (hue < 60)
440         r = m1 + (m2 - m1) * hue / 60;
441       else if (hue < 180)
442         r = m2;
443       else if (hue < 240)
444         r = m1 + (m2 - m1) * (240 - hue) / 60;
445       else
446         r = m1;
447       
448       hue = *h;
449       while (hue > 360)
450         hue -= 360;
451       while (hue < 0)
452         hue += 360;
453       
454       if (hue < 60)
455         g = m1 + (m2 - m1) * hue / 60;
456       else if (hue < 180)
457         g = m2;
458       else if (hue < 240)
459         g = m1 + (m2 - m1) * (240 - hue) / 60;
460       else
461         g = m1;
462       
463       hue = *h - 120;
464       while (hue > 360)
465         hue -= 360;
466       while (hue < 0)
467         hue += 360;
468       
469       if (hue < 60)
470         b = m1 + (m2 - m1) * hue / 60;
471       else if (hue < 180)
472         b = m2;
473       else if (hue < 240)
474         b = m1 + (m2 - m1) * (240 - hue) / 60;
475       else
476         b = m1;
477       
478       *h = r;
479       *l = g;
480       *s = b;
481     }
482 }
483
484 static void
485 _shade_color (GdkRGBA *color,
486               gdouble  factor)
487 {
488   GdkRGBA temp;
489
490   temp = *color;
491   rgb_to_hls (&temp.red, &temp.green, &temp.blue);
492
493   temp.green *= factor;
494   if (temp.green > 1.0)
495     temp.green = 1.0;
496   else if (temp.green < 0.0)
497     temp.green = 0.0;
498
499   temp.blue *= factor;
500   if (temp.blue > 1.0)
501     temp.blue = 1.0;
502   else if (temp.blue < 0.0)
503     temp.blue = 0.0;
504
505   hls_to_rgb (&temp.red, &temp.green, &temp.blue);
506   *color = temp;
507 }
508
509 /**
510  * gtk_symbolic_color_resolve:
511  * @color: a #GtkSymbolicColor
512  * @props: (allow-none): #GtkStyleProperties to use when resolving
513  *    named colors, or %NULL
514  * @resolved_color: (out): return location for the resolved color
515  *
516  * If @color is resolvable, @resolved_color will be filled in
517  * with the resolved color, and %TRUE will be returned. Generally,
518  * if @color can't be resolved, it is due to it being defined on
519  * top of a named color that doesn't exist in @props.
520  *
521  * When @props is %NULL, resolving of named colors will fail, so if
522  * your @color is or references such a color, this function will
523  * return %FALSE.
524  *
525  * Returns: %TRUE if the color has been resolved
526  *
527  * Since: 3.0
528  **/
529 gboolean
530 gtk_symbolic_color_resolve (GtkSymbolicColor   *color,
531                             GtkStyleProperties *props,
532                             GdkRGBA            *resolved_color)
533 {
534   g_return_val_if_fail (color != NULL, FALSE);
535   g_return_val_if_fail (resolved_color != NULL, FALSE);
536   g_return_val_if_fail (props == NULL || GTK_IS_STYLE_PROPERTIES (props), FALSE);
537
538   switch (color->type)
539     {
540     case COLOR_TYPE_LITERAL:
541       *resolved_color = color->color;
542       return TRUE;
543     case COLOR_TYPE_NAME:
544       {
545         GtkSymbolicColor *named_color;
546
547         if (props == NULL)
548           return FALSE;
549
550         named_color = gtk_style_properties_lookup_color (props, color->name);
551
552         if (!named_color)
553           return FALSE;
554
555         return gtk_symbolic_color_resolve (named_color, props, resolved_color);
556       }
557
558       break;
559     case COLOR_TYPE_SHADE:
560       {
561         GdkRGBA shade;
562
563         if (!gtk_symbolic_color_resolve (color->shade.color, props, &shade))
564           return FALSE;
565
566         _shade_color (&shade, color->shade.factor);
567         *resolved_color = shade;
568
569         return TRUE;
570       }
571
572       break;
573     case COLOR_TYPE_ALPHA:
574       {
575         GdkRGBA alpha;
576
577         if (!gtk_symbolic_color_resolve (color->alpha.color, props, &alpha))
578           return FALSE;
579
580         *resolved_color = alpha;
581         resolved_color->alpha = CLAMP (alpha.alpha * color->alpha.factor, 0, 1);
582
583         return TRUE;
584       }
585     case COLOR_TYPE_MIX:
586       {
587         GdkRGBA color1, color2;
588
589         if (!gtk_symbolic_color_resolve (color->mix.color1, props, &color1))
590           return FALSE;
591
592         if (!gtk_symbolic_color_resolve (color->mix.color2, props, &color2))
593           return FALSE;
594
595         resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 1);
596         resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 1);
597         resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 1);
598         resolved_color->alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->mix.factor), 0, 1);
599
600         return TRUE;
601       }
602
603       break;
604     case COLOR_TYPE_WIN32:
605       return _gtk_win32_theme_color_resolve (color->win32.theme_class,
606                                              color->win32.id,
607                                              resolved_color);
608
609       break;
610     default:
611       g_assert_not_reached ();
612     }
613
614   return FALSE;
615 }
616
617 /**
618  * gtk_symbolic_color_to_string:
619  * @color: color to convert to a string
620  *
621  * Converts the given @color to a string representation. This is useful
622  * both for debugging and for serialization of strings. The format of
623  * the string may change between different versions of GTK, but it is
624  * guaranteed that the GTK css parser is able to read the string and
625  * create the same symbolic color from it.
626  *
627  * Returns: a new string representing @color
628  **/
629 char *
630 gtk_symbolic_color_to_string (GtkSymbolicColor *color)
631 {
632   char *s;
633
634   g_return_val_if_fail (color != NULL, NULL);
635
636   switch (color->type)
637     {
638     case COLOR_TYPE_LITERAL:
639       s = gdk_rgba_to_string (&color->color);
640       break;
641     case COLOR_TYPE_NAME:
642       s = g_strconcat ("@", color->name, NULL);
643       break;
644     case COLOR_TYPE_SHADE:
645       {
646         char *color_string = gtk_symbolic_color_to_string (color->shade.color);
647         char factor[G_ASCII_DTOSTR_BUF_SIZE];
648
649         g_ascii_dtostr (factor, sizeof (factor), color->shade.factor);
650         s = g_strdup_printf ("shade (%s, %s)", color_string, factor);
651         g_free (color_string);
652       }
653       break;
654     case COLOR_TYPE_ALPHA:
655       {
656         char *color_string = gtk_symbolic_color_to_string (color->shade.color);
657         char factor[G_ASCII_DTOSTR_BUF_SIZE];
658
659         g_ascii_dtostr (factor, sizeof (factor), color->alpha.factor);
660         s = g_strdup_printf ("alpha (%s, %s)", color_string, factor);
661         g_free (color_string);
662       }
663       break;
664     case COLOR_TYPE_MIX:
665       {
666         char *color_string1 = gtk_symbolic_color_to_string (color->mix.color1);
667         char *color_string2 = gtk_symbolic_color_to_string (color->mix.color2);
668         char factor[G_ASCII_DTOSTR_BUF_SIZE];
669
670         g_ascii_dtostr (factor, sizeof (factor), color->mix.factor);
671         s = g_strdup_printf ("mix (%s, %s, %s)", color_string1, color_string2, factor);
672         g_free (color_string1);
673         g_free (color_string2);
674       }
675       break;
676     case COLOR_TYPE_WIN32:
677       {
678         s = g_strdup_printf (GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME"(%s, %d)", 
679                              color->win32.theme_class, color->win32.id);
680       }
681       break;
682     default:
683       g_assert_not_reached ();
684     }
685
686   return s;
687
688 }