gtk2/gtk/gtkcssshorthandpropertyimpl.c
Benjamin Otte fb47a8d714 css: Remove deprecated number-as-pixels compatibility
Previously, for compatibility with GTK 3.0, we allowed specifying
numbers without units and interpreted them as pixels, even when the CSS
specification didn't.

Remove that now that we can break API.
2017-01-18 04:13:56 +01:00

1224 lines
41 KiB
C

/*
* Copyright © 2011 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtkcssshorthandpropertyprivate.h"
#include <cairo-gobject.h>
#include <math.h>
#include "gtkcssarrayvalueprivate.h"
#include "gtkcssbgsizevalueprivate.h"
#include "gtkcssbordervalueprivate.h"
#include "gtkcsscolorvalueprivate.h"
#include "gtkcsscornervalueprivate.h"
#include "gtkcsseasevalueprivate.h"
#include "gtkcssenumvalueprivate.h"
#include "gtkcssimageprivate.h"
#include "gtkcssimagevalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcsspositionvalueprivate.h"
#include "gtkcssrepeatvalueprivate.h"
#include "gtkcssstringvalueprivate.h"
#include "gtkcssstylefuncsprivate.h"
#include "gtkcssvalueprivate.h"
#include "gtktypebuiltins.h"
/* this is in case round() is not provided by the compiler,
* such as in the case of C89 compilers, like MSVC
*/
#include "fallback-c89.c"
/*** PARSING ***/
static gboolean
value_is_done_parsing (GtkCssParser *parser)
{
return _gtk_css_parser_is_eof (parser) ||
_gtk_css_parser_begins_with (parser, ',') ||
_gtk_css_parser_begins_with (parser, ';') ||
_gtk_css_parser_begins_with (parser, '}');
}
static gboolean
parse_four_numbers (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser,
GtkCssNumberParseFlags flags)
{
guint i;
for (i = 0; i < 4; i++)
{
if (!gtk_css_number_value_can_parse (parser))
break;
values[i] = _gtk_css_number_value_parse (parser, flags);
if (values[i] == NULL)
return FALSE;
}
if (i == 0)
{
_gtk_css_parser_error (parser, "Expected a length");
return FALSE;
}
for (; i < 4; i++)
{
values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
}
return TRUE;
}
static gboolean
parse_margin (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
return parse_four_numbers (shorthand,
values,
parser,
GTK_CSS_PARSE_LENGTH);
}
static gboolean
parse_padding (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
return parse_four_numbers (shorthand,
values,
parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_LENGTH);
}
static gboolean
parse_border_width (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
return parse_four_numbers (shorthand,
values,
parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_LENGTH);
}
static gboolean
parse_border_radius (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
GtkCssValue *x[4] = { NULL, }, *y[4] = { NULL, };
guint i;
for (i = 0; i < 4; i++)
{
if (!gtk_css_number_value_can_parse (parser))
break;
x[i] = _gtk_css_number_value_parse (parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_PERCENT
| GTK_CSS_PARSE_LENGTH);
if (x[i] == NULL)
goto fail;
}
if (i == 0)
{
_gtk_css_parser_error (parser, "Expected a number");
goto fail;
}
/* The magic (i - 1) >> 1 below makes it take the correct value
* according to spec. Feel free to check the 4 cases
*/
for (; i < 4; i++)
x[i] = _gtk_css_value_ref (x[(i - 1) >> 1]);
if (_gtk_css_parser_try (parser, "/", TRUE))
{
for (i = 0; i < 4; i++)
{
if (!gtk_css_number_value_can_parse (parser))
break;
y[i] = _gtk_css_number_value_parse (parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_PERCENT
| GTK_CSS_PARSE_LENGTH);
if (y[i] == NULL)
goto fail;
}
if (i == 0)
{
_gtk_css_parser_error (parser, "Expected a number");
goto fail;
}
for (; i < 4; i++)
y[i] = _gtk_css_value_ref (y[(i - 1) >> 1]);
}
else
{
for (i = 0; i < 4; i++)
y[i] = _gtk_css_value_ref (x[i]);
}
for (i = 0; i < 4; i++)
{
values[i] = _gtk_css_corner_value_new (x[i], y[i]);
}
return TRUE;
fail:
for (i = 0; i < 4; i++)
{
if (x[i])
_gtk_css_value_unref (x[i]);
if (y[i])
_gtk_css_value_unref (y[i]);
}
return FALSE;
}
static gboolean
parse_border_color (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
guint i;
for (i = 0; i < 4; i++)
{
values[i] = _gtk_css_color_value_parse (parser);
if (values[i] == NULL)
return FALSE;
if (value_is_done_parsing (parser))
break;
}
for (i++; i < 4; i++)
{
values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
}
return TRUE;
}
static gboolean
parse_border_style (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
guint i;
for (i = 0; i < 4; i++)
{
values[i] = _gtk_css_border_style_value_try_parse (parser);
if (values[i] == NULL)
break;
}
if (i == 0)
{
_gtk_css_parser_error (parser, "Expected a border style");
return FALSE;
}
for (; i < 4; i++)
values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
return TRUE;
}
static gboolean
parse_border_image (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
if (values[0] == NULL &&
(_gtk_css_parser_has_prefix (parser, "none") ||
_gtk_css_image_can_parse (parser)))
{
GtkCssImage *image;
if (_gtk_css_parser_try (parser, "none", TRUE))
image = NULL;
else
{
image = _gtk_css_image_new_parse (parser);
if (image == NULL)
return FALSE;
}
values[0] = _gtk_css_image_value_new (image);
}
else if (values[3] == NULL &&
(values[3] = _gtk_css_border_repeat_value_try_parse (parser)))
{
/* please move along */
}
else if (values[1] == NULL)
{
values[1] = _gtk_css_border_value_parse (parser,
GTK_CSS_PARSE_PERCENT
| GTK_CSS_PARSE_NUMBER
| GTK_CSS_POSITIVE_ONLY,
FALSE,
TRUE);
if (values[1] == NULL)
return FALSE;
if (_gtk_css_parser_try (parser, "/", TRUE))
{
values[2] = _gtk_css_border_value_parse (parser,
GTK_CSS_PARSE_PERCENT
| GTK_CSS_PARSE_LENGTH
| GTK_CSS_PARSE_NUMBER
| GTK_CSS_POSITIVE_ONLY,
TRUE,
FALSE);
if (values[2] == NULL)
return FALSE;
}
}
else
{
/* We parsed everything and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error
*/
break;
}
}
while (!value_is_done_parsing (parser));
return TRUE;
}
static gboolean
parse_border_side (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
if (values[0] == NULL &&
gtk_css_number_value_can_parse (parser))
{
values[0] = _gtk_css_number_value_parse (parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_LENGTH);
if (values[0] == NULL)
return FALSE;
}
else if (values[1] == NULL &&
(values[1] = _gtk_css_border_style_value_try_parse (parser)))
{
/* Nothing to do */
}
else if (values[2] == NULL)
{
values[2] = _gtk_css_color_value_parse (parser);
if (values[2] == NULL)
return FALSE;
}
else
{
/* We parsed and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error
*/
break;
}
}
while (!value_is_done_parsing (parser));
return TRUE;
}
static gboolean
parse_border (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
if (values[0] == NULL &&
gtk_css_number_value_can_parse (parser))
{
values[0] = _gtk_css_number_value_parse (parser,
GTK_CSS_POSITIVE_ONLY
| GTK_CSS_PARSE_LENGTH);
if (values[0] == NULL)
return FALSE;
values[1] = _gtk_css_value_ref (values[0]);
values[2] = _gtk_css_value_ref (values[0]);
values[3] = _gtk_css_value_ref (values[0]);
}
else if (values[4] == NULL &&
(values[4] = _gtk_css_border_style_value_try_parse (parser)))
{
values[5] = _gtk_css_value_ref (values[4]);
values[6] = _gtk_css_value_ref (values[4]);
values[7] = _gtk_css_value_ref (values[4]);
}
else if (!G_IS_VALUE (&values[8]))
{
values[8] = _gtk_css_color_value_parse (parser);
if (values[8] == NULL)
return FALSE;
values[9] = _gtk_css_value_ref (values[8]);
values[10] = _gtk_css_value_ref (values[8]);
values[11] = _gtk_css_value_ref (values[8]);
}
else
{
/* We parsed everything and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error
*/
break;
}
}
while (!value_is_done_parsing (parser));
/* Note that border-image values are not set: according to the spec
* they just need to be reset when using the border shorthand
*/
return TRUE;
}
static gboolean
parse_font_with_pango (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
PangoFontDescription *desc;
guint mask;
char *str;
str = _gtk_css_parser_read_value (parser);
if (str == NULL)
return FALSE;
desc = pango_font_description_from_string (str);
g_free (str);
mask = pango_font_description_get_set_fields (desc);
if (mask & PANGO_FONT_MASK_FAMILY)
{
values[0] = _gtk_css_array_value_new (_gtk_css_string_value_new (pango_font_description_get_family (desc)));
}
if (mask & PANGO_FONT_MASK_STYLE)
{
values[1] = _gtk_css_font_style_value_new (pango_font_description_get_style (desc));
}
if (mask & PANGO_FONT_MASK_VARIANT)
{
values[2] = _gtk_css_font_variant_value_new (pango_font_description_get_variant (desc));
}
if (mask & PANGO_FONT_MASK_WEIGHT)
{
values[3] = _gtk_css_font_weight_value_new (pango_font_description_get_weight (desc));
}
if (mask & PANGO_FONT_MASK_STRETCH)
{
values[4] = _gtk_css_font_stretch_value_new (pango_font_description_get_stretch (desc));
}
if (mask & PANGO_FONT_MASK_SIZE)
{
values[5] = _gtk_css_number_value_new ((double) pango_font_description_get_size (desc) / PANGO_SCALE, GTK_CSS_PX);
}
pango_font_description_free (desc);
return TRUE;
}
static gboolean
parse_font (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
gboolean parsed_one;
do
{
parsed_one = FALSE;
if (values[1] == NULL)
{
values[1] = _gtk_css_font_style_value_try_parse (parser);
parsed_one = parsed_one || values[1] != NULL;
}
if (values[2] == NULL)
{
values[2] = _gtk_css_font_variant_value_try_parse (parser);
parsed_one = parsed_one || values[2] != NULL;
}
if (values[3] == NULL)
{
values[3] = _gtk_css_font_weight_value_try_parse (parser);
parsed_one = parsed_one || values[3] != NULL;
}
if (values[4] == NULL)
{
values[4] = _gtk_css_font_stretch_value_try_parse (parser);
parsed_one = parsed_one || values[4] != NULL;
}
}
while (parsed_one && !value_is_done_parsing (parser));
values[5] = gtk_css_font_size_value_parse (parser);
if (values[1] == NULL && values[2] == NULL && values[3] == NULL &&
values[4] == NULL && values[5] == NULL)
{
if (parse_font_with_pango (shorthand, values, parser))
{
_gtk_css_parser_error_full (parser,
GTK_CSS_PROVIDER_ERROR_DEPRECATED,
"Using Pango syntax for the font: style property is deprecated; please use CSS syntax");
return TRUE;
}
}
values[0] = gtk_css_font_family_value_parse (parser);
return values[0] != NULL && values[5] != NULL;
}
static gboolean
parse_one_background (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
GtkCssValue *value = NULL;
do
{
/* the image part */
if (values[0] == NULL &&
(_gtk_css_parser_has_prefix (parser, "none") ||
_gtk_css_image_can_parse (parser)))
{
GtkCssImage *image;
if (_gtk_css_parser_try (parser, "none", TRUE))
image = NULL;
else
{
image = _gtk_css_image_new_parse (parser);
if (image == NULL)
return FALSE;
}
values[0] = _gtk_css_image_value_new (image);
}
else if (values[1] == NULL &&
(value = _gtk_css_position_value_try_parse (parser)))
{
values[1] = value;
value = NULL;
if (_gtk_css_parser_try (parser, "/", TRUE) &&
(value = _gtk_css_bg_size_value_parse (parser)))
{
values[2] = value;
value = NULL;
}
}
else if (values[3] == NULL &&
(value = _gtk_css_background_repeat_value_try_parse (parser)))
{
values[3] = value;
value = NULL;
}
else if ((values[4] == NULL || values[5] == NULL) &&
(value = _gtk_css_area_value_try_parse (parser)))
{
values[4] = value;
if (values[5] == NULL)
{
values[5] = values[4];
values[4] = NULL;
}
value = NULL;
}
else if (values[6] == NULL)
{
value = _gtk_css_color_value_parse (parser);
if (value == NULL)
values[6] = _gtk_css_value_ref (_gtk_css_style_property_get_initial_value
(_gtk_css_shorthand_property_get_subproperty (shorthand, 6)));
else
values[6] = value;
value = NULL;
}
else
{
/* We parsed everything and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error
*/
break;
}
}
while (!value_is_done_parsing (parser));
if (values[5] != NULL && values[4] == NULL)
values[4] = _gtk_css_value_ref (values[5]);
return TRUE;
}
static gboolean
parse_background (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
GtkCssValue *step_values[7];
GPtrArray *arrays[6];
guint i;
for (i = 0; i < 6; i++)
{
arrays[i] = g_ptr_array_new ();
step_values[i] = NULL;
}
step_values[6] = NULL;
do {
if (!parse_one_background (shorthand, step_values, parser))
{
for (i = 0; i < 6; i++)
{
g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
g_ptr_array_unref (arrays[i]);
}
return FALSE;
}
for (i = 0; i < 6; i++)
{
if (step_values[i] == NULL)
{
GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
_gtk_css_shorthand_property_get_subproperty (shorthand, i));
step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
}
g_ptr_array_add (arrays[i], step_values[i]);
step_values[i] = NULL;
}
} while (_gtk_css_parser_try (parser, ",", TRUE));
for (i = 0; i < 6; i++)
{
values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
g_ptr_array_unref (arrays[i]);
}
values[6] = step_values[6];
return TRUE;
}
static gboolean
parse_one_transition (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
/* the image part */
if (values[2] == NULL &&
gtk_css_number_value_can_parse (parser) && !_gtk_css_parser_begins_with (parser, '-'))
{
GtkCssValue *number = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_TIME);
if (number == NULL)
return FALSE;
if (values[1] == NULL)
values[1] = number;
else
values[2] = number;
}
else if (values[3] == NULL &&
_gtk_css_ease_value_can_parse (parser))
{
values[3] = _gtk_css_ease_value_parse (parser);
if (values[3] == NULL)
return FALSE;
}
else if (values[0] == NULL)
{
values[0] = _gtk_css_ident_value_try_parse (parser);
if (values[0] == NULL)
{
_gtk_css_parser_error (parser, "Unknown value for property");
return FALSE;
}
}
else
{
/* We parsed everything and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error
*/
break;
}
}
while (!value_is_done_parsing (parser));
return TRUE;
}
static gboolean
parse_transition (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
GtkCssValue *step_values[4];
GPtrArray *arrays[4];
guint i;
for (i = 0; i < 4; i++)
{
arrays[i] = g_ptr_array_new ();
step_values[i] = NULL;
}
do {
if (!parse_one_transition (shorthand, step_values, parser))
{
for (i = 0; i < 4; i++)
{
g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
g_ptr_array_unref (arrays[i]);
}
return FALSE;
}
for (i = 0; i < 4; i++)
{
if (step_values[i] == NULL)
{
GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
_gtk_css_shorthand_property_get_subproperty (shorthand, i));
step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
}
g_ptr_array_add (arrays[i], step_values[i]);
step_values[i] = NULL;
}
} while (_gtk_css_parser_try (parser, ",", TRUE));
for (i = 0; i < 4; i++)
{
values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
g_ptr_array_unref (arrays[i]);
}
return TRUE;
}
static gboolean
parse_one_animation (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
if (values[1] == NULL && _gtk_css_parser_try (parser, "infinite", TRUE))
{
values[1] = _gtk_css_number_value_new (HUGE_VAL, GTK_CSS_NUMBER);
}
else if ((values[1] == NULL || values[3] == NULL) &&
gtk_css_number_value_can_parse (parser))
{
GtkCssValue *value;
value = _gtk_css_number_value_parse (parser,
GTK_CSS_POSITIVE_ONLY
| (values[1] == NULL ? GTK_CSS_PARSE_NUMBER : 0)
| (values[3] == NULL ? GTK_CSS_PARSE_TIME : 0));
if (value == NULL)
return FALSE;
if (gtk_css_number_value_get_dimension (value) == GTK_CSS_DIMENSION_NUMBER)
values[1] = value;
else if (values[2] == NULL)
values[2] = value;
else
values[3] = value;
}
else if (values[4] == NULL &&
_gtk_css_ease_value_can_parse (parser))
{
values[4] = _gtk_css_ease_value_parse (parser);
if (values[4] == NULL)
return FALSE;
}
else if (values[5] == NULL &&
(values[5] = _gtk_css_direction_value_try_parse (parser)))
{
/* nothing to do */
}
else if (values[6] == NULL &&
(values[6] = _gtk_css_fill_mode_value_try_parse (parser)))
{
/* nothing to do */
}
else if (values[0] == NULL &&
(values[0] = _gtk_css_ident_value_try_parse (parser)))
{
/* nothing to do */
/* keep in mind though that this needs to come last as fill modes, directions
* etc are valid idents */
}
else
{
/* We parsed everything and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error */
break;
}
}
while (!value_is_done_parsing (parser));
return TRUE;
}
static gboolean
parse_animation (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
GtkCssValue *step_values[7];
GPtrArray *arrays[7];
guint i;
for (i = 0; i < 7; i++)
{
arrays[i] = g_ptr_array_new ();
step_values[i] = NULL;
}
do {
if (!parse_one_animation (shorthand, step_values, parser))
{
for (i = 0; i < 7; i++)
{
g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
g_ptr_array_unref (arrays[i]);
}
return FALSE;
}
for (i = 0; i < 7; i++)
{
if (step_values[i] == NULL)
{
GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
_gtk_css_shorthand_property_get_subproperty (shorthand, i));
step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
}
g_ptr_array_add (arrays[i], step_values[i]);
step_values[i] = NULL;
}
} while (_gtk_css_parser_try (parser, ",", TRUE));
for (i = 0; i < 7; i++)
{
values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
g_ptr_array_unref (arrays[i]);
}
return TRUE;
}
static gboolean
parse_text_decoration (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
do
{
if (values[0] == NULL &&
(values[0] = _gtk_css_text_decoration_line_value_try_parse (parser)))
{
if (values[0] == NULL)
return FALSE;
}
else if (values[1] == NULL &&
(values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
{
if (values[1] == NULL)
return FALSE;
}
else if (values[2] == NULL)
{
values[2] = _gtk_css_color_value_parse (parser);
if (values[2] == NULL)
return FALSE;
}
else
{
/* We parsed and there's still stuff left?
* Pretend we didn't notice and let the normal code produce
* a 'junk at end of value' error */
break;
}
}
while (!value_is_done_parsing (parser));
return TRUE;
}
static gboolean
parse_all (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
_gtk_css_parser_error (parser, "The 'all' property can only be set to 'initial', 'inherit' or 'unset'");
return FALSE;
}
/*** PACKING ***/
static void
pack_border (GtkCssShorthandProperty *shorthand,
GValue *value,
GtkStyleQueryFunc query_func,
gpointer query_data)
{
GtkCssStyleProperty *prop;
GtkBorder border;
GValue v;
prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
_gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
border.top = g_value_get_int (&v);
g_value_unset (&v);
prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 1);
_gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
border.right = g_value_get_int (&v);
g_value_unset (&v);
prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 2);
_gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
border.bottom = g_value_get_int (&v);
g_value_unset (&v);
prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 3);
_gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
border.left = g_value_get_int (&v);
g_value_unset (&v);
g_value_init (value, GTK_TYPE_BORDER);
g_value_set_boxed (value, &border);
}
static void
pack_border_radius (GtkCssShorthandProperty *shorthand,
GValue *value,
GtkStyleQueryFunc query_func,
gpointer query_data)
{
GtkCssStyleProperty *prop;
GtkCssValue *v;
int i = 0;
prop = GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("border-top-left-radius"));
v = (* query_func) (_gtk_css_style_property_get_id (prop), query_data);
if (v)
i = _gtk_css_corner_value_get_x (v, 100);
g_value_init (value, G_TYPE_INT);
g_value_set_int (value, i);
}
static void
pack_font_description (GtkCssShorthandProperty *shorthand,
GValue *value,
GtkStyleQueryFunc query_func,
gpointer query_data)
{
PangoFontDescription *description;
GtkCssValue *v;
double dpi;
description = pango_font_description_new ();
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-family"))), query_data);
if (v)
{
/* xxx: Can we set all the families here somehow? */
pango_font_description_set_family (description, _gtk_css_string_value_get (_gtk_css_array_value_get_nth (v, 0)));
}
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("-gtk-dpi"))), query_data);
dpi = _gtk_css_number_value_get (v, 96);
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-size"))), query_data);
if (v)
pango_font_description_set_size (description, round (_gtk_css_number_value_get (v, 100) * PANGO_SCALE * 72 / dpi));
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-style"))), query_data);
if (v)
pango_font_description_set_style (description, _gtk_css_font_style_value_get (v));
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-variant"))), query_data);
if (v)
pango_font_description_set_variant (description, _gtk_css_font_variant_value_get (v));
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-weight"))), query_data);
if (v)
pango_font_description_set_weight (description, _gtk_css_font_weight_value_get (v));
v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-stretch"))), query_data);
if (v)
pango_font_description_set_stretch (description, _gtk_css_font_stretch_value_get (v));
g_value_init (value, PANGO_TYPE_FONT_DESCRIPTION);
g_value_take_boxed (value, description);
}
static void
pack_first_element (GtkCssShorthandProperty *shorthand,
GValue *value,
GtkStyleQueryFunc query_func,
gpointer query_data)
{
GtkCssStyleProperty *prop;
/* NB: This is a fallback for properties that originally were
* not used as shorthand. We just pick the first subproperty
* as a representative.
* Lesson learned: Don't query the shorthand, query the
* real properties instead. */
prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
_gtk_style_property_query (GTK_STYLE_PROPERTY (prop),
value,
query_func,
query_data);
}
static void
_gtk_css_shorthand_property_register (const char *name,
GType value_type,
const char **subproperties,
GtkCssShorthandPropertyParseFunc parse_func,
GtkCssShorthandPropertyQueryFunc query_func)
{
GtkCssShorthandProperty *node;
node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
"name", name,
"value-type", value_type,
"subproperties", subproperties,
NULL);
node->parse = parse_func;
node->query = query_func;
}
/* NB: return value is transfer: container */
static const char **
get_all_subproperties (void)
{
const char **properties;
guint i, n;
n = _gtk_css_style_property_get_n_properties ();
properties = g_new (const char *, n + 1);
properties[n] = NULL;
for (i = 0; i < n; i++)
{
properties[i] = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (i)));
}
return properties;
}
void
_gtk_css_shorthand_property_init_properties (void)
{
/* The order is important here, be careful when changing it */
const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-stretch", "font-size", NULL };
const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
"border-bottom-right-radius", "border-bottom-left-radius", NULL };
const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
const char *border_top_subproperties[] = { "border-top-width", "border-top-style", "border-top-color", NULL };
const char *border_right_subproperties[] = { "border-right-width", "border-right-style", "border-right-color", NULL };
const char *border_bottom_subproperties[] = { "border-bottom-width", "border-bottom-style", "border-bottom-color", NULL };
const char *border_left_subproperties[] = { "border-left-width", "border-left-style", "border-left-color", NULL };
const char *border_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
"border-top-style", "border-right-style", "border-bottom-style", "border-left-style",
"border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
"border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
const char *outline_subproperties[] = { "outline-width", "outline-style", "outline-color", NULL };
const char *outline_radius_subproperties[] = { "outline-top-left-radius", "outline-top-right-radius",
"outline-bottom-right-radius", "outline-bottom-left-radius", NULL };
const char *background_subproperties[] = { "background-image", "background-position", "background-size", "background-repeat", "background-clip", "background-origin",
"background-color", NULL };
const char *transition_subproperties[] = { "transition-property", "transition-duration", "transition-delay", "transition-timing-function", NULL };
const char *animation_subproperties[] = { "animation-name", "animation-iteration-count", "animation-duration", "animation-delay",
"animation-timing-function", "animation-direction", "animation-fill-mode", NULL };
const char *text_decoration_subproperties[] = { "text-decoration-line", "text-decoration-style", "text-decoration-color", NULL };
const char **all_subproperties;
_gtk_css_shorthand_property_register ("font",
PANGO_TYPE_FONT_DESCRIPTION,
font_subproperties,
parse_font,
pack_font_description);
_gtk_css_shorthand_property_register ("margin",
GTK_TYPE_BORDER,
margin_subproperties,
parse_margin,
pack_border);
_gtk_css_shorthand_property_register ("padding",
GTK_TYPE_BORDER,
padding_subproperties,
parse_padding,
pack_border);
_gtk_css_shorthand_property_register ("border-width",
GTK_TYPE_BORDER,
border_width_subproperties,
parse_border_width,
pack_border);
_gtk_css_shorthand_property_register ("border-radius",
G_TYPE_INT,
border_radius_subproperties,
parse_border_radius,
pack_border_radius);
_gtk_css_shorthand_property_register ("border-color",
GDK_TYPE_RGBA,
border_color_subproperties,
parse_border_color,
pack_first_element);
_gtk_css_shorthand_property_register ("border-style",
GTK_TYPE_BORDER_STYLE,
border_style_subproperties,
parse_border_style,
pack_first_element);
_gtk_css_shorthand_property_register ("border-image",
G_TYPE_NONE,
border_image_subproperties,
parse_border_image,
NULL);
_gtk_css_shorthand_property_register ("border-top",
G_TYPE_NONE,
border_top_subproperties,
parse_border_side,
NULL);
_gtk_css_shorthand_property_register ("border-right",
G_TYPE_NONE,
border_right_subproperties,
parse_border_side,
NULL);
_gtk_css_shorthand_property_register ("border-bottom",
G_TYPE_NONE,
border_bottom_subproperties,
parse_border_side,
NULL);
_gtk_css_shorthand_property_register ("border-left",
G_TYPE_NONE,
border_left_subproperties,
parse_border_side,
NULL);
_gtk_css_shorthand_property_register ("border",
G_TYPE_NONE,
border_subproperties,
parse_border,
NULL);
_gtk_css_shorthand_property_register ("-gtk-outline-radius",
G_TYPE_INT,
outline_radius_subproperties,
parse_border_radius,
pack_border_radius);
_gtk_style_property_add_alias ("-gtk-outline-radius", "outline-radius");
_gtk_css_shorthand_property_register ("outline",
G_TYPE_NONE,
outline_subproperties,
parse_border_side,
NULL);
_gtk_css_shorthand_property_register ("background",
G_TYPE_NONE,
background_subproperties,
parse_background,
NULL);
_gtk_css_shorthand_property_register ("transition",
G_TYPE_NONE,
transition_subproperties,
parse_transition,
NULL);
_gtk_css_shorthand_property_register ("animation",
G_TYPE_NONE,
animation_subproperties,
parse_animation,
NULL);
_gtk_css_shorthand_property_register ("text-decoration",
G_TYPE_NONE,
text_decoration_subproperties,
parse_text_decoration,
NULL);
all_subproperties = get_all_subproperties ();
_gtk_css_shorthand_property_register ("all",
G_TYPE_NONE,
all_subproperties,
parse_all,
NULL);
g_free (all_subproperties);
}