X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbuilderparser.c;h=634425890528f2f76147756c681f7874c04c0545;hb=HEAD;hp=62e2059636441a3e408eb7cd64bfc5b0a1332ad7;hpb=670775d8d8301b4593726a81c8ed3dc7a7bf7cc3;p=~andy%2Fgtk diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c index 62e205963..634425890 100644 --- a/gtk/gtkbuilderparser.c +++ b/gtk/gtkbuilderparser.c @@ -13,24 +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 +#include "config.h" #include #include -#include "gtktypeutils.h" +#include #include "gtkbuilderprivate.h" #include "gtkbuilder.h" #include "gtkbuildable.h" #include "gtkdebug.h" -#include "gtktypeutils.h" +#include "gtkversion.h" +#include "gtktypebuiltins.h" #include "gtkintl.h" -#include "gtkalias.h" + static void free_property_info (PropertyInfo *info); static void free_object_info (ObjectInfo *info); @@ -225,11 +224,84 @@ _get_type_by_symbol (const gchar* symbol) } 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; @@ -237,6 +309,7 @@ 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) @@ -262,10 +335,11 @@ parse_object (ParserData *data, object_class = _get_type_by_symbol (values[i]); if (!object_class) { - g_set_error (error, GTK_BUILDER_ERROR, + 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: `%s'"), - values[i]); + _("Invalid type function on line %d: '%s'"), + line, values[i]); return; } } @@ -288,6 +362,30 @@ 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; @@ -297,6 +395,20 @@ parse_object (ParserData *data, 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 @@ -313,6 +425,14 @@ free_object_info (ObjectInfo *info) g_slice_free (ObjectInfo, info); } +static void +free_menu_info (MenuInfo *info) +{ + g_free (info->id); + g_hash_table_unref (info->objects); + g_slice_free (MenuInfo, info); +} + static void parse_child (ParserData *data, const gchar *element_name, @@ -521,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, @@ -534,9 +662,23 @@ parse_interface (ParserData *data, { if (strcmp (names[i], "domain") == 0) { - g_free (data->domain); - 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); @@ -647,6 +789,7 @@ parse_custom (GMarkupParseContext *context, 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); @@ -698,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; @@ -729,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) @@ -740,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 @@ -755,25 +907,6 @@ start_element (GMarkupParseContext *context, element_name); } -static const char * -_g_dpgettext (const char *domain, - const char *msgctxt, - const char *msgid) -{ - size_t msgctxt_len = strlen (msgctxt) + 1; - size_t msgid_len = strlen (msgid) + 1; - const char *translation; - char* msg_ctxt_id; - - msg_ctxt_id = g_alloca (msgctxt_len + msgid_len); - - memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); - msg_ctxt_id[msgctxt_len - 1] = '|'; - memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); - - return g_dpgettext (domain, msg_ctxt_id, 0); -} - gchar * _gtk_builder_parser_translate (const gchar *domain, const gchar *context, @@ -782,7 +915,7 @@ _gtk_builder_parser_translate (const gchar *domain, const char *s; if (context) - s = _g_dpgettext (domain, context, text); + s = g_dpgettext2 (domain, context, text); else s = g_dgettext (domain, text); @@ -806,11 +939,57 @@ 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); + 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) { @@ -872,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) { } @@ -925,15 +1101,19 @@ text (GMarkupParseContext *context, static void free_info (CommonInfo *info) { - if (strcmp (info->tag.name, "object") == 0) + if (strcmp (info->tag.name, "object") == 0) free_object_info ((ObjectInfo *)info); - else if (strcmp (info->tag.name, "child") == 0) + else if (strcmp (info->tag.name, "child") == 0) free_child_info ((ChildInfo *)info); - else if (strcmp (info->tag.name, "property") == 0) + else if (strcmp (info->tag.name, "property") == 0) free_property_info ((PropertyInfo *)info); - else if (strcmp (info->tag.name, "signal") == 0) + else if (strcmp (info->tag.name, "signal") == 0) _free_signal_info ((SignalInfo *)info, NULL); - else + 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 (); } @@ -942,7 +1122,6 @@ static const GMarkupParser parser = { end_element, text, NULL, - NULL }; void @@ -950,15 +1129,44 @@ _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->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, @@ -966,7 +1174,7 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, if (!g_markup_parse_context_parse (data->ctx, buffer, length, error)) goto out; - + _gtk_builder_finish (builder); /* Custom parser_finished */ @@ -997,7 +1205,13 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, 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); }