]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtksymboliccolor.c
Bug 663856 - Make option-foo accelerators use the right symbol
[~andy/gtk] / gtk / gtksymboliccolor.c
index 310e2113422abd35d88f6dfee1716664a80c9883..41a81a1cabcc36470580678bf68878dded5e79e3 100644 (file)
 
 #include "config.h"
 #include "gtksymboliccolor.h"
-#include "gtkstyleset.h"
+#include "gtkstyleproperties.h"
 #include "gtkintl.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)
-G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
-                     gtk_gradient_ref, gtk_gradient_unref)
+                     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
 } ColorType;
 
-struct GtkSymbolicColor
+struct _GtkSymbolicColor
 {
   ColorType type;
   guint ref_count;
 
   union
   {
-    GdkColor color;
+    GdkRGBA color;
     gchar *name;
 
     struct
     {
       GtkSymbolicColor *color;
       gdouble factor;
-    } shade;
+    } shade, alpha;
 
     struct
     {
@@ -60,28 +78,18 @@ struct GtkSymbolicColor
   };
 };
 
-typedef struct ColorStop ColorStop;
-
-struct ColorStop
-{
-  gdouble offset;
-  GtkSymbolicColor *color;
-};
-
-struct GtkGradient
-{
-  gdouble x0;
-  gdouble y0;
-  gdouble x1;
-  gdouble y1;
-
-  GArray *stops;
-
-  guint ref_count;
-};
-
+/**
+ * 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 (GdkColor *color)
+gtk_symbolic_color_new_literal (const GdkRGBA *color)
 {
   GtkSymbolicColor *symbolic_color;
 
@@ -95,6 +103,18 @@ gtk_symbolic_color_new_literal (GdkColor *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)
 {
@@ -110,6 +130,20 @@ gtk_symbolic_color_new_name (const gchar *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)
@@ -121,12 +155,57 @@ gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
   symbolic_color = g_slice_new0 (GtkSymbolicColor);
   symbolic_color->type = COLOR_TYPE_SHADE;
   symbolic_color->shade.color = gtk_symbolic_color_ref (color);
-  symbolic_color->shade.factor = CLAMP (factor, 0, 1);
+  symbolic_color->shade.factor = factor;
   symbolic_color->ref_count = 1;
 
   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 = g_slice_new0 (GtkSymbolicColor);
+  symbolic_color->type = COLOR_TYPE_ALPHA;
+  symbolic_color->alpha.color = gtk_symbolic_color_ref (color);
+  symbolic_color->alpha.factor = factor;
+  symbolic_color->ref_count = 1;
+
+  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,
@@ -141,12 +220,22 @@ gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
   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 = CLAMP (factor, 0, 1);
+  symbolic_color->mix.factor = factor;
   symbolic_color->ref_count = 1;
 
   return symbolic_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)
 {
@@ -157,6 +246,15 @@ gtk_symbolic_color_ref (GtkSymbolicColor *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)
 {
@@ -174,6 +272,9 @@ gtk_symbolic_color_unref (GtkSymbolicColor *color)
         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);
@@ -186,14 +287,209 @@ gtk_symbolic_color_unref (GtkSymbolicColor *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;
+}
+
+/**
+ * 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,
-                            GtkStyleSet         *style_set,
-                            GdkColor            *resolved_color)
+gtk_symbolic_color_resolve (GtkSymbolicColor   *color,
+                            GtkStyleProperties *props,
+                            GdkRGBA            *resolved_color)
 {
   g_return_val_if_fail (color != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
   g_return_val_if_fail (resolved_color != NULL, FALSE);
+  g_return_val_if_fail (props == NULL || GTK_IS_STYLE_PROPERTIES (props), FALSE);
 
   switch (color->type)
     {
@@ -204,43 +500,58 @@ gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
       {
         GtkSymbolicColor *named_color;
 
-        named_color = gtk_style_set_lookup_color (style_set, color->name);
+        if (props == NULL)
+          return FALSE;
+
+        named_color = gtk_style_properties_lookup_color (props, color->name);
 
         if (!named_color)
           return FALSE;
 
-        return gtk_symbolic_color_resolve (named_color, style_set, resolved_color);
+        return gtk_symbolic_color_resolve (named_color, props, resolved_color);
       }
 
       break;
     case COLOR_TYPE_SHADE:
       {
-        GdkColor shade;
+        GdkRGBA shade;
 
-        if (!gtk_symbolic_color_resolve (color->shade.color, style_set, &shade))
+        if (!gtk_symbolic_color_resolve (color->shade.color, props, &shade))
           return FALSE;
 
-        resolved_color->red = CLAMP (shade.red * color->shade.factor, 0, 65535);
-        resolved_color->green = CLAMP (shade.green * color->shade.factor, 0, 65535);
-        resolved_color->blue = CLAMP (shade.blue * color->shade.factor, 0, 65535);
+        _shade_color (&shade, color->shade.factor);
+        *resolved_color = shade;
 
         return TRUE;
       }
 
       break;
+    case COLOR_TYPE_ALPHA:
+      {
+        GdkRGBA alpha;
+
+        if (!gtk_symbolic_color_resolve (color->alpha.color, props, &alpha))
+          return FALSE;
+
+        *resolved_color = alpha;
+        resolved_color->alpha = CLAMP (alpha.alpha * color->alpha.factor, 0, 1);
+
+        return TRUE;
+      }
     case COLOR_TYPE_MIX:
       {
-        GdkColor color1, color2;
+        GdkRGBA color1, color2;
 
-        if (!gtk_symbolic_color_resolve (color->mix.color1, style_set, &color1))
+        if (!gtk_symbolic_color_resolve (color->mix.color1, props, &color1))
           return FALSE;
 
-        if (!gtk_symbolic_color_resolve (color->mix.color2, style_set, &color2))
+        if (!gtk_symbolic_color_resolve (color->mix.color2, props, &color2))
           return FALSE;
 
-        resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 65535);
-        resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 65535);
-        resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 65535);
+        resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 1);
+        resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 1);
+        resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 1);
+        resolved_color->alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->mix.factor), 0, 1);
 
         return TRUE;
       }
@@ -253,111 +564,69 @@ gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
   return FALSE;
 }
 
-/* GtkGradient */
-GtkGradient *
-gtk_gradient_new_linear (gdouble x0,
-                         gdouble y0,
-                         gdouble x1,
-                         gdouble y1)
-{
-  GtkGradient *gradient;
-
-  gradient = g_slice_new (GtkGradient);
-  gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
-
-  gradient->x0 = x0;
-  gradient->y0 = y0;
-  gradient->x1 = x1;
-  gradient->y1 = y1;
-
-  gradient->ref_count = 1;
-
-  return gradient;
-}
-
-void
-gtk_gradient_add_color_stop (GtkGradient      *gradient,
-                             gdouble           offset,
-                             GtkSymbolicColor *color)
-{
-  ColorStop stop;
-
-  g_return_if_fail (gradient != NULL);
-
-  stop.offset = offset;
-  stop.color = gtk_symbolic_color_ref (color);
-
-  g_array_append_val (gradient->stops, stop);
-}
-
-GtkGradient *
-gtk_gradient_ref (GtkGradient *gradient)
-{
-  g_return_val_if_fail (gradient != NULL, NULL);
-
-  gradient->ref_count++;
-
-  return gradient;
-}
-
-void
-gtk_gradient_unref (GtkGradient *gradient)
+/**
+ * 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)
 {
-  g_return_if_fail (gradient != NULL);
+  char *s;
 
-  gradient->ref_count--;
+  g_return_val_if_fail (color != NULL, NULL);
 
-  if (gradient->ref_count == 0)
+  switch (color->type)
     {
-      guint i;
-
-      for (i = 0; i < gradient->stops->len; i++)
-        {
-          ColorStop *stop;
+    case COLOR_TYPE_LITERAL:
+      s = gdk_rgba_to_string (&color->color);
+      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];
 
-          stop = &g_array_index (gradient->stops, ColorStop, i);
-          gtk_symbolic_color_unref (stop->color);
-        }
+        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_array_free (gradient->stops, TRUE);
-      g_slice_free (GtkGradient, gradient);
+        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;
+    default:
+      g_assert_not_reached ();
     }
-}
-
-gboolean
-gtk_gradient_resolve (GtkGradient      *gradient,
-                      GtkStyleSet      *style_set,
-                      cairo_pattern_t **resolved_gradient)
-{
-  cairo_pattern_t *pattern;
-  guint i;
-
-  g_return_val_if_fail (gradient != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
-  g_return_val_if_fail (resolved_gradient != NULL, FALSE);
-
-  pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
-                                         gradient->x1, gradient->y1);
 
-  for (i = 0; i < gradient->stops->len; i++)
-    {
-      ColorStop *stop;
-      GdkColor color;
-
-      stop = &g_array_index (gradient->stops, ColorStop, i);
-
-      if (!gtk_symbolic_color_resolve (stop->color, style_set, &color))
-        {
-          cairo_pattern_destroy (pattern);
-          return FALSE;
-        }
-
-      cairo_pattern_add_color_stop_rgb (pattern, stop->offset,
-                                        color.red / 65535.,
-                                        color.green / 65535.,
-                                        color.blue / 65535.);
-    }
+  return s;
 
-  *resolved_gradient = pattern;
-  return TRUE;
 }