]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtksymboliccolor.c
Change FSF Address
[~andy/gtk] / gtk / gtksymboliccolor.c
index bd33efaf6c5aae7625cbef35255273faaa09bc1b..bb4c2b8e898e6202e1d1a0f2ceda974cd36a1c30 100644 (file)
  * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
-#include "gtksymboliccolor.h"
-#include "gtkstyleset.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_MIX
+  COLOR_TYPE_ALPHA,
+  COLOR_TYPE_MIX,
+  COLOR_TYPE_WIN32,
+  COLOR_TYPE_CURRENT_COLOR
 } 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
     {
@@ -52,11 +76,27 @@ struct GtkSymbolicColor
       GtkSymbolicColor *color2;
       gdouble factor;
     } mix;
+
+    struct
+    {
+      gchar *theme_class;
+      gint id;
+    } win32;
   };
 };
 
+/**
+ * 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;
 
@@ -65,10 +105,23 @@ gtk_symbolic_color_new_literal (GdkColor *color)
   symbolic_color = g_slice_new0 (GtkSymbolicColor);
   symbolic_color->type = COLOR_TYPE_LITERAL;
   symbolic_color->color = *color;
+  symbolic_color->ref_count = 1;
 
   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)
 {
@@ -79,10 +132,25 @@ gtk_symbolic_color_new_name (const gchar *name)
   symbolic_color = g_slice_new0 (GtkSymbolicColor);
   symbolic_color->type = COLOR_TYPE_NAME;
   symbolic_color->name = g_strdup (name);
+  symbolic_color->ref_count = 1;
 
   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)
@@ -94,11 +162,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,
@@ -113,11 +227,79 @@ 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_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 = g_slice_new0 (GtkSymbolicColor);
+  symbolic_color->type = COLOR_TYPE_WIN32;
+  symbolic_color->win32.theme_class = g_strdup (theme_class);
+  symbolic_color->win32.id = id;
+  symbolic_color->ref_count = 1;
 
   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 = g_slice_new0 (GtkSymbolicColor);
+      current_color->type = COLOR_TYPE_CURRENT_COLOR;
+      current_color->ref_count = 1;
+    }
+
+  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)
 {
@@ -128,22 +310,278 @@ 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)
 {
   g_return_if_fail (color != NULL);
 
   color->ref_count--;
+
+  if (color->ref_count == 0)
+    {
+      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 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)
+{
+  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);
+
+  return _gtk_symbolic_color_resolve_full (color,
+                                           resolve_lookup_color,
+                                           props,
+                                           resolved_color);
 }
 
 gboolean
-gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
-                            GtkStyleSet         *style_set,
-                            GdkColor            *resolved_color)
+_gtk_symbolic_color_resolve_full (GtkSymbolicColor           *color,
+                                  GtkSymbolicColorLookupFunc  func,
+                                  gpointer                    data,
+                                  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 (func != NULL, FALSE);
 
   switch (color->type)
     {
@@ -154,47 +592,68 @@ gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
       {
         GtkSymbolicColor *named_color;
 
-        named_color = gtk_style_set_lookup_color (style_set, color->name);
+        named_color = func (data, color->name);
 
         if (!named_color)
           return FALSE;
 
-        return gtk_symbolic_color_resolve (named_color, style_set, resolved_color);
+        return _gtk_symbolic_color_resolve_full (named_color, func, data, 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_full (color->shade.color, func, data, &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_full (color->alpha.color, func, data, &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_full (color->mix.color1, func, data, &color1))
           return FALSE;
 
-        if (!gtk_symbolic_color_resolve (color->mix.color2, style_set, &color2))
+        if (!_gtk_symbolic_color_resolve_full (color->mix.color2, func, data, &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;
       }
 
+      break;
+    case COLOR_TYPE_WIN32:
+      return _gtk_win32_theme_color_resolve (color->win32.theme_class,
+                                            color->win32.id,
+                                            resolved_color);
+
+      break;
+    case COLOR_TYPE_CURRENT_COLOR:
+      return FALSE;
       break;
     default:
       g_assert_not_reached ();
@@ -203,18 +662,78 @@ gtk_symbolic_color_resolve (GtkSymbolicColor    *color,
   return FALSE;
 }
 
-GType
-gtk_symbolic_color_get_type (void)
+/**
+ * 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)
 {
-  static GType type = 0;
+  char *s;
 
-  if (G_UNLIKELY (!type))
-    type = g_boxed_type_register_static (I_("GtkSymbolicColor"),
-                                        (GBoxedCopyFunc) gtk_symbolic_color_ref,
-                                        (GBoxedFreeFunc) gtk_symbolic_color_unref);
+  g_return_val_if_fail (color != NULL, NULL);
 
-  return type;
-}
+  switch (color->type)
+    {
+    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];
 
-#define __GTK_SYMBOLIC_COLOR_C__
-#include "gtkaliasdef.c"
+        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;
+
+}