gtk2/gtk/gtksymboliccolor.c
Benjamin Otte 0e2f35ed88 css: Introduce dependencies for value computations
When values are computed, they might depend on various other values and
we need to track this so we can update the values when those other
values change. This is the first step in making that happen.

This patch does not do any dependency tracking at all, instead it uses
GTK_CSS_DEPENDS_ON_EVERYTHING as a sort of FIXME.
2012-08-28 15:42:23 +02:00

1182 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GTK - The GIMP Toolkit
* Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkstylepropertyprivate.h"
#include "gtksymboliccolorprivate.h"
#include "gtkstyleproperties.h"
#include "gtkintl.h"
#include "gtkwin32themeprivate.h"
/**
* SECTION:gtksymboliccolor
* @Short_description: Symbolic colors
* @Title: GtkSymbolicColor
*
* GtkSymbolicColor is a boxed type that represents a symbolic color.
* It is the result of parsing a
* <link linkend="gtkcssprovider-symbolic-colors">color expression</link>.
* To obtain the color represented by a GtkSymbolicColor, it has to
* be resolved with gtk_symbolic_color_resolve(), which replaces all
* symbolic color references by the colors they refer to (in a given
* context) and evaluates mix, shade and other expressions, resulting
* in a #GdkRGBA value.
*
* It is not normally necessary to deal directly with #GtkSymbolicColors,
* since they are mostly used behind the scenes by #GtkStyleContext and
* #GtkCssProvider.
*/
G_DEFINE_BOXED_TYPE (GtkSymbolicColor, gtk_symbolic_color,
gtk_symbolic_color_ref, gtk_symbolic_color_unref)
/* Symbolic colors */
typedef enum {
COLOR_TYPE_LITERAL,
COLOR_TYPE_NAME,
COLOR_TYPE_SHADE,
COLOR_TYPE_ALPHA,
COLOR_TYPE_MIX,
COLOR_TYPE_WIN32,
COLOR_TYPE_CURRENT_COLOR
} ColorType;
struct _GtkSymbolicColor
{
GTK_CSS_VALUE_BASE
ColorType type;
GtkCssValue *last_value;
union
{
gchar *name;
struct
{
GtkSymbolicColor *color;
gdouble factor;
} shade, alpha;
struct
{
GtkSymbolicColor *color1;
GtkSymbolicColor *color2;
gdouble factor;
} mix;
struct
{
gchar *theme_class;
gint id;
} win32;
};
};
static void
gtk_css_value_symbolic_free (GtkCssValue *value)
{
GtkSymbolicColor *color = (GtkSymbolicColor *) value;
_gtk_css_value_unref (color->last_value);
switch (color->type)
{
case COLOR_TYPE_NAME:
g_free (color->name);
break;
case COLOR_TYPE_SHADE:
gtk_symbolic_color_unref (color->shade.color);
break;
case COLOR_TYPE_ALPHA:
gtk_symbolic_color_unref (color->alpha.color);
break;
case COLOR_TYPE_MIX:
gtk_symbolic_color_unref (color->mix.color1);
gtk_symbolic_color_unref (color->mix.color2);
break;
case COLOR_TYPE_WIN32:
g_free (color->win32.theme_class);
break;
default:
break;
}
g_slice_free (GtkSymbolicColor, color);
}
static GtkCssValue *
gtk_css_value_symbolic_get_fallback (guint property_id,
GtkStyleContext *context)
{
static const GdkRGBA transparent = { 0, 0, 0, 0 };
switch (property_id)
{
case GTK_CSS_PROPERTY_BACKGROUND_IMAGE:
case GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE:
case GTK_CSS_PROPERTY_TEXT_SHADOW:
case GTK_CSS_PROPERTY_ICON_SHADOW:
case GTK_CSS_PROPERTY_BOX_SHADOW:
return _gtk_css_rgba_value_new_from_rgba (&transparent);
case GTK_CSS_PROPERTY_COLOR:
case GTK_CSS_PROPERTY_BACKGROUND_COLOR:
case GTK_CSS_PROPERTY_BORDER_TOP_COLOR:
case GTK_CSS_PROPERTY_BORDER_RIGHT_COLOR:
case GTK_CSS_PROPERTY_BORDER_BOTTOM_COLOR:
case GTK_CSS_PROPERTY_BORDER_LEFT_COLOR:
case GTK_CSS_PROPERTY_OUTLINE_COLOR:
return _gtk_css_value_compute (_gtk_css_style_property_get_initial_value (_gtk_css_style_property_lookup_by_id (property_id)),
property_id,
context,
NULL);
default:
if (property_id < GTK_CSS_PROPERTY_N_PROPERTIES)
g_warning ("No fallback color defined for property '%s'",
_gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (property_id))));
return _gtk_css_rgba_value_new_from_rgba (&transparent);
}
}
static GtkCssValue *
gtk_css_value_symbolic_compute (GtkCssValue *value,
guint property_id,
GtkStyleContext *context,
GtkCssDependencies *dependencies)
{
GtkCssValue *resolved, *current;
*dependencies = GTK_CSS_DEPENDS_ON_EVERYTHING;
/* The computed value of the currentColor keyword is the computed
* value of the color property. If the currentColor keyword is
* set on the color property itself, it is treated as color: inherit.
*/
if (property_id == GTK_CSS_PROPERTY_COLOR)
{
GtkStyleContext *parent = gtk_style_context_get_parent (context);
if (parent)
current = _gtk_style_context_peek_property (parent, GTK_CSS_PROPERTY_COLOR);
else
current = _gtk_css_style_property_get_initial_value (_gtk_css_style_property_lookup_by_id (GTK_CSS_PROPERTY_COLOR));
}
else
{
current = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR);
}
resolved = _gtk_style_context_resolve_color_value (context, current, value);
if (resolved == NULL)
return gtk_css_value_symbolic_get_fallback (property_id, context);
return resolved;
}
static gboolean
gtk_css_value_symbolic_equal (const GtkCssValue *value1,
const GtkCssValue *value2)
{
GtkSymbolicColor *color1 = (GtkSymbolicColor *) value1;
GtkSymbolicColor *color2 = (GtkSymbolicColor *) value2;
if (color1->type != color2->type)
return FALSE;
switch (color1->type)
{
case COLOR_TYPE_LITERAL:
return _gtk_css_value_equal (color1->last_value, color2->last_value);
case COLOR_TYPE_NAME:
return g_str_equal (color1->name, color2->name);
case COLOR_TYPE_SHADE:
return color1->shade.factor == color2->shade.factor &&
_gtk_css_value_equal ((GtkCssValue *) color1->shade.color,
(GtkCssValue *) color2->shade.color);
case COLOR_TYPE_ALPHA:
return color1->alpha.factor == color2->alpha.factor &&
_gtk_css_value_equal ((GtkCssValue *) color1->alpha.color,
(GtkCssValue *) color2->alpha.color);
case COLOR_TYPE_MIX:
return color1->mix.factor == color2->mix.factor &&
_gtk_css_value_equal ((GtkCssValue *) color1->mix.color1,
(GtkCssValue *) color2->mix.color1) &&
_gtk_css_value_equal ((GtkCssValue *) color1->mix.color2,
(GtkCssValue *) color2->mix.color2);
case COLOR_TYPE_WIN32:
return g_str_equal (color1->win32.theme_class, color2->win32.theme_class) &&
color1->win32.id == color2->win32.id;
case COLOR_TYPE_CURRENT_COLOR:
return TRUE;
default:
g_assert_not_reached ();
return FALSE;
}
}
static GtkCssValue *
gtk_css_value_symbolic_transition (GtkCssValue *start,
GtkCssValue *end,
double progress)
{
return NULL;
}
static void
gtk_css_value_symbolic_print (const GtkCssValue *value,
GString *string)
{
GtkSymbolicColor *color = (GtkSymbolicColor *) value;
char *s;
s = gtk_symbolic_color_to_string (color);
g_string_append (string, s);
g_free (s);
}
static const GtkCssValueClass GTK_CSS_VALUE_SYMBOLIC = {
gtk_css_value_symbolic_free,
gtk_css_value_symbolic_compute,
gtk_css_value_symbolic_equal,
gtk_css_value_symbolic_transition,
gtk_css_value_symbolic_print
};
/**
* gtk_symbolic_color_new_literal:
* @color: a #GdkRGBA
*
* Creates a symbolic color pointing to a literal color.
*
* Returns: a newly created #GtkSymbolicColor
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_new_literal (const GdkRGBA *color)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_LITERAL;
symbolic_color->last_value = _gtk_css_rgba_value_new_from_rgba (color);
return symbolic_color;
}
/**
* gtk_symbolic_color_new_name:
* @name: color name
*
* Creates a symbolic color pointing to an unresolved named
* color. See gtk_style_context_lookup_color() and
* gtk_style_properties_lookup_color().
*
* Returns: a newly created #GtkSymbolicColor
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_new_name (const gchar *name)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (name != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_NAME;
symbolic_color->name = g_strdup (name);
return symbolic_color;
}
/**
* gtk_symbolic_color_new_shade: (constructor)
* @color: another #GtkSymbolicColor
* @factor: shading factor to apply to @color
*
* Creates a symbolic color defined as a shade of
* another color. A factor > 1.0 would resolve to
* a brighter color, while < 1.0 would resolve to
* a darker color.
*
* Returns: A newly created #GtkSymbolicColor
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
gdouble factor)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_SHADE;
symbolic_color->shade.color = gtk_symbolic_color_ref (color);
symbolic_color->shade.factor = factor;
return symbolic_color;
}
/**
* gtk_symbolic_color_new_alpha: (constructor)
* @color: another #GtkSymbolicColor
* @factor: factor to apply to @color alpha
*
* Creates a symbolic color by modifying the relative alpha
* value of @color. A factor < 1.0 would resolve to a more
* transparent color, while > 1.0 would resolve to a more
* opaque color.
*
* Returns: A newly created #GtkSymbolicColor
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_new_alpha (GtkSymbolicColor *color,
gdouble factor)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_ALPHA;
symbolic_color->alpha.color = gtk_symbolic_color_ref (color);
symbolic_color->alpha.factor = factor;
return symbolic_color;
}
/**
* gtk_symbolic_color_new_mix: (constructor)
* @color1: color to mix
* @color2: another color to mix
* @factor: mix factor
*
* Creates a symbolic color defined as a mix of another
* two colors. a mix factor of 0 would resolve to @color1,
* while a factor of 1 would resolve to @color2.
*
* Returns: A newly created #GtkSymbolicColor
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
GtkSymbolicColor *color2,
gdouble factor)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (color1 != NULL, NULL);
g_return_val_if_fail (color1 != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_MIX;
symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
symbolic_color->mix.factor = factor;
return symbolic_color;
}
/**
* gtk_symbolic_color_new_win32: (constructor)
* @theme_class: The theme class to pull color from
* @id: The color id
*
* Creates a symbolic color based on the current win32
* theme.
*
* Note that while this call is available on all platforms
* the actual value returned is not reliable on non-win32
* platforms.
*
* Returns: A newly created #GtkSymbolicColor
*
* Since: 3.4
*/
GtkSymbolicColor *
gtk_symbolic_color_new_win32 (const gchar *theme_class,
gint id)
{
GtkSymbolicColor *symbolic_color;
g_return_val_if_fail (theme_class != NULL, NULL);
symbolic_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
symbolic_color->type = COLOR_TYPE_WIN32;
symbolic_color->win32.theme_class = g_strdup (theme_class);
symbolic_color->win32.id = id;
return symbolic_color;
}
/**
* _gtk_symbolic_color_get_current_color:
*
* Gets the color representing the CSS 'currentColor' keyword.
* This color will resolve to the color set for the color property.
*
* Returns: (transfer none): The singleton representing the
* 'currentColor' keyword
**/
GtkSymbolicColor *
_gtk_symbolic_color_get_current_color (void)
{
static GtkSymbolicColor *current_color = NULL;
if (G_UNLIKELY (current_color == NULL))
{
current_color = _gtk_css_value_new (GtkSymbolicColor, &GTK_CSS_VALUE_SYMBOLIC);
current_color->type = COLOR_TYPE_CURRENT_COLOR;
}
return current_color;
}
/**
* gtk_symbolic_color_ref:
* @color: a #GtkSymbolicColor
*
* Increases the reference count of @color
*
* Returns: the same @color
*
* Since: 3.0
**/
GtkSymbolicColor *
gtk_symbolic_color_ref (GtkSymbolicColor *color)
{
g_return_val_if_fail (color != NULL, NULL);
_gtk_css_value_ref ((GtkCssValue *) color);
return color;
}
/**
* gtk_symbolic_color_unref:
* @color: a #GtkSymbolicColor
*
* Decreases the reference count of @color, freeing its memory if the
* reference count reaches 0.
*
* Since: 3.0
**/
void
gtk_symbolic_color_unref (GtkSymbolicColor *color)
{
g_return_if_fail (color != NULL);
_gtk_css_value_unref ((GtkCssValue *) color);
}
static void
rgb_to_hls (gdouble *r,
gdouble *g,
gdouble *b)
{
gdouble min;
gdouble max;
gdouble red;
gdouble green;
gdouble blue;
gdouble h, l, s;
gdouble delta;
red = *r;
green = *g;
blue = *b;
if (red > green)
{
if (red > blue)
max = red;
else
max = blue;
if (green < blue)
min = green;
else
min = blue;
}
else
{
if (green > blue)
max = green;
else
max = blue;
if (red < blue)
min = red;
else
min = blue;
}
l = (max + min) / 2;
s = 0;
h = 0;
if (max != min)
{
if (l <= 0.5)
s = (max - min) / (max + min);
else
s = (max - min) / (2 - max - min);
delta = max -min;
if (red == max)
h = (green - blue) / delta;
else if (green == max)
h = 2 + (blue - red) / delta;
else if (blue == max)
h = 4 + (red - green) / delta;
h *= 60;
if (h < 0.0)
h += 360;
}
*r = h;
*g = l;
*b = s;
}
static void
hls_to_rgb (gdouble *h,
gdouble *l,
gdouble *s)
{
gdouble hue;
gdouble lightness;
gdouble saturation;
gdouble m1, m2;
gdouble r, g, b;
lightness = *l;
saturation = *s;
if (lightness <= 0.5)
m2 = lightness * (1 + saturation);
else
m2 = lightness + saturation - lightness * saturation;
m1 = 2 * lightness - m2;
if (saturation == 0)
{
*h = lightness;
*l = lightness;
*s = lightness;
}
else
{
hue = *h + 120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
r = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
r = m2;
else if (hue < 240)
r = m1 + (m2 - m1) * (240 - hue) / 60;
else
r = m1;
hue = *h;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
g = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
g = m2;
else if (hue < 240)
g = m1 + (m2 - m1) * (240 - hue) / 60;
else
g = m1;
hue = *h - 120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
b = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
b = m2;
else if (hue < 240)
b = m1 + (m2 - m1) * (240 - hue) / 60;
else
b = m1;
*h = r;
*l = g;
*s = b;
}
}
static void
_shade_color (GdkRGBA *color,
gdouble factor)
{
GdkRGBA temp;
temp = *color;
rgb_to_hls (&temp.red, &temp.green, &temp.blue);
temp.green *= factor;
if (temp.green > 1.0)
temp.green = 1.0;
else if (temp.green < 0.0)
temp.green = 0.0;
temp.blue *= factor;
if (temp.blue > 1.0)
temp.blue = 1.0;
else if (temp.blue < 0.0)
temp.blue = 0.0;
hls_to_rgb (&temp.red, &temp.green, &temp.blue);
*color = temp;
}
static GtkSymbolicColor *
resolve_lookup_color (gpointer data, const char *name)
{
if (data == NULL)
return NULL;
return gtk_style_properties_lookup_color (data, name);
}
/**
* gtk_symbolic_color_resolve:
* @color: a #GtkSymbolicColor
* @props: (allow-none): #GtkStyleProperties to use when resolving
* named colors, or %NULL
* @resolved_color: (out): return location for the resolved color
*
* If @color is resolvable, @resolved_color will be filled in
* with the resolved color, and %TRUE will be returned. Generally,
* if @color can't be resolved, it is due to it being defined on
* top of a named color that doesn't exist in @props.
*
* When @props is %NULL, resolving of named colors will fail, so if
* your @color is or references such a color, this function will
* return %FALSE.
*
* Returns: %TRUE if the color has been resolved
*
* Since: 3.0
**/
gboolean
gtk_symbolic_color_resolve (GtkSymbolicColor *color,
GtkStyleProperties *props,
GdkRGBA *resolved_color)
{
GdkRGBA pink = { 1.0, 0.5, 0.5, 1.0 };
GtkCssValue *v, *current;
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (resolved_color != NULL, FALSE);
g_return_val_if_fail (props == NULL || GTK_IS_STYLE_PROPERTIES (props), FALSE);
current = _gtk_css_rgba_value_new_from_rgba (&pink);
v =_gtk_symbolic_color_resolve_full (color,
current,
resolve_lookup_color,
props);
_gtk_css_value_unref (current);
if (v == NULL)
return FALSE;
*resolved_color = *_gtk_css_rgba_value_get_rgba (v);
_gtk_css_value_unref (v);
return TRUE;
}
GtkCssValue *
_gtk_symbolic_color_resolve_full (GtkSymbolicColor *color,
GtkCssValue *current,
GtkSymbolicColorLookupFunc func,
gpointer data)
{
GtkCssValue *value;
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (current != NULL, FALSE);
g_return_val_if_fail (func != NULL, FALSE);
value = NULL;
switch (color->type)
{
case COLOR_TYPE_LITERAL:
return _gtk_css_value_ref (color->last_value);
case COLOR_TYPE_NAME:
{
GtkSymbolicColor *named_color;
named_color = func (data, color->name);
if (!named_color)
return NULL;
return _gtk_symbolic_color_resolve_full (named_color, current, func, data);
}
break;
case COLOR_TYPE_SHADE:
{
GtkCssValue *val;
GdkRGBA shade;
val = _gtk_symbolic_color_resolve_full (color->shade.color, current, func, data);
if (val == NULL)
return NULL;
shade = *_gtk_css_rgba_value_get_rgba (val);
_shade_color (&shade, color->shade.factor);
_gtk_css_value_unref (val);
value = _gtk_css_rgba_value_new_from_rgba (&shade);
}
break;
case COLOR_TYPE_ALPHA:
{
GtkCssValue *val;
GdkRGBA alpha;
val = _gtk_symbolic_color_resolve_full (color->alpha.color, current, func, data);
if (val == NULL)
return NULL;
alpha = *_gtk_css_rgba_value_get_rgba (val);
alpha.alpha = CLAMP (alpha.alpha * color->alpha.factor, 0, 1);
_gtk_css_value_unref (val);
value = _gtk_css_rgba_value_new_from_rgba (&alpha);
}
break;
case COLOR_TYPE_MIX:
{
GtkCssValue *val;
GdkRGBA color1, color2, res;
val = _gtk_symbolic_color_resolve_full (color->mix.color1, current, func, data);
if (val == NULL)
return NULL;
color1 = *_gtk_css_rgba_value_get_rgba (val);
_gtk_css_value_unref (val);
val = _gtk_symbolic_color_resolve_full (color->mix.color2, current, func, data);
if (val == NULL)
return NULL;
color2 = *_gtk_css_rgba_value_get_rgba (val);
_gtk_css_value_unref (val);
res.red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 1);
res.green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 1);
res.blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 1);
res.alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->mix.factor), 0, 1);
value =_gtk_css_rgba_value_new_from_rgba (&res);
}
break;
case COLOR_TYPE_WIN32:
{
GdkRGBA res;
if (!_gtk_win32_theme_color_resolve (color->win32.theme_class,
color->win32.id,
&res))
return NULL;
value = _gtk_css_rgba_value_new_from_rgba (&res);
}
break;
case COLOR_TYPE_CURRENT_COLOR:
if (current)
return _gtk_css_value_ref (current);
else
return NULL;
break;
default:
g_assert_not_reached ();
}
if (value != NULL)
{
if (color->last_value != NULL &&
_gtk_css_value_equal (color->last_value, value))
{
_gtk_css_value_unref (value);
value = _gtk_css_value_ref (color->last_value);
}
else
{
if (color->last_value != NULL)
_gtk_css_value_unref (color->last_value);
color->last_value = _gtk_css_value_ref (value);
}
}
_gtk_css_rgba_value_get_rgba (value);
return value;
}
/**
* gtk_symbolic_color_to_string:
* @color: color to convert to a string
*
* Converts the given @color to a string representation. This is useful
* both for debugging and for serialization of strings. The format of
* the string may change between different versions of GTK, but it is
* guaranteed that the GTK css parser is able to read the string and
* create the same symbolic color from it.
*
* Returns: a new string representing @color
**/
char *
gtk_symbolic_color_to_string (GtkSymbolicColor *color)
{
char *s;
g_return_val_if_fail (color != NULL, NULL);
switch (color->type)
{
case COLOR_TYPE_LITERAL:
s = gdk_rgba_to_string (_gtk_css_rgba_value_get_rgba (color->last_value));
break;
case COLOR_TYPE_NAME:
s = g_strconcat ("@", color->name, NULL);
break;
case COLOR_TYPE_SHADE:
{
char *color_string = gtk_symbolic_color_to_string (color->shade.color);
char factor[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (factor, sizeof (factor), color->shade.factor);
s = g_strdup_printf ("shade (%s, %s)", color_string, factor);
g_free (color_string);
}
break;
case COLOR_TYPE_ALPHA:
{
char *color_string = gtk_symbolic_color_to_string (color->shade.color);
char factor[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (factor, sizeof (factor), color->alpha.factor);
s = g_strdup_printf ("alpha (%s, %s)", color_string, factor);
g_free (color_string);
}
break;
case COLOR_TYPE_MIX:
{
char *color_string1 = gtk_symbolic_color_to_string (color->mix.color1);
char *color_string2 = gtk_symbolic_color_to_string (color->mix.color2);
char factor[G_ASCII_DTOSTR_BUF_SIZE];
g_ascii_dtostr (factor, sizeof (factor), color->mix.factor);
s = g_strdup_printf ("mix (%s, %s, %s)", color_string1, color_string2, factor);
g_free (color_string1);
g_free (color_string2);
}
break;
case COLOR_TYPE_WIN32:
{
s = g_strdup_printf (GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME"(%s, %d)",
color->win32.theme_class, color->win32.id);
}
break;
case COLOR_TYPE_CURRENT_COLOR:
s = g_strdup ("currentColor");
break;
default:
g_assert_not_reached ();
}
return s;
}
typedef enum {
COLOR_RGBA,
COLOR_RGB,
COLOR_LIGHTER,
COLOR_DARKER,
COLOR_SHADE,
COLOR_ALPHA,
COLOR_MIX,
COLOR_WIN32
} ColorParseType;
static GtkSymbolicColor * gtk_css_parser_read_symbolic_color (GtkCssParser *parser);
static GtkSymbolicColor *
gtk_css_parser_read_symbolic_color_function (GtkCssParser *parser,
ColorParseType color)
{
GtkSymbolicColor *symbolic;
GtkSymbolicColor *child1, *child2;
double value;
if (!_gtk_css_parser_try (parser, "(", TRUE))
{
_gtk_css_parser_error (parser, "Missing opening bracket in color definition");
return NULL;
}
if (color == COLOR_RGB || color == COLOR_RGBA)
{
GdkRGBA rgba;
double tmp;
guint i;
for (i = 0; i < 3; i++)
{
if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
{
_gtk_css_parser_error (parser, "Expected ',' in color definition");
return NULL;
}
if (!_gtk_css_parser_try_double (parser, &tmp))
{
_gtk_css_parser_error (parser, "Invalid number for color value");
return NULL;
}
if (_gtk_css_parser_try (parser, "%", TRUE))
tmp /= 100.0;
else
tmp /= 255.0;
if (i == 0)
rgba.red = tmp;
else if (i == 1)
rgba.green = tmp;
else if (i == 2)
rgba.blue = tmp;
else
g_assert_not_reached ();
}
if (color == COLOR_RGBA)
{
if (i > 0 && !_gtk_css_parser_try (parser, ",", TRUE))
{
_gtk_css_parser_error (parser, "Expected ',' in color definition");
return NULL;
}
if (!_gtk_css_parser_try_double (parser, &rgba.alpha))
{
_gtk_css_parser_error (parser, "Invalid number for alpha value");
return NULL;
}
}
else
rgba.alpha = 1.0;
symbolic = gtk_symbolic_color_new_literal (&rgba);
}
else if (color == COLOR_WIN32)
{
symbolic = _gtk_win32_theme_color_parse (parser);
if (symbolic == NULL)
return NULL;
}
else
{
child1 = gtk_css_parser_read_symbolic_color (parser);
if (child1 == NULL)
return NULL;
if (color == COLOR_MIX)
{
if (!_gtk_css_parser_try (parser, ",", TRUE))
{
_gtk_css_parser_error (parser, "Expected ',' in color definition");
gtk_symbolic_color_unref (child1);
return NULL;
}
child2 = gtk_css_parser_read_symbolic_color (parser);
if (child2 == NULL)
{
gtk_symbolic_color_unref (child1);
return NULL;
}
}
else
child2 = NULL;
if (color == COLOR_LIGHTER)
value = 1.3;
else if (color == COLOR_DARKER)
value = 0.7;
else
{
if (!_gtk_css_parser_try (parser, ",", TRUE))
{
_gtk_css_parser_error (parser, "Expected ',' in color definition");
gtk_symbolic_color_unref (child1);
if (child2)
gtk_symbolic_color_unref (child2);
return NULL;
}
if (!_gtk_css_parser_try_double (parser, &value))
{
_gtk_css_parser_error (parser, "Expected number in color definition");
gtk_symbolic_color_unref (child1);
if (child2)
gtk_symbolic_color_unref (child2);
return NULL;
}
}
switch (color)
{
case COLOR_LIGHTER:
case COLOR_DARKER:
case COLOR_SHADE:
symbolic = gtk_symbolic_color_new_shade (child1, value);
break;
case COLOR_ALPHA:
symbolic = gtk_symbolic_color_new_alpha (child1, value);
break;
case COLOR_MIX:
symbolic = gtk_symbolic_color_new_mix (child1, child2, value);
break;
default:
g_assert_not_reached ();
symbolic = NULL;
}
gtk_symbolic_color_unref (child1);
if (child2)
gtk_symbolic_color_unref (child2);
}
if (!_gtk_css_parser_try (parser, ")", TRUE))
{
_gtk_css_parser_error (parser, "Expected ')' in color definition");
gtk_symbolic_color_unref (symbolic);
return NULL;
}
return symbolic;
}
static GtkSymbolicColor *
gtk_css_parser_read_symbolic_color (GtkCssParser *parser)
{
GtkSymbolicColor *symbolic;
GdkRGBA rgba;
guint color;
const char *names[] = {"rgba", "rgb", "lighter", "darker", "shade", "alpha", "mix",
GTK_WIN32_THEME_SYMBOLIC_COLOR_NAME};
char *name;
if (_gtk_css_parser_try (parser, "currentColor", TRUE))
return gtk_symbolic_color_ref (_gtk_symbolic_color_get_current_color ());
if (_gtk_css_parser_try (parser, "transparent", TRUE))
{
GdkRGBA transparent = { 0, 0, 0, 0 };
return gtk_symbolic_color_new_literal (&transparent);
}
if (_gtk_css_parser_try (parser, "@", FALSE))
{
name = _gtk_css_parser_try_name (parser, TRUE);
if (name)
{
symbolic = gtk_symbolic_color_new_name (name);
}
else
{
_gtk_css_parser_error (parser, "'%s' is not a valid symbolic color name", name);
symbolic = NULL;
}
g_free (name);
return symbolic;
}
for (color = 0; color < G_N_ELEMENTS (names); color++)
{
if (_gtk_css_parser_try (parser, names[color], TRUE))
break;
}
if (color < G_N_ELEMENTS (names))
return gtk_css_parser_read_symbolic_color_function (parser, color);
if (_gtk_css_parser_try_hash_color (parser, &rgba))
return gtk_symbolic_color_new_literal (&rgba);
name = _gtk_css_parser_try_name (parser, TRUE);
if (name)
{
if (gdk_rgba_parse (&rgba, name))
{
symbolic = gtk_symbolic_color_new_literal (&rgba);
}
else
{
_gtk_css_parser_error (parser, "'%s' is not a valid color name", name);
symbolic = NULL;
}
g_free (name);
return symbolic;
}
_gtk_css_parser_error (parser, "Not a color definition");
return NULL;
}
GtkCssValue *
_gtk_css_symbolic_value_new (GtkCssParser *parser)
{
g_return_val_if_fail (parser != NULL, NULL);
return _gtk_css_symbolic_value_new_take_symbolic_color (gtk_css_parser_read_symbolic_color (parser));
}