Files
gtk/gtk/gtkcssreferencevalue.c
2024-03-20 22:46:32 +04:00

269 lines
7.4 KiB
C

/*
* Copyright (C) 2023 GNOME Foundation 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: Alice Mikhaylenko <alicem@gnome.org>
*/
#include "config.h"
#include "gtkcssreferencevalueprivate.h"
#include "gtkcssarrayvalueprivate.h"
#include "gtkcsscustompropertypoolprivate.h"
#include "gtkcssshorthandpropertyprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkcssunsetvalueprivate.h"
#include "gtkcssvalueprivate.h"
#include "gtkstyleproviderprivate.h"
#define MAX_TOKEN_LENGTH 65536
struct _GtkCssValue {
GTK_CSS_VALUE_BASE
GtkStyleProperty *property;
GtkCssVariableValue *value;
GFile *file;
guint subproperty;
};
static void
gtk_css_value_reference_free (GtkCssValue *value)
{
gtk_css_variable_value_unref (value->value);
if (value->file)
g_object_unref (value->file);
}
static gboolean
resolve_references_do (GtkCssVariableValue *value,
GtkCssVariableSet *style_variables,
gboolean root,
GPtrArray *refs,
gsize *out_length,
gsize *out_n_refs)
{
GtkCssCustomPropertyPool *pool = gtk_css_custom_property_pool_get ();
gsize i;
gsize length = value->length;
gsize n_refs = 0;
if (value->is_invalid)
goto error;
if (!root)
{
n_refs += 1;
g_ptr_array_add (refs, value);
}
for (i = 0; i < value->n_references; i++)
{
GtkCssVariableValueReference *ref = &value->references[i];
int id = gtk_css_custom_property_pool_lookup (pool, ref->name);
GtkCssVariableValue *var_value = NULL;
gsize var_length, var_refs;
GtkCssVariableSet *source = style_variables;
if (!var_value && style_variables)
var_value = gtk_css_variable_set_lookup (style_variables, id, &source);
if (!var_value || !resolve_references_do (var_value, source,
FALSE, refs, &var_length, &var_refs))
{
var_value = ref->fallback;
if (!var_value || !resolve_references_do (var_value, style_variables,
FALSE, refs, &var_length, &var_refs))
goto error;
}
length += var_length - ref->length;
n_refs += var_refs;
if (length > MAX_TOKEN_LENGTH)
goto error;
}
if (out_length)
*out_length = length;
if (out_n_refs)
*out_n_refs = n_refs;
return TRUE;
error:
/* Remove the references we added as if nothing happened */
g_ptr_array_remove_range (refs, refs->len - n_refs, n_refs);
if (out_length)
*out_length = 0;
if (out_n_refs)
*out_n_refs = 0;
return FALSE;
}
static GtkCssVariableValue **
resolve_references (GtkCssVariableValue *input,
GtkCssStyle *style,
gsize *n_refs)
{
GPtrArray *refs = g_ptr_array_new ();
if (!resolve_references_do (input, style->variables, TRUE, refs, NULL, NULL))
return NULL;
return (GtkCssVariableValue **) g_ptr_array_steal (refs, n_refs);
}
static void
parser_error (GtkCssParser *parser,
const GtkCssLocation *start,
const GtkCssLocation *end,
const GError *error,
gpointer user_data)
{
GtkStyleProvider *provider = user_data;
GtkCssSection *section;
section = gtk_css_section_new (gtk_css_parser_get_file (parser),
start,
end);
gtk_style_provider_emit_error (provider, section, (GError *) error);
gtk_css_section_unref (section);
}
static GtkCssValue *
gtk_css_value_reference_compute (GtkCssValue *value,
guint property_id,
GtkStyleProvider *provider,
GtkCssStyle *style,
GtkCssStyle *parent_style)
{
GtkCssValue *result = NULL, *computed;
GtkCssVariableValue **refs;
gsize n_refs = 0;
refs = resolve_references (value->value, style, &n_refs);
if (refs != NULL)
{
GtkCssParser *value_parser =
gtk_css_parser_new_for_token_stream (value->value,
value->file,
refs,
n_refs,
parser_error, provider, NULL);
result = _gtk_style_property_parse_value (value->property, value_parser);
gtk_css_parser_unref (value_parser);
}
if (result == NULL)
result = _gtk_css_unset_value_new ();
if (GTK_IS_CSS_SHORTHAND_PROPERTY (value->property))
{
GtkCssValue *sub = gtk_css_value_ref (_gtk_css_array_value_get_nth (result, value->subproperty));
gtk_css_value_unref (result);
result = sub;
}
computed = _gtk_css_value_compute (result,
property_id,
provider,
style,
parent_style);
gtk_css_value_unref (result);
return computed;
}
static gboolean
gtk_css_value_reference_equal (const GtkCssValue *value1,
const GtkCssValue *value2)
{
return FALSE;
}
static GtkCssValue *
gtk_css_value_reference_transition (GtkCssValue *start,
GtkCssValue *end,
guint property_id,
double progress)
{
return NULL;
}
static gboolean
gtk_css_value_reference_contains_variables (const GtkCssValue *value)
{
return TRUE;
}
static void
gtk_css_value_reference_print (const GtkCssValue *value,
GString *string)
{
gtk_css_variable_value_print (value->value, string);
}
static const GtkCssValueClass GTK_CSS_VALUE_REFERENCE = {
"GtkCssReferenceValue",
gtk_css_value_reference_free,
gtk_css_value_reference_compute,
gtk_css_value_reference_equal,
gtk_css_value_reference_transition,
NULL,
NULL,
gtk_css_value_reference_contains_variables,
gtk_css_value_reference_print
};
GtkCssValue *
_gtk_css_reference_value_new (GtkStyleProperty *property,
GtkCssVariableValue *value,
GFile *file)
{
GtkCssValue *result;
result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_REFERENCE);
result->property = property;
result->value = gtk_css_variable_value_ref (value);
if (file)
result->file = g_object_ref (file);
else
result->file = NULL;
return result;
}
void
_gtk_css_reference_value_set_subproperty (GtkCssValue *value,
guint property)
{
g_assert (GTK_IS_CSS_SHORTHAND_PROPERTY (value->property));
value->subproperty = property;
}