* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include "config.h"
#include "gtktextiter.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
+#include "gtkintl.h"
#include "gtkdebug.h"
+#include "gtkalias.h"
#include <string.h>
-#include <ctype.h>
#define FIX_OVERFLOWS(varname) if ((varname) == G_MININT) (varname) = G_MININT + 1
typedef struct _GtkTextRealIter GtkTextRealIter;
-struct _GtkTextRealIter
+struct G_GNUC_MAY_ALIAS _GtkTextRealIter
{
/* Always-valid information */
GtkTextBTree *tree;
and ditto for char offsets. */
gint segment_byte_offset;
gint segment_char_offset;
+
+ /* padding */
+ gint pad1;
+ gpointer pad2;
};
/* These "set" functions should not assume any fields
return real;
}
-static inline void
-invalidate_segment (GtkTextRealIter *iter)
-{
- iter->segments_changed_stamp -= 1;
-}
-
static inline void
invalidate_char_index (GtkTextRealIter *iter)
{
iter->cached_char_index = -1;
}
-static inline void
-invalidate_line_number (GtkTextRealIter *iter)
-{
- iter->cached_line_number = -1;
-}
-
static inline void
adjust_char_index (GtkTextRealIter *iter, gint count)
{
iter->cached_line_number += count;
}
-static inline void
-adjust_char_offsets (GtkTextRealIter *iter, gint count)
-{
- if (iter->line_char_offset >= 0)
- {
- iter->line_char_offset += count;
- g_assert (iter->segment_char_offset >= 0);
- iter->segment_char_offset += count;
- }
-}
-
-static inline void
-adjust_byte_offsets (GtkTextRealIter *iter, gint count)
-{
- if (iter->line_byte_offset >= 0)
- {
- iter->line_byte_offset += count;
- g_assert (iter->segment_byte_offset >= 0);
- iter->segment_byte_offset += count;
- }
-}
-
static inline void
ensure_char_offsets (GtkTextRealIter *iter)
{
return real->segment_byte_offset == 0 || real->segment_char_offset == 0;
}
-#if 1
+#ifdef G_ENABLE_DEBUG
static void
check_invariants (const GtkTextIter *iter)
{
_gtk_text_iter_check (iter);
}
#else
-#define check_invariants (x)
+#define check_invariants(x)
#endif
/**
* gtk_text_iter_get_buffer:
* @iter: an iterator
*
- * Return the #GtkTextBuffer this iterator is associated with
+ * Returns the #GtkTextBuffer this iterator is associated with.
*
* Return value: the buffer
**/
* gtk_text_iter_copy:
* @iter: an iterator
*
- * Create a dynamically-allocated copy of an iterator. This function
+ * Creates a dynamically-allocated copy of an iterator. This function
* is not useful in applications, because iterators can be copied with a
* simple assignment (<literal>GtkTextIter i = j;</literal>). The
* function is used by language bindings.
g_return_val_if_fail (iter != NULL, NULL);
- new_iter = g_new (GtkTextIter, 1);
+ new_iter = g_slice_new (GtkTextIter);
*new_iter = *iter;
* is intended for use in language bindings, and is not
* especially useful for applications, because iterators can
* simply be allocated on the stack.
- *
**/
void
gtk_text_iter_free (GtkTextIter *iter)
{
g_return_if_fail (iter != NULL);
- g_free (iter);
+ g_slice_free (GtkTextIter, iter);
+}
+
+GType
+gtk_text_iter_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (I_("GtkTextIter"),
+ (GBoxedCopyFunc) gtk_text_iter_copy,
+ (GBoxedFreeFunc) gtk_text_iter_free);
+
+ return our_type;
}
GtkTextLineSegment*
{
GtkTextRealIter *real;
- g_return_val_if_fail (iter != NULL, 0);
+ g_return_val_if_fail (iter != NULL, NULL);
real = gtk_text_iter_make_real (iter);
{
GtkTextRealIter *real;
- g_return_val_if_fail (iter != NULL, 0);
+ g_return_val_if_fail (iter != NULL, NULL);
real = gtk_text_iter_make_real (iter);
{
const GtkTextRealIter *real;
- g_return_val_if_fail (iter != NULL, 0);
+ g_return_val_if_fail (iter != NULL, NULL);
real = (const GtkTextRealIter*)iter;
{
const GtkTextRealIter *real;
- g_return_val_if_fail (iter != NULL, 0);
+ g_return_val_if_fail (iter != NULL, NULL);
real = (const GtkTextRealIter*)iter;
ensure_char_offsets (real);
check_invariants (iter);
-
+
vis_offset = real->line_char_offset;
+ g_assert (vis_offset >= 0);
+
_gtk_text_btree_get_iter_at_line (real->tree,
&pos,
real->line,
if (real == NULL)
return 0;
- ensure_char_offsets (real);
+ ensure_byte_offsets (real);
check_invariants (iter);
vis_offset = real->line_byte_offset;
+ g_assert (vis_offset >= 0);
+
_gtk_text_btree_get_iter_at_line (real->tree,
&pos,
real->line,
* @iter: an iterator
*
* Returns the Unicode character at this iterator. (Equivalent to
- * operator* on a C++ iterator.) If the iterator points at a
+ * operator* on a C++ iterator.) If the element at this iterator is a
* non-character element, such as an image embedded in the buffer, the
* Unicode "unknown" character 0xFFFC is returned. If invoked on
* the end iterator, zero is returned; zero is not a valid Unicode character.
* gtk_text_iter_get_pixbuf:
* @iter: an iterator
*
- * If the location pointed to by @iter contains a pixbuf, the pixbuf
- * is returned (with no new reference count added). Otherwise,
- * NULL is returned.
+ * If the element at @iter is a pixbuf, the pixbuf is returned
+ * (with no new reference count added). Otherwise,
+ * %NULL is returned.
*
* Return value: the pixbuf at @iter
**/
* gtk_text_iter_get_child_anchor:
* @iter: an iterator
*
- * If the location pointed to by @iter contains a child anchor, the
+ * If the location at @iter contains a child anchor, the
* anchor is returned (with no new reference count added). Otherwise,
- * NULL is returned.
+ * %NULL is returned.
*
* Return value: the anchor at @iter
**/
/**
* gtk_text_iter_get_toggled_tags:
* @iter: an iterator
- * @toggled_on: TRUE to get toggled-on tags
+ * @toggled_on: %TRUE to get toggled-on tags
*
* Returns a list of #GtkTextTag that are toggled on or off at this
- * point. (If @toggled_on is TRUE, the list contains tags that are
+ * point. (If @toggled_on is %TRUE, the list contains tags that are
* toggled on.) If a tag is toggled on at @iter, then some non-empty
* range of characters following @iter has that tag applied to it. If
* a tag is toggled off, then some non-empty range following @iter
/**
* gtk_text_iter_begins_tag:
* @iter: an iterator
- * @tag: a #GtkTextTag, or NULL
+ * @tag: a #GtkTextTag, or %NULL
*
- * Returns TRUE if @tag is toggled on at exactly this point. If @tag
- * is NULL, returns TRUE if any tag is toggled on at this point. Note
- * that the gtk_text_iter_begins_tag () returns TRUE if @iter is the
+ * Returns %TRUE if @tag is toggled on at exactly this point. If @tag
+ * is %NULL, returns %TRUE if any tag is toggled on at this point. Note
+ * that the gtk_text_iter_begins_tag () returns %TRUE if @iter is the
* <emphasis>start</emphasis> of the tagged range;
* gtk_text_iter_has_tag () tells you whether an iterator is
* <emphasis>within</emphasis> a tagged range.
/**
* gtk_text_iter_ends_tag:
* @iter: an iterator
- * @tag: a #GtkTextTag, or NULL
+ * @tag: a #GtkTextTag, or %NULL
*
- * Returns TRUE if @tag is toggled off at exactly this point. If @tag
- * is NULL, returns TRUE if any tag is toggled off at this point. Note
- * that the gtk_text_iter_ends_tag () returns TRUE if @iter is the
+ * Returns %TRUE if @tag is toggled off at exactly this point. If @tag
+ * is %NULL, returns %TRUE if any tag is toggled off at this point. Note
+ * that the gtk_text_iter_ends_tag () returns %TRUE if @iter is the
* <emphasis>end</emphasis> of the tagged range;
* gtk_text_iter_has_tag () tells you whether an iterator is
* <emphasis>within</emphasis> a tagged range.
/**
* gtk_text_iter_toggles_tag:
* @iter: an iterator
- * @tag: a #GtkTextTag, or NULL
+ * @tag: a #GtkTextTag, or %NULL
*
* This is equivalent to (gtk_text_iter_begins_tag () ||
* gtk_text_iter_ends_tag ()), i.e. it tells you whether a range with
* Return value: whether @tag is toggled on or off at @iter
**/
gboolean
-gtk_text_iter_toggles_tag (const GtkTextIter *iter,
- GtkTextTag *tag)
+gtk_text_iter_toggles_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
{
GtkTextRealIter *real;
GtkTextLineSegment *seg;
* @iter: an iterator
* @tag: a #GtkTextTag
*
- * Returns TRUE if @iter is within a range tagged with @tag.
+ * Returns %TRUE if @iter is within a range tagged with @tag.
*
* Return value: whether @iter is tagged with @tag
**/
gboolean
-gtk_text_iter_has_tag (const GtkTextIter *iter,
- GtkTextTag *tag)
+gtk_text_iter_has_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
{
GtkTextRealIter *real;
if (real->line_byte_offset >= 0)
{
return _gtk_text_line_byte_has_tag (real->line, real->tree,
- real->line_byte_offset, tag);
+ real->line_byte_offset, tag);
}
else
{
g_assert (real->line_char_offset >= 0);
return _gtk_text_line_char_has_tag (real->line, real->tree,
- real->line_char_offset, tag);
+ real->line_char_offset, tag);
}
}
/* No tags, use default style */
if (tags == NULL || tag_count == 0)
{
- if (tags)
- g_free (tags);
+ g_free (tags);
return NULL;
}
- /* Sort tags in ascending order of priority */
- _gtk_text_tag_array_sort (tags, tag_count);
-
retval = NULL;
i = 0;
while (i < tag_count)
/**
* gtk_text_iter_editable:
* @iter: an iterator
- * @default_setting: TRUE if text is editable by default
+ * @default_setting: %TRUE if text is editable by default
*
- * Returns whether @iter is within an editable region of text.
- * Non-editable text is "locked" and can't be changed by the user via
- * #GtkTextView. This function is simply a convenience wrapper around
- * gtk_text_iter_get_attributes (). If no tags applied to this text
- * affect editability, @default_setting will be returned.
+ * Returns whether the character at @iter is within an editable region
+ * of text. Non-editable text is "locked" and can't be changed by the
+ * user via #GtkTextView. This function is simply a convenience
+ * wrapper around gtk_text_iter_get_attributes (). If no tags applied
+ * to this text affect editability, @default_setting will be returned.
*
+ * You don't want to use this function to decide whether text can be
+ * inserted at @iter, because for insertion you don't want to know
+ * whether the char at @iter is inside an editable range, you want to
+ * know whether a new character inserted at @iter would be inside an
+ * editable range. Use gtk_text_iter_can_insert() to handle this
+ * case.
+ *
* Return value: whether @iter is inside an editable range
**/
gboolean
GtkTextAttributes *values;
gboolean retval;
+ g_return_val_if_fail (iter != NULL, FALSE);
+
values = gtk_text_attributes_new ();
values->editable = default_setting;
return retval;
}
+/**
+ * gtk_text_iter_can_insert:
+ * @iter: an iterator
+ * @default_editability: %TRUE if text is editable by default
+ *
+ * Considering the default editability of the buffer, and tags that
+ * affect editability, determines whether text inserted at @iter would
+ * be editable. If text inserted at @iter would be editable then the
+ * user should be allowed to insert text at @iter.
+ * gtk_text_buffer_insert_interactive() uses this function to decide
+ * whether insertions are allowed at a given position.
+ *
+ * Return value: whether text inserted at @iter would be editable
+ **/
+gboolean
+gtk_text_iter_can_insert (const GtkTextIter *iter,
+ gboolean default_editability)
+{
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (gtk_text_iter_editable (iter, default_editability))
+ return TRUE;
+ /* If at start/end of buffer, default editability is used */
+ else if ((gtk_text_iter_is_start (iter) ||
+ gtk_text_iter_is_end (iter)) &&
+ default_editability)
+ return TRUE;
+ else
+ {
+ /* if iter isn't editable, and the char before iter is,
+ * then iter is the first char in an editable region
+ * and thus insertion at iter results in editable text.
+ */
+ GtkTextIter prev = *iter;
+ gtk_text_iter_backward_char (&prev);
+ return gtk_text_iter_editable (&prev, default_editability);
+ }
+}
+
+
/**
* gtk_text_iter_get_language:
* @iter: an iterator
*
* A convenience wrapper around gtk_text_iter_get_attributes (),
* which returns the language in effect at @iter. If no tags affecting
- * language * apply to @iter, the return value is identical to that of
+ * language apply to @iter, the return value is identical to that of
* gtk_get_default_language ().
*
* Return value: language in effect at @iter
* gtk_text_iter_starts_line:
* @iter: an iterator
*
- * Returns TRUE if @iter begins a paragraph,
+ * Returns %TRUE if @iter begins a paragraph,
* i.e. if gtk_text_iter_get_line_offset () would return 0.
* However this function is potentially more efficient than
* gtk_text_iter_get_line_offset () because it doesn't have to compute
* gtk_text_iter_ends_line:
* @iter: an iterator
*
- * Returns TRUE if @iter points to the start of the paragraph delimiter
- * characters for a line (delimiters will be either a newline, a
- * carriage return, a carriage return followed by a newline, or a
- * Unicode paragraph separator character). Note that an iterator pointing
- * to the \n of a \r\n pair will not be counted as the end of a line,
- * the line ends before the \r.
+ * Returns %TRUE if @iter points to the start of the paragraph
+ * delimiter characters for a line (delimiters will be either a
+ * newline, a carriage return, a carriage return followed by a
+ * newline, or a Unicode paragraph separator character). Note that an
+ * iterator pointing to the \n of a \r\n pair will not be counted as
+ * the end of a line, the line ends before the \r. The end iterator is
+ * considered to be at the end of a line, even though there are no
+ * paragraph delimiter chars there.
*
* Return value: whether @iter is at the end of a line
**/
gboolean
gtk_text_iter_ends_line (const GtkTextIter *iter)
{
- GtkTextRealIter *real;
gunichar wc;
g_return_val_if_fail (iter != NULL, FALSE);
- real = gtk_text_iter_make_real (iter);
-
check_invariants (iter);
/* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
wc = gtk_text_iter_get_char (iter);
- if (wc == '\r' || wc == PARAGRAPH_SEPARATOR)
+ if (wc == '\r' || wc == PARAGRAPH_SEPARATOR || wc == 0) /* wc == 0 is end iterator */
return TRUE;
else if (wc == '\n')
{
+ GtkTextIter tmp = *iter;
+
/* need to determine if a \r precedes the \n, in which case
- * we aren't the end of the line
+ * we aren't the end of the line.
+ * Note however that if \r and \n are on different lines, they
+ * both are terminators. This for instance may happen after
+ * deleting some text:
+
+ 1 some text\r delete 'a' 1 some text\r
+ 2 a\n ---------> 2 \n
+ 3 ... 3 ...
+
*/
- GtkTextIter tmp = *iter;
+
+ if (gtk_text_iter_get_line_offset (&tmp) == 0)
+ return TRUE;
+
if (!gtk_text_iter_backward_char (&tmp))
return TRUE;
* gtk_text_iter_is_end:
* @iter: an iterator
*
- * Returns TRUE if @iter is the end iterator, i.e. one past the last
+ * Returns %TRUE if @iter is the end iterator, i.e. one past the last
* dereferenceable iterator in the buffer. gtk_text_iter_is_end () is
* the most efficient way to check whether an iterator is the end
* iterator.
check_invariants (iter);
- return _gtk_text_line_is_last (real->line, real->tree);
+ if (!_gtk_text_line_contains_end_iter (real->line, real->tree))
+ return FALSE;
+
+ /* Now we need the segments validated */
+ real = gtk_text_iter_make_real (iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ return _gtk_text_btree_is_end (real->tree, real->line,
+ real->segment,
+ real->segment_byte_offset,
+ real->segment_char_offset);
}
/**
* gtk_text_iter_is_start:
* @iter: an iterator
*
- * Returns TRUE if @iter is the first iterator in the buffer, that is
+ * Returns %TRUE if @iter is the first iterator in the buffer, that is
* if @iter has a character offset of 0.
*
* Return value: whether @iter is the first in the buffer
gint count;
GtkTextLineSegment *seg;
- g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter != NULL, 0);
real = gtk_text_iter_make_surreal (iter);
seg = seg->next;
}
+ if (_gtk_text_line_contains_end_iter (real->line, real->tree))
+ count -= 1; /* Dump the newline that was in the last segment of the end iter line */
+
return count;
}
gint count;
GtkTextLineSegment *seg;
- g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter != NULL, 0);
real = gtk_text_iter_make_surreal (iter);
seg = seg->next;
}
+ if (_gtk_text_line_contains_end_iter (real->line, real->tree))
+ count -= 1; /* Dump the newline that was in the last segment of the end iter line */
+
return count;
}
**/
gboolean
gtk_text_iter_get_attributes (const GtkTextIter *iter,
- GtkTextAttributes *values)
+ GtkTextAttributes *values)
{
GtkTextTag** tags;
gint tag_count = 0;
/* No tags, use default style */
if (tags == NULL || tag_count == 0)
{
- if (tags)
- g_free (tags);
+ g_free (tags);
return FALSE;
}
- /* Sort tags in ascending order of priority */
- _gtk_text_tag_array_sort (tags, tag_count);
-
_gtk_text_attributes_fill_from_tags (values,
tags,
tag_count);
/* The return value of this indicates WHETHER WE MOVED.
* The return value of public functions indicates
* (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
+ *
+ * This function will not change the iterator if
+ * it's already on the last (end iter) line, i.e. it
+ * won't move to the end of the last line.
*/
static gboolean
forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
{
- GtkTextLine *new_line;
-
- new_line = _gtk_text_line_next (real->line);
-
- g_assert (new_line != real->line);
-
- if (new_line != NULL)
+ if (!_gtk_text_line_contains_end_iter (real->line, real->tree))
{
+ GtkTextLine *new_line;
+
+ new_line = _gtk_text_line_next (real->line);
+ g_assert (new_line);
+ g_assert (new_line != real->line);
+ g_assert (!_gtk_text_line_is_last (new_line, real->tree));
+
real->line = new_line;
real->line_byte_offset = 0;
}
else
{
- /* There is no way to move forward; we were already
- at the "end" index. (the end index is the last
- line pointer, segment_byte_offset of 0) */
-
- g_assert (real->line_char_offset == 0 ||
- real->line_byte_offset == 0);
-
- /* The only indexable segment allowed on the bogus
- line at the end is a single char segment containing
- a newline. */
- if (real->segments_changed_stamp ==
- _gtk_text_btree_get_segments_changed_stamp (real->tree))
- {
- g_assert (real->segment->type == >k_text_char_type);
- g_assert (real->segment->char_count == 1);
- }
- /* We leave real->line as-is */
-
+ /* There is no way to move forward a line; we were already at
+ * the line containing the end iterator.
+ * However we may not be at the end iterator itself.
+ */
+
return FALSE;
}
}
-
+#if 0
/* The return value of this indicates WHETHER WE MOVED.
* The return value of public functions indicates
* (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
+ *
+ * This function is currently unused, thus it is #if-0-ed. It is
+ * left here, since it's non-trivial code that might be useful in
+ * the future.
*/
static gboolean
backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
return FALSE;
}
}
+#endif
/* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
* DEREFERENCEABLE)
check_invariants (iter);
- return TRUE;
+ return !gtk_text_iter_is_end (iter);
}
else
{
check_invariants (iter);
- if (gtk_text_iter_is_end (iter))
- return FALSE;
- else
- return TRUE;
+ return !gtk_text_iter_is_end (iter);
}
else
{
- /* End of buffer */
-
+ /* End of buffer, but iter is still at start of last segment,
+ * not at the end iterator. We put it on the end iterator.
+ */
+
check_invariants (iter);
+ g_assert (!_gtk_text_line_is_last (real->line, real->tree));
+ g_assert (_gtk_text_line_contains_end_iter (real->line, real->tree));
+
+ gtk_text_iter_forward_to_line_end (iter);
+
+ g_assert (gtk_text_iter_is_end (iter));
+
return FALSE;
}
}
* segment just before our current segment.
*/
g_assert (seg != real->segment);
- while (seg != real->segment)
+ do
{
prev_seg = seg;
prev_any_seg = any_seg;
while (seg->char_count == 0)
seg = seg->next;
}
+ while (seg != real->segment);
g_assert (prev_seg != NULL);
g_assert (prev_any_seg != NULL);
* gtk_text_iter_forward_char () may actually move onto an image instead
* of a character, if you have images in your buffer. If @iter is the
* end iterator or one character before it, @iter will now point at
- * the end iterator, and gtk_text_iter_forward_char () returns FALSE for
+ * the end iterator, and gtk_text_iter_forward_char () returns %FALSE for
* convenience when writing loops.
*
- * Return value: whether the new position is the end iterator
+ * Return value: whether @iter moved and is dereferenceable
**/
gboolean
gtk_text_iter_forward_char (GtkTextIter *iter)
* gtk_text_iter_backward_char:
* @iter: an iterator
*
- * Moves backward by one character offset. Returns TRUE if movement
+ * Moves backward by one character offset. Returns %TRUE if movement
* was possible; if @iter was the first in the buffer (character
- * offset 0), gtk_text_iter_backward_char () returns FALSE for convenience when
+ * offset 0), gtk_text_iter_backward_char () returns %FALSE for convenience when
* writing loops.
*
* Return value: whether movement was possible
* buffer). The return value indicates whether the new position of
* @iter is different from its original position, and dereferenceable
* (the last iterator in the buffer is not dereferenceable). If @count
- * is 0, the function does nothing and returns FALSE.
+ * is 0, the function does nothing and returns %FALSE.
*
* Return value: whether @iter moved and is dereferenceable
**/
* past the start or end of the buffer, moves to the start or end of
* the buffer). The return value indicates whether the iterator moved
* onto a dereferenceable position; if the iterator didn't move, or
- * moved onto the end iterator, then FALSE is returned. If @count is 0,
- * the function does nothing and returns FALSE.
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE.
*
* Return value: whether @iter moved and is dereferenceable
*
ensure_char_offsets (real);
check_invariants (iter);
- if (count <= real->segment_char_offset)
+ /* <, not <=, because if count == segment_char_offset
+ * we're going to the front of the segment and the any_segment
+ * might change
+ */
+ if (count < real->segment_char_offset)
{
/* Optimize the within-segment case */
g_assert (real->segment->char_count > 0);
g_assert (real->segment->type == >k_text_char_type);
- real->segment_char_offset -= count;
- g_assert (real->segment_char_offset >= 0);
-
if (real->line_byte_offset >= 0)
{
+ const char *p;
gint new_byte_offset;
- gint i;
-
- new_byte_offset = 0;
- i = 0;
- while (i < real->segment_char_offset)
- {
- const char * start = real->segment->body.chars + new_byte_offset;
- new_byte_offset += g_utf8_next_char (start) - start;
- ++i;
- }
+ /* if in the last fourth of the segment walk backwards */
+ if (count < real->segment_char_offset / 4)
+ p = g_utf8_offset_to_pointer (real->segment->body.chars + real->segment_byte_offset,
+ -count);
+ else
+ p = g_utf8_offset_to_pointer (real->segment->body.chars,
+ real->segment_char_offset - count);
+ new_byte_offset = p - real->segment->body.chars;
real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
real->segment_byte_offset = new_byte_offset;
}
+ real->segment_char_offset -= count;
real->line_char_offset -= count;
adjust_char_index (real, 0 - count);
new_char_index = current_char_index - count;
if (new_char_index < 0)
new_char_index = 0;
+
gtk_text_iter_set_offset (iter, new_char_index);
check_invariants (iter);
* gtk_text_iter_forward_line:
* @iter: an iterator
*
- * Moves @iter to the start of the next line. Returns TRUE if there
- * was a next line to move to, and FALSE if @iter was simply moved to
- * the end of the buffer and is now not dereferenceable, or if @iter was
- * already at the end of the buffer.
+ * Moves @iter to the start of the next line. If the iter is already on the
+ * last line of the buffer, moves the iter to the end of the current line.
+ * If after the operation, the iter is at the end of the buffer and not
+ * dereferencable, returns %FALSE. Otherwise, returns %TRUE.
*
* Return value: whether @iter can be dereferenced
**/
}
else
{
+ /* On the last line, move to end of it */
+
+ if (!gtk_text_iter_is_end (iter))
+ gtk_text_iter_forward_to_end (iter);
+
check_invariants (iter);
return FALSE;
}
* gtk_text_iter_backward_line:
* @iter: an iterator
*
- * Moves @iter to the start of the previous line. Returns TRUE if
+ * Moves @iter to the start of the previous line. Returns %TRUE if
* @iter could be moved; i.e. if @iter was at character offset 0, this
- * function returns FALSE. Therefore if @iter was already on line 0,
+ * function returns %FALSE. Therefore if @iter was already on line 0,
* but not at the start of the line, @iter is snapped to the start of
- * the line and the function returns TRUE. (Note that this implies that
+ * the line and the function returns %TRUE. (Note that this implies that
* in a loop calling this function, the line number may not change on
* every iteration, if your first iteration is on line 0.)
*
/* Find first segment in line */
real->any_segment = real->line->segments;
real->segment = _gtk_text_line_byte_to_segment (real->line,
- 0, &offset);
+ 0, &offset);
g_assert (offset == 0);
* past the start or end of the buffer, moves to the start or end of
* the buffer). The return value indicates whether the iterator moved
* onto a dereferenceable position; if the iterator didn't move, or
- * moved onto the end iterator, then FALSE is returned. If @count is 0,
- * the function does nothing and returns FALSE. If @count is negative,
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
* moves backward by 0 - @count lines.
*
* Return value: whether @iter moved and is dereferenceable
{
gint old_line;
+ if (gtk_text_iter_is_end (iter))
+ return FALSE;
+
old_line = gtk_text_iter_get_line (iter);
gtk_text_iter_set_line (iter, old_line + count);
- check_invariants (iter);
-
- /* return whether it moved, and is dereferenceable. */
- return
- (gtk_text_iter_get_line (iter) != old_line) &&
- !gtk_text_iter_is_end (iter);
+ if ((gtk_text_iter_get_line (iter) - old_line) < count)
+ {
+ /* count went past the last line, so move to end of last line */
+ if (!gtk_text_iter_is_end (iter))
+ gtk_text_iter_forward_to_end (iter);
+ }
+
+ return !gtk_text_iter_is_end (iter);
}
}
* past the start or end of the buffer, moves to the start or end of
* the buffer). The return value indicates whether the iterator moved
* onto a dereferenceable position; if the iterator didn't move, or
- * moved onto the end iterator, then FALSE is returned. If @count is 0,
- * the function does nothing and returns FALSE. If @count is negative,
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
* moves forward by 0 - @count lines.
*
* Return value: whether @iter moved and is dereferenceable
}
}
+/**
+ * gtk_text_iter_forward_visible_line:
+ * @iter: an iterator
+ *
+ * Moves @iter to the start of the next visible line. Returns %TRUE if there
+ * was a next line to move to, and %FALSE if @iter was simply moved to
+ * the end of the buffer and is now not dereferenceable, or if @iter was
+ * already at the end of the buffer.
+ *
+ * Return value: whether @iter can be dereferenced
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_forward_visible_line (GtkTextIter *iter)
+{
+ while (gtk_text_iter_forward_line (iter))
+ {
+ if (!_gtk_text_btree_char_is_invisible (iter))
+ return TRUE;
+ else
+ {
+ do
+ {
+ if (!gtk_text_iter_forward_char (iter))
+ return FALSE;
+
+ if (!_gtk_text_btree_char_is_invisible (iter))
+ return TRUE;
+ }
+ while (!gtk_text_iter_ends_line (iter));
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_text_iter_backward_visible_line:
+ * @iter: an iterator
+ *
+ * Moves @iter to the start of the previous visible line. Returns %TRUE if
+ * @iter could be moved; i.e. if @iter was at character offset 0, this
+ * function returns %FALSE. Therefore if @iter was already on line 0,
+ * but not at the start of the line, @iter is snapped to the start of
+ * the line and the function returns %TRUE. (Note that this implies that
+ * in a loop calling this function, the line number may not change on
+ * every iteration, if your first iteration is on line 0.)
+ *
+ * Return value: whether @iter moved
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_backward_visible_line (GtkTextIter *iter)
+{
+ while (gtk_text_iter_backward_line (iter))
+ {
+ if (!_gtk_text_btree_char_is_invisible (iter))
+ return TRUE;
+ else
+ {
+ do
+ {
+ if (!gtk_text_iter_backward_char (iter))
+ return FALSE;
+
+ if (!_gtk_text_btree_char_is_invisible (iter))
+ return TRUE;
+ }
+ while (!gtk_text_iter_starts_line (iter));
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_text_iter_forward_visible_lines:
+ * @iter: a #GtkTextIter
+ * @count: number of lines to move forward
+ *
+ * Moves @count visible lines forward, if possible (if @count would move
+ * past the start or end of the buffer, moves to the start or end of
+ * the buffer). The return value indicates whether the iterator moved
+ * onto a dereferenceable position; if the iterator didn't move, or
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
+ * moves backward by 0 - @count lines.
+ *
+ * Return value: whether @iter moved and is dereferenceable
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_forward_visible_lines (GtkTextIter *iter,
+ gint count)
+{
+ FIX_OVERFLOWS (count);
+
+ if (count < 0)
+ return gtk_text_iter_backward_visible_lines (iter, 0 - count);
+ else if (count == 0)
+ return FALSE;
+ else if (count == 1)
+ {
+ check_invariants (iter);
+ return gtk_text_iter_forward_visible_line (iter);
+ }
+ else
+ {
+ while (gtk_text_iter_forward_visible_line (iter) && count > 0)
+ count--;
+ return count == 0;
+ }
+}
+
+/**
+ * gtk_text_iter_backward_visible_lines:
+ * @iter: a #GtkTextIter
+ * @count: number of lines to move backward
+ *
+ * Moves @count visible lines backward, if possible (if @count would move
+ * past the start or end of the buffer, moves to the start or end of
+ * the buffer). The return value indicates whether the iterator moved
+ * onto a dereferenceable position; if the iterator didn't move, or
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
+ * moves forward by 0 - @count lines.
+ *
+ * Return value: whether @iter moved and is dereferenceable
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_backward_visible_lines (GtkTextIter *iter,
+ gint count)
+{
+ FIX_OVERFLOWS (count);
+
+ if (count < 0)
+ return gtk_text_iter_forward_visible_lines (iter, 0 - count);
+ else if (count == 0)
+ return FALSE;
+ else if (count == 1)
+ {
+ return gtk_text_iter_backward_visible_line (iter);
+ }
+ else
+ {
+ while (gtk_text_iter_backward_visible_line (iter) && count > 0)
+ count--;
+ return count == 0;
+ }
+}
+
typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
gint offset,
gint min_offset,
!(attrs[offset].is_word_start || attrs[offset].is_word_end))
--offset;
- return attrs[offset].is_word_start;
+ if (offset >= 0)
+ return attrs[offset].is_word_start;
+ else
+ return FALSE;
}
/* Sentence funcs */
offset = gtk_text_iter_get_line_offset (iter);
- g_assert (char_len > 0);
+ /* char_len may be 0 and attrs will be NULL if so, if
+ * iter is the end iter and the last line is empty.
+ *
+ * offset may be equal to char_len, since attrs contains an entry
+ * for one past the end
+ */
- if (offset < char_len)
+ if (attrs && offset <= char_len)
result = (* func) (attrs, offset, 0, char_len);
return result;
offset = gtk_text_iter_get_line_offset (iter);
- g_assert (char_len > 0);
+ /* char_len may be 0 and attrs will be NULL if so, if
+ * iter is the end iter and the last line is empty
+ */
- if (offset < char_len)
+ if (attrs)
result = (* func) (attrs, offset, 0, char_len, found_offset,
already_moved_initially);
}
else
{
- /* go to end of previous line */
- gtk_text_iter_set_line_offset (iter, 0);
-
- if (gtk_text_iter_backward_char (iter))
- return find_by_log_attrs (iter, func, forward,
- TRUE);
- else
- return FALSE;
- }
+ /* go to end of previous line. need to check that
+ * line is > 0 because backward_line snaps to start of
+ * line 0 if it's on line 0
+ */
+ if (gtk_text_iter_get_line (iter) > 0 &&
+ gtk_text_iter_backward_line (iter))
+ {
+ if (!gtk_text_iter_ends_line (iter))
+ gtk_text_iter_forward_to_line_end (iter);
+
+ return find_by_log_attrs (iter, func, forward,
+ TRUE);
+ }
+ else
+ return FALSE;
+ }
}
else
- {
+ {
gtk_text_iter_set_line_offset (iter, offset);
return
- !gtk_text_iter_equal (iter, &orig) &&
+ (already_moved_initially || !gtk_text_iter_equal (iter, &orig)) &&
!gtk_text_iter_is_end (iter);
}
}
+static gboolean
+find_visible_by_log_attrs (GtkTextIter *iter,
+ FindLogAttrFunc func,
+ gboolean forward,
+ gboolean already_moved_initially)
+{
+ GtkTextIter pos;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ pos = *iter;
+
+ while (find_by_log_attrs (&pos, func, forward, already_moved_initially))
+ {
+ if (!_gtk_text_btree_char_is_invisible (&pos))
+ {
+ *iter = pos;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+typedef gboolean (* OneStepFunc) (GtkTextIter *iter);
+typedef gboolean (* MultipleStepFunc) (GtkTextIter *iter, gint count);
+
+static gboolean
+move_multiple_steps (GtkTextIter *iter,
+ gint count,
+ OneStepFunc step_forward,
+ MultipleStepFunc n_steps_backward)
+{
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ FIX_OVERFLOWS (count);
+
+ if (count == 0)
+ return FALSE;
+
+ if (count < 0)
+ return n_steps_backward (iter, -count);
+
+ if (!step_forward (iter))
+ return FALSE;
+ --count;
+
+ while (count > 0)
+ {
+ if (!step_forward (iter))
+ break;
+ --count;
+ }
+
+ return !gtk_text_iter_is_end (iter);
+}
+
+
/**
* gtk_text_iter_forward_word_end:
* @iter: a #GtkTextIter
* gtk_text_iter_backward_word_start:
* @iter: a #GtkTextIter
*
- * Moves backward to the next word start. (If @iter is currently on a
+ * Moves backward to the previous word start. (If @iter is currently on a
* word start, moves backward to the next one after that.) Word breaks
* are determined by Pango and should be correct for nearly any
* language (if not, the correct fix would be to the Pango word break
gtk_text_iter_forward_word_ends (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- FIX_OVERFLOWS (count);
-
- if (count == 0)
- return FALSE;
-
- if (count < 0)
- return gtk_text_iter_backward_word_starts (iter, -count);
-
- if (!gtk_text_iter_forward_word_end (iter))
- return FALSE;
- --count;
-
- while (count > 0)
- {
- if (!gtk_text_iter_forward_word_end (iter))
- break;
- --count;
- }
- return TRUE;
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_forward_word_end,
+ gtk_text_iter_backward_word_starts);
}
/**
* @iter: a #GtkTextIter
* @count: number of times to move
*
- * Calls gtk_text_iter_backward_word_starts() up to @count times.
+ * Calls gtk_text_iter_backward_word_start() up to @count times.
*
* Return value: %TRUE if @iter moved and is not the end iterator
**/
gtk_text_iter_backward_word_starts (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_backward_word_start,
+ gtk_text_iter_forward_word_ends);
+}
- FIX_OVERFLOWS (count);
-
- if (count < 0)
- return gtk_text_iter_forward_word_ends (iter, -count);
+/**
+ * gtk_text_iter_forward_visible_word_end:
+ * @iter: a #GtkTextIter
+ *
+ * Moves forward to the next visible word end. (If @iter is currently on a
+ * word end, moves forward to the next one after that.) Word breaks
+ * are determined by Pango and should be correct for nearly any
+ * language (if not, the correct fix would be to the Pango word break
+ * algorithms).
+ *
+ * Return value: %TRUE if @iter moved and is not the end iterator
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_word_end (GtkTextIter *iter)
+{
+ return find_visible_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
+}
- if (!gtk_text_iter_backward_word_start (iter))
- return FALSE;
- --count;
+/**
+ * gtk_text_iter_backward_visible_word_start:
+ * @iter: a #GtkTextIter
+ *
+ * Moves backward to the previous visible word start. (If @iter is currently
+ * on a word start, moves backward to the next one after that.) Word breaks
+ * are determined by Pango and should be correct for nearly any
+ * language (if not, the correct fix would be to the Pango word break
+ * algorithms).
+ *
+ * Return value: %TRUE if @iter moved and is not the end iterator
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_word_start (GtkTextIter *iter)
+{
+ return find_visible_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
+}
- while (count > 0)
- {
- if (!gtk_text_iter_backward_word_start (iter))
- break;
- --count;
- }
- return TRUE;
+/**
+ * gtk_text_iter_forward_visible_word_ends:
+ * @iter: a #GtkTextIter
+ * @count: number of times to move
+ *
+ * Calls gtk_text_iter_forward_visible_word_end() up to @count times.
+ *
+ * Return value: %TRUE if @iter moved and is not the end iterator
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_word_ends (GtkTextIter *iter,
+ gint count)
+{
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_forward_visible_word_end,
+ gtk_text_iter_backward_visible_word_starts);
+}
+
+/**
+ * gtk_text_iter_backward_visible_word_starts
+ * @iter: a #GtkTextIter
+ * @count: number of times to move
+ *
+ * Calls gtk_text_iter_backward_visible_word_start() up to @count times.
+ *
+ * Return value: %TRUE if @iter moved and is not the end iterator
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_word_starts (GtkTextIter *iter,
+ gint count)
+{
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_backward_visible_word_start,
+ gtk_text_iter_forward_visible_word_ends);
}
/**
* gtk_text_iter_backward_sentence_start:
* @iter: a #GtkTextIter
*
- * Moves backward to the next sentence start; if @iter is already at
+ * Moves backward to the previous sentence start; if @iter is already at
* the start of a sentence, moves backward to the next one. Sentence
* boundaries are determined by Pango and should be correct for nearly
* any language (if not, the correct fix would be to the Pango text
gtk_text_iter_forward_sentence_ends (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- if (count == 0)
- return FALSE;
-
- if (count < 0)
- return gtk_text_iter_backward_sentence_starts (iter, -count);
-
- if (!gtk_text_iter_forward_sentence_end (iter))
- return FALSE;
- --count;
-
- while (count > 0)
- {
- if (!gtk_text_iter_forward_sentence_end (iter))
- break;
- --count;
- }
- return TRUE;
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_forward_sentence_end,
+ gtk_text_iter_backward_sentence_starts);
}
/**
gtk_text_iter_backward_sentence_starts (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- if (count < 0)
- return gtk_text_iter_forward_sentence_ends (iter, -count);
-
- if (!gtk_text_iter_backward_sentence_start (iter))
- return FALSE;
- --count;
-
- while (count > 0)
- {
- if (!gtk_text_iter_backward_sentence_start (iter))
- break;
- --count;
- }
- return TRUE;
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_backward_sentence_start,
+ gtk_text_iter_forward_sentence_ends);
}
static gboolean
gint len,
gint *found_offset,
gboolean already_moved_initially)
-{
+{
if (!already_moved_initially)
--offset;
*
* Like gtk_text_iter_forward_cursor_position(), but moves backward.
*
- * Return value: %TRUE if we moved and the new position is dereferenceable
+ * Return value: %TRUE if we moved
**/
gboolean
gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- FIX_OVERFLOWS (count);
-
- if (count == 0)
- return FALSE;
-
- if (count < 0)
- return gtk_text_iter_backward_cursor_positions (iter, -count);
-
- if (!gtk_text_iter_forward_cursor_position (iter))
- return FALSE;
- --count;
-
- while (count > 0)
- {
- if (!gtk_text_iter_forward_cursor_position (iter))
- break;
- --count;
- }
- return TRUE;
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_forward_cursor_position,
+ gtk_text_iter_backward_cursor_positions);
}
/**
gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
gint count)
{
- g_return_val_if_fail (iter != NULL, FALSE);
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_backward_cursor_position,
+ gtk_text_iter_forward_cursor_positions);
+}
- FIX_OVERFLOWS (count);
-
- if (count == 0)
- return FALSE;
+/**
+ * gtk_text_iter_forward_visible_cursor_position:
+ * @iter: a #GtkTextIter
+ *
+ * Moves @iter forward to the next visible cursor position. See
+ * gtk_text_iter_forward_cursor_position() for details.
+ *
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_cursor_position (GtkTextIter *iter)
+{
+ return find_visible_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
+}
- if (count < 0)
- return gtk_text_iter_forward_cursor_positions (iter, -count);
-
- if (!gtk_text_iter_backward_cursor_position (iter))
- return FALSE;
- --count;
+/**
+ * gtk_text_iter_backward_visible_cursor_position:
+ * @iter: a #GtkTextIter
+ *
+ * Moves @iter forward to the previous visible cursor position. See
+ * gtk_text_iter_backward_cursor_position() for details.
+ *
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_cursor_position (GtkTextIter *iter)
+{
+ return find_visible_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
+}
- while (count > 0)
- {
- if (!gtk_text_iter_backward_cursor_position (iter))
- break;
- --count;
- }
- return TRUE;
+/**
+ * gtk_text_iter_forward_visible_cursor_positions:
+ * @iter: a #GtkTextIter
+ * @count: number of positions to move
+ *
+ * Moves up to @count visible cursor positions. See
+ * gtk_text_iter_forward_cursor_position() for details.
+ *
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_cursor_positions (GtkTextIter *iter,
+ gint count)
+{
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_forward_visible_cursor_position,
+ gtk_text_iter_backward_visible_cursor_positions);
+}
+
+/**
+ * gtk_text_iter_backward_visible_cursor_positions:
+ * @iter: a #GtkTextIter
+ * @count: number of positions to move
+ *
+ * Moves up to @count visible cursor positions. See
+ * gtk_text_iter_backward_cursor_position() for details.
+ *
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_cursor_positions (GtkTextIter *iter,
+ gint count)
+{
+ return move_multiple_steps (iter, count,
+ gtk_text_iter_backward_visible_cursor_position,
+ gtk_text_iter_forward_visible_cursor_positions);
}
/**
iter_set_from_char_offset (real, real->line, char_on_line);
else
gtk_text_iter_forward_line (iter); /* set to start of next line */
-
+
check_invariants (iter);
}
g_return_if_fail (iter != NULL);
+ gtk_text_iter_set_line_offset (iter, 0);
+
pos = *iter;
/* For now we use a ludicrously slow implementation */
gtk_text_iter_forward_line (iter);
}
-static gint
-bytes_in_char (GtkTextIter *iter)
-{
- return g_unichar_to_utf8 (gtk_text_iter_get_char (iter), NULL);
-}
-
/**
* gtk_text_iter_set_visible_line_index:
* @iter: a #GtkTextIter
* in the index.
**/
void
-gtk_text_iter_set_visible_line_index (GtkTextIter *iter,
- gint byte_on_line)
+gtk_text_iter_set_visible_line_index (GtkTextIter *iter,
+ gint byte_on_line)
{
- gint bytes_seen = 0;
+ GtkTextRealIter *real;
+ gint offset = 0;
GtkTextIter pos;
-
- g_return_if_fail (iter != NULL);
+ GtkTextLineSegment *seg;
+ g_return_if_fail (iter != NULL);
+
+ gtk_text_iter_set_line_offset (iter, 0);
+
pos = *iter;
- /* For now we use a ludicrously slow implementation */
- while (bytes_seen < byte_on_line)
+ real = gtk_text_iter_make_real (&pos);
+
+ if (real == NULL)
+ return;
+
+ ensure_byte_offsets (real);
+
+ check_invariants (&pos);
+
+ seg = _gtk_text_iter_get_indexable_segment (&pos);
+
+ while (seg != NULL && byte_on_line > 0)
{
if (!_gtk_text_btree_char_is_invisible (&pos))
- bytes_seen += bytes_in_char (&pos);
-
- if (!gtk_text_iter_forward_char (&pos))
- break;
+ {
+ if (byte_on_line < seg->byte_count)
+ {
+ iter_set_from_byte_offset (real, real->line, offset + byte_on_line);
+ byte_on_line = 0;
+ break;
+ }
+ else
+ byte_on_line -= seg->byte_count;
+ }
- if (bytes_seen >= byte_on_line)
- break;
+ offset += seg->byte_count;
+ _gtk_text_iter_forward_indexable_segment (&pos);
+ seg = _gtk_text_iter_get_indexable_segment (&pos);
}
- if (bytes_seen > byte_on_line)
- g_warning ("%s: Incorrect visible byte index %d falls in the middle of a UTF-8 "
- "character; this will crash the text buffer. "
- "Byte indexes must refer to the start of a character.",
- G_STRLOC, byte_on_line);
-
- if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
+ if (byte_on_line == 0)
*iter = pos;
else
gtk_text_iter_forward_line (iter);
* @iter: a #GtkTextIter
* @line_number: line number (counted from 0)
*
- * Moves iterator @iter to the start of the line @line_number.
+ * Moves iterator @iter to the start of the line @line_number. If
+ * @line_number is negative or larger than the number of lines in the
+ * buffer, moves @iter to the start of the last line in the buffer.
*
**/
void
check_invariants (iter);
- line = _gtk_text_btree_get_line (real->tree, line_number, &real_line);
+ line = _gtk_text_btree_get_line_no_last (real->tree, line_number, &real_line);
iter_set_from_char_offset (real, line, 0);
*
* Sets @iter to point to @char_offset. @char_offset counts from the start
* of the entire text buffer, starting with 0.
- *
**/
void
gtk_text_iter_set_offset (GtkTextIter *iter,
* Moves @iter forward to the "end iterator," which points one past the last
* valid character in the buffer. gtk_text_iter_get_char() called on the
* end iterator returns 0, which is convenient for writing loops.
- *
**/
void
gtk_text_iter_forward_to_end (GtkTextIter *iter)
gtk_text_buffer_get_end_iter (buffer, iter);
}
+/* FIXME this and gtk_text_iter_forward_to_line_end() could be cleaned up
+ * and made faster. Look at iter_ends_line() for inspiration, perhaps.
+ * If all else fails we could cache the para delimiter pos in the iter.
+ * I think forward_to_line_end() actually gets called fairly often.
+ */
+static int
+find_paragraph_delimiter_for_line (GtkTextIter *iter)
+{
+ GtkTextIter end;
+ end = *iter;
+
+ if (_gtk_text_line_contains_end_iter (_gtk_text_iter_get_text_line (&end),
+ _gtk_text_iter_get_btree (&end)))
+ {
+ gtk_text_iter_forward_to_end (&end);
+ }
+ else
+ {
+ /* if we aren't on the last line, go forward to start of next line, then scan
+ * back for the delimiters on the previous line
+ */
+ gtk_text_iter_forward_line (&end);
+ gtk_text_iter_backward_char (&end);
+ while (!gtk_text_iter_ends_line (&end))
+ gtk_text_iter_backward_char (&end);
+ }
+
+ return gtk_text_iter_get_line_offset (&end);
+}
+
/**
* gtk_text_iter_forward_to_line_end:
* @iter: a #GtkTextIter
* return/newline in sequence, or the Unicode paragraph separator
* character. If the iterator is already at the paragraph delimiter
* characters, moves to the paragraph delimiter characters for the
- * next line.
+ * next line. If @iter is on the last line in the buffer, which does
+ * not end in paragraph delimiters, moves to the end iterator (end of
+ * the last line), and returns %FALSE.
*
* Return value: %TRUE if we moved and the new location is not the end iterator
**/
gint current_offset;
gint new_offset;
+
g_return_val_if_fail (iter != NULL, FALSE);
current_offset = gtk_text_iter_get_line_offset (iter);
- /* FIXME assumption that line ends in a newline; broken */
- new_offset = gtk_text_iter_get_chars_in_line (iter) - 1;
-
+ new_offset = find_paragraph_delimiter_for_line (iter);
+
if (current_offset < new_offset)
{
/* Move to end of this line. */
gtk_text_iter_set_line_offset (iter, new_offset);
- return TRUE;
+ return !gtk_text_iter_is_end (iter);
}
else
{
*/
if (!gtk_text_iter_ends_line (iter))
gtk_text_iter_forward_to_line_end (iter);
- return TRUE;
+ return !gtk_text_iter_is_end (iter);
}
else
return FALSE;
/**
* gtk_text_iter_forward_to_tag_toggle:
* @iter: a #GtkTextIter
- * @tag: a #GtkTextTag, or NULL
+ * @tag: a #GtkTextTag, or %NULL
*
* Moves forward to the next toggle (on or off) of the
* #GtkTextTag @tag, or to the next toggle of any tag if
- * @tag is NULL. If no matching tag toggles are found,
- * returns FALSE, otherwise TRUE. Does not return toggles
+ * @tag is %NULL. If no matching tag toggles are found,
+ * returns %FALSE, otherwise %TRUE. Does not return toggles
* located at @iter, only toggles after @iter. Sets @iter to
* the location of the toggle, or to the end of the buffer
* if no toggle is found.
current_line = real->line;
next_line = _gtk_text_line_next_could_contain_tag (current_line,
- real->tree, tag);
+ real->tree, tag);
while (_gtk_text_iter_forward_indexable_segment (iter))
{
current_line = real->line;
next_line = _gtk_text_line_next_could_contain_tag (current_line,
- real->tree,
- tag);
+ real->tree,
+ tag);
}
if (gtk_text_iter_toggles_tag (iter, tag))
/**
* gtk_text_iter_backward_to_tag_toggle:
* @iter: a #GtkTextIter
- * @tag: a #GtkTextTag, or NULL
+ * @tag: a #GtkTextTag, or %NULL
*
* Moves backward to the next toggle (on or off) of the
* #GtkTextTag @tag, or to the next toggle of any tag if
- * @tag is NULL. If no matching tag toggles are found,
- * returns FALSE, otherwise TRUE. Does not return toggles
+ * @tag is %NULL. If no matching tag toggles are found,
+ * returns %FALSE, otherwise %TRUE. Does not return toggles
* located at @iter, only toggles before @iter. Sets @iter
* to the location of the toggle, or the start of the buffer
* if no toggle is found.
* gtk_text_iter_forward_search:
* @iter: start of search
* @str: a search string
- * @visible_only: if %TRUE, search only visible text
- * @slice: if %TRUE, @str contains 0xFFFC when we want to match widgets, pixbufs
+ * @flags: flags affecting how the search is done
* @match_start: return location for start of match, or %NULL
* @match_end: return location for end of match, or %NULL
* @limit: bound for the search, or %NULL for the end of the buffer
*
- * Searches forward for @str. Any match is returned as the range @match_start,
- * @match_end. If you specify @visible_only or @slice, the match may have
- * invisible text, pixbufs, or child widgets interspersed in @str.
+ * Searches forward for @str. Any match is returned by setting
+ * @match_start to the first character of the match and @match_end to the
+ * first character after the match. The search will not continue past
+ * @limit. Note that a search is a linear or O(n) operation, so you
+ * may wish to use @limit to avoid locking up your UI on large
+ * buffers.
*
+ * If the #GTK_TEXT_SEARCH_VISIBLE_ONLY flag is present, the match may
+ * have invisible text interspersed in @str. i.e. @str will be a
+ * possibly-noncontiguous subsequence of the matched range. similarly,
+ * if you specify #GTK_TEXT_SEARCH_TEXT_ONLY, the match may have
+ * pixbufs or child widgets mixed inside the matched range. If these
+ * flags are not given, the match must be exact; the special 0xFFFC
+ * character in @str will match embedded pixbufs or child widgets.
+ *
* Return value: whether a match was found
**/
gboolean
gtk_text_iter_forward_search (const GtkTextIter *iter,
const gchar *str,
- gboolean visible_only,
- gboolean slice,
+ GtkTextSearchFlags flags,
GtkTextIter *match_start,
GtkTextIter *match_end,
const GtkTextIter *limit)
GtkTextIter match;
gboolean retval = FALSE;
GtkTextIter search;
-
+ gboolean visible_only;
+ gboolean slice;
+
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (str != NULL, FALSE);
return FALSE;
}
+ visible_only = (flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
+ slice = (flags & GTK_TEXT_SEARCH_TEXT_ONLY) == 0;
+
/* locate all lines */
lines = strbreakup (str, "\n", -1);
{
if (limit == NULL ||
(limit &&
- gtk_text_iter_compare (&end, limit) < 0))
+ gtk_text_iter_compare (&end, limit) <= 0))
{
retval = TRUE;
g_strfreev (win->lines);
}
-static gchar*
-my_strrstr (const gchar *haystack,
- const gchar *needle)
-{
- /* FIXME GLib should have a nice implementation in it, this
- * is slow-ass crap.
- */
-
- gint haystack_len = strlen (haystack);
- gint needle_len = strlen (needle);
- const gchar *needle_end = needle + needle_len;
- const gchar *haystack_rend = haystack - 1;
- const gchar *needle_rend = needle - 1;
- const gchar *p;
-
- p = haystack + haystack_len;
- while (p != haystack)
- {
- const gchar *n = needle_end - 1;
- const gchar *s = p - 1;
- while (s != haystack_rend &&
- n != needle_rend &&
- *s == *n)
- {
- --n;
- --s;
- }
-
- if (n == needle_rend)
- return (gchar*)++s;
-
- --p;
- }
-
- return NULL;
-}
-
/**
* gtk_text_iter_backward_search:
* @iter: a #GtkTextIter where the search begins
* @str: search string
- * @visible_only: if %TRUE search only visible text
- * @slice: if %TRUE the search string contains 0xFFFC to match pixbufs, widgets
+ * @flags: bitmask of flags affecting the search
* @match_start: return location for start of match, or %NULL
* @match_end: return location for end of match, or %NULL
* @limit: location of last possible @match_start, or %NULL for start of buffer
gboolean
gtk_text_iter_backward_search (const GtkTextIter *iter,
const gchar *str,
- gboolean visible_only,
- gboolean slice,
+ GtkTextSearchFlags flags,
GtkTextIter *match_start,
GtkTextIter *match_end,
const GtkTextIter *limit)
gint n_lines;
LinesWindow win;
gboolean retval = FALSE;
-
+ gboolean visible_only;
+ gboolean slice;
+
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (str != NULL, FALSE);
return FALSE;
}
+ visible_only = (flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
+ slice = (flags & GTK_TEXT_SEARCH_TEXT_ONLY) == 0;
+
/* locate all lines */
lines = strbreakup (str, "\n", -1);
* end in '\n', so this will only match at the
* end of the first line, which is correct.
*/
- first_line_match = my_strrstr (*win.lines, *lines);
+ first_line_match = g_strrstr (*win.lines, *lines);
if (first_line_match &&
vectors_equal_ignoring_trailing (lines + 1, win.lines + 1))
* @start: start of range
* @end: end of range
*
- * @start and @end must be in order, unlike most text buffer
- * functions, for efficiency reasons. The function returns %TRUE if
- * @iter falls in the range [@start, @end).
+ * Checks whether @iter falls in the range [@start, @end).
+ * @start and @end must be in ascending order.
*
* Return value: %TRUE if @iter is in the range
**/
const GtkTextIter *start,
const GtkTextIter *end)
{
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+ g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE);
+
return gtk_text_iter_compare (iter, start) >= 0 &&
gtk_text_iter_compare (iter, end) < 0;
}
g_return_if_fail (tree != NULL);
line = _gtk_text_btree_get_line_at_char (tree, char_index,
- &line_start, &real_char_index);
+ &line_start, &real_char_index);
iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
void
_gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
- GtkTextIter *iter,
- gint line_number,
- gint char_on_line)
+ GtkTextIter *iter,
+ gint line_number,
+ gint char_on_line)
{
GtkTextRealIter *real = (GtkTextRealIter*)iter;
GtkTextLine *line;
g_return_if_fail (iter != NULL);
g_return_if_fail (tree != NULL);
- line = _gtk_text_btree_get_line (tree, line_number, &real_line);
+ line = _gtk_text_btree_get_line_no_last (tree, line_number, &real_line);
iter_init_from_char_offset (iter, tree, line, char_on_line);
g_return_if_fail (iter != NULL);
g_return_if_fail (tree != NULL);
- line = _gtk_text_btree_get_line (tree, line_number, &real_line);
+ line = _gtk_text_btree_get_line_no_last (tree, line_number, &real_line);
iter_init_from_byte_offset (iter, tree, line, byte_index);
gboolean
_gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree,
- GtkTextIter *iter,
- GtkTextTag *tag)
+ GtkTextIter *iter,
+ GtkTextTag *tag)
{
GtkTextLine *line;
else
{
iter_init_from_byte_offset (iter, tree, line, 0);
- gtk_text_iter_forward_to_tag_toggle (iter, tag);
+
+ if (!gtk_text_iter_toggles_tag (iter, tag))
+ gtk_text_iter_forward_to_tag_toggle (iter, tag);
+
check_invariants (iter);
return TRUE;
}
gboolean
_gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree,
- GtkTextIter *iter,
- GtkTextTag *tag)
+ GtkTextIter *iter,
+ GtkTextTag *tag)
{
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (tree != NULL, FALSE);
gboolean
_gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
- GtkTextIter *iter,
- const gchar *mark_name)
+ GtkTextIter *iter,
+ const gchar *mark_name)
{
GtkTextMark *mark;
void
_gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
- GtkTextIter *iter,
- GtkTextMark *mark)
+ GtkTextIter *iter,
+ GtkTextMark *mark)
{
GtkTextLineSegment *seg;
void
_gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree *tree,
- GtkTextIter *iter,
- GtkTextChildAnchor *anchor)
+ GtkTextIter *iter,
+ GtkTextChildAnchor *anchor)
{
GtkTextLineSegment *seg;
g_return_if_fail (iter != NULL);
g_return_if_fail (tree != NULL);
g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
+
+ seg = anchor->segment;
- seg = anchor->segment;
+ g_assert (seg->body.child.line != NULL);
iter_init_from_segment (iter, tree,
seg->body.child.line, seg);
g_error ("wrong char index was cached");
}
}
+
+ if (_gtk_text_line_is_last (real->line, real->tree))
+ g_error ("Iterator was on last line (past the end iterator)");
}
+#define __GTK_TEXT_ITER_C__
+#include "gtkaliasdef.c"