/*
* Copyright © 2012 Red Hat Inc.
*
* 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.1 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 .
*
* Authors: Benjamin Otte
*/
#include "config.h"
#include "gtkprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkcssanimationprivate.h"
#include "gtkcssarrayvalueprivate.h"
#include "gtkcssenumvalueprivate.h"
#include "gtkcssinheritvalueprivate.h"
#include "gtkcssinitialvalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkcsssectionprivate.h"
#include "gtkcssshorthandpropertyprivate.h"
#include "gtkcssstringvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include "gtkcsstransitionprivate.h"
#include "gtkstyleanimationprivate.h"
#include "gtkstylepropertyprivate.h"
#include "gtkstyleproviderprivate.h"
G_DEFINE_ABSTRACT_TYPE (GtkCssStyle, gtk_css_style, G_TYPE_OBJECT)
static GtkCssSection *
gtk_css_style_real_get_section (GtkCssStyle *style,
guint id)
{
return NULL;
}
static gboolean
gtk_css_style_real_is_static (GtkCssStyle *style)
{
return TRUE;
}
static void
gtk_css_style_class_init (GtkCssStyleClass *klass)
{
klass->get_section = gtk_css_style_real_get_section;
klass->is_static = gtk_css_style_real_is_static;
}
static void
gtk_css_style_init (GtkCssStyle *style)
{
}
GtkCssValue *
gtk_css_style_get_value (GtkCssStyle *style,
guint id)
{
return GTK_CSS_STYLE_GET_CLASS (style)->get_value (style, id);
}
GtkCssSection *
gtk_css_style_get_section (GtkCssStyle *style,
guint id)
{
gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
return GTK_CSS_STYLE_GET_CLASS (style)->get_section (style, id);
}
GtkBitmask *
gtk_css_style_add_difference (GtkBitmask *accumulated,
GtkCssStyle *style,
GtkCssStyle *other)
{
gint len, i;
if (style == other)
return accumulated;
len = _gtk_css_style_property_get_n_properties ();
for (i = 0; i < len; i++)
{
if (_gtk_bitmask_get (accumulated, i))
continue;
if (!_gtk_css_value_equal (gtk_css_style_get_value (style, i),
gtk_css_style_get_value (other, i)))
accumulated = _gtk_bitmask_set (accumulated, i, TRUE);
}
return accumulated;
}
gboolean
gtk_css_style_is_static (GtkCssStyle *style)
{
gtk_internal_return_val_if_fail (GTK_IS_CSS_STYLE (style), TRUE);
return GTK_CSS_STYLE_GET_CLASS (style)->is_static (style);
}
/*
* gtk_css_style_print:
* @style: a #GtkCssStyle
* @string: the #GString to print to
* @indent: level of indentation to use
* @skip_initial: %TRUE to skip properties that have their initial value
*
* Print the @style to @string, in CSS format. Every property is printed
* on a line by itself, indented by @indent spaces. If @skip_initial is
* %TRUE, properties are only printed if their value in @style is different
* from the initial value of the property.
*
* Returns: %TRUE is any properties have been printed
*/
gboolean
gtk_css_style_print (GtkCssStyle *style,
GString *string,
guint indent,
gboolean skip_initial)
{
guint i;
gboolean retval = FALSE;
g_return_val_if_fail (GTK_IS_CSS_STYLE (style), FALSE);
g_return_val_if_fail (string != NULL, FALSE);
for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
{
GtkCssSection *section;
GtkCssStyleProperty *prop;
GtkCssValue *value;
const char *name;
section = gtk_css_style_get_section (style, i);
if (!section && skip_initial)
continue;
prop = _gtk_css_style_property_lookup_by_id (i);
name = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop));
value = gtk_css_style_get_value (style, i);
g_string_append_printf (string, "%*s%s: ", indent, "", name);
_gtk_css_value_print (value, string);
g_string_append_c (string, ';');
if (section)
{
g_string_append (string, " /* ");
_gtk_css_section_print (section, string);
g_string_append (string, " */");
}
g_string_append_c (string, '\n');
retval = TRUE;
}
return retval;
}
char *
gtk_css_style_to_string (GtkCssStyle *style)
{
GString *string;
g_return_val_if_fail (GTK_IS_CSS_STYLE (style), NULL);
string = g_string_new ("");
gtk_css_style_print (style, string, 0, FALSE);
return g_string_free (string, FALSE);
}
static PangoUnderline
get_pango_underline_from_style (GtkTextDecorationStyle style)
{
switch (style)
{
case GTK_CSS_TEXT_DECORATION_STYLE_DOUBLE:
return PANGO_UNDERLINE_DOUBLE;
case GTK_CSS_TEXT_DECORATION_STYLE_WAVY:
return PANGO_UNDERLINE_ERROR;
case GTK_CSS_TEXT_DECORATION_STYLE_SOLID:
default:
return PANGO_UNDERLINE_SINGLE;
}
g_return_val_if_reached (PANGO_UNDERLINE_SINGLE);
}
static PangoAttrList *
add_pango_attr (PangoAttrList *attrs,
PangoAttribute *attr)
{
if (attrs == NULL)
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, attr);
return attrs;
}
static void
append_separated (GString *s, const char *text)
{
if (s->len > 0)
g_string_append (s, ", ");
g_string_append (s, text);
}
PangoAttrList *
gtk_css_style_get_pango_attributes (GtkCssStyle *style)
{
PangoAttrList *attrs = NULL;
GtkTextDecorationLine decoration_line;
GtkTextDecorationStyle decoration_style;
const GdkRGBA *color;
const GdkRGBA *decoration_color;
gint letter_spacing;
GtkCssValue *value;
GtkCssFontVariantLigature ligatures;
GtkCssFontVariantNumeric numeric;
GtkCssFontVariantEastAsian east_asian;
GString *s;
/* text-decoration */
decoration_line = _gtk_css_text_decoration_line_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_LINE));
decoration_style = _gtk_css_text_decoration_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_STYLE));
color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_COLOR));
decoration_color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_TEXT_DECORATION_COLOR));
switch (decoration_line)
{
case GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE:
attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
if (!gdk_rgba_equal (color, decoration_color))
attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
decoration_color->green * 65535. + 0.5,
decoration_color->blue * 65535. + 0.5));
break;
case GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH:
attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
if (!gdk_rgba_equal (color, decoration_color))
attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
decoration_color->green * 65535. + 0.5,
decoration_color->blue * 65535. + 0.5));
break;
case GTK_CSS_TEXT_DECORATION_LINE_NONE:
default:
break;
}
/* letter-spacing */
letter_spacing = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_LETTER_SPACING), 100);
if (letter_spacing != 0)
{
attrs = add_pango_attr (attrs, pango_attr_letter_spacing_new (letter_spacing * PANGO_SCALE));
}
/* OpenType features */
s = g_string_new ("");
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_KERNING);
switch (_gtk_css_font_kerning_value_get (value))
{
case GTK_CSS_FONT_KERNING_NORMAL:
append_separated (s, "kern 1");
break;
case GTK_CSS_FONT_KERNING_NONE:
append_separated (s, "kern 0");
break;
default: ;
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_LIGATURES);
ligatures = _gtk_css_font_variant_ligature_value_get (value);
if (ligatures == GTK_CSS_FONT_VARIANT_LIGATURE_NORMAL)
{
/* all defaults */
}
else if (ligatures == GTK_CSS_FONT_VARIANT_LIGATURE_NONE)
append_separated (s, "liga 0, clig 0, dlig 0, hlig 0, calt 0");
else
{
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_COMMON_LIGATURES)
append_separated (s, "liga 1, clig 1");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_COMMON_LIGATURES)
append_separated (s, "liga 0, clig 0");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_DISCRETIONARY_LIGATURES)
append_separated (s, "dlig 1");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_DISCRETIONARY_LIGATURES)
append_separated (s, "dlig 0");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_HISTORICAL_LIGATURES)
append_separated (s, "hlig 1");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_HISTORICAL_LIGATURES)
append_separated (s, "hlig 0");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_CONTEXTUAL)
append_separated (s, "calt 1");
if (ligatures & GTK_CSS_FONT_VARIANT_LIGATURE_NO_CONTEXTUAL)
append_separated (s, "calt 0");
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_POSITION);
switch (_gtk_css_font_variant_position_value_get (value))
{
case GTK_CSS_FONT_VARIANT_POSITION_SUB:
append_separated (s, "subs 1");
break;
case GTK_CSS_FONT_VARIANT_POSITION_SUPER:
append_separated (s, "sups 1");
break;
default: ;
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_CAPS);
switch (_gtk_css_font_variant_caps_value_get (value))
{
case GTK_CSS_FONT_VARIANT_CAPS_SMALL_CAPS:
append_separated (s, "smcp 1");
break;
case GTK_CSS_FONT_VARIANT_CAPS_ALL_SMALL_CAPS:
append_separated (s, "c2sc 1, smcp 1");
break;
case GTK_CSS_FONT_VARIANT_CAPS_PETITE_CAPS:
append_separated (s, "pcap 1");
break;
case GTK_CSS_FONT_VARIANT_CAPS_ALL_PETITE_CAPS:
append_separated (s, "c2pc 1, pcap 1");
break;
case GTK_CSS_FONT_VARIANT_CAPS_UNICASE:
append_separated (s, "unic 1");
break;
case GTK_CSS_FONT_VARIANT_CAPS_TITLING_CAPS:
append_separated (s, "titl 1");
break;
default: ;
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_NUMERIC);
numeric = _gtk_css_font_variant_numeric_value_get (value);
if (numeric == GTK_CSS_FONT_VARIANT_NUMERIC_NORMAL)
{
/* all defaults */
}
else
{
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS)
append_separated (s, "lnum 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS)
append_separated (s, "onum 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS)
append_separated (s, "pnum 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS)
append_separated (s, "tnum 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS)
append_separated (s, "frac 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS)
append_separated (s, "afrc 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_ORDINAL)
append_separated (s, "ordn 1");
if (numeric & GTK_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO)
append_separated (s, "zero 1");
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_ALTERNATES);
switch (_gtk_css_font_variant_alternate_value_get (value))
{
case GTK_CSS_FONT_VARIANT_ALTERNATE_HISTORICAL_FORMS:
append_separated (s, "hist 1");
break;
default: ;
}
value = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_VARIANT_EAST_ASIAN);
east_asian = _gtk_css_font_variant_east_asian_value_get (value);
if (east_asian == GTK_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL)
{
/* all defaults */
}
else
{
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS78)
append_separated (s, "jp78 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS83)
append_separated (s, "jp83 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS90)
append_separated (s, "jp90 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_JIS04)
append_separated (s, "jp04 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED)
append_separated (s, "smpl 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL)
append_separated (s, "trad 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH)
append_separated (s, "fwid 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL)
append_separated (s, "pwid 1");
if (east_asian & GTK_CSS_FONT_VARIANT_EAST_ASIAN_RUBY)
append_separated (s, "ruby 1");
}
attrs = add_pango_attr (attrs, pango_attr_font_features_new (s->str));
g_string_free (s, TRUE);
return attrs;
}
static GtkCssValue *
query_func (guint id,
gpointer values)
{
return gtk_css_style_get_value (values, id);
}
PangoFontDescription *
gtk_css_style_get_pango_font (GtkCssStyle *style)
{
GtkStyleProperty *prop;
GValue value = { 0, };
prop = _gtk_style_property_lookup ("font");
_gtk_style_property_query (prop, &value, query_func, style);
return (PangoFontDescription *)g_value_get_boxed (&value);
}