text: Store the markup attributes separately

Previously when the markup property is set it would generate an
attribute list from the markup and then merge it with the attributes
from the attribute property and store it as the effective
attributes. The markup attributes and the marked up text would then be
forgotten. This breaks if the application then later changes the
attributes property because it would try to regenerate the effective
attributes from the markup text but the stored text no longer contains
any markup. If the original markup text happened to contain entities
like '<' they would end up causing parse errors because they would
be converted to the actual symbols.

To fix this the attributes from the markup are now stored
independently from the effective attributes. The effective attributes
are now regenerated if either set of attributes changes right before a
layout is created.

http://bugzilla.openedhand.com/show_bug.cgi?id=1940
This commit is contained in:
Neil Roberts 2010-01-05 11:58:12 +00:00
parent 32b456fc8c
commit 59105341bc

View File

@ -112,8 +112,18 @@ struct _ClutterTextPrivate
LayoutCache cached_layouts[N_CACHED_LAYOUTS];
guint cache_age;
/* These are the attributes set by the attributes property */
PangoAttrList *attrs;
/* These are the attributes derived from the text when the
use-markup property is set */
PangoAttrList *markup_attrs;
/* This is the combination of the above two lists. It is set to NULL
whenever either of them changes and then regenerated by merging
the two lists whenever a layout is needed */
PangoAttrList *effective_attrs;
/* These are the attributes for the preedit string. These are merged
with the effective attributes into a temporary list before
creating a layout */
PangoAttrList *preedit_attrs;
guint alignment : 2;
@ -287,6 +297,52 @@ clutter_text_get_display_text (ClutterText *self)
}
}
static void
clutter_text_ensure_effective_attributes (ClutterText *self)
{
ClutterTextPrivate *priv = self->priv;
/* If we already have the effective attributes then we don't need to
do anything */
if (priv->effective_attrs == NULL)
{
if (priv->attrs)
{
/* If there are no markup attributes then we can just use
these attributes directly */
if (priv->markup_attrs == NULL)
priv->effective_attrs = pango_attr_list_ref (priv->attrs);
else
{
/* Otherwise we need to merge the two lists */
PangoAttrIterator *iter;
GSList *attributes, *l;
priv->effective_attrs = pango_attr_list_copy (priv->markup_attrs);
iter = pango_attr_list_get_iterator (priv->attrs);
do
{
attributes = pango_attr_iterator_get_attrs (iter);
for (l = attributes; l != NULL; l = l->next)
{
PangoAttribute *attr = l->data;
pango_attr_list_insert (priv->effective_attrs, attr);
}
g_slist_free (attributes);
}
while (pango_attr_iterator_next (iter));
}
}
else if (priv->markup_attrs)
/* We can just use the markup attributes directly */
priv->effective_attrs = pango_attr_list_ref (priv->markup_attrs);
}
}
static PangoLayout *
clutter_text_create_layout_no_cache (ClutterText *text,
gfloat allocation_width,
@ -333,8 +389,15 @@ clutter_text_create_layout_no_cache (ClutterText *text,
else
pango_layout_set_text (layout, contents, contents_len);
if (!priv->editable && priv->effective_attrs)
pango_layout_set_attributes (layout, priv->effective_attrs);
if (!priv->editable)
{
/* This will merge the markup attributes and the attributes
property if needed */
clutter_text_ensure_effective_attributes (text);
if (priv->effective_attrs)
pango_layout_set_attributes (layout, priv->effective_attrs);
}
pango_layout_set_alignment (layout, priv->alignment);
pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode);
@ -749,39 +812,6 @@ clutter_text_delete_selection (ClutterText *self)
return TRUE;
}
static void
clutter_text_merge_attributes (ClutterText *self)
{
ClutterTextPrivate *priv = self->priv;
PangoAttrIterator *iter;
GSList *attributes, *l;
if (!priv->attrs)
return;
if (!priv->effective_attrs)
{
priv->effective_attrs = pango_attr_list_ref (priv->attrs);
return;
}
iter = pango_attr_list_get_iterator (priv->attrs);
do
{
attributes = pango_attr_iterator_get_attrs (iter);
for (l = attributes; l != NULL; l = l->next)
{
PangoAttribute *attr = l->data;
pango_attr_list_insert (priv->effective_attrs, attr);
}
g_slist_free (attributes);
}
while (pango_attr_iterator_next (iter));
}
static inline void
clutter_text_set_text_internal (ClutterText *self,
const gchar *text)
@ -885,15 +915,18 @@ clutter_text_set_markup_internal (ClutterText *self,
g_free (text);
}
if (attrs)
/* Store the new markup attributes */
if (priv->markup_attrs)
pango_attr_list_unref (priv->markup_attrs);
priv->markup_attrs = attrs;
/* Clear the effective attributes so they will be regenerated when a
layout is created */
if (priv->effective_attrs)
{
if (priv->effective_attrs)
pango_attr_list_unref (priv->effective_attrs);
priv->effective_attrs = attrs;
pango_attr_list_unref (priv->effective_attrs);
priv->effective_attrs = NULL;
}
clutter_text_merge_attributes (self);
}
static void
@ -1146,6 +1179,8 @@ clutter_text_finalize (GObject *gobject)
if (priv->attrs)
pango_attr_list_unref (priv->attrs);
if (priv->markup_attrs)
pango_attr_list_unref (priv->markup_attrs);
if (priv->effective_attrs)
pango_attr_list_unref (priv->effective_attrs);
if (priv->preedit_attrs)
@ -3858,18 +3893,13 @@ clutter_text_set_attributes (ClutterText *self,
priv->attrs = attrs;
if (!priv->use_markup)
/* Clear the effective attributes so they will be regenerated when a
layout is created */
if (priv->effective_attrs)
{
if (attrs)
pango_attr_list_ref (attrs);
if (priv->effective_attrs)
pango_attr_list_unref (priv->effective_attrs);
priv->effective_attrs = attrs;
pango_attr_list_unref (priv->effective_attrs);
priv->effective_attrs = NULL;
}
else
clutter_text_set_markup_internal (self, priv->text);
clutter_text_dirty_cache (self);