mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-23 10:42:11 +01:00
gmarkup: add utility functions for text
WIP patch. https://bugzilla.gnome.org/show_bug.cgi?id=689035
This commit is contained in:
parent
6b3ec82732
commit
c28469faa0
@ -1140,6 +1140,9 @@ g_markup_parse_context_unref
|
|||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
GMarkupCollectType
|
GMarkupCollectType
|
||||||
g_markup_collect_attributes
|
g_markup_collect_attributes
|
||||||
|
g_markup_string_parser_start
|
||||||
|
g_markup_string_parser_end
|
||||||
|
g_markup_parser_reject_text
|
||||||
<SUBSECTION Private>
|
<SUBSECTION Private>
|
||||||
g_markup_error_quark
|
g_markup_error_quark
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
359
glib/gmarkup.c
359
glib/gmarkup.c
@ -37,6 +37,7 @@
|
|||||||
#include "gtestutils.h"
|
#include "gtestutils.h"
|
||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
#include "gthread.h"
|
#include "gthread.h"
|
||||||
|
#include "ggettext.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:markup
|
* SECTION:markup
|
||||||
@ -2865,3 +2866,361 @@ failure:
|
|||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_markup_string_parser_start_element (GMarkupParseContext *context,
|
||||||
|
const gchar *element_name,
|
||||||
|
const gchar **attribute_names,
|
||||||
|
const gchar **attribute_values,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
|
||||||
|
"Only text may appear inside element <%s> (not <%s>)",
|
||||||
|
g_markup_parse_context_get_element (context), element_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GString *str;
|
||||||
|
gboolean translatable;
|
||||||
|
gchar *context;
|
||||||
|
gchar *domain;
|
||||||
|
} StringParserState;
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_markup_string_parser_text (GMarkupParseContext *context,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
StringParserState *state = user_data;
|
||||||
|
|
||||||
|
g_string_append_len (state->str, text, text_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
g_markup_string_parser_error (GMarkupParseContext *context,
|
||||||
|
GError *error,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
StringParserState *state = user_data;
|
||||||
|
|
||||||
|
g_string_free (state->str, TRUE);
|
||||||
|
g_free (state->context);
|
||||||
|
g_free (state->domain);
|
||||||
|
|
||||||
|
g_slice_free (StringParserState, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_markup_string_parser_start:
|
||||||
|
* @context: a #GMarkupParseContext
|
||||||
|
* @translatable: if the text is meant to be translatable
|
||||||
|
* @gettext_domain: the gettext domain for translation
|
||||||
|
* @gettext_context: the gettext context for translation
|
||||||
|
* @error: the #GError passed into your 'start_element'.
|
||||||
|
*
|
||||||
|
* Starts reading an all-text section inside of an element from an
|
||||||
|
* invocation of a #GMarkupParser 'start_element' function.
|
||||||
|
*
|
||||||
|
* In many cases you probably want to parse the arguments of the tag
|
||||||
|
* starting the text section to determine appropriate values for
|
||||||
|
* @translatable, @gettext_domain and particularly @gettext_context.
|
||||||
|
*
|
||||||
|
* If @translatable is true then the text will be translated according
|
||||||
|
* to @gettext_domain and @gettext_context. Whitespace is normalised
|
||||||
|
* according to (FIXME: some rules... probably the ones intltool already
|
||||||
|
* knows about) before translation (if any) occurs.
|
||||||
|
*
|
||||||
|
* There may not be nested tags in the text section. If any are
|
||||||
|
* encountered then an error is generated (and the next and final call
|
||||||
|
* that your #GMarkupParser will receive is to its 'error' handler, if
|
||||||
|
* any).
|
||||||
|
*
|
||||||
|
* You should call g_markup_string_parser_end() in the corresponding
|
||||||
|
* 'end_element' invocation (ie: the one called immediately after, with
|
||||||
|
* the same @element_name).
|
||||||
|
*
|
||||||
|
* If you intend to parse all of the text sections in your document type
|
||||||
|
* using this function then you should probably use
|
||||||
|
* g_markup_parser_reject_text() as the 'text' handler in your
|
||||||
|
* #GMarkupParser vtable.
|
||||||
|
*
|
||||||
|
* Consider an example parser to parse a file of the following form:
|
||||||
|
*
|
||||||
|
* |[
|
||||||
|
* <![CDATA[
|
||||||
|
* <attrlist gettext-domain='myapp'>
|
||||||
|
* <attribute name='identifier'>
|
||||||
|
* myapp
|
||||||
|
* </attribute>
|
||||||
|
* <attribute name='label' translatable='yes'
|
||||||
|
* comment='This is the text in the main window'>
|
||||||
|
* Hello world!
|
||||||
|
* </attribute>
|
||||||
|
* <attribute name='24-hour-time' context='24 hour time'>
|
||||||
|
* false
|
||||||
|
* </attribute>
|
||||||
|
* </attrlist>
|
||||||
|
* ]]>
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* with the corresponding <literal>myapp.po</literal> fragment:
|
||||||
|
* |[
|
||||||
|
* # This is the text in the main window
|
||||||
|
* #: file.xml:7
|
||||||
|
* msgid "Hello world!"
|
||||||
|
* msgstr ""
|
||||||
|
*
|
||||||
|
* #: file.xml:10
|
||||||
|
* msgctxt "24 hour time"
|
||||||
|
* msgid "false"
|
||||||
|
* msgstr ""
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* The code for the parser follows:
|
||||||
|
*
|
||||||
|
* |[
|
||||||
|
* typedef struct
|
||||||
|
* {
|
||||||
|
* // For the file
|
||||||
|
* gchar *gettext_domain;
|
||||||
|
*
|
||||||
|
* // For the current <attribute/>
|
||||||
|
* gchar *name;
|
||||||
|
* } ParserState;
|
||||||
|
*
|
||||||
|
* void
|
||||||
|
* start_element (GMarkupParseContext *context,
|
||||||
|
* const gchar *element_name,
|
||||||
|
* const gchar **attribute_names,
|
||||||
|
* const gchar **attribute_values,
|
||||||
|
* gpointer user_data,
|
||||||
|
* GError **error)
|
||||||
|
* {
|
||||||
|
* ParserState *state = user_data;
|
||||||
|
* const GSList *element_stack;
|
||||||
|
* const gchar *container;
|
||||||
|
*
|
||||||
|
* element_stack = g_markup_parse_context_get_element_stack (context);
|
||||||
|
* container = element_stack->next ? element_stack->next->data : NULL;
|
||||||
|
*
|
||||||
|
* if (container == NULL)
|
||||||
|
* {
|
||||||
|
* if (g_str_equal (element_name, "attrlist"))
|
||||||
|
* {
|
||||||
|
* g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
|
||||||
|
* G_MARKUP_COLLECT_STRDUP | G_MARKUP_COLLECT_OPTIONAL,
|
||||||
|
* "gettext-domain", &state->gettext_domain,
|
||||||
|
* G_MARKUP_COLLECT_INVALID);
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* else if (g_str_equal (container, "attrlist"))
|
||||||
|
* {
|
||||||
|
* if (g_str_equal (element_name, "attribute"))
|
||||||
|
* {
|
||||||
|
* const gchar *gettext_context;
|
||||||
|
* gboolean translatable;
|
||||||
|
*
|
||||||
|
* if (g_markup_collect_attributes (element_name, attribute_names, attribute_values, error,
|
||||||
|
* G_MARKUP_COLLECT_STRDUP,
|
||||||
|
* "name", &state->name,
|
||||||
|
* G_MARKUP_COLLECT_OPTIONAL | G_MARKUP_COLLECT_BOOLEAN,
|
||||||
|
* "translatable", &translatable,
|
||||||
|
* G_MARKUP_COLLECT_OPTIONAL | G_MARKUP_COLLECT_STRING,
|
||||||
|
* "context", &gettext_context,
|
||||||
|
* G_MARKUP_COLLECT_OPTIONAL | G_MARKUP_COLLECT_STRING,
|
||||||
|
* "comment", NULL, // just for translators; ignore here
|
||||||
|
* G_MARKUP_COLLECT_INVALID))
|
||||||
|
* {
|
||||||
|
* g_markup_string_parser_start (context, translatable, state->gettext_domain, gettext_context, error);
|
||||||
|
* }
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if (container)
|
||||||
|
* g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
|
||||||
|
* _("Element <%s> not allowed inside <%s>"),
|
||||||
|
* element_name, container);
|
||||||
|
* else
|
||||||
|
* g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
|
||||||
|
* _("Element <%s> not allowed at the top level"), element_name);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void
|
||||||
|
* end_element (GMarkupParseContext *context,
|
||||||
|
* const gchar *element_name,
|
||||||
|
* gpointer user_data,
|
||||||
|
* GError **error)
|
||||||
|
* {
|
||||||
|
* ParserState *state = user_data;
|
||||||
|
*
|
||||||
|
* if (g_str_equal (element_name, "attrlist"))
|
||||||
|
* {
|
||||||
|
* g_clear_pointer (&state->gettext_domain, g_free);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* else if (g_str_equal (element_name, "attribute"))
|
||||||
|
* {
|
||||||
|
* gchar *value;
|
||||||
|
*
|
||||||
|
* value = g_markup_string_parser_end (context);
|
||||||
|
* g_print ("Got '%s'='%s'\n", state->name, value);
|
||||||
|
* g_clear_pointer (&state->name, g_free);
|
||||||
|
* g_free (value);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void
|
||||||
|
* error (GMarkupParseContext *context,
|
||||||
|
* GError *error,
|
||||||
|
* gpointer user_data)
|
||||||
|
* {
|
||||||
|
* ParserState *state = user_data;
|
||||||
|
*
|
||||||
|
* g_clear_pointer (&state->gettext_domain, g_free);
|
||||||
|
* g_clear_pointer (&state->name, g_free);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static GMarkupParser parser_vtable = {
|
||||||
|
* start_element,
|
||||||
|
* end_element,
|
||||||
|
* g_markup_parser_reject_text,
|
||||||
|
* NULL, // passthrough
|
||||||
|
* error
|
||||||
|
* };
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_markup_string_parser_start (GMarkupParseContext *context,
|
||||||
|
gboolean translatable,
|
||||||
|
const gchar *gettext_domain,
|
||||||
|
const gchar *gettext_context,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
static const GMarkupParser parser = {
|
||||||
|
g_markup_string_parser_start_element,
|
||||||
|
NULL,
|
||||||
|
g_markup_string_parser_text,
|
||||||
|
NULL,
|
||||||
|
g_markup_string_parser_error
|
||||||
|
};
|
||||||
|
StringParserState *state;
|
||||||
|
|
||||||
|
if (translatable && gettext_domain == NULL)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE,
|
||||||
|
"translation was requested for <%s> but no gettext domain was given",
|
||||||
|
g_markup_parse_context_get_element (context));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = g_slice_new (StringParserState);
|
||||||
|
state->str = g_string_new (NULL);
|
||||||
|
state->domain = g_strdup (gettext_domain);
|
||||||
|
state->context = g_strdup (gettext_context);
|
||||||
|
state->translatable = translatable;
|
||||||
|
|
||||||
|
g_markup_parse_context_push (context, &parser, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_markup_string_parser_end:
|
||||||
|
* @context: the #GMarkupParseContext
|
||||||
|
*
|
||||||
|
* Collect the result of an instance of the built-in #GMarkup string
|
||||||
|
* subparser.
|
||||||
|
*
|
||||||
|
* This should be called from the 'end_element' invocation that is
|
||||||
|
* run immediately after the 'start_element' invocation that called
|
||||||
|
* g_markup_string_parser_start().
|
||||||
|
*
|
||||||
|
* The resulting string will have been whitespace normalised and
|
||||||
|
* translated, if applicable.
|
||||||
|
*
|
||||||
|
* Returns: the string that was read from the markup
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
gchar *
|
||||||
|
g_markup_string_parser_end (GMarkupParseContext *context)
|
||||||
|
{
|
||||||
|
StringParserState *state = g_markup_parse_context_pop (context);
|
||||||
|
const gchar *translated;
|
||||||
|
gchar *result;
|
||||||
|
|
||||||
|
/* TODO: whitespace normalisation before translation? */
|
||||||
|
|
||||||
|
if (state->translatable)
|
||||||
|
{
|
||||||
|
if (state->context)
|
||||||
|
translated = g_dpgettext2 (state->domain, state->context, state->str->str);
|
||||||
|
else
|
||||||
|
translated = g_dgettext (state->domain, state->str->str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
translated = state->str->str;
|
||||||
|
|
||||||
|
if (translated != state->str->str)
|
||||||
|
{
|
||||||
|
g_string_free (state->str, TRUE);
|
||||||
|
result = g_strdup (translated);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = g_string_free (state->str, FALSE);
|
||||||
|
|
||||||
|
g_free (state->context);
|
||||||
|
g_free (state->domain);
|
||||||
|
|
||||||
|
g_slice_free (StringParserState, state);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_markup_parser_reject_text:
|
||||||
|
* @context: do not call this function directly
|
||||||
|
* @text: do not call this function directly
|
||||||
|
* @text_len: do not call this function directly
|
||||||
|
* @user_data: do not call this function directly
|
||||||
|
* @error: do not call this function directly
|
||||||
|
*
|
||||||
|
* This function is not designed to be used directly. Rather, it should
|
||||||
|
* be used as the 'text' callback in the #GMarkupParser vtable if your
|
||||||
|
* parser is not interested in parsing any text.
|
||||||
|
*
|
||||||
|
* This is useful for document types where no text is valid (ie: only
|
||||||
|
* elements are allowed) or for parsers that want to parse all text
|
||||||
|
* sections using subparsers (with g_markup_string_parser_text() for
|
||||||
|
* example).
|
||||||
|
*
|
||||||
|
* The function will reject any text that does not consist entirely of
|
||||||
|
* whitespace characters, issuing an appropriate warning via #GError.
|
||||||
|
*
|
||||||
|
* Since: 2.36
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_markup_parser_reject_text (GMarkupParseContext *context,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
for (i = 0; i < text_len; i++)
|
||||||
|
if (!g_ascii_isspace (text[i]))
|
||||||
|
{
|
||||||
|
g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
|
||||||
|
"text may not appear inside <%s>",
|
||||||
|
g_markup_parse_context_get_element (context));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -252,6 +252,23 @@ gboolean g_markup_collect_attributes (const gchar *element_name,
|
|||||||
const gchar *first_attr,
|
const gchar *first_attr,
|
||||||
...);
|
...);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
void g_markup_string_parser_start (GMarkupParseContext *context,
|
||||||
|
gboolean translatable,
|
||||||
|
const gchar *gettext_domain,
|
||||||
|
const gchar *gettext_context,
|
||||||
|
GError **error);
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
gchar * g_markup_string_parser_end (GMarkupParseContext *context);
|
||||||
|
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_36
|
||||||
|
void g_markup_parser_reject_text (GMarkupParseContext *context,
|
||||||
|
const gchar *text,
|
||||||
|
gsize text_len,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_MARKUP_H__ */
|
#endif /* __G_MARKUP_H__ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user