[gtk/wip/otte/listview: 208/215] builder: Add support for parsing expressions
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview: 208/215] builder: Add support for parsing expressions
- Date: Tue, 26 Nov 2019 03:16:03 +0000 (UTC)
commit 1e6a6ca01b2c7b39c0733220a0b290bf07337120
Author: Benjamin Otte <otte redhat com>
Date: Mon Nov 18 04:35:36 2019 +0100
builder: Add support for parsing expressions
gtk/gtkbuilder.c | 13 +-
gtk/gtkbuilderparser.c | 365 +++++++++++++++++++++++++++++++++++++++++++++++-
gtk/gtkbuilderprivate.h | 26 ++++
testsuite/gtk/builder.c | 59 ++++++++
4 files changed, 461 insertions(+), 2 deletions(-)
---
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 8221240835..5489916f0a 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -565,7 +565,18 @@ gtk_builder_get_parameters (GtkBuilder *builder,
const char *property_name = g_intern_string (prop->pspec->name);
GValue property_value = G_VALUE_INIT;
- if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
+ if (prop->value)
+ {
+ g_value_init (&property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
+
+ if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION)
+ g_value_set_boxed (&property_value, prop->value);
+ else
+ {
+ g_assert_not_reached();
+ }
+ }
+ else if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PIXBUF) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_TEXTURE) &&
(G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PAINTABLE) &&
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index a853151806..564d777b3b 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -947,7 +947,7 @@ parse_property (ParserData *data,
return;
}
- info = g_slice_new (PropertyInfo);
+ info = g_slice_new0 (PropertyInfo);
info->tag_type = TAG_PROPERTY;
info->pspec = pspec;
info->text = g_string_new ("");
@@ -963,11 +963,325 @@ parse_property (ParserData *data,
static void
free_property_info (PropertyInfo *info)
{
+ if (info->value)
+ {
+ if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
+ gtk_expression_unref (info->value);
+ else
+ g_assert_not_reached();
+ }
g_string_free (info->text, TRUE);
g_free (info->context);
g_slice_free (PropertyInfo, info);
}
+static void
+free_expression_info (ExpressionInfo *info)
+{
+ switch (info->expression_type)
+ {
+ case EXPRESSION_EXPRESSION:
+ gtk_expression_unref (info->expression);
+ break;
+
+ case EXPRESSION_CONSTANT:
+ g_string_free (info->constant.text, TRUE);
+ break;
+
+ case EXPRESSION_CLOSURE:
+ g_free (info->closure.function_name);
+ g_free (info->closure.object_name);
+ g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ g_slice_free (ExpressionInfo, info);
+}
+
+static gboolean
+check_expression_parent (ParserData *data)
+{
+ CommonInfo *common_info = state_peek_info (data, CommonInfo);
+
+ if (common_info == NULL)
+ return FALSE;
+
+ if (common_info->tag_type == TAG_PROPERTY)
+ {
+ PropertyInfo *prop_info = (PropertyInfo *) common_info;
+
+ return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
+ }
+
+ if (common_info->tag_type == TAG_EXPRESSION)
+ {
+ ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
+
+ return expr_info->expression_type = EXPRESSION_CLOSURE;
+ }
+
+ return FALSE;
+}
+
+static void
+parse_constant_expression (ParserData *data,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ GError **error)
+{
+ ExpressionInfo *info;
+ const char *type_name;
+ GType type;
+
+ if (!check_expression_parent (data))
+ {
+ error_invalid_tag (data, element_name, NULL, error);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_STRING, "type", &type_name,
+ G_MARKUP_COLLECT_INVALID))
+ {
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ type = gtk_builder_get_type_from_name (data->builder, type_name);
+ if (type == G_TYPE_INVALID)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Invalid type '%s'", type_name);
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ info = g_slice_new0 (ExpressionInfo);
+ info->tag_type = TAG_EXPRESSION;
+ info->expression_type = EXPRESSION_CONSTANT;
+ info->constant.type = type;
+ info->constant.text = g_string_new (NULL);
+
+ state_push (data, info);
+}
+
+static void
+parse_closure_expression (ParserData *data,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ GError **error)
+{
+ ExpressionInfo *info;
+ const char *type_name;
+ const char *function_name;
+ const char *object_name = NULL;
+ gboolean swapped = -1;
+ GType type;
+
+ if (!check_expression_parent (data))
+ {
+ error_invalid_tag (data, element_name, NULL, error);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_STRING, "type", &type_name,
+ G_MARKUP_COLLECT_STRING, "function", &function_name,
+ G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object",
&object_name,
+ G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
+ G_MARKUP_COLLECT_INVALID))
+ {
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ type = gtk_builder_get_type_from_name (data->builder, type_name);
+ if (type == G_TYPE_INVALID)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Invalid type '%s'", type_name);
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ /* Swapped defaults to FALSE except when object is set */
+ if (swapped == -1)
+ {
+ if (object_name)
+ swapped = TRUE;
+ else
+ swapped = FALSE;
+ }
+
+ info = g_slice_new0 (ExpressionInfo);
+ info->tag_type = TAG_EXPRESSION;
+ info->expression_type = EXPRESSION_CLOSURE;
+ info->closure.type = type;
+ info->closure.swapped = swapped;
+ info->closure.function_name = g_strdup (function_name);
+ info->closure.object_name = g_strdup (object_name);
+
+ state_push (data, info);
+}
+
+static void
+parse_lookup_expression (ParserData *data,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ GError **error)
+{
+ ExpressionInfo *info;
+ const char *property_name;
+ const char *type_name;
+ GType type;
+
+ if (!check_expression_parent (data))
+ {
+ error_invalid_tag (data, element_name, NULL, error);
+ return;
+ }
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_STRING, "type", &type_name,
+ G_MARKUP_COLLECT_STRING, "name", &property_name,
+ G_MARKUP_COLLECT_INVALID))
+ {
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ type = gtk_builder_get_type_from_name (data->builder, type_name);
+ if (type == G_TYPE_INVALID)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Invalid type '%s'", type_name);
+ _gtk_builder_prefix_error (data->builder, &data->ctx, error);
+ return;
+ }
+
+ info = g_slice_new0 (ExpressionInfo);
+ info->tag_type = TAG_EXPRESSION;
+ info->expression_type = EXPRESSION_EXPRESSION;
+ info->expression = gtk_property_expression_new (type, property_name);
+
+ state_push (data, info);
+}
+
+static GtkExpression *
+expression_info_construct (GtkBuilder *builder,
+ ExpressionInfo *info,
+ GError **error)
+{
+ switch (info->expression_type)
+ {
+ case EXPRESSION_EXPRESSION:
+ break;
+
+ case EXPRESSION_CONSTANT:
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (!gtk_builder_value_from_string_type (builder,
+ info->constant.type,
+ info->constant.text->str,
+ &value,
+ error))
+ return NULL;
+
+ g_string_free (info->constant.text, TRUE);
+ info->expression_type = EXPRESSION_EXPRESSION;
+ info->expression = gtk_constant_expression_new_for_value (&value);
+ g_value_unset (&value);
+ }
+ break;
+
+ case EXPRESSION_CLOSURE:
+ {
+ GCallback callback;
+ GObject *object;
+ GClosure *closure;
+ guint i, n_params;
+ GtkExpression **params;
+ GtkExpression *expression;
+ GSList *l;
+
+ callback = gtk_builder_lookup_callback_symbol (builder, info->closure.function_name);
+ if (!callback)
+ {
+ GModule *module = gtk_builder_get_module (builder);
+
+ if (module)
+ g_module_symbol (module, info->closure.function_name, (gpointer) &callback);
+ }
+ if (!callback)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Could not find function '%s'", info->closure.function_name);
+ return NULL;
+ }
+ if (info->closure.object_name)
+ {
+ object = gtk_builder_get_object (builder, info->closure.object_name);
+ if (object == NULL)
+ {
+ g_set_error (error,
+ GTK_BUILDER_ERROR,
+ GTK_BUILDER_ERROR_INVALID_VALUE,
+ "Could not find object '%s'", info->closure.object_name);
+ return NULL;
+ }
+ if (info->closure.swapped)
+ closure = g_cclosure_new_object_swap (callback, object);
+ else
+ closure = g_cclosure_new_object (callback, object);
+ }
+ else
+ {
+ if (info->closure.swapped)
+ closure = g_cclosure_new_swap (callback, NULL, NULL);
+ else
+ closure = g_cclosure_new (callback, NULL, NULL);
+ }
+
+ n_params = g_slist_length (info->closure.params);
+ params = g_newa (GtkExpression *, n_params);
+ i = n_params;
+ for (l = info->closure.params; l; l = l->next)
+ {
+ params[--i] = expression_info_construct (builder, l->data, error);
+ if (params[i] == NULL)
+ return NULL;
+ }
+ expression = gtk_closure_expression_new (info->closure.type, closure, n_params, params);
+ g_free (info->closure.function_name);
+ g_free (info->closure.object_name);
+ g_slist_free_full (info->closure.params, (GDestroyNotify) free_expression_info);
+ info->expression_type = EXPRESSION_EXPRESSION;
+ info->expression = expression;
+ }
+ break;
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ return info->expression;
+}
+
static void
parse_signal (ParserData *data,
const gchar *element_name,
@@ -1302,6 +1616,12 @@ start_element (GtkBuildableParseContext *context,
parse_requires (data, element_name, names, values, error);
else if (strcmp (element_name, "interface") == 0)
parse_interface (data, element_name, names, values, error);
+ else if (strcmp (element_name, "constant") == 0)
+ parse_constant_expression (data, element_name, names, values, error);
+ else if (strcmp (element_name, "closure") == 0)
+ parse_closure_expression (data, element_name, names, values, error);
+ else if (strcmp (element_name, "lookup") == 0)
+ parse_lookup_expression (data, element_name, names, values, error);
else if (strcmp (element_name, "menu") == 0)
_gtk_builder_menu_start (data, element_name, names, values, error);
else if (strcmp (element_name, "placeholder") == 0)
@@ -1437,6 +1757,40 @@ end_element (GtkBuildableParseContext *context,
signal_info->object_name = g_strdup (object_info->id);
object_info->signals = g_slist_prepend (object_info->signals, signal_info);
}
+ else if (strcmp (element_name, "constant") == 0 ||
+ strcmp (element_name, "closure") == 0 ||
+ strcmp (element_name, "lookup") == 0)
+ {
+ ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
+ CommonInfo *parent_info = state_peek_info (data, CommonInfo);
+
+ if (parent_info->tag_type == TAG_PROPERTY)
+ {
+ PropertyInfo *prop_info = (PropertyInfo *) parent_info;
+
+ prop_info->value = expression_info_construct (data->builder, expression_info, error);
+ }
+ else if (parent_info->tag_type == TAG_EXPRESSION)
+ {
+ ExpressionInfo *expr_info = (ExpressionInfo *) parent_info;
+
+ switch (expr_info->expression_type)
+ {
+ case EXPRESSION_CLOSURE:
+ expr_info->closure.params = g_slist_prepend (expr_info->closure.params, expression_info);
+ break;
+ case EXPRESSION_EXPRESSION:
+ case EXPRESSION_CONSTANT:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+ }
else if (strcmp (element_name, "requires") == 0)
{
RequiresInfo *req_info = state_pop_info (data, RequiresInfo);
@@ -1517,6 +1871,12 @@ text (GtkBuildableParseContext *context,
g_string_append_len (prop_info->text, text, text_len);
}
+ else if (strcmp (gtk_buildable_parse_context_get_element (context), "constant") == 0)
+ {
+ ExpressionInfo *expr_info = (ExpressionInfo *) info;
+
+ g_string_append_len (expr_info->constant.text, text, text_len);
+ }
}
static void
@@ -1540,6 +1900,9 @@ free_info (CommonInfo *info)
case TAG_REQUIRES:
free_requires_info ((RequiresInfo *)info, NULL);
break;
+ case TAG_EXPRESSION:
+ free_expression_info ((ExpressionInfo *)info);
+ break;
default:
g_assert_not_reached ();
}
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 0e72efe273..5160ab535d 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -21,6 +21,7 @@
#include "gtkbuilder.h"
#include "gtkbuildable.h"
+#include "gtkexpression.h"
enum {
TAG_PROPERTY,
@@ -31,6 +32,7 @@ enum {
TAG_SIGNAL,
TAG_INTERFACE,
TAG_TEMPLATE,
+ TAG_EXPRESSION,
};
typedef struct {
@@ -64,6 +66,7 @@ typedef struct {
typedef struct {
guint tag_type;
GParamSpec *pspec;
+ gpointer value;
GString *text;
gboolean translatable:1;
gboolean bound:1;
@@ -72,6 +75,29 @@ typedef struct {
gint col;
} PropertyInfo;
+typedef struct {
+ guint tag_type;
+ enum {
+ EXPRESSION_EXPRESSION,
+ EXPRESSION_CONSTANT,
+ EXPRESSION_CLOSURE
+ } expression_type;
+ union {
+ GtkExpression *expression;
+ struct {
+ GType type;
+ GString *text;
+ } constant;
+ struct {
+ GType type;
+ char *function_name;
+ char *object_name;
+ gboolean swapped;
+ GSList *params;
+ } closure;
+ };
+} ExpressionInfo;
+
typedef struct {
guint tag_type;
gchar *object_name;
diff --git a/testsuite/gtk/builder.c b/testsuite/gtk/builder.c
index 5513834256..a5c733c413 100644
--- a/testsuite/gtk/builder.c
+++ b/testsuite/gtk/builder.c
@@ -2643,6 +2643,64 @@ test_file_filter (void)
g_object_unref (builder);
}
+char *
+builder_get_search (gpointer item)
+{
+ return g_strdup (gtk_string_filter_get_search (item));
+}
+
+char *
+builder_copy_arg (gpointer item, const char *arg)
+{
+ return g_strdup (arg);
+}
+
+static void
+test_expressions (void)
+{
+ const char *tests[] = {
+ "<interface>"
+ " <object class='GtkStringFilter' id='filter'>"
+ " <property name='search'>Hello World</property>"
+ " <property name='expression'><constant type='gchararray'>Hello World</constant></property>"
+ " </object>"
+ "</interface>",
+ "<interface>"
+ " <object class='GtkStringFilter' id='filter'>"
+ " <property name='search'>Hello World</property>"
+ " <property name='expression'><closure type='gchararray'
function='builder_get_search'></closure></property>"
+ " </object>"
+ "</interface>",
+ "<interface>"
+ " <object class='GtkStringFilter' id='filter'>"
+ " <property name='search'>Hello World</property>"
+ " <property name='expression'><lookup type='GtkStringFilter' name='search'></lookup></property>"
+ " </object>"
+ "</interface>",
+ "<interface>"
+ " <object class='GtkStringFilter' id='filter'>"
+ " <property name='search'>Hello World</property>"
+ " <property name='expression'><closure type='gchararray' function='builder_copy_arg'>"
+ " <constant type='gchararray'>Hello World</constant>"
+ " </closure></property>"
+ " </object>"
+ "</interface>",
+ };
+ GtkBuilder *builder;
+ GObject *obj;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (tests); i++)
+ {
+ builder = builder_new_from_string (tests[i], -1, NULL);
+ obj = gtk_builder_get_object (builder, "filter");
+ g_assert (GTK_IS_FILTER (obj));
+ g_assert (gtk_filter_filter (GTK_FILTER (obj), obj));
+
+ g_object_unref (builder);
+ }
+}
+
int
main (int argc, char **argv)
{
@@ -2695,6 +2753,7 @@ main (int argc, char **argv)
g_test_add_func ("/Builder/Property Bindings", test_property_bindings);
g_test_add_func ("/Builder/anaconda-signal", test_anaconda_signal);
g_test_add_func ("/Builder/FileFilter", test_file_filter);
+ g_test_add_func ("/Builder/Expressions", test_expressions);
return g_test_run();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]