gtk2/gtk/gtkcssarrayvalue.c
Alexander Larsson af2c40bd54 css: Add a scale argument to css-value compute vfunc
We need to be able to compute different GtkCssImage values
depending on the scale, and we need this at compute time so that
we don't need to read any images other than the scale in used (to
e.g. calculate the image size). GtkStyleProviderPrivate is shared
for all style contexts, so its not right.
2013-07-03 14:39:25 +02:00

403 lines
12 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 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 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 "gtkcssarrayvalueprivate.h"
#include "gtkcssimagevalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
#include <string.h>
struct _GtkCssValue {
GTK_CSS_VALUE_BASE
guint n_values;
GtkCssValue *values[1];
};
static void
gtk_css_value_array_free (GtkCssValue *value)
{
guint i;
for (i = 0; i < value->n_values; i++)
{
_gtk_css_value_unref (value->values[i]);
}
g_slice_free1 (sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (value->n_values - 1), value);
}
static GtkCssValue *
gtk_css_value_array_compute (GtkCssValue *value,
guint property_id,
GtkStyleProviderPrivate *provider,
int scale,
GtkCssComputedValues *values,
GtkCssComputedValues *parent_values,
GtkCssDependencies *dependencies)
{
GtkCssValue *result;
GtkCssValue *i_value;
guint i, j;
GtkCssDependencies child_deps;
result = NULL;
for (i = 0; i < value->n_values; i++)
{
i_value = _gtk_css_value_compute (value->values[i], property_id, provider, scale, values, parent_values, &child_deps);
*dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
if (result == NULL &&
i_value != value->values[i])
{
result = _gtk_css_array_value_new_from_array (value->values, value->n_values);
for (j = 0; j < i; j++)
_gtk_css_value_ref (result->values[j]);
}
if (result != NULL)
result->values[i] = i_value;
else
_gtk_css_value_unref (i_value);
}
if (result == NULL)
return _gtk_css_value_ref (value);
return result;
}
static gboolean
gtk_css_value_array_equal (const GtkCssValue *value1,
const GtkCssValue *value2)
{
guint i;
if (value1->n_values != value2->n_values)
return FALSE;
for (i = 0; i < value1->n_values; i++)
{
if (!_gtk_css_value_equal (value1->values[i],
value2->values[i]))
return FALSE;
}
return TRUE;
}
static guint
gcd (guint a, guint b)
{
while (b != 0)
{
guint t = b;
b = a % b;
a = t;
}
return a;
}
static guint
lcm (guint a, guint b)
{
return a / gcd (a, b) * b;
}
static GtkCssValue *
gtk_css_value_array_transition_repeat (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
GtkCssValue **transitions;
guint i, n;
n = lcm (start->n_values, end->n_values);
transitions = g_newa (GtkCssValue *, n);
for (i = 0; i < n; i++)
{
transitions[i] = _gtk_css_value_transition (start->values[i % start->n_values],
end->values[i % end->n_values],
property_id,
progress);
if (transitions[i] == NULL)
{
while (i--)
_gtk_css_value_unref (transitions[i]);
return NULL;
}
}
return _gtk_css_array_value_new_from_array (transitions, n);
}
static GtkCssValue *
gtk_css_array_value_create_default_transition_value (guint property_id)
{
switch (property_id)
{
case GTK_CSS_PROPERTY_BACKGROUND_IMAGE:
return _gtk_css_image_value_new (NULL);
default:
g_return_val_if_reached (NULL);
}
}
static GtkCssValue *
gtk_css_value_array_transition_extend (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
GtkCssValue **transitions;
guint i, n;
n = MAX (start->n_values, end->n_values);
transitions = g_newa (GtkCssValue *, n);
for (i = 0; i < MIN (start->n_values, end->n_values); i++)
{
transitions[i] = _gtk_css_value_transition (start->values[i],
end->values[i],
property_id,
progress);
if (transitions[i] == NULL)
{
while (i--)
_gtk_css_value_unref (transitions[i]);
return NULL;
}
}
if (start->n_values != end->n_values)
{
GtkCssValue *default_value;
default_value = gtk_css_array_value_create_default_transition_value (property_id);
for (; i < start->n_values; i++)
{
transitions[i] = _gtk_css_value_transition (start->values[i],
default_value,
property_id,
progress);
if (transitions[i] == NULL)
{
while (i--)
_gtk_css_value_unref (transitions[i]);
return NULL;
}
}
for (; i < end->n_values; i++)
{
transitions[i] = _gtk_css_value_transition (default_value,
end->values[i],
property_id,
progress);
if (transitions[i] == NULL)
{
while (i--)
_gtk_css_value_unref (transitions[i]);
return NULL;
}
}
}
g_assert (i == n);
return _gtk_css_array_value_new_from_array (transitions, n);
}
static GtkCssValue *
gtk_css_value_array_transition (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
switch (property_id)
{
case GTK_CSS_PROPERTY_BACKGROUND_CLIP:
case GTK_CSS_PROPERTY_BACKGROUND_ORIGIN:
case GTK_CSS_PROPERTY_BACKGROUND_SIZE:
case GTK_CSS_PROPERTY_BACKGROUND_POSITION:
case GTK_CSS_PROPERTY_BACKGROUND_REPEAT:
return gtk_css_value_array_transition_repeat (start, end, property_id, progress);
case GTK_CSS_PROPERTY_BACKGROUND_IMAGE:
return gtk_css_value_array_transition_extend (start, end, property_id, progress);
case GTK_CSS_PROPERTY_COLOR:
case GTK_CSS_PROPERTY_FONT_SIZE:
case GTK_CSS_PROPERTY_BACKGROUND_COLOR:
case GTK_CSS_PROPERTY_FONT_FAMILY:
case GTK_CSS_PROPERTY_FONT_STYLE:
case GTK_CSS_PROPERTY_FONT_VARIANT:
case GTK_CSS_PROPERTY_FONT_WEIGHT:
case GTK_CSS_PROPERTY_TEXT_SHADOW:
case GTK_CSS_PROPERTY_ICON_SHADOW:
case GTK_CSS_PROPERTY_BOX_SHADOW:
case GTK_CSS_PROPERTY_MARGIN_TOP:
case GTK_CSS_PROPERTY_MARGIN_LEFT:
case GTK_CSS_PROPERTY_MARGIN_BOTTOM:
case GTK_CSS_PROPERTY_MARGIN_RIGHT:
case GTK_CSS_PROPERTY_PADDING_TOP:
case GTK_CSS_PROPERTY_PADDING_LEFT:
case GTK_CSS_PROPERTY_PADDING_BOTTOM:
case GTK_CSS_PROPERTY_PADDING_RIGHT:
case GTK_CSS_PROPERTY_BORDER_TOP_STYLE:
case GTK_CSS_PROPERTY_BORDER_TOP_WIDTH:
case GTK_CSS_PROPERTY_BORDER_LEFT_STYLE:
case GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH:
case GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE:
case GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
case GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE:
case GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH:
case GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS:
case GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS:
case GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS:
case GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS:
case GTK_CSS_PROPERTY_OUTLINE_STYLE:
case GTK_CSS_PROPERTY_OUTLINE_WIDTH:
case GTK_CSS_PROPERTY_OUTLINE_OFFSET:
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:
case GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE:
case GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT:
case GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE:
case GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH:
case GTK_CSS_PROPERTY_ENGINE:
default:
/* keep all values that are not arrays here, so we get a warning if we ever turn them
* into arrays and start animating them. */
g_warning ("Don't know how to transition arrays for property '%s'",
_gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (property_id))));
case GTK_CSS_PROPERTY_TRANSITION_PROPERTY:
case GTK_CSS_PROPERTY_TRANSITION_DURATION:
case GTK_CSS_PROPERTY_TRANSITION_TIMING_FUNCTION:
case GTK_CSS_PROPERTY_TRANSITION_DELAY:
case GTK_CSS_PROPERTY_GTK_KEY_BINDINGS:
return NULL;
}
}
static void
gtk_css_value_array_print (const GtkCssValue *value,
GString *string)
{
guint i;
if (value->n_values == 0)
{
g_string_append (string, "none");
return;
}
for (i = 0; i < value->n_values; i++)
{
if (i > 0)
g_string_append (string, ", ");
_gtk_css_value_print (value->values[i], string);
}
}
static const GtkCssValueClass GTK_CSS_VALUE_ARRAY = {
gtk_css_value_array_free,
gtk_css_value_array_compute,
gtk_css_value_array_equal,
gtk_css_value_array_transition,
gtk_css_value_array_print
};
GtkCssValue *
_gtk_css_array_value_new (GtkCssValue *content)
{
g_return_val_if_fail (content != NULL, NULL);
return _gtk_css_array_value_new_from_array (&content, 1);
}
GtkCssValue *
_gtk_css_array_value_new_from_array (GtkCssValue **values,
guint n_values)
{
GtkCssValue *result;
g_return_val_if_fail (values != NULL, NULL);
g_return_val_if_fail (n_values > 0, NULL);
result = _gtk_css_value_alloc (&GTK_CSS_VALUE_ARRAY, sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (n_values - 1));
result->n_values = n_values;
memcpy (&result->values[0], values, sizeof (GtkCssValue *) * n_values);
return result;
}
GtkCssValue *
_gtk_css_array_value_parse (GtkCssParser *parser,
GtkCssValue *(* parse_func) (GtkCssParser *parser))
{
GtkCssValue *value, *result;
GPtrArray *values;
values = g_ptr_array_new ();
do {
value = parse_func (parser);
if (value == NULL)
{
g_ptr_array_set_free_func (values, (GDestroyNotify) _gtk_css_value_unref);
g_ptr_array_free (values, TRUE);
return NULL;
}
g_ptr_array_add (values, value);
} while (_gtk_css_parser_try (parser, ",", TRUE));
result = _gtk_css_array_value_new_from_array ((GtkCssValue **) values->pdata, values->len);
g_ptr_array_free (values, TRUE);
return result;
}
GtkCssValue *
_gtk_css_array_value_get_nth (const GtkCssValue *value,
guint i)
{
g_return_val_if_fail (value != NULL, NULL);
g_return_val_if_fail (value->class == &GTK_CSS_VALUE_ARRAY, NULL);
g_return_val_if_fail (value->n_values > 0, NULL);
return value->values[i % value->n_values];
}
guint
_gtk_css_array_value_get_n_values (const GtkCssValue *value)
{
g_return_val_if_fail (value != NULL, 0);
g_return_val_if_fail (value->class == &GTK_CSS_VALUE_ARRAY, 0);
return value->n_values;
}