X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbuilderparser.c;h=634425890528f2f76147756c681f7874c04c0545;hb=bda5987335b8c7828ebf1d289a91accfe2e74dbe;hp=a18d5ec7c4c8667ad2d9ff02dc8838975f8f80c6;hpb=357e2cbfff1c0b36035ea73007c8faf7c20daf64;p=~andy%2Fgtk diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c index a18d5ec7c..634425890 100644 --- a/gtk/gtkbuilderparser.c +++ b/gtk/gtkbuilderparser.c @@ -13,30 +13,23 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ +#include "config.h" + #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include #include "gtkbuilderprivate.h" #include "gtkbuilder.h" #include "gtkbuildable.h" #include "gtkdebug.h" -#include "gtktypeutils.h" -#include "gtkalias.h" +#include "gtkversion.h" +#include "gtktypebuiltins.h" +#include "gtkintl.h" + static void free_property_info (PropertyInfo *info); static void free_object_info (ObjectInfo *info); @@ -139,31 +132,12 @@ error_invalid_tag (ParserData *data, line_number, char_number, tag); } -static void -error_missing_property_value (ParserData *data, - GError **error) -{ - gint line_number, char_number; - - g_markup_parse_context_get_position (data->ctx, - &line_number, - &char_number); - - g_set_error (error, - GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_MISSING_PROPERTY_VALUE, - "%s:%d:%d must have a value set", - data->filename, - line_number, char_number); -} - gboolean -_gtk_builder_parse_boolean (const gchar *string, - gboolean *value, - GError **error) +_gtk_builder_boolean_from_string (const gchar *string, + gboolean *value, + GError **error) { gboolean retval = TRUE; - int i; int length; g_assert (string != NULL); @@ -183,9 +157,7 @@ _gtk_builder_parse_boolean (const gchar *string, } else { - gchar *lower = g_strdup (string); - for (i = 0; i < strlen (string); i++) - lower[i] = g_ascii_tolower (string[i]); + gchar *lower = g_ascii_strdown (string, length); if (strcmp (lower, "yes") == 0 || strcmp (lower, "true") == 0) *value = TRUE; @@ -207,8 +179,9 @@ _gtk_builder_parse_boolean (const gchar *string, } static GObject * -builder_construct (ParserData *data, - ObjectInfo *object_info) +builder_construct (ParserData *data, + ObjectInfo *object_info, + GError **error) { GObject *object; @@ -219,7 +192,10 @@ builder_construct (ParserData *data, object_info->properties = g_slist_reverse (object_info->properties); - object = _gtk_builder_construct (data->builder, object_info); + object = _gtk_builder_construct (data->builder, object_info, error); + if (!object) + return NULL; + g_assert (G_IS_OBJECT (object)); object_info->object = object; @@ -227,12 +203,105 @@ builder_construct (ParserData *data, return object; } +static gchar * +_get_type_by_symbol (const gchar* symbol) +{ + static GModule *module = NULL; + GTypeGetFunc func; + GType type; + + if (!module) + module = g_module_open (NULL, 0); + + if (!g_module_symbol (module, symbol, (gpointer)&func)) + return NULL; + + type = func (); + if (type == G_TYPE_INVALID) + return NULL; + + return g_strdup (g_type_name (type)); +} + static void -parse_object (ParserData *data, - const gchar *element_name, - const gchar **names, - const gchar **values, - GError **error) +parse_requires (ParserData *data, + const gchar *element_name, + const gchar **names, + const gchar **values, + GError **error) +{ + RequiresInfo *req_info; + const gchar *library = NULL; + const gchar *version = NULL; + gchar **split; + gint i, version_major = 0, version_minor = 0; + gint line_number, char_number; + + g_markup_parse_context_get_position (data->ctx, + &line_number, + &char_number); + + for (i = 0; names[i] != NULL; i++) + { + if (strcmp (names[i], "lib") == 0) + library = values[i]; + else if (strcmp (names[i], "version") == 0) + version = values[i]; + else + error_invalid_attribute (data, element_name, names[i], error); + } + + if (!library || !version) + { + error_missing_attribute (data, element_name, + version ? "lib" : "version", error); + return; + } + + if (!(split = g_strsplit (version, ".", 2)) || !split[0] || !split[1]) + { + g_set_error (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_INVALID_VALUE, + "%s:%d:%d <%s> attribute has malformed value \"%s\"", + data->filename, + line_number, char_number, "version", version); + return; + } + version_major = g_ascii_strtoll (split[0], NULL, 10); + version_minor = g_ascii_strtoll (split[1], NULL, 10); + g_strfreev (split); + + req_info = g_slice_new0 (RequiresInfo); + req_info->library = g_strdup (library); + req_info->major = version_major; + req_info->minor = version_minor; + state_push (data, req_info); + req_info->tag.name = element_name; +} + +static gboolean +is_requested_object (const gchar *object, + ParserData *data) +{ + GSList *l; + + for (l = data->requested_objects; l; l = l->next) + { + if (strcmp (l->data, object) == 0) + return TRUE; + } + + return FALSE; +} + +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; @@ -240,13 +309,12 @@ parse_object (ParserData *data, 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); - if (child_info) - free_object_info ((ObjectInfo*)child_info); return; } @@ -258,6 +326,23 @@ parse_object (ParserData *data, 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); @@ -277,16 +362,53 @@ parse_object (ParserData *data, 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); - g_assert (state_peek (data) != NULL); 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)); } static void @@ -303,24 +425,12 @@ free_object_info (ObjectInfo *info) g_slice_free (ObjectInfo, info); } -static gchar * -_get_type_by_symbol (const gchar* symbol) +static void +free_menu_info (MenuInfo *info) { - static GModule *module = NULL; - GTypeGetFunc func; - GType type; - - if (!module) - module = g_module_open (NULL, 0); - - if (!g_module_symbol (module, symbol, (gpointer)&func)) - return NULL; - - type = func (); - if (type == G_TYPE_INVALID) - return NULL; - - return g_strdup (g_type_name (type)); + g_free (info->id); + g_hash_table_unref (info->objects); + g_slice_free (MenuInfo, info); } static void @@ -338,15 +448,12 @@ parse_child (ParserData *data, object_info = state_peek_info (data, ObjectInfo); if (!object_info || strcmp (object_info->tag.name, "object") != 0) { - error_invalid_tag (data, element_name, "object", error); - if (object_info) - free_object_info (object_info); + error_invalid_tag (data, element_name, NULL, error); return; } child_info = g_slice_new0 (ChildInfo); state_push (data, child_info); - g_assert (state_peek (data) != NULL); child_info->tag.name = element_name; for (i = 0; names[i]; i++) { @@ -354,25 +461,13 @@ parse_child (ParserData *data, child_info->type = g_strdup (values[i]); else if (strcmp (names[i], "internal-child") == 0) child_info->internal_child = g_strdup (values[i]); - else if (strcmp (names[i], "type-func") == 0) - { - child_info->type = _get_type_by_symbol (values[i]); - if (!child_info->type) - { - g_set_error (error, GTK_BUILDER_ERROR, - GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION, - _("Invalid type function: `%s'"), - values[i]); - return; - } - } else error_invalid_attribute (data, element_name, names[i], error); } child_info->parent = (CommonInfo*)object_info; - object_info->object = builder_construct (data, object_info); + object_info->object = builder_construct (data, object_info, error); } static void @@ -392,10 +487,17 @@ parse_property (ParserData *data, { PropertyInfo *info; gchar *name = NULL; + gchar *context = NULL; gboolean translatable = FALSE; + ObjectInfo *object_info; int i; - g_assert (data->stack != NULL); + 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++) { @@ -403,9 +505,18 @@ parse_property (ParserData *data, name = g_strdelimit (g_strdup (values[i]), "_", '-'); else if (strcmp (names[i], "translatable") == 0) { - if (!_gtk_builder_parse_boolean (values[i], &translatable, error)) + 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); @@ -422,6 +533,8 @@ parse_property (ParserData *data, 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; @@ -449,9 +562,15 @@ parse_signal (ParserData *data, gboolean after = FALSE; gboolean swapped = FALSE; gboolean swapped_set = FALSE; + ObjectInfo *object_info; int i; - g_assert (data->stack != NULL); + 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++) { @@ -461,17 +580,20 @@ parse_signal (ParserData *data, handler = g_strdup (values[i]); else if (strcmp (names[i], "after") == 0) { - if (!_gtk_builder_parse_boolean (values[i], &after, error)) + if (!_gtk_builder_boolean_from_string (values[i], &after, error)) return; } else if (strcmp (names[i], "swapped") == 0) { - if (!_gtk_builder_parse_boolean (values[i], &swapped, error)) + 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); @@ -519,6 +641,14 @@ _free_signal_info (SignalInfo *info, g_slice_free (SignalInfo, info); } +static void +free_requires_info (RequiresInfo *info, + gpointer user_data) +{ + g_free (info->library); + g_slice_free (RequiresInfo, info); +} + static void parse_interface (ParserData *data, const gchar *element_name, @@ -530,10 +660,25 @@ parse_interface (ParserData *data, for (i = 0; names[i] != NULL; i++) { - if (strcmp (names[i], "domain") == 0 && !data->domain) + if (strcmp (names[i], "domain") == 0) { - data->domain = g_strdup (values[i]); - break; + if (data->domain) + { + if (strcmp (data->domain, values[i]) == 0) + continue; + else + g_warning ("%s: interface domain '%s' overrides " + "programically set domain '%s'", + data->filename, + values[i], + data->domain + ); + + g_free (data->domain); + } + + data->domain = g_strdup (values[i]); + gtk_builder_set_translation_domain (data->builder, data->domain); } else error_invalid_attribute (data, "interface", names[i], error); @@ -631,7 +776,7 @@ parse_custom (GMarkupParseContext *context, { CommonInfo* parent_info; GMarkupParser parser; - gpointer *subparser_data; + gpointer subparser_data; GObject *object; GObject *child; @@ -643,8 +788,14 @@ parse_custom (GMarkupParseContext *context, { ObjectInfo* object_info = (ObjectInfo*)parent_info; if (!object_info->object) - object_info->object = _gtk_builder_construct (data->builder, - object_info); + { + 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; @@ -666,7 +817,7 @@ parse_custom (GMarkupParseContext *context, child, element_name, &parser, - (gpointer*)&subparser_data)) + &subparser_data)) return FALSE; data->subparser = create_subparser (object, child, element_name, @@ -690,7 +841,7 @@ start_element (GMarkupParseContext *context, ParserData *data = (ParserData*)user_data; #ifdef GTK_ENABLE_DEBUG - if (gtk_debug_flags & GTK_DEBUG_BUILDER) + if (gtk_get_debug_flags () & GTK_DEBUG_BUILDER) { GString *tags = g_string_new (""); int i; @@ -721,9 +872,16 @@ start_element (GMarkupParseContext *context, if (!subparser_start (context, element_name, names, values, data, error)) return; - - if (strcmp (element_name, "object") == 0) - parse_object (data, element_name, names, values, error); + + if (strcmp (element_name, "requires") == 0) + parse_requires (data, element_name, names, values, error); + else if (strcmp (element_name, "object") == 0) + parse_object (context, data, element_name, names, values, error); + else if (data->requested_objects && !data->inside_requested_object) + { + /* If outside a requested object, simply ignore this tag */ + return; + } else if (strcmp (element_name, "child") == 0) parse_child (data, element_name, names, values, error); else if (strcmp (element_name, "property") == 0) @@ -732,6 +890,8 @@ start_element (GMarkupParseContext *context, parse_signal (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, "menu") == 0) + _gtk_builder_menu_start (data, element_name, names, values, error); else if (strcmp (element_name, "placeholder") == 0) { /* placeholder has no special treatmeant, but it needs an @@ -747,6 +907,21 @@ start_element (GMarkupParseContext *context, element_name); } +gchar * +_gtk_builder_parser_translate (const gchar *domain, + const gchar *context, + const gchar *text) +{ + const char *s; + + if (context) + s = g_dpgettext2 (domain, context, text); + else + s = g_dgettext (domain, text); + + return g_strdup (s); +} + /* Called for close tags */ static void end_element (GMarkupParseContext *context, @@ -764,19 +939,71 @@ end_element (GMarkupParseContext *context, return; } - if (strcmp (element_name, "object") == 0) + 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, + "%s: required %s version %d.%d, current version is %d.%d", + data->filename, req_info->library, + req_info->major, req_info->minor, + GTK_MAJOR_VERSION, GTK_MINOR_VERSION); + } + 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 */ + return; + } + else if (strcmp (element_name, "menu") == 0) + { + _gtk_builder_menu_end (data); + } + else if (strcmp (element_name, "object") == 0) { ObjectInfo *object_info = state_pop_info (data, ObjectInfo); ChildInfo* child_info = state_peek_info (data, ChildInfo); - object_info->object = builder_construct (data, object_info); + if (data->requested_objects && data->inside_requested_object && + (data->cur_object_level == data->requested_object_level)) + { + GTK_NOTE (BUILDER, g_print ("requested object end found at level %d\n", + 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) @@ -784,19 +1011,24 @@ end_element (GMarkupParseContext *context, PropertyInfo *prop_info = state_pop_info (data, PropertyInfo); CommonInfo *info = state_peek_info (data, CommonInfo); - if (!prop_info->data) - { - error_missing_property_value (data, error); - free_property_info (prop_info); - if (strcmp (info->tag.name, "object") == 0) - free_object_info((ObjectInfo*)info); - return; - } - /* Normal properties */ if (strcmp (info->tag.name, "object") == 0) { ObjectInfo *object_info = (ObjectInfo*)info; + + if (prop_info->translatable && prop_info->text->len) + { + prop_info->data = _gtk_builder_parser_translate (data->domain, + prop_info->context, + prop_info->text->str); + g_string_free (prop_info->text, TRUE); + } + else + { + prop_info->data = g_string_free (prop_info->text, FALSE); + + } + object_info->properties = g_slist_prepend (object_info->properties, prop_info); } @@ -819,9 +1051,6 @@ end_element (GMarkupParseContext *context, object_info->signals = g_slist_prepend (object_info->signals, signal_info); } - else if (strcmp (element_name, "interface") == 0) - { - } else if (strcmp (element_name, "placeholder") == 0) { } @@ -845,9 +1074,13 @@ text (GMarkupParseContext *context, 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, error); + data->subparser->data, &tmp_error); + if (tmp_error) + g_propagate_error (error, tmp_error); return; } @@ -861,23 +1094,34 @@ text (GMarkupParseContext *context, { PropertyInfo *prop_info = (PropertyInfo*)info; - if (prop_info->translatable && text_len) - { - if (data->domain) - text = dgettext (data->domain, text); - else - text = gettext (text); - } - prop_info->data = g_strndup (text, text_len); + g_string_append_len (prop_info->text, text, text_len); } } +static void +free_info (CommonInfo *info) +{ + if (strcmp (info->tag.name, "object") == 0) + free_object_info ((ObjectInfo *)info); + else if (strcmp (info->tag.name, "child") == 0) + free_child_info ((ChildInfo *)info); + else if (strcmp (info->tag.name, "property") == 0) + free_property_info ((PropertyInfo *)info); + else if (strcmp (info->tag.name, "signal") == 0) + _free_signal_info ((SignalInfo *)info, NULL); + else if (strcmp (info->tag.name, "requires") == 0) + free_requires_info ((RequiresInfo *)info, NULL); + else if (strcmp (info->tag.name, "menu") == 0) + free_menu_info ((MenuInfo *)info); + else + g_assert_not_reached (); +} + static const GMarkupParser parser = { start_element, end_element, text, NULL, - NULL }; void @@ -885,23 +1129,52 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, const gchar *filename, const gchar *buffer, gsize length, + gchar **requested_objs, GError **error) { + const gchar* domain; ParserData *data; GSList *l; + /* Store the original domain so that interface domain attribute can be + * applied for the builder and the original domain can be restored after + * parsing has finished. This allows subparsers to translate elements with + * gtk_builder_get_translation_domain() without breaking the ABI or API + */ + domain = gtk_builder_get_translation_domain (builder); + data = g_new0 (ParserData, 1); data->builder = builder; data->filename = filename; - data->domain = g_strdup (gtk_builder_get_translation_domain (builder)); + data->domain = g_strdup (domain); + data->object_ids = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, NULL); - data->ctx = g_markup_parse_context_new ( - &parser, G_MARKUP_TREAT_CDATA_AS_TEXT, data, NULL); + data->requested_objects = NULL; + if (requested_objs) + { + gint i; + + data->inside_requested_object = FALSE; + for (i = 0; requested_objs[i]; ++i) + { + data->requested_objects = g_slist_prepend (data->requested_objects, + g_strdup (requested_objs[i])); + } + } + else + { + /* get all the objects */ + data->inside_requested_object = TRUE; + } + + data->ctx = g_markup_parse_context_new (&parser, + G_MARKUP_TREAT_CDATA_AS_TEXT, + data, NULL); if (!g_markup_parse_context_parse (data->ctx, buffer, length, error)) goto out; - - gtk_builder_set_translation_domain (data->builder, data->domain); + _gtk_builder_finish (builder); /* Custom parser_finished */ @@ -915,7 +1188,6 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, sub->child, sub->tagname, sub->data); - free_subparser (sub); } /* Common parser_finished, for all created objects */ @@ -927,11 +1199,19 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, } out: - g_markup_parse_context_free (data->ctx); + g_slist_foreach (data->stack, (GFunc)free_info, NULL); g_slist_free (data->stack); + g_slist_foreach (data->custom_finalizers, (GFunc)free_subparser, NULL); g_slist_free (data->custom_finalizers); g_slist_free (data->finalizers); + g_slist_foreach (data->requested_objects, (GFunc) g_free, NULL); + g_slist_free (data->requested_objects); g_free (data->domain); + g_hash_table_destroy (data->object_ids); + g_markup_parse_context_free (data->ctx); g_free (data); + + /* restore the original domain */ + gtk_builder_set_translation_domain (builder, domain); }