diff options
Diffstat (limited to 'pango/pango-attributes.c')
-rw-r--r-- | pango/pango-attributes.c | 2264 |
1 files changed, 10 insertions, 2254 deletions
diff --git a/pango/pango-attributes.c b/pango/pango-attributes.c index 73fd5799..8e8e1746 100644 --- a/pango/pango-attributes.c +++ b/pango/pango-attributes.c @@ -23,346 +23,10 @@ #include <string.h> #include "pango-attributes.h" -#include "pango-attributes-private.h" +#include "pango-attr-private.h" #include "pango-impl-utils.h" - -/* {{{ Generic attribute code */ - -G_LOCK_DEFINE_STATIC (attr_type); -static GArray *attr_type; - -#define MIN_CUSTOM 1000 - -typedef struct _PangoAttrClass PangoAttrClass; - -struct _PangoAttrClass { - PangoAttrType type; - const char *name; - PangoAttrDataCopyFunc copy; - GDestroyNotify destroy; - GEqualFunc equal; - PangoAttrDataSerializeFunc serialize; -}; - -static const char * -get_attr_type_nick (PangoAttrType type) -{ - GEnumClass *enum_class; - GEnumValue *enum_value; - - enum_class = g_type_class_ref (pango_attr_type_get_type ()); - enum_value = g_enum_get_value (enum_class, type); - g_type_class_unref (enum_class); - - return enum_value->value_nick; -} - -/** - * pango_attr_type_register: - * @copy: function to copy the data of an attribute - * of this type - * @destroy: function to free the data of an attribute - * of this type - * @equal: function to compare the data of two attributes - * of this type - * @name: (nullable): an identifier for the type - * @serialize: (nullable): function to serialize the data - * of an attribute of this type - * - * Allocate a new attribute type ID. - * - * The attribute type name can be accessed later - * by using [func@Pango.AttrType.get_name]. - * - * If @name and to @serialize are provided, they will be used - * to serialize attributes of this type. - * - * Return value: the new type ID. - */ -PangoAttrType -pango_attr_type_register (PangoAttrDataCopyFunc copy, - GDestroyNotify destroy, - GEqualFunc equal, - const char *name, - PangoAttrDataSerializeFunc serialize) -{ - static guint current_id = MIN_CUSTOM; /* MT-safe */ - PangoAttrClass class; - - G_LOCK (attr_type); - - class.type = PANGO_ATTR_VALUE_POINTER | (current_id << 8); - current_id++; - - class.copy = copy; - class.destroy = destroy; - class.equal = equal; - class.serialize = serialize; - - if (name) - class.name = g_intern_string (name); - - if (attr_type == NULL) - attr_type = g_array_new (FALSE, FALSE, sizeof (PangoAttrClass)); - - g_array_append_val (attr_type, class); - - G_UNLOCK (attr_type); - - return class.type; -} - -/** - * pango_attr_type_get_name: - * @type: an attribute type ID to fetch the name for - * - * Fetches the attribute type name. - * - * The attribute type name is the string passed in - * when registering the type using - * [func@Pango.AttrType.register]. - * - * The returned value is an interned string (see - * g_intern_string() for what that means) that should - * not be modified or freed. - * - * Return value: (nullable): the type ID name (which - * may be %NULL), or %NULL if @type is a built-in Pango - * attribute type or invalid. - * - * Since: 1.22 - */ -const char * -pango_attr_type_get_name (PangoAttrType type) -{ - const char *result = NULL; - - if ((type >> 8) < MIN_CUSTOM) - return get_attr_type_nick (type); - - G_LOCK (attr_type); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == type) - { - result = class->name; - break; - } - } - - G_UNLOCK (attr_type); - - return result; -} - -/** - * pango_attribute_copy: - * @attr: a `PangoAttribute` - * - * Make a copy of an attribute. - * - * Return value: (transfer full): the newly allocated - * `PangoAttribute`, which should be freed with - * [method@Pango.Attribute.destroy]. - */ -PangoAttribute * -pango_attribute_copy (const PangoAttribute *attr) -{ - PangoAttribute *result; - - g_return_val_if_fail (attr != NULL, NULL); - - result = g_slice_dup (PangoAttribute, attr); - - switch (PANGO_ATTR_VALUE_TYPE (attr)) - { - case PANGO_ATTR_VALUE_STRING: - result->str_value = g_strdup (attr->str_value); - break; - - case PANGO_ATTR_VALUE_FONT_DESC: - result->font_value = pango_font_description_copy (attr->font_value); - break; - - case PANGO_ATTR_VALUE_POINTER: - { - PangoAttrDataCopyFunc copy = NULL; - - G_LOCK (attr_type); - - g_assert (attr_type != NULL); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr->type) - { - copy = class->copy; - break; - } - } - - G_UNLOCK (attr_type); - - g_assert (copy != NULL); - - result->pointer_value = copy (attr->pointer_value); - } - break; - - case PANGO_ATTR_VALUE_INT: - case PANGO_ATTR_VALUE_BOOLEAN: - case PANGO_ATTR_VALUE_FLOAT: - case PANGO_ATTR_VALUE_COLOR: - case PANGO_ATTR_VALUE_LANGUAGE: - default: ; - } - - return result; -} - -/** - * pango_attribute_destroy: - * @attr: a `PangoAttribute`. - * - * Destroy a `PangoAttribute` and free all associated memory. - */ -void -pango_attribute_destroy (PangoAttribute *attr) -{ - g_return_if_fail (attr != NULL); - - switch (PANGO_ATTR_VALUE_TYPE (attr)) - { - case PANGO_ATTR_VALUE_STRING: - g_free (attr->str_value); - break; - - case PANGO_ATTR_VALUE_FONT_DESC: - pango_font_description_free (attr->font_value); - break; - - case PANGO_ATTR_VALUE_POINTER: - { - GDestroyNotify destroy = NULL; - - G_LOCK (attr_type); - - g_assert (attr_type != NULL); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr->type) - { - destroy = class->destroy; - break; - } - } - - G_UNLOCK (attr_type); - - g_assert (destroy != NULL); - - destroy (attr->pointer_value); - } - break; - - case PANGO_ATTR_VALUE_INT: - case PANGO_ATTR_VALUE_BOOLEAN: - case PANGO_ATTR_VALUE_FLOAT: - case PANGO_ATTR_VALUE_COLOR: - case PANGO_ATTR_VALUE_LANGUAGE: - default: ; - } - - g_slice_free (PangoAttribute, attr); -} - -G_DEFINE_BOXED_TYPE (PangoAttribute, pango_attribute, - pango_attribute_copy, - pango_attribute_destroy); - -/** - * pango_attribute_equal: - * @attr1: a `PangoAttribute` - * @attr2: another `PangoAttribute` - * - * Compare two attributes for equality. - * - * This compares only the actual value of the two - * attributes and not the ranges that the attributes - * apply to. - * - * Return value: %TRUE if the two attributes have the same value - */ -gboolean -pango_attribute_equal (const PangoAttribute *attr1, - const PangoAttribute *attr2) -{ - g_return_val_if_fail (attr1 != NULL, FALSE); - g_return_val_if_fail (attr2 != NULL, FALSE); - - if (attr1->type != attr2->type) - return FALSE; - - switch (PANGO_ATTR_VALUE_TYPE (attr1)) - { - case PANGO_ATTR_VALUE_STRING: - return strcmp (attr1->str_value, attr2->str_value) == 0; - - case PANGO_ATTR_VALUE_INT: - return attr1->int_value == attr2->int_value; - - case PANGO_ATTR_VALUE_BOOLEAN: - return attr1->boolean_value == attr2->boolean_value; - - case PANGO_ATTR_VALUE_FLOAT: - return attr1->double_value == attr2->double_value; - - case PANGO_ATTR_VALUE_COLOR: - return memcmp (&attr1->color_value, &attr2->color_value, sizeof (PangoColor)) == 0; - - case PANGO_ATTR_VALUE_LANGUAGE: - return attr1->lang_value == attr2->lang_value; - - case PANGO_ATTR_VALUE_FONT_DESC: - return pango_font_description_equal (attr1->font_value, attr2->font_value); - - case PANGO_ATTR_VALUE_POINTER: - { - GEqualFunc equal = NULL; - - G_LOCK (attr_type); - - g_assert (attr_type != NULL); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr1->type) - { - equal = class->equal; - break; - } - } - - G_UNLOCK (attr_type); - - g_assert (equal != NULL); - - return equal (attr1->pointer_value, attr2->pointer_value); - } - - default: - g_assert_not_reached (); - } -} - -/* {{{ Builtin Attribute value types */ +/* {{{ Attribute value types */ static inline PangoAttribute * pango_attr_init (PangoAttrType type) @@ -377,7 +41,7 @@ pango_attr_init (PangoAttrType type) return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_string_new (PangoAttrType type, const char *value) { @@ -391,7 +55,7 @@ pango_attr_string_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_int_new (PangoAttrType type, int value) { @@ -405,7 +69,7 @@ pango_attr_int_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_boolean_new (PangoAttrType type, gboolean value) { @@ -419,7 +83,7 @@ pango_attr_boolean_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_float_new (PangoAttrType type, double value) { @@ -433,7 +97,7 @@ pango_attr_float_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_color_new (PangoAttrType type, guint16 red, guint16 green, @@ -451,7 +115,7 @@ pango_attr_color_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_lang_new (PangoAttrType type, PangoLanguage *value) { @@ -465,7 +129,7 @@ pango_attr_lang_new (PangoAttrType type, return attr; } -static PangoAttribute * +static inline PangoAttribute * pango_attr_font_description_new (PangoAttrType type, const PangoFontDescription *value) { @@ -478,39 +142,9 @@ pango_attr_font_description_new (PangoAttrType type, return attr; } -/* }}} */ -/* }}} */ -/* {{{ Private API */ - -char * -pango_attr_value_serialize (PangoAttribute *attr) -{ - PangoAttrDataSerializeFunc serialize = NULL; - - G_LOCK (attr_type); - - g_assert (attr_type != NULL); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *class = &g_array_index (attr_type, PangoAttrClass, i); - if (class->type == attr->type) - { - serialize = class->serialize; - break; - } - } - - G_UNLOCK (attr_type); - - if (serialize) - return serialize (attr->pointer_value); - - return NULL; -} /* }}} */ -/* {{{ Public API */ +/* {{{ Attribute types */ /** * pango_attr_family_new: @@ -1214,1884 +848,6 @@ pango_attr_text_transform_new (PangoTextTransform transform) return pango_attr_int_new (PANGO_ATTR_TEXT_TRANSFORM, transform); } -/** - * pango_attr_custom_new: - * @type: the attribute type - * @user_data: data for the attribute - * - * Creates a new attribute for the given type. - * - * The type must have been registered with [func@Pango.register_attr_type] - * before. @user_data will be copied with the copy function that - * was given when the type was registered. - * - * Return value: (transfer full): the newly allocated - * `PangoAttribute`, which should be freed with - * [method@Pango.Attribute.destroy] - */ -PangoAttribute * -pango_attr_custom_new (PangoAttrType type, - gpointer user_data) -{ - PangoAttrClass *class = NULL; - PangoAttribute *attr; - - g_return_val_if_fail (PANGO_ATTR_TYPE_VALUE_TYPE (type) == PANGO_ATTR_VALUE_POINTER, NULL); - - G_LOCK (attr_type); - - g_assert (attr_type != NULL); - - for (int i = 0; i < attr_type->len; i++) - { - PangoAttrClass *c = &g_array_index (attr_type, PangoAttrClass, i); - if (c->type == type) - { - class = c; - break; - } - } - - g_assert (class != NULL); - - G_UNLOCK (attr_type); - - attr = pango_attr_init (type); - attr->pointer_value = class->copy (user_data); - - return attr; -} - -/* }}} */ -/* {{{ Binding Helpers */ - -gboolean -pango_attribute_get_string (PangoAttribute *attribute, - const char **value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_STRING) - return FALSE; - - *value = attribute->str_value; - return TRUE; -} - -gboolean -pango_attribute_get_language (PangoAttribute *attribute, - PangoLanguage **value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_LANGUAGE) - return FALSE; - - *value = attribute->lang_value; - return TRUE; -} - -gboolean -pango_attribute_get_int (PangoAttribute *attribute, - int *value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_INT) - return FALSE; - - *value = attribute->int_value; - return TRUE; -} - -gboolean -pango_attribute_get_boolean (PangoAttribute *attribute, - int *value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_BOOLEAN) - return FALSE; - - *value = attribute->boolean_value; - return TRUE; -} - -gboolean -pango_attribute_get_float (PangoAttribute *attribute, - double *value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FLOAT) - return FALSE; - - *value = attribute->double_value; - return TRUE; -} - -gboolean -pango_attribute_get_color (PangoAttribute *attribute, - PangoColor *value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_COLOR) - return FALSE; - - *value = attribute->color_value; - return TRUE; -} - -gboolean -pango_attribute_get_font_desc (PangoAttribute *attribute, - PangoFontDescription **value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_FONT_DESC) - return FALSE; - - *value = attribute->font_value; - return TRUE; -} - -gboolean -pango_attribute_get_custom (PangoAttribute *attribute, - gpointer *value) -{ - if (PANGO_ATTR_VALUE_TYPE (attribute) != PANGO_ATTR_VALUE_POINTER) - return FALSE; - - *value = attribute->pointer_value; - return TRUE; -} - -/* }}} */ -/* {{{ Attribute List */ - -G_DEFINE_BOXED_TYPE (PangoAttrList, pango_attr_list, - pango_attr_list_copy, - pango_attr_list_unref); - -void -_pango_attr_list_init (PangoAttrList *list) -{ - list->ref_count = 1; - list->attributes = NULL; -} - -/** - * pango_attr_list_new: - * - * Create a new empty attribute list with a reference - * count of one. - * - * Return value: (transfer full): the newly allocated - * `PangoAttrList`, which should be freed with - * [method@Pango.AttrList.unref] - */ -PangoAttrList * -pango_attr_list_new (void) -{ - PangoAttrList *list = g_slice_new (PangoAttrList); - - _pango_attr_list_init (list); - - return list; -} - -/** - * pango_attr_list_ref: - * @list: (nullable): a `PangoAttrList` - * - * Increase the reference count of the given attribute - * list by one. - * - * Return value: The attribute list passed in - * - * Since: 1.10 - */ -PangoAttrList * -pango_attr_list_ref (PangoAttrList *list) -{ - if (list == NULL) - return NULL; - - g_atomic_int_inc ((int *) &list->ref_count); - - return list; -} - -void -_pango_attr_list_destroy (PangoAttrList *list) -{ -// guint i, p; - - if (!list->attributes) - return; - -#if 0 - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - //attr->klass->destroy (attr); - } -#endif - - g_ptr_array_free (list->attributes, TRUE); -} - -/** - * pango_attr_list_unref: - * @list: (nullable): a `PangoAttrList` - * - * Decrease the reference count of the given attribute - * list by one. - * - * If the result is zero, free the attribute list - * and the attributes it contains. - */ -void -pango_attr_list_unref (PangoAttrList *list) -{ - if (list == NULL) - return; - - g_return_if_fail (list->ref_count > 0); - - if (g_atomic_int_dec_and_test ((int *) &list->ref_count)) - { - _pango_attr_list_destroy (list); - g_slice_free (PangoAttrList, list); - } -} - -/** - * pango_attr_list_copy: - * @list: (nullable): a `PangoAttrList` - * - * Copy @list and return an identical new list. - * - * Return value: (nullable): the newly allocated - * `PangoAttrList`, with a reference count of one, - * which should be freed with [method@Pango.AttrList.unref]. - * Returns %NULL if @list was %NULL. - */ -PangoAttrList * -pango_attr_list_copy (PangoAttrList *list) -{ - PangoAttrList *new; - - if (list == NULL) - return NULL; - - new = pango_attr_list_new (); - if (!list->attributes || list->attributes->len == 0) - return new; - - new->attributes = g_ptr_array_copy (list->attributes, (GCopyFunc)pango_attribute_copy, NULL); - - return new; -} - -static void -pango_attr_list_insert_internal (PangoAttrList *list, - PangoAttribute *attr, - gboolean before) -{ - const guint start_index = attr->start_index; - PangoAttribute *last_attr; - - if (G_UNLIKELY (!list->attributes)) - list->attributes = g_ptr_array_new (); - - if (list->attributes->len == 0) - { - g_ptr_array_add (list->attributes, attr); - return; - } - - g_assert (list->attributes->len > 0); - - last_attr = g_ptr_array_index (list->attributes, list->attributes->len - 1); - - if (last_attr->start_index < start_index || - (!before && last_attr->start_index == start_index)) - { - g_ptr_array_add (list->attributes, attr); - } - else - { - guint i, p; - - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *cur = g_ptr_array_index (list->attributes, i); - - if (cur->start_index > start_index || - (before && cur->start_index == start_index)) - { - g_ptr_array_insert (list->attributes, i, attr); - break; - } - } - } -} - -/** - * pango_attr_list_insert: - * @list: a `PangoAttrList` - * @attr: (transfer full): the attribute to insert - * - * Insert the given attribute into the `PangoAttrList`. - * - * It will be inserted after all other attributes with a - * matching @start_index. - */ -void -pango_attr_list_insert (PangoAttrList *list, - PangoAttribute *attr) -{ - g_return_if_fail (list != NULL); - g_return_if_fail (attr != NULL); - - pango_attr_list_insert_internal (list, attr, FALSE); -} - -/** - * pango_attr_list_insert_before: - * @list: a `PangoAttrList` - * @attr: (transfer full): the attribute to insert - * - * Insert the given attribute into the `PangoAttrList`. - * - * It will be inserted before all other attributes with a - * matching @start_index. - */ -void -pango_attr_list_insert_before (PangoAttrList *list, - PangoAttribute *attr) -{ - g_return_if_fail (list != NULL); - g_return_if_fail (attr != NULL); - - pango_attr_list_insert_internal (list, attr, TRUE); -} - -/** - * pango_attr_list_change: - * @list: a `PangoAttrList` - * @attr: (transfer full): the attribute to insert - * - * Insert the given attribute into the `PangoAttrList`. - * - * It will replace any attributes of the same type - * on that segment and be merged with any adjoining - * attributes that are identical. - * - * This function is slower than [method@Pango.AttrList.insert] - * for creating an attribute list in order (potentially - * much slower for large lists). However, - * [method@Pango.AttrList.insert] is not suitable for - * continually changing a set of attributes since it - * never removes or combines existing attributes. - */ -void -pango_attr_list_change (PangoAttrList *list, - PangoAttribute *attr) -{ - guint i, p; - guint start_index = attr->start_index; - guint end_index = attr->end_index; - gboolean inserted; - - g_return_if_fail (list != NULL); - - if (start_index == end_index) /* empty, nothing to do */ - { - pango_attribute_destroy (attr); - return; - } - - if (!list->attributes || list->attributes->len == 0) - { - pango_attr_list_insert (list, attr); - return; - } - - inserted = FALSE; - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i); - - if (tmp_attr->start_index > start_index) - { - g_ptr_array_insert (list->attributes, i, attr); - inserted = TRUE; - break; - } - - if (tmp_attr->type != attr->type) - continue; - - if (tmp_attr->end_index < start_index) - continue; /* This attr does not overlap with the new one */ - - g_assert (tmp_attr->start_index <= start_index); - g_assert (tmp_attr->end_index >= start_index); - - if (pango_attribute_equal (tmp_attr, attr)) - { - /* We can merge the new attribute with this attribute - */ - if (tmp_attr->end_index >= end_index) - { - /* We are totally overlapping the previous attribute. - * No action is needed. - */ - pango_attribute_destroy (attr); - return; - } - - tmp_attr->end_index = end_index; - pango_attribute_destroy (attr); - - attr = tmp_attr; - inserted = TRUE; - break; - } - else - { - /* Split, truncate, or remove the old attribute - */ - if (tmp_attr->end_index > end_index) - { - PangoAttribute *end_attr = pango_attribute_copy (tmp_attr); - - end_attr->start_index = end_index; - pango_attr_list_insert (list, end_attr); - } - - if (tmp_attr->start_index == start_index) - { - pango_attribute_destroy (tmp_attr); - g_ptr_array_remove_index (list->attributes, i); - break; - } - else - { - tmp_attr->end_index = start_index; - } - } - } - - if (!inserted) - /* we didn't insert attr yet */ - pango_attr_list_insert (list, attr); - - /* We now have the range inserted into the list one way or the - * other. Fix up the remainder - */ - /* Attention: No i = 0 here. */ - for (i = i + 1, p = list->attributes->len; i < p; i++) - { - PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i); - - if (tmp_attr->start_index > end_index) - break; - - if (tmp_attr->type != attr->type) - continue; - - if (tmp_attr->end_index <= attr->end_index || - pango_attribute_equal (tmp_attr, attr)) - { - /* We can merge the new attribute with this attribute. */ - attr->end_index = MAX (end_index, tmp_attr->end_index); - pango_attribute_destroy (tmp_attr); - g_ptr_array_remove_index (list->attributes, i); - i--; - p--; - continue; - } - else - { - /* Trim the start of this attribute that it begins at the end - * of the new attribute. This may involve moving it in the list - * to maintain the required non-decreasing order of start indices. - */ - int k, m; - - tmp_attr->start_index = attr->end_index; - - for (k = i + 1, m = list->attributes->len; k < m; k++) - { - PangoAttribute *tmp_attr2 = g_ptr_array_index (list->attributes, k); - - if (tmp_attr2->start_index >= tmp_attr->start_index) - break; - } - } - } -} - -/** - * pango_attr_list_update: - * @list: a `PangoAttrList` - * @pos: the position of the change - * @remove: the number of removed bytes - * @add: the number of added bytes - * - * Update indices of attributes in @list for a change in the - * text they refer to. - * - * The change that this function applies is removing @remove - * bytes at position @pos and inserting @add bytes instead. - * - * Attributes that fall entirely in the (@pos, @pos + @remove) - * range are removed. - * - * Attributes that start or end inside the (@pos, @pos + @remove) - * range are shortened to reflect the removal. - * - * Attributes start and end positions are updated if they are - * behind @pos + @remove. - * - * Since: 1.44 - */ -void -pango_attr_list_update (PangoAttrList *list, - int pos, - int remove, - int add) -{ - guint i, p; - - g_return_if_fail (pos >= 0); - g_return_if_fail (remove >= 0); - g_return_if_fail (add >= 0); - - if (list->attributes) - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - - if (attr->start_index >= pos && - attr->end_index < pos + remove) - { - pango_attribute_destroy (attr); - g_ptr_array_remove_index (list->attributes, i); - i--; /* Look at this index again */ - p--; - continue; - } - - if (attr->start_index != PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING) - { - if (attr->start_index >= pos && - attr->start_index < pos + remove) - { - attr->start_index = pos + add; - } - else if (attr->start_index >= pos + remove) - { - attr->start_index += add - remove; - } - } - - if (attr->end_index != PANGO_ATTR_INDEX_TO_TEXT_END) - { - if (attr->end_index >= pos && - attr->end_index < pos + remove) - { - attr->end_index = pos; - } - else if (attr->end_index >= pos + remove) - { - if (add > remove && - G_MAXUINT - attr->end_index < add - remove) - attr->end_index = G_MAXUINT; - else - attr->end_index += add - remove; - } - } - } -} - -/** - * pango_attr_list_splice: - * @list: a `PangoAttrList` - * @other: another `PangoAttrList` - * @pos: the position in @list at which to insert @other - * @len: the length of the spliced segment. (Note that this - * must be specified since the attributes in @other may only - * be present at some subsection of this range) - * - * This function opens up a hole in @list, fills it - * in with attributes from the left, and then merges - * @other on top of the hole. - * - * This operation is equivalent to stretching every attribute - * that applies at position @pos in @list by an amount @len, - * and then calling [method@Pango.AttrList.change] with a copy - * of each attribute in @other in sequence (offset in position - * by @pos, and limited in length to @len). - * - * This operation proves useful for, for instance, inserting - * a pre-edit string in the middle of an edit buffer. - * - * For backwards compatibility, the function behaves differently - * when @len is 0. In this case, the attributes from @other are - * not imited to @len, and are just overlayed on top of @list. - * - * This mode is useful for merging two lists of attributes together. - */ -void -pango_attr_list_splice (PangoAttrList *list, - PangoAttrList *other, - gint pos, - gint len) -{ - guint i, p; - guint upos, ulen; - guint end; - - g_return_if_fail (list != NULL); - g_return_if_fail (other != NULL); - g_return_if_fail (pos >= 0); - g_return_if_fail (len >= 0); - - upos = (guint)pos; - ulen = (guint)len; - -/* This definition only works when a and b are unsigned; overflow - * isn't defined in the C standard for signed integers - */ -#define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b)) - - end = CLAMP_ADD (upos, ulen); - - if (list->attributes) - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i);; - - if (attr->start_index <= upos) - { - if (attr->end_index > upos) - attr->end_index = CLAMP_ADD (attr->end_index, ulen); - } - else - { - /* This could result in a zero length attribute if it - * gets squashed up against G_MAXUINT, but deleting such - * an element could (in theory) suprise the caller, so - * we don't delete it. - */ - attr->start_index = CLAMP_ADD (attr->start_index, ulen); - attr->end_index = CLAMP_ADD (attr->end_index, ulen); - } - } - - if (!other->attributes || other->attributes->len == 0) - return; - - for (i = 0, p = other->attributes->len; i < p; i++) - { - PangoAttribute *attr = pango_attribute_copy (g_ptr_array_index (other->attributes, i)); - if (ulen > 0) - { - attr->start_index = MIN (CLAMP_ADD (attr->start_index, upos), end); - attr->end_index = MIN (CLAMP_ADD (attr->end_index, upos), end); - } - else - { - attr->start_index = CLAMP_ADD (attr->start_index, upos); - attr->end_index = CLAMP_ADD (attr->end_index, upos); - } - - /* Same as above, the attribute could be squashed to zero-length; here - * pango_attr_list_change() will take care of deleting it. - */ - pango_attr_list_change (list, attr); - } -#undef CLAMP_ADD -} - -/** - * pango_attr_list_get_attributes: - * @list: a `PangoAttrList` - * - * Gets a list of all attributes in @list. - * - * Return value: (element-type Pango.Attribute) (transfer full): - * a list of all attributes in @list. To free this value, - * call [method@Pango.Attribute.destroy] on each value and - * g_slist_free() on the list. - * - * Since: 1.44 - */ -GSList * -pango_attr_list_get_attributes (PangoAttrList *list) -{ - GSList *result = NULL; - guint i, p; - - g_return_val_if_fail (list != NULL, NULL); - - if (!list->attributes || list->attributes->len == 0) - return NULL; - - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - - result = g_slist_prepend (result, pango_attribute_copy (attr)); - } - - return g_slist_reverse (result); -} - -/** - * pango_attr_list_equal: - * @list: a `PangoAttrList` - * @other_list: the other `PangoAttrList` - * - * Checks whether @list and @other_list contain the same - * attributes and whether those attributes apply to the - * same ranges. - * - * Beware that this will return wrong values if any list - * contains duplicates. - * - * Return value: %TRUE if the lists are equal, %FALSE if - * they aren't - * - * Since: 1.46 - */ -gboolean -pango_attr_list_equal (PangoAttrList *list, - PangoAttrList *other_list) -{ - GPtrArray *attrs, *other_attrs; - guint64 skip_bitmask = 0; - guint i; - - if (list == other_list) - return TRUE; - - if (list == NULL || other_list == NULL) - return FALSE; - - if (list->attributes == NULL || other_list->attributes == NULL) - return list->attributes == other_list->attributes; - - attrs = list->attributes; - other_attrs = other_list->attributes; - - if (attrs->len != other_attrs->len) - return FALSE; - - for (i = 0; i < attrs->len; i++) - { - PangoAttribute *attr = g_ptr_array_index (attrs, i); - gboolean attr_equal = FALSE; - guint other_attr_index; - - for (other_attr_index = 0; other_attr_index < other_attrs->len; other_attr_index++) - { - PangoAttribute *other_attr = g_ptr_array_index (other_attrs, other_attr_index); - guint64 other_attr_bitmask = other_attr_index < 64 ? 1 << other_attr_index : 0; - - if ((skip_bitmask & other_attr_bitmask) != 0) - continue; - - if (attr->start_index == other_attr->start_index && - attr->end_index == other_attr->end_index && - pango_attribute_equal (attr, other_attr)) - { - skip_bitmask |= other_attr_bitmask; - attr_equal = TRUE; - break; - } - - } - - if (!attr_equal) - return FALSE; - } - - return TRUE; -} - -gboolean -_pango_attr_list_has_attributes (const PangoAttrList *list) -{ - return list && list->attributes != NULL && list->attributes->len > 0; -} - -/** - * pango_attr_list_filter: - * @list: a `PangoAttrList` - * @func: (scope call) (closure data): callback function; - * returns %TRUE if an attribute should be filtered out - * @data: (closure): Data to be passed to @func - * - * Given a `PangoAttrList` and callback function, removes - * any elements of @list for which @func returns %TRUE and - * inserts them into a new list. - * - * Return value: (transfer full) (nullable): the new - * `PangoAttrList` or %NULL if no attributes of the - * given types were found - * - * Since: 1.2 - */ -PangoAttrList * -pango_attr_list_filter (PangoAttrList *list, - PangoAttrFilterFunc func, - gpointer data) - -{ - PangoAttrList *new = NULL; - guint i, p; - - g_return_val_if_fail (list != NULL, NULL); - - if (!list->attributes || list->attributes->len == 0) - return NULL; - - for (i = 0, p = list->attributes->len; i < p; i++) - { - PangoAttribute *tmp_attr = g_ptr_array_index (list->attributes, i); - - if ((*func) (tmp_attr, data)) - { - g_ptr_array_remove_index (list->attributes, i); - i--; /* Need to look at this index again */ - p--; - - if (G_UNLIKELY (!new)) - { - new = pango_attr_list_new (); - new->attributes = g_ptr_array_new (); - } - - g_ptr_array_add (new->attributes, tmp_attr); - } - } - - return new; -} - -/* {{{ PangoAttrList serialization */ - -/* We serialize attribute lists to strings. The format - * is a comma-separated list of the attributes in the order - * in which they are in the list, with each attribute having - * this format: - * - * START END NICK VALUE - * - * Values that can contain a comma, such as font descriptions - * are quoted with "". - */ - -static GType -get_attr_value_type (PangoAttrType type) -{ - switch ((int)type) - { - case PANGO_ATTR_STYLE: return PANGO_TYPE_STYLE; - case PANGO_ATTR_WEIGHT: return PANGO_TYPE_WEIGHT; - case PANGO_ATTR_VARIANT: return PANGO_TYPE_VARIANT; - case PANGO_ATTR_STRETCH: return PANGO_TYPE_STRETCH; - case PANGO_ATTR_GRAVITY: return PANGO_TYPE_GRAVITY; - case PANGO_ATTR_GRAVITY_HINT: return PANGO_TYPE_GRAVITY_HINT; - case PANGO_ATTR_UNDERLINE: return PANGO_TYPE_UNDERLINE; - case PANGO_ATTR_OVERLINE: return PANGO_TYPE_OVERLINE; - case PANGO_ATTR_BASELINE_SHIFT: return PANGO_TYPE_BASELINE_SHIFT; - case PANGO_ATTR_FONT_SCALE: return PANGO_TYPE_FONT_SCALE; - case PANGO_ATTR_TEXT_TRANSFORM: return PANGO_TYPE_TEXT_TRANSFORM; - default: return G_TYPE_INVALID; - } -} - -static void -append_enum_value (GString *str, - GType type, - int value) -{ - GEnumClass *enum_class; - GEnumValue *enum_value; - - enum_class = g_type_class_ref (type); - enum_value = g_enum_get_value (enum_class, value); - g_type_class_unref (enum_class); - - if (enum_value) - g_string_append_printf (str, " %s", enum_value->value_nick); - else - g_string_append_printf (str, " %d", value); -} - -static void -attr_print (GString *str, - PangoAttribute *attr) -{ - const char *name; - - name = pango_attr_type_get_name (attr->type); - if (!name) - return; - - g_string_append_printf (str, "%u %u %s", attr->start_index, attr->end_index, name); - - switch (PANGO_ATTR_VALUE_TYPE (attr)) - { - case PANGO_ATTR_VALUE_INT: - if (attr->type == PANGO_ATTR_WEIGHT || - attr->type == PANGO_ATTR_STYLE || - attr->type == PANGO_ATTR_STRETCH || - attr->type == PANGO_ATTR_VARIANT || - attr->type == PANGO_ATTR_GRAVITY || - attr->type == PANGO_ATTR_GRAVITY_HINT || - attr->type == PANGO_ATTR_UNDERLINE || - attr->type == PANGO_ATTR_OVERLINE || - attr->type == PANGO_ATTR_BASELINE_SHIFT || - attr->type == PANGO_ATTR_FONT_SCALE || - attr->type == PANGO_ATTR_TEXT_TRANSFORM) - append_enum_value (str, get_attr_value_type (attr->type), attr->int_value); - else - g_string_append_printf (str, " %d", attr->int_value); - break; - - case PANGO_ATTR_VALUE_BOOLEAN: - g_string_append (str, attr->int_value ? " true" : " false"); - break; - - case PANGO_ATTR_VALUE_STRING: - g_string_append_printf (str, " \"%s\"", attr->str_value); - break; - - case PANGO_ATTR_VALUE_LANGUAGE: - g_string_append_printf (str, " %s", pango_language_to_string (attr->lang_value)); - break; - - case PANGO_ATTR_VALUE_FLOAT: - { - char buf[20]; - g_ascii_formatd (buf, 20, "%f", attr->double_value); - g_string_append_printf (str, " %s", buf); - } - break; - - case PANGO_ATTR_VALUE_FONT_DESC: - { - char *s = pango_font_description_to_string (attr->font_value); - g_string_append_printf (str, " \"%s\"", s); - g_free (s); - } - break; - - case PANGO_ATTR_VALUE_COLOR: - { - char *s = pango_color_to_string (&attr->color_value); - g_string_append_printf (str, " %s", s); - g_free (s); - } - break; - - case PANGO_ATTR_VALUE_POINTER: - { - char *s = pango_attr_value_serialize (attr); - g_string_append_printf (str, " %s", s); - g_free (s); - } - break; - - default: - g_assert_not_reached (); - } -} - -/** - * pango_attr_list_to_string: - * @list: a `PangoAttrList` - * - * Serializes a `PangoAttrList` to a string. - * - * No guarantees are made about the format of the string, - * it may change between Pango versions. - * - * The intended use of this function is testing and - * debugging. The format is not meant as a permanent - * storage format. - * - * Returns: (transfer full): a newly allocated string - * Since: 1.50 - */ -char * -pango_attr_list_to_string (PangoAttrList *list) -{ - GString *s; - - s = g_string_new (""); - - if (list->attributes) - for (int i = 0; i < list->attributes->len; i++) - { - PangoAttribute *attr = g_ptr_array_index (list->attributes, i); - - if (i > 0) - g_string_append (s, "\n"); - attr_print (s, attr); - } - - return g_string_free (s, FALSE); -} - -static PangoAttrType -get_attr_type_by_nick (const char *nick, - int len) -{ - GEnumClass *enum_class; - - enum_class = g_type_class_ref (pango_attr_type_get_type ()); - for (GEnumValue *ev = enum_class->values; ev->value_name; ev++) - { - if (ev->value_nick && strncmp (ev->value_nick, nick, len) == 0) - { - g_type_class_unref (enum_class); - return (PangoAttrType) ev->value; - } - } - - g_type_class_unref (enum_class); - return PANGO_ATTR_INVALID; -} - -static int -get_attr_value (PangoAttrType type, - const char *str, - int len) -{ - GEnumClass *enum_class; - char *endp; - int value; - - enum_class = g_type_class_ref (get_attr_value_type (type)); - for (GEnumValue *ev = enum_class->values; ev->value_name; ev++) - { - if (ev->value_nick && strncmp (ev->value_nick, str, len) == 0) - { - g_type_class_unref (enum_class); - return ev->value; - } - } - g_type_class_unref (enum_class); - - value = g_ascii_strtoll (str, &endp, 10); - if (endp - str == len) - return value; - - return -1; -} - -static gboolean -is_valid_end_char (char c) -{ - return c == ',' || c == '\n' || c == '\0'; -} - -/** - * pango_attr_list_from_string: - * @text: a string - * - * Deserializes a `PangoAttrList` from a string. - * - * This is the counterpart to [method@Pango.AttrList.to_string]. - * See that functions for details about the format. - * - * Returns: (transfer full) (nullable): a new `PangoAttrList` - * Since: 1.50 - */ -PangoAttrList * -pango_attr_list_from_string (const char *text) -{ - PangoAttrList *list; - const char *p; - - g_return_val_if_fail (text != NULL, NULL); - - list = pango_attr_list_new (); - - if (*text == '\0') - return list; - - list->attributes = g_ptr_array_new (); - - p = text + strspn (text, " \t\n"); - while (*p) - { - char *endp; - gint64 start_index; - gint64 end_index; - char *str; - PangoAttrType attr_type; - PangoAttribute *attr; - PangoLanguage *lang; - gint64 integer; - PangoFontDescription *desc; - PangoColor color; - double num; - - start_index = g_ascii_strtoll (p, &endp, 10); - if (*endp != ' ') - goto fail; - - p = endp + strspn (endp, " "); - if (!*p) - goto fail; - - end_index = g_ascii_strtoll (p, &endp, 10); - if (*endp != ' ') - goto fail; - - p = endp + strspn (endp, " "); - - endp = (char *)p + strcspn (p, " "); - attr_type = get_attr_type_by_nick (p, endp - p); - - p = endp + strspn (endp, " "); - if (*p == '\0') - goto fail; - -#define INT_ATTR(name,type) \ - integer = g_ascii_strtoll (p, &endp, 10); \ - if (!is_valid_end_char (*endp)) goto fail; \ - attr = pango_attr_##name##_new ((type)integer); - -#define BOOLEAN_ATTR(name,type) \ - if (strncmp (p, "true", strlen ("true")) == 0) \ - { \ - integer = 1; \ - endp = (char *)(p + strlen ("true")); \ - } \ - else if (strncmp (p, "false", strlen ("false")) == 0) \ - { \ - integer = 0; \ - endp = (char *)(p + strlen ("false")); \ - } \ - else \ - integer = g_ascii_strtoll (p, &endp, 10); \ - if (!is_valid_end_char (*endp)) goto fail; \ - attr = pango_attr_##name##_new ((type)integer); - -#define ENUM_ATTR(name, type, min, max) \ - endp = (char *)p + strcspn (p, ",\n"); \ - integer = get_attr_value (attr_type, p, endp - p); \ - attr = pango_attr_##name##_new ((type) CLAMP (integer, min, max)); - -#define FLOAT_ATTR(name) \ - num = g_ascii_strtod (p, &endp); \ - if (!is_valid_end_char (*endp)) goto fail; \ - attr = pango_attr_##name##_new ((float)num); - -#define COLOR_ATTR(name) \ - endp = (char *)p + strcspn (p, ",\n"); \ - if (!is_valid_end_char (*endp)) goto fail; \ - str = g_strndup (p, endp - p); \ - if (!pango_color_parse (&color, str)) \ - { \ - g_free (str); \ - goto fail; \ - } \ - attr = pango_attr_##name##_new (color.red, color.green, color.blue); \ - g_free (str); - - switch (attr_type) - { - case PANGO_ATTR_INVALID: - pango_attr_list_unref (list); - return NULL; - - case PANGO_ATTR_LANGUAGE: - endp = (char *)p + strcspn (p, ",\n"); - if (!is_valid_end_char (*endp)) goto fail; - str = g_strndup (p, endp - p); - lang = pango_language_from_string (str); - attr = pango_attr_language_new (lang); - g_free (str); - break; - - case PANGO_ATTR_FAMILY: - p++; - endp = strchr (p, '"'); - if (!endp) goto fail; - str = g_strndup (p, endp - p); - attr = pango_attr_family_new (str); - g_free (str); - endp++; - if (!is_valid_end_char (*endp)) goto fail; - break; - - case PANGO_ATTR_STYLE: - ENUM_ATTR(style, PangoStyle, PANGO_STYLE_NORMAL, PANGO_STYLE_ITALIC); - break; - - case PANGO_ATTR_WEIGHT: - ENUM_ATTR(weight, PangoWeight, PANGO_WEIGHT_THIN, PANGO_WEIGHT_ULTRAHEAVY); - break; - - case PANGO_ATTR_VARIANT: - ENUM_ATTR(variant, PangoVariant, PANGO_VARIANT_NORMAL, PANGO_VARIANT_TITLE_CAPS); - break; - - case PANGO_ATTR_STRETCH: - ENUM_ATTR(stretch, PangoStretch, PANGO_STRETCH_ULTRA_CONDENSED, PANGO_STRETCH_ULTRA_EXPANDED); - break; - - case PANGO_ATTR_SIZE: - INT_ATTR(size, int); - break; - - case PANGO_ATTR_FONT_DESC: - p++; - endp = strchr (p, '"'); - if (!endp) goto fail; - str = g_strndup (p, endp - p); - desc = pango_font_description_from_string (str); - attr = pango_attr_font_desc_new (desc); - pango_font_description_free (desc); - g_free (str); - endp++; - if (!is_valid_end_char (*endp)) goto fail; - break; - - case PANGO_ATTR_FOREGROUND: - COLOR_ATTR(foreground); - break; - - case PANGO_ATTR_BACKGROUND: - COLOR_ATTR(background); - break; - - case PANGO_ATTR_UNDERLINE: - ENUM_ATTR(underline, PangoUnderline, PANGO_UNDERLINE_NONE, PANGO_UNDERLINE_ERROR_LINE); - break; - - case PANGO_ATTR_STRIKETHROUGH: - BOOLEAN_ATTR(strikethrough, gboolean); - break; - - case PANGO_ATTR_RISE: - INT_ATTR(rise, int); - break; - - case PANGO_ATTR_SCALE: - FLOAT_ATTR(scale); - break; - - case PANGO_ATTR_FALLBACK: - BOOLEAN_ATTR(fallback, gboolean); - break; - - case PANGO_ATTR_LETTER_SPACING: - INT_ATTR(letter_spacing, int); - break; - - case PANGO_ATTR_UNDERLINE_COLOR: - COLOR_ATTR(underline_color); - break; - - case PANGO_ATTR_STRIKETHROUGH_COLOR: - COLOR_ATTR(strikethrough_color); - break; - - case PANGO_ATTR_ABSOLUTE_SIZE: - integer = g_ascii_strtoll (p, &endp, 10); - if (!is_valid_end_char (*endp)) goto fail; - attr = pango_attr_size_new_absolute (integer); - break; - - case PANGO_ATTR_GRAVITY: - ENUM_ATTR(gravity, PangoGravity, PANGO_GRAVITY_SOUTH, PANGO_GRAVITY_WEST); - break; - - case PANGO_ATTR_FONT_FEATURES: - p++; - endp = strchr (p, '"'); - if (!endp) goto fail; - str = g_strndup (p, endp - p); - attr = pango_attr_font_features_new (str); - g_free (str); - endp++; - if (!is_valid_end_char (*endp)) goto fail; - break; - - case PANGO_ATTR_GRAVITY_HINT: - ENUM_ATTR(gravity_hint, PangoGravityHint, PANGO_GRAVITY_HINT_NATURAL, PANGO_GRAVITY_HINT_LINE); - break; - - case PANGO_ATTR_FOREGROUND_ALPHA: - INT_ATTR(foreground_alpha, int); - break; - - case PANGO_ATTR_BACKGROUND_ALPHA: - INT_ATTR(background_alpha, int); - break; - - case PANGO_ATTR_ALLOW_BREAKS: - BOOLEAN_ATTR(allow_breaks, gboolean); - break; - - case PANGO_ATTR_SHOW: - INT_ATTR(show, PangoShowFlags); - break; - - case PANGO_ATTR_INSERT_HYPHENS: - BOOLEAN_ATTR(insert_hyphens, gboolean); - break; - - case PANGO_ATTR_OVERLINE: - ENUM_ATTR(overline, PangoOverline, PANGO_OVERLINE_NONE, PANGO_OVERLINE_SINGLE); - break; - - case PANGO_ATTR_OVERLINE_COLOR: - COLOR_ATTR(overline_color); - break; - - case PANGO_ATTR_LINE_HEIGHT: - FLOAT_ATTR(line_height); - break; - - case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT: - integer = g_ascii_strtoll (p, &endp, 10); - if (!is_valid_end_char (*endp)) goto fail; - attr = pango_attr_line_height_new_absolute (integer); - break; - - case PANGO_ATTR_TEXT_TRANSFORM: - ENUM_ATTR(text_transform, PangoTextTransform, PANGO_TEXT_TRANSFORM_NONE, PANGO_TEXT_TRANSFORM_CAPITALIZE); - break; - - case PANGO_ATTR_WORD: - integer = g_ascii_strtoll (p, &endp, 10); - if (!is_valid_end_char (*endp)) goto fail; - attr = pango_attr_word_new (); - break; - - case PANGO_ATTR_SENTENCE: - integer = g_ascii_strtoll (p, &endp, 10); - if (!is_valid_end_char (*endp)) goto fail; - attr = pango_attr_sentence_new (); - break; - - case PANGO_ATTR_BASELINE_SHIFT: - ENUM_ATTR(baseline_shift, PangoBaselineShift, 0, G_MAXINT); - break; - - case PANGO_ATTR_FONT_SCALE: - ENUM_ATTR(font_scale, PangoFontScale, PANGO_FONT_SCALE_NONE, PANGO_FONT_SCALE_SMALL_CAPS); - break; - - default: - g_assert_not_reached (); - } - - attr->start_index = (guint)start_index; - attr->end_index = (guint)end_index; - g_ptr_array_add (list->attributes, attr); - - p = endp; - if (*p) - { - if (*p == ',') - p++; - p += strspn (p, " \n"); - } - } - - goto success; - -fail: - pango_attr_list_unref (list); - list = NULL; - -success: - return list; -} - -/* }}} */ -/* {{{ Attribute Iterator */ - -G_DEFINE_BOXED_TYPE (PangoAttrIterator, - pango_attr_iterator, - pango_attr_iterator_copy, - pango_attr_iterator_destroy) - -void -_pango_attr_list_get_iterator (PangoAttrList *list, - PangoAttrIterator *iterator) -{ - iterator->attribute_stack = NULL; - iterator->attrs = list->attributes; - iterator->n_attrs = iterator->attrs ? iterator->attrs->len : 0; - - iterator->attr_index = 0; - iterator->start_index = 0; - iterator->end_index = 0; - - if (!pango_attr_iterator_next (iterator)) - iterator->end_index = G_MAXUINT; -} - -/** - * pango_attr_list_get_iterator: - * @list: a `PangoAttrList` - * - * Create a iterator initialized to the beginning of the list. - * - * @list must not be modified until this iterator is freed. - * - * Return value: (transfer full): the newly allocated - * `PangoAttrIterator`, which should be freed with - * [method@Pango.AttrIterator.destroy] - */ -PangoAttrIterator * -pango_attr_list_get_iterator (PangoAttrList *list) -{ - PangoAttrIterator *iterator; - - g_return_val_if_fail (list != NULL, NULL); - - iterator = g_slice_new (PangoAttrIterator); - _pango_attr_list_get_iterator (list, iterator); - - return iterator; -} - -/** - * pango_attr_iterator_range: - * @iterator: a PangoAttrIterator - * @start: (out): location to store the start of the range - * @end: (out): location to store the end of the range - * - * Get the range of the current segment. - * - * Note that the stored return values are signed, not unsigned - * like the values in `PangoAttribute`. To deal with this API - * oversight, stored return values that wouldn't fit into - * a signed integer are clamped to %G_MAXINT. - */ -void -pango_attr_iterator_range (PangoAttrIterator *iterator, - gint *start, - gint *end) -{ - g_return_if_fail (iterator != NULL); - - if (start) - *start = MIN (iterator->start_index, G_MAXINT); - if (end) - *end = MIN (iterator->end_index, G_MAXINT); -} - -/** - * pango_attr_iterator_next: - * @iterator: a `PangoAttrIterator` - * - * Advance the iterator until the next change of style. - * - * Return value: %FALSE if the iterator is at the end - * of the list, otherwise %TRUE - */ -gboolean -pango_attr_iterator_next (PangoAttrIterator *iterator) -{ - int i; - - g_return_val_if_fail (iterator != NULL, FALSE); - - if (iterator->attr_index >= iterator->n_attrs && - (!iterator->attribute_stack || iterator->attribute_stack->len == 0)) - return FALSE; - - iterator->start_index = iterator->end_index; - iterator->end_index = G_MAXUINT; - - if (iterator->attribute_stack) - { - for (i = iterator->attribute_stack->len - 1; i>= 0; i--) - { - const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); - - if (attr->end_index == iterator->start_index) - g_ptr_array_remove_index (iterator->attribute_stack, i); /* Can't use index_fast :( */ - else - iterator->end_index = MIN (iterator->end_index, attr->end_index); - } - } - - while (1) - { - PangoAttribute *attr; - - if (iterator->attr_index >= iterator->n_attrs) - break; - - attr = g_ptr_array_index (iterator->attrs, iterator->attr_index); - - if (attr->start_index != iterator->start_index) - break; - - if (attr->end_index > iterator->start_index) - { - if (G_UNLIKELY (!iterator->attribute_stack)) - iterator->attribute_stack = g_ptr_array_new (); - - g_ptr_array_add (iterator->attribute_stack, attr); - - iterator->end_index = MIN (iterator->end_index, attr->end_index); - } - - iterator->attr_index++; /* NEXT! */ - } - - if (iterator->attr_index < iterator->n_attrs) - { - PangoAttribute *attr = g_ptr_array_index (iterator->attrs, iterator->attr_index); - - iterator->end_index = MIN (iterator->end_index, attr->start_index); - } - - return TRUE; -} - -/** - * pango_attr_iterator_copy: - * @iterator: a `PangoAttrIterator` - * - * Copy a `PangoAttrIterator`. - * - * Return value: (transfer full): the newly allocated - * `PangoAttrIterator`, which should be freed with - * [method@Pango.AttrIterator.destroy] - */ -PangoAttrIterator * -pango_attr_iterator_copy (PangoAttrIterator *iterator) -{ - PangoAttrIterator *copy; - - g_return_val_if_fail (iterator != NULL, NULL); - - copy = g_slice_new (PangoAttrIterator); - - *copy = *iterator; - - if (iterator->attribute_stack) - copy->attribute_stack = g_ptr_array_copy (iterator->attribute_stack, NULL, NULL); - else - copy->attribute_stack = NULL; - - return copy; -} - -void -_pango_attr_iterator_destroy (PangoAttrIterator *iterator) -{ - if (iterator->attribute_stack) - g_ptr_array_free (iterator->attribute_stack, TRUE); -} - -/** - * pango_attr_iterator_destroy: - * @iterator: a `PangoAttrIterator` - * - * Destroy a `PangoAttrIterator` and free all associated memory. - */ -void -pango_attr_iterator_destroy (PangoAttrIterator *iterator) -{ - g_return_if_fail (iterator != NULL); - - _pango_attr_iterator_destroy (iterator); - g_slice_free (PangoAttrIterator, iterator); -} - -/** - * pango_attr_iterator_get: - * @iterator: a `PangoAttrIterator` - * @type: the type of attribute to find - * - * Find the current attribute of a particular type - * at the iterator location. - * - * When multiple attributes of the same type overlap, - * the attribute whose range starts closest to the - * current location is used. - * - * Return value: (nullable) (transfer none): the current - * attribute of the given type, or %NULL if no attribute - * of that type applies to the current location. - */ -PangoAttribute * -pango_attr_iterator_get (PangoAttrIterator *iterator, - PangoAttrType type) -{ - int i; - - g_return_val_if_fail (iterator != NULL, NULL); - - if (!iterator->attribute_stack) - return NULL; - - for (i = iterator->attribute_stack->len - 1; i>= 0; i--) - { - PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); - - if (attr->type == type) - return attr; - } - - return NULL; -} - -/** - * pango_attr_iterator_get_font: - * @iterator: a `PangoAttrIterator` - * @desc: a `PangoFontDescription` to fill in with the current - * values. The family name in this structure will be set using - * [method@Pango.FontDescription.set_family_static] using - * values from an attribute in the `PangoAttrList` associated - * with the iterator, so if you plan to keep it around, you - * must call: - * `pango_font_description_set_family (desc, pango_font_description_get_family (desc))`. - * @language: (out) (optional): location to store language tag - * for item, or %NULL if none is found. - * @extra_attrs: (out) (optional) (element-type Pango.Attribute) (transfer full): - * location in which to store a list of non-font attributes - * at the the current position; only the highest priority - * value of each attribute will be added to this list. In - * order to free this value, you must call - * [method@Pango.Attribute.destroy] on each member. - * - * Get the font and other attributes at the current - * iterator position. - */ -void -pango_attr_iterator_get_font (PangoAttrIterator *iterator, - PangoFontDescription *desc, - PangoLanguage **language, - GSList **extra_attrs) -{ - PangoFontMask mask = 0; - gboolean have_language = FALSE; - gdouble scale = 0; - gboolean have_scale = FALSE; - int i; - - g_return_if_fail (iterator != NULL); - g_return_if_fail (desc != NULL); - - if (language) - *language = NULL; - - if (extra_attrs) - *extra_attrs = NULL; - - if (!iterator->attribute_stack) - return; - - for (i = iterator->attribute_stack->len - 1; i >= 0; i--) - { - const PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); - - switch ((int) attr->type) - { - case PANGO_ATTR_FONT_DESC: - { - PangoFontMask new_mask = pango_font_description_get_set_fields (attr->font_value) & ~mask; - mask |= new_mask; - pango_font_description_unset_fields (desc, new_mask); - pango_font_description_merge_static (desc, attr->font_value, FALSE); - - break; - } - case PANGO_ATTR_FAMILY: - if (!(mask & PANGO_FONT_MASK_FAMILY)) - { - mask |= PANGO_FONT_MASK_FAMILY; - pango_font_description_set_family (desc, attr->str_value); - } - break; - case PANGO_ATTR_STYLE: - if (!(mask & PANGO_FONT_MASK_STYLE)) - { - mask |= PANGO_FONT_MASK_STYLE; - pango_font_description_set_style (desc, attr->int_value); - } - break; - case PANGO_ATTR_VARIANT: - if (!(mask & PANGO_FONT_MASK_VARIANT)) - { - mask |= PANGO_FONT_MASK_VARIANT; - pango_font_description_set_variant (desc, attr->int_value); - } - break; - case PANGO_ATTR_WEIGHT: - if (!(mask & PANGO_FONT_MASK_WEIGHT)) - { - mask |= PANGO_FONT_MASK_WEIGHT; - pango_font_description_set_weight (desc, attr->int_value); - } - break; - case PANGO_ATTR_STRETCH: - if (!(mask & PANGO_FONT_MASK_STRETCH)) - { - mask |= PANGO_FONT_MASK_STRETCH; - pango_font_description_set_stretch (desc, attr->int_value); - } - break; - case PANGO_ATTR_SIZE: - if (!(mask & PANGO_FONT_MASK_SIZE)) - { - mask |= PANGO_FONT_MASK_SIZE; - pango_font_description_set_size (desc, attr->int_value); - } - break; - case PANGO_ATTR_ABSOLUTE_SIZE: - if (!(mask & PANGO_FONT_MASK_SIZE)) - { - mask |= PANGO_FONT_MASK_SIZE; - pango_font_description_set_absolute_size (desc, attr->int_value); - } - break; - case PANGO_ATTR_SCALE: - if (!have_scale) - { - have_scale = TRUE; - scale = attr->double_value; - } - break; - case PANGO_ATTR_LANGUAGE: - if (language) - { - if (!have_language) - { - have_language = TRUE; - *language = attr->lang_value; - } - } - break; - default: - if (extra_attrs) - { - gboolean found = FALSE; - - /* Hack: special-case FONT_FEATURES, BASELINE_SHIFT and FONT_SCALE. - * We don't want these to accumulate, not override each other, - * so we never merge them. - * This needs to be handled more systematically. - */ - if (attr->type != PANGO_ATTR_FONT_FEATURES && - attr->type != PANGO_ATTR_BASELINE_SHIFT && - attr->type != PANGO_ATTR_FONT_SCALE) - { - GSList *tmp_list = *extra_attrs; - while (tmp_list) - { - PangoAttribute *old_attr = tmp_list->data; - if (attr->type == old_attr->type) - { - found = TRUE; - break; - } - - tmp_list = tmp_list->next; - } - } - - if (!found) - *extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr)); - } - } - } - - if (have_scale) - { - /* We need to use a local variable to ensure that the compiler won't - * implicitly cast it to integer while the result is kept in registers, - * leading to a wrong approximation in i386 (with 387 FPU) - */ - volatile double size = scale * pango_font_description_get_size (desc); - - if (pango_font_description_get_size_is_absolute (desc)) - pango_font_description_set_absolute_size (desc, size); - else - pango_font_description_set_size (desc, size); - } -} - -/** - * pango_attr_iterator_get_attrs: - * @iterator: a `PangoAttrIterator` - * - * Gets a list of all attributes at the current position of the - * iterator. - * - * Return value: (element-type Pango.Attribute) (transfer full): - * a list of all attributes for the current range. To free - * this value, call [method@Pango.Attribute.destroy] on each - * value and g_slist_free() on the list. - * - * Since: 1.2 - */ -GSList * -pango_attr_iterator_get_attrs (PangoAttrIterator *iterator) -{ - GSList *attrs = NULL; - int i; - - if (!iterator->attribute_stack || - iterator->attribute_stack->len == 0) - return NULL; - - for (i = iterator->attribute_stack->len - 1; i >= 0; i--) - { - PangoAttribute *attr = g_ptr_array_index (iterator->attribute_stack, i); - GSList *tmp_list2; - gboolean found = FALSE; - - if (attr->type != PANGO_ATTR_FONT_DESC && - attr->type != PANGO_ATTR_BASELINE_SHIFT && - attr->type != PANGO_ATTR_FONT_SCALE) - for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next) - { - PangoAttribute *old_attr = tmp_list2->data; - if (attr->type == old_attr->type) - { - found = TRUE; - break; - } - } - - if (!found) - attrs = g_slist_prepend (attrs, pango_attribute_copy (attr)); - } - - return attrs; -} - -gboolean -pango_attr_iterator_advance (PangoAttrIterator *iterator, - int index) -{ - int start_range, end_range; - - pango_attr_iterator_range (iterator, &start_range, &end_range); - - while (index >= end_range) - { - if (!pango_attr_iterator_next (iterator)) - return FALSE; - pango_attr_iterator_range (iterator, &start_range, &end_range); - } - - if (start_range > index) - g_warning ("pango_attr_iterator_advance(): iterator had already " - "moved beyond the index"); - - return TRUE; -} /* }}} */ /* vim:set foldmethod=marker expandtab: */ |