/* text is not nul-terminated */ static void text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { ParserData *data = (ParserData*)user_data; CommonInfo *info; if (data->subparser && data->subparser->start) { GError *tmp_error = NULL; if (data->subparser->parser->text) data->subparser->parser->text (context, text, text_len, data->subparser->data, &tmp_error); if (tmp_error) g_propagate_error (error, tmp_error); return; } if (!data->stack) return; info = state_peek_info (data, CommonInfo); g_assert (info != NULL); if (strcmp (g_markup_parse_context_get_element (context), "property") == 0) { PropertyInfo *prop_info = (PropertyInfo*)info; g_string_append_len (prop_info->text, text, text_len); } }
static void parse_child (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { ObjectInfo* object_info; ChildInfo *child_info; const gchar *type = NULL; const gchar *internal_child = NULL; object_info = state_peek_info (data, ObjectInfo); if (!object_info || !(strcmp (object_info->tag.name, "object") == 0 || strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; } if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type", &type, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "internal-child", &internal_child, G_MARKUP_COLLECT_INVALID)) { _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } child_info = g_slice_new0 (ChildInfo); child_info->tag.name = element_name; child_info->type = g_strdup (type); child_info->internal_child = g_strdup (internal_child); child_info->parent = (CommonInfo*)object_info; state_push (data, child_info); object_info->object = builder_construct (data, object_info, error); }
static void parse_child (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { ObjectInfo* object_info; ChildInfo *child_info; guint i; object_info = state_peek_info (data, ObjectInfo); if (!object_info || !(strcmp (object_info->tag.name, "object") == 0 || strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; } child_info = g_slice_new0 (ChildInfo); state_push (data, child_info); child_info->tag.name = element_name; for (i = 0; names[i]; i++) { if (strcmp (names[i], "type") == 0) child_info->type = g_strdup (values[i]); else if (strcmp (names[i], "internal-child") == 0) child_info->internal_child = g_strdup (values[i]); else error_invalid_attribute (data, element_name, names[i], error); } child_info->parent = (CommonInfo*)object_info; object_info->object = builder_construct (data, object_info, error); }
static gboolean parse_custom (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, ParserData *data, GError **error) { CommonInfo* parent_info; GMarkupParser parser; gpointer subparser_data; GObject *object; GObject *child; parent_info = state_peek_info (data, CommonInfo); if (!parent_info) return FALSE; if (strcmp (parent_info->tag.name, "object") == 0 || strcmp (parent_info->tag.name, "template") == 0) { ObjectInfo* object_info = (ObjectInfo*)parent_info; if (!object_info->object) { object_info->properties = g_slist_reverse (object_info->properties); object_info->object = _gtk_builder_construct (data->builder, object_info, error); if (!object_info->object) return TRUE; /* A GError is already set */ } g_assert (object_info->object); object = object_info->object; child = NULL; } else if (strcmp (parent_info->tag.name, "child") == 0) { ChildInfo* child_info = (ChildInfo*)parent_info; _gtk_builder_add (data->builder, child_info); object = ((ObjectInfo*)child_info->parent)->object; child = child_info->object; } else return FALSE; if (!gtk_buildable_custom_tag_start (GTK_BUILDABLE (object), data->builder, child, element_name, &parser, &subparser_data)) return FALSE; data->subparser = create_subparser (object, child, element_name, &parser, subparser_data); if (parser.start_element) parser.start_element (context, element_name, names, values, subparser_data, error); return TRUE; }
static void parse_signal (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { SignalInfo *info; const gchar *name; const gchar *handler = NULL; const gchar *object = NULL; gboolean after = FALSE; gboolean swapped = -1; ObjectInfo *object_info; guint id = 0; GQuark detail = 0; object_info = state_peek_info (data, ObjectInfo); if (!object_info || !(strcmp (object_info->tag.name, "object") == 0 || strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; } if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_STRING, "handler", &handler, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "last_modification_time", NULL, G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "after", &after, G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped, G_MARKUP_COLLECT_INVALID)) { _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } if (!g_signal_parse_name (name, object_info->type, &id, &detail, FALSE)) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_SIGNAL, "Invalid signal '%s' for type '%s'", name, g_type_name (object_info->type)); _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } /* Swapped defaults to FALSE except when object is set */ if (swapped == -1) { if (object) swapped = TRUE; else swapped = FALSE; } info = g_slice_new0 (SignalInfo); info->id = id; info->detail = detail; info->handler = g_strdup (handler); if (after) info->flags |= G_CONNECT_AFTER; if (swapped) info->flags |= G_CONNECT_SWAPPED; info->connect_object_name = g_strdup (object); state_push (data, info); info->tag.name = element_name; }
static void parse_property (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { PropertyInfo *info; const gchar *name = NULL; const gchar *context = NULL; const gchar *bind_source = NULL; const gchar *bind_property = NULL; const gchar *bind_flags_str = NULL; GBindingFlags bind_flags = G_BINDING_DEFAULT; gboolean translatable = FALSE; ObjectInfo *object_info; GParamSpec *pspec = NULL; gint line, col; object_info = state_peek_info (data, ObjectInfo); if (!object_info || !(strcmp (object_info->tag.name, "object") == 0 || strcmp (object_info->tag.name, "template") == 0)) { error_invalid_tag (data, element_name, NULL, error); return; } if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &context, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-source", &bind_source, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-property", &bind_property, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-flags", &bind_flags_str, G_MARKUP_COLLECT_INVALID)) { _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } pspec = g_object_class_find_property (object_info->oclass, name); if (!pspec) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_PROPERTY, "Invalid property: %s.%s", g_type_name (object_info->type), name); _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } if (bind_flags_str) { if (!_gtk_builder_flags_from_string (G_TYPE_BINDING_FLAGS, NULL, bind_flags_str, &bind_flags, error)) { _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } } g_markup_parse_context_get_position (data->ctx, &line, &col); if (bind_source && bind_property) { BindingInfo *binfo; binfo = g_slice_new (BindingInfo); binfo->target = NULL; binfo->target_pspec = pspec; binfo->source = g_strdup (bind_source); binfo->source_property = g_strdup (bind_property); binfo->flags = bind_flags; binfo->line = line; binfo->col = col; object_info->bindings = g_slist_prepend (object_info->bindings, binfo); } else if (bind_source || bind_property) { error_missing_attribute (data, element_name, (bind_source) ? "bind-property" : "bind-source", error); return; } info = g_slice_new (PropertyInfo); info->tag.name = element_name; info->pspec = pspec; info->text = g_string_new (""); info->translatable = translatable; info->bound = (bind_source && bind_property); info->context = g_strdup (context); info->line = line; info->col = col; state_push (data, info); }
static void parse_object (GMarkupParseContext *context, ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { ObjectInfo *object_info; ChildInfo* child_info; GType object_type = G_TYPE_INVALID; const gchar *object_class = NULL; const gchar *constructor = NULL; const gchar *type_func = NULL; const gchar *object_id = NULL; gchar *internal_id = NULL; gint line; child_info = state_peek_info (data, ChildInfo); if (child_info && strcmp (child_info->tag.name, "object") == 0) { error_invalid_tag (data, element_name, NULL, error); return; } if (!g_markup_collect_attributes (element_name, names, values, error, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "class", &object_class, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "constructor", &constructor, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type-func", &type_func, G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "id", &object_id, G_MARKUP_COLLECT_INVALID)) { _gtk_builder_prefix_error (data->builder, data->ctx, error); return; } if (type_func) { /* Call the GType function, and return the GType, it's guaranteed afterwards * that g_type_from_name on the name will return our GType */ object_type = _get_type_by_symbol (type_func); if (object_type == G_TYPE_INVALID) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION, "Invalid type function '%s'", type_func); _gtk_builder_prefix_error (data->builder, context, error); return; } } else if (object_class) { object_type = gtk_builder_get_type_from_name (data->builder, object_class); if (object_type == G_TYPE_INVALID) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_VALUE, "Invalid object type '%s'", object_class); _gtk_builder_prefix_error (data->builder, context, error); return; } } else { error_missing_attribute (data, element_name, "class", error); return; } if (!object_id) { internal_id = g_strdup_printf ("___object_%d___", ++data->object_counter); object_id = internal_id; } ++data->cur_object_level; /* check if we reached a requested object (if it is specified) */ if (data->requested_objects && !data->inside_requested_object) { if (is_requested_object (object_id, data)) { data->requested_object_level = data->cur_object_level; GTK_NOTE (BUILDER, g_message ("requested object \"%s\" found at level %d", object_id, data->requested_object_level)); data->inside_requested_object = TRUE; } else { g_free (internal_id); return; } } object_info = g_slice_new0 (ObjectInfo); object_info->tag.name = element_name; object_info->type = object_type; object_info->oclass = g_type_class_ref (object_type); object_info->id = (internal_id) ? internal_id : g_strdup (object_id); object_info->constructor = g_strdup (constructor); object_info->parent = (CommonInfo*)child_info; state_push (data, object_info); line = GPOINTER_TO_INT (g_hash_table_lookup (data->object_ids, object_id)); if (line != 0) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_DUPLICATE_ID, "Duplicate object ID '%s' (previously on line %d)", object_id, line); _gtk_builder_prefix_error (data->builder, context, error); return; } g_markup_parse_context_get_position (context, &line, NULL); g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line)); }
static void end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { ParserData *data = (ParserData*)user_data; GTK_NOTE (BUILDER, g_message ("</%s>", element_name)); if (data->subparser && data->subparser->start) { subparser_end (context, element_name, data, error); return; } if (strcmp (element_name, "requires") == 0) { RequiresInfo *req_info = state_pop_info (data, RequiresInfo); /* TODO: Allow third party widget developers to check thier * required versions, possibly throw a signal allowing them * to check thier library versions here. */ if (!strcmp (req_info->library, "gtk+")) { if (!GTK_CHECK_VERSION (req_info->major, req_info->minor, 0)) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_VERSION_MISMATCH, "Required %s version %d.%d, current version is %d.%d", req_info->library, req_info->major, req_info->minor, GTK_MAJOR_VERSION, GTK_MINOR_VERSION); _gtk_builder_prefix_error (data->builder, context, error); } } free_requires_info (req_info, NULL); } else if (strcmp (element_name, "interface") == 0) { } else if (data->requested_objects && !data->inside_requested_object) { /* If outside a requested object, simply ignore this tag */ } else if (strcmp (element_name, "menu") == 0) { _gtk_builder_menu_end (data); } else if (strcmp (element_name, "object") == 0 || strcmp (element_name, "template") == 0) { ObjectInfo *object_info = state_pop_info (data, ObjectInfo); ChildInfo* child_info = state_peek_info (data, ChildInfo); if (data->requested_objects && data->inside_requested_object && (data->cur_object_level == data->requested_object_level)) { GTK_NOTE (BUILDER, g_message ("requested object end found at level %d", data->requested_object_level)); data->inside_requested_object = FALSE; } --data->cur_object_level; g_assert (data->cur_object_level >= 0); object_info->object = builder_construct (data, object_info, error); if (!object_info->object) { free_object_info (object_info); return; } if (child_info) child_info->object = object_info->object; if (GTK_IS_BUILDABLE (object_info->object) && GTK_BUILDABLE_GET_IFACE (object_info->object)->parser_finished) data->finalizers = g_slist_prepend (data->finalizers, object_info->object); _gtk_builder_add_signals (data->builder, object_info->signals); free_object_info (object_info); } else if (strcmp (element_name, "property") == 0) { PropertyInfo *prop_info = state_pop_info (data, PropertyInfo); CommonInfo *info = state_peek_info (data, CommonInfo); g_assert (info != NULL); /* Normal properties */ if (strcmp (info->tag.name, "object") == 0 || strcmp (info->tag.name, "template") == 0) { ObjectInfo *object_info = (ObjectInfo*)info; if (prop_info->translatable && prop_info->text->len) { const gchar *translated; translated = _gtk_builder_parser_translate (data->domain, prop_info->context, prop_info->text->str); g_string_assign (prop_info->text, translated); } object_info->properties = g_slist_prepend (object_info->properties, prop_info); } else g_assert_not_reached (); } else if (strcmp (element_name, "child") == 0) { ChildInfo *child_info = state_pop_info (data, ChildInfo); _gtk_builder_add (data->builder, child_info); free_child_info (child_info); } else if (strcmp (element_name, "signal") == 0) { SignalInfo *signal_info = state_pop_info (data, SignalInfo); ObjectInfo *object_info = (ObjectInfo*)state_peek_info (data, CommonInfo); g_assert (object_info != NULL); 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, "placeholder") == 0) { } else { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG, "Unhandled tag: <%s>", element_name); _gtk_builder_prefix_error (data->builder, context, error); } }
static void parse_signal (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { SignalInfo *info; gchar *name = NULL; gchar *handler = NULL; gchar *object = NULL; gboolean after = FALSE; gboolean swapped = FALSE; gboolean swapped_set = FALSE; ObjectInfo *object_info; int i; object_info = state_peek_info (data, ObjectInfo); if (!object_info || strcmp (object_info->tag.name, "object") != 0) { error_invalid_tag (data, element_name, NULL, error); return; } for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "name") == 0) name = g_strdup (values[i]); else if (strcmp (names[i], "handler") == 0) handler = g_strdup (values[i]); else if (strcmp (names[i], "after") == 0) { if (!_gtk_builder_boolean_from_string (values[i], &after, error)) return; } else if (strcmp (names[i], "swapped") == 0) { if (!_gtk_builder_boolean_from_string (values[i], &swapped, error)) return; swapped_set = TRUE; } else if (strcmp (names[i], "object") == 0) object = g_strdup (values[i]); else if (strcmp (names[i], "last_modification_time") == 0) /* parse but ignore */ ; else { error_invalid_attribute (data, element_name, names[i], error); return; } } if (!name) { error_missing_attribute (data, element_name, "name", error); return; } else if (!handler) { error_missing_attribute (data, element_name, "handler", error); return; } /* Swapped defaults to FALSE except when object is set */ if (object && !swapped_set) swapped = TRUE; info = g_slice_new0 (SignalInfo); info->name = name; info->handler = handler; if (after) info->flags |= G_CONNECT_AFTER; if (swapped) info->flags |= G_CONNECT_SWAPPED; info->connect_object_name = object; state_push (data, info); info->tag.name = element_name; }
static void parse_property (ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { PropertyInfo *info; gchar *name = NULL; gchar *context = NULL; gboolean translatable = FALSE; ObjectInfo *object_info; int i; object_info = state_peek_info (data, ObjectInfo); if (!object_info || strcmp (object_info->tag.name, "object") != 0) { error_invalid_tag (data, element_name, NULL, error); return; } for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "name") == 0) name = g_strdelimit (g_strdup (values[i]), "_", '-'); else if (strcmp (names[i], "translatable") == 0) { if (!_gtk_builder_boolean_from_string (values[i], &translatable, error)) return; } else if (strcmp (names[i], "comments") == 0) { /* do nothing, comments are for translators */ } else if (strcmp (names[i], "context") == 0) { context = g_strdup (values[i]); } else { error_invalid_attribute (data, element_name, names[i], error); return; } } if (!name) { error_missing_attribute (data, element_name, "name", error); return; } info = g_slice_new0 (PropertyInfo); info->name = name; info->translatable = translatable; info->context = context; info->text = g_string_new (""); state_push (data, info); info->tag.name = element_name; }
static void parse_object (GMarkupParseContext *context, ParserData *data, const gchar *element_name, const gchar **names, const gchar **values, GError **error) { ObjectInfo *object_info; ChildInfo* child_info; int i; gchar *object_class = NULL; gchar *object_id = NULL; gchar *constructor = NULL; gint line, line2; child_info = state_peek_info (data, ChildInfo); if (child_info && strcmp (child_info->tag.name, "object") == 0) { error_invalid_tag (data, element_name, NULL, error); return; } for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "class") == 0) object_class = g_strdup (values[i]); else if (strcmp (names[i], "id") == 0) object_id = g_strdup (values[i]); else if (strcmp (names[i], "constructor") == 0) constructor = g_strdup (values[i]); else if (strcmp (names[i], "type-func") == 0) { /* Call the GType function, and return the name of the GType, * it's guaranteed afterwards that g_type_from_name on the name * will return our GType */ object_class = _get_type_by_symbol (values[i]); if (!object_class) { g_markup_parse_context_get_position (context, &line, NULL); g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION, _("Invalid type function on line %d: '%s'"), line, values[i]); return; } } else { error_invalid_attribute (data, element_name, names[i], error); return; } } if (!object_class) { error_missing_attribute (data, element_name, "class", error); return; } if (!object_id) { error_missing_attribute (data, element_name, "id", error); return; } ++data->cur_object_level; /* check if we reached a requested object (if it is specified) */ if (data->requested_objects && !data->inside_requested_object) { if (is_requested_object (object_id, data)) { data->requested_object_level = data->cur_object_level; GTK_NOTE (BUILDER, g_print ("requested object \"%s\" found at level %d\n", object_id, data->requested_object_level)); data->inside_requested_object = TRUE; } else { g_free (object_class); g_free (object_id); g_free (constructor); return; } } object_info = g_slice_new0 (ObjectInfo); object_info->class_name = object_class; object_info->id = object_id; object_info->constructor = constructor; state_push (data, object_info); object_info->tag.name = element_name; if (child_info) object_info->parent = (CommonInfo*)child_info; g_markup_parse_context_get_position (context, &line, NULL); line2 = GPOINTER_TO_INT (g_hash_table_lookup (data->object_ids, object_id)); if (line2 != 0) { g_set_error (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_DUPLICATE_ID, _("Duplicate object id '%s' on line %d (previously on line %d)"), object_id, line, line2); return; } g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line)); }