summaryrefslogtreecommitdiff
path: root/pango2/pango-attr-iterator.c
diff options
context:
space:
mode:
Diffstat (limited to 'pango2/pango-attr-iterator.c')
-rw-r--r--pango2/pango-attr-iterator.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/pango2/pango-attr-iterator.c b/pango2/pango-attr-iterator.c
new file mode 100644
index 00000000..59bb1255
--- /dev/null
+++ b/pango2/pango-attr-iterator.c
@@ -0,0 +1,509 @@
+/* Pango2
+ * pango-attr-iterator.c: Attribute iterator
+ *
+ * Copyright (C) 2000-2002 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "pango-attr-iterator-private.h"
+#include "pango-attr-list-private.h"
+#include "pango-attr-private.h"
+#include "pango-impl-utils.h"
+
+
+G_DEFINE_BOXED_TYPE (Pango2AttrIterator,
+ pango2_attr_iterator,
+ pango2_attr_iterator_copy,
+ pango2_attr_iterator_destroy)
+
+/* {{{ Private API */
+
+void
+pango2_attr_list_init_iterator (Pango2AttrList *list,
+ Pango2AttrIterator *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 (!pango2_attr_iterator_next (iterator))
+ iterator->end_index = G_MAXUINT;
+}
+
+void
+pango2_attr_iterator_clear (Pango2AttrIterator *iterator)
+{
+ if (iterator->attribute_stack)
+ g_ptr_array_free (iterator->attribute_stack, TRUE);
+}
+
+gboolean
+pango2_attr_iterator_advance (Pango2AttrIterator *iterator,
+ int index)
+{
+ int start_range, end_range;
+
+ pango2_attr_iterator_range (iterator, &start_range, &end_range);
+
+ while (index >= end_range)
+ {
+ if (!pango2_attr_iterator_next (iterator))
+ return FALSE;
+ pango2_attr_iterator_range (iterator, &start_range, &end_range);
+ }
+
+ if (start_range > index)
+ g_warning ("pango2_attr_iterator_advance(): iterator had already "
+ "moved beyond the index");
+
+ return TRUE;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango2_attr_list_get_iterator:
+ * @list: a `Pango2AttrList`
+ *
+ * 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
+ * `Pango2AttrIterator`, which should be freed with
+ * [method@Pango2.AttrIterator.destroy]
+ */
+Pango2AttrIterator *
+pango2_attr_list_get_iterator (Pango2AttrList *list)
+{
+ Pango2AttrIterator *iterator;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ iterator = g_slice_new (Pango2AttrIterator);
+ pango2_attr_list_init_iterator (list, iterator);
+
+ return iterator;
+}
+
+/**
+ * pango2_attr_iterator_destroy:
+ * @iterator: a `Pango2AttrIterator`
+ *
+ * Destroy a `Pango2AttrIterator` and free all associated memory.
+ */
+void
+pango2_attr_iterator_destroy (Pango2AttrIterator *iterator)
+{
+ g_return_if_fail (iterator != NULL);
+
+ pango2_attr_iterator_clear (iterator);
+ g_slice_free (Pango2AttrIterator, iterator);
+}
+
+/**
+ * pango2_attr_iterator_copy:
+ * @iterator: a `Pango2AttrIterator`
+ *
+ * Copy a `Pango2AttrIterator`.
+ *
+ * Return value: (transfer full): the newly allocated
+ * `Pango2AttrIterator`, which should be freed with
+ * [method@Pango2.AttrIterator.destroy]
+ */
+Pango2AttrIterator *
+pango2_attr_iterator_copy (Pango2AttrIterator *iterator)
+{
+ Pango2AttrIterator *copy;
+
+ g_return_val_if_fail (iterator != NULL, NULL);
+
+ copy = g_slice_new (Pango2AttrIterator);
+
+ *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;
+}
+
+/**
+ * pango2_attr_iterator_range:
+ * @iterator: a Pango2AttrIterator
+ * @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 `Pango2Attribute`. To deal with this API
+ * oversight, stored return values that wouldn't fit into
+ * a signed integer are clamped to %G_MAXINT.
+ */
+void
+pango2_attr_iterator_range (Pango2AttrIterator *iterator,
+ int *start,
+ int *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);
+}
+
+/**
+ * pango2_attr_iterator_next:
+ * @iterator: a `Pango2AttrIterator`
+ *
+ * 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
+pango2_attr_iterator_next (Pango2AttrIterator *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 Pango2Attribute *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)
+ {
+ Pango2Attribute *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)
+ {
+ Pango2Attribute *attr = g_ptr_array_index (iterator->attrs, iterator->attr_index);
+
+ iterator->end_index = MIN (iterator->end_index, attr->start_index);
+ }
+
+ return TRUE;
+}
+
+/**
+ * pango2_attr_iterator_get:
+ * @iterator: a `Pango2AttrIterator`
+ * @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.
+ */
+Pango2Attribute *
+pango2_attr_iterator_get (Pango2AttrIterator *iterator,
+ guint 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--)
+ {
+ Pango2Attribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+
+ if (attr->type == type)
+ return attr;
+ }
+
+ return NULL;
+}
+
+/**
+ * pango2_attr_iterator_get_font:
+ * @iterator: a `Pango2AttrIterator`
+ * @desc: (out caller-allocates): a `Pango2FontDescription` to fill in with the current
+ * values. The family name in this structure will be set using
+ * [method@Pango2.FontDescription.set_family_static] using
+ * values from an attribute in the `Pango2AttrList` associated
+ * with the iterator, so if you plan to keep it around, you
+ * must call:
+ * `pango2_font_description_set_family (desc, pango2_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 Pango2.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@Pango2.Attribute.destroy] on each member.
+ *
+ * Get the font and other attributes at the current
+ * iterator position.
+ */
+void
+pango2_attr_iterator_get_font (Pango2AttrIterator *iterator,
+ Pango2FontDescription *desc,
+ Pango2Language **language,
+ GSList **extra_attrs)
+{
+ Pango2FontMask mask = 0;
+ gboolean have_language = FALSE;
+ double 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 Pango2Attribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+
+ switch ((int) attr->type)
+ {
+ case PANGO2_ATTR_FONT_DESC:
+ {
+ Pango2FontMask new_mask = pango2_font_description_get_set_fields (attr->font_value) & ~mask;
+ mask |= new_mask;
+ pango2_font_description_unset_fields (desc, new_mask);
+ pango2_font_description_merge_static (desc, attr->font_value, FALSE);
+
+ break;
+ }
+ case PANGO2_ATTR_FAMILY:
+ if (!(mask & PANGO2_FONT_MASK_FAMILY))
+ {
+ mask |= PANGO2_FONT_MASK_FAMILY;
+ pango2_font_description_set_family (desc, attr->str_value);
+ }
+ break;
+ case PANGO2_ATTR_STYLE:
+ if (!(mask & PANGO2_FONT_MASK_STYLE))
+ {
+ mask |= PANGO2_FONT_MASK_STYLE;
+ pango2_font_description_set_style (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_VARIANT:
+ if (!(mask & PANGO2_FONT_MASK_VARIANT))
+ {
+ mask |= PANGO2_FONT_MASK_VARIANT;
+ pango2_font_description_set_variant (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_WEIGHT:
+ if (!(mask & PANGO2_FONT_MASK_WEIGHT))
+ {
+ mask |= PANGO2_FONT_MASK_WEIGHT;
+ pango2_font_description_set_weight (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_STRETCH:
+ if (!(mask & PANGO2_FONT_MASK_STRETCH))
+ {
+ mask |= PANGO2_FONT_MASK_STRETCH;
+ pango2_font_description_set_stretch (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_SIZE:
+ if (!(mask & PANGO2_FONT_MASK_SIZE))
+ {
+ mask |= PANGO2_FONT_MASK_SIZE;
+ pango2_font_description_set_size (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_ABSOLUTE_SIZE:
+ if (!(mask & PANGO2_FONT_MASK_SIZE))
+ {
+ mask |= PANGO2_FONT_MASK_SIZE;
+ pango2_font_description_set_absolute_size (desc, attr->int_value);
+ }
+ break;
+ case PANGO2_ATTR_SCALE:
+ if (!have_scale)
+ {
+ have_scale = TRUE;
+ scale = attr->double_value;
+ }
+ break;
+ case PANGO2_ATTR_LANGUAGE:
+ if (language)
+ {
+ if (!have_language)
+ {
+ have_language = TRUE;
+ *language = attr->lang_value;
+ }
+ }
+ break;
+ default:
+ if (extra_attrs)
+ {
+ gboolean found = FALSE;
+
+ if (PANGO2_ATTR_MERGE (attr) == PANGO2_ATTR_MERGE_OVERRIDES)
+ {
+ GSList *tmp_list = *extra_attrs;
+ while (tmp_list)
+ {
+ Pango2Attribute *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, pango2_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 * pango2_font_description_get_size (desc);
+
+ if (pango2_font_description_get_size_is_absolute (desc))
+ pango2_font_description_set_absolute_size (desc, size);
+ else
+ pango2_font_description_set_size (desc, size);
+ }
+}
+
+/**
+ * pango2_attr_iterator_get_attrs:
+ * @iterator: a `Pango2AttrIterator`
+ *
+ * Gets a list of all attributes at the current position of the
+ * iterator.
+ *
+ * Return value: (element-type Pango2.Attribute) (transfer full):
+ * a list of all attributes for the current range. To free
+ * this value, call [method@Pango2.Attribute.destroy] on each
+ * value and [func@GLib.SList.free] on the list.
+ */
+GSList *
+pango2_attr_iterator_get_attrs (Pango2AttrIterator *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--)
+ {
+ Pango2Attribute *attr = g_ptr_array_index (iterator->attribute_stack, i);
+ GSList *tmp_list2;
+ gboolean found = FALSE;
+
+ if (PANGO2_ATTR_MERGE (attr) == PANGO2_ATTR_MERGE_OVERRIDES)
+ {
+ for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
+ {
+ Pango2Attribute *old_attr = tmp_list2->data;
+ if (attr->type == old_attr->type)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ attrs = g_slist_prepend (attrs, pango2_attribute_copy (attr));
+ }
+
+ return attrs;
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */