X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextiter.c;h=37d4c2d4d2ddb7b98113acc6d514fd97b0cf352e;hb=ab9348b98c2f9c265f25064eac5ec0057156337c;hp=acf977137af610a3094fafa9f550f961d70beac9;hpb=bacb35ba3cdc5d089d7cc07006c54cff957081d7;p=~andy%2Fgtk diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c index acf977137..37d4c2d4d 100644 --- a/gtk/gtktextiter.c +++ b/gtk/gtktextiter.c @@ -24,16 +24,21 @@ * 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 -#include + +#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; @@ -62,6 +67,10 @@ struct _GtkTextRealIter 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 @@ -92,13 +101,14 @@ iter_set_from_byte_offset (GtkTextRealIter *iter, { iter_set_common (iter, line); - _gtk_text_line_byte_locate (iter->line, - byte_offset, - &iter->segment, - &iter->any_segment, - &iter->segment_byte_offset, - &iter->line_byte_offset); - + if (!_gtk_text_line_byte_locate (iter->line, + byte_offset, + &iter->segment, + &iter->any_segment, + &iter->segment_byte_offset, + &iter->line_byte_offset)) + g_error ("Byte index %d is off the end of the line", + byte_offset); } static void @@ -108,12 +118,14 @@ iter_set_from_char_offset (GtkTextRealIter *iter, { iter_set_common (iter, line); - _gtk_text_line_char_locate (iter->line, - char_offset, - &iter->segment, - &iter->any_segment, - &iter->segment_char_offset, - &iter->line_char_offset); + if (!_gtk_text_line_char_locate (iter->line, + char_offset, + &iter->segment, + &iter->any_segment, + &iter->segment_char_offset, + &iter->line_char_offset)) + g_error ("Char offset %d is off the end of the line", + char_offset); } static void @@ -261,6 +273,13 @@ iter_init_from_byte_offset (GtkTextIter *iter, iter_set_from_byte_offset (real, line, line_byte_offset); + if (real->segment->type == >k_text_char_type && + (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80) + g_warning ("Incorrect line 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.", + line_byte_offset); + return real; } @@ -281,24 +300,12 @@ iter_init_from_char_offset (GtkTextIter *iter, 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) { @@ -313,28 +320,6 @@ adjust_line_number (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) { @@ -342,7 +327,7 @@ ensure_char_offsets (GtkTextRealIter *iter) { g_assert (iter->line_byte_offset >= 0); - __gtk_text_line_byte_to_char_offsets (iter->line, + _gtk_text_line_byte_to_char_offsets (iter->line, iter->line_byte_offset, &iter->line_char_offset, &iter->segment_char_offset); @@ -356,7 +341,7 @@ ensure_byte_offsets (GtkTextRealIter *iter) { g_assert (iter->line_char_offset >= 0); - __gtk_text_line_char_to_byte_offsets (iter->line, + _gtk_text_line_char_to_byte_offsets (iter->line, iter->line_char_offset, &iter->line_byte_offset, &iter->segment_byte_offset); @@ -369,22 +354,22 @@ is_segment_start (GtkTextRealIter *real) return real->segment_byte_offset == 0 || real->segment_char_offset == 0; } -#if 1 +#ifdef G_ENABLE_DEBUG static void check_invariants (const GtkTextIter *iter) { if (gtk_debug_flags & GTK_DEBUG_TEXT) - gtk_text_iter_check (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 **/ @@ -409,7 +394,7 @@ gtk_text_iter_get_buffer (const GtkTextIter *iter) * 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 (GtkTextIter i = j;). The * function is used by language bindings. @@ -423,7 +408,7 @@ gtk_text_iter_copy (const GtkTextIter *iter) g_return_val_if_fail (iter != NULL, NULL); - new_iter = g_new (GtkTextIter, 1); + new_iter = g_slice_new (GtkTextIter); *new_iter = *iter; @@ -438,22 +423,34 @@ gtk_text_iter_copy (const GtkTextIter *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* -gtk_text_iter_get_indexable_segment (const GtkTextIter *iter) +_gtk_text_iter_get_indexable_segment (const GtkTextIter *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); @@ -468,11 +465,11 @@ gtk_text_iter_get_indexable_segment (const GtkTextIter *iter) } GtkTextLineSegment* -gtk_text_iter_get_any_segment (const GtkTextIter *iter) +_gtk_text_iter_get_any_segment (const GtkTextIter *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); @@ -487,7 +484,7 @@ gtk_text_iter_get_any_segment (const GtkTextIter *iter) } gint -gtk_text_iter_get_segment_byte (const GtkTextIter *iter) +_gtk_text_iter_get_segment_byte (const GtkTextIter *iter) { GtkTextRealIter *real; @@ -506,7 +503,7 @@ gtk_text_iter_get_segment_byte (const GtkTextIter *iter) } gint -gtk_text_iter_get_segment_char (const GtkTextIter *iter) +_gtk_text_iter_get_segment_char (const GtkTextIter *iter) { GtkTextRealIter *real; @@ -527,11 +524,11 @@ gtk_text_iter_get_segment_char (const GtkTextIter *iter) /* This function does not require a still-valid iterator */ GtkTextLine* -gtk_text_iter_get_text_line (const GtkTextIter *iter) +_gtk_text_iter_get_text_line (const GtkTextIter *iter) { const GtkTextRealIter *real; - g_return_val_if_fail (iter != NULL, 0); + g_return_val_if_fail (iter != NULL, NULL); real = (const GtkTextRealIter*)iter; @@ -541,11 +538,11 @@ gtk_text_iter_get_text_line (const GtkTextIter *iter) /* This function does not require a still-valid iterator */ GtkTextBTree* -gtk_text_iter_get_btree (const GtkTextIter *iter) +_gtk_text_iter_get_btree (const GtkTextIter *iter) { const GtkTextRealIter *real; - g_return_val_if_fail (iter != NULL, 0); + g_return_val_if_fail (iter != NULL, NULL); real = (const GtkTextRealIter*)iter; @@ -640,7 +637,6 @@ gtk_text_iter_get_line (const GtkTextIter *iter) gint gtk_text_iter_get_line_offset (const GtkTextIter *iter) { - GtkTextRealIter *real; g_return_val_if_fail (iter != NULL, 0); @@ -673,7 +669,7 @@ gint gtk_text_iter_get_line_index (const GtkTextIter *iter) { GtkTextRealIter *real; - + g_return_val_if_fail (iter != NULL, 0); real = gtk_text_iter_make_surreal (iter); @@ -688,6 +684,135 @@ gtk_text_iter_get_line_index (const GtkTextIter *iter) return real->line_byte_offset; } +/** + * gtk_text_iter_get_visible_line_offset: + * @iter: a #GtkTextIter + * + * Returns the offset in characters from the start of the + * line to the given @iter, not counting characters that + * are invisible due to tags with the "invisible" flag + * toggled on. + * + * Return value: offset in visible characters from the start of the line + **/ +gint +gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter) +{ + GtkTextRealIter *real; + gint vis_offset; + GtkTextLineSegment *seg; + GtkTextIter pos; + + g_return_val_if_fail (iter != NULL, 0); + + real = gtk_text_iter_make_real (iter); + + if (real == NULL) + return 0; + + 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, + 0); + + seg = _gtk_text_iter_get_indexable_segment (&pos); + + while (seg != real->segment) + { + /* This is a pretty expensive call, making the + * whole function pretty lame; we could keep track + * of current invisibility state by looking at toggle + * segments as we loop, and then call this function + * only once per line, in order to speed up the loop + * quite a lot. + */ + if (_gtk_text_btree_char_is_invisible (&pos)) + vis_offset -= seg->char_count; + + _gtk_text_iter_forward_indexable_segment (&pos); + + seg = _gtk_text_iter_get_indexable_segment (&pos); + } + + if (_gtk_text_btree_char_is_invisible (&pos)) + vis_offset -= real->segment_char_offset; + + return vis_offset; +} + + +/** + * gtk_text_iter_get_visible_line_index: + * @iter: a #GtkTextIter + * + * Returns the number of bytes from the start of the + * line to the given @iter, not counting bytes that + * are invisible due to tags with the "invisible" flag + * toggled on. + * + * Return value: byte index of @iter with respect to the start of the line + **/ +gint +gtk_text_iter_get_visible_line_index (const GtkTextIter *iter) +{ + GtkTextRealIter *real; + gint vis_offset; + GtkTextLineSegment *seg; + GtkTextIter pos; + + g_return_val_if_fail (iter != NULL, 0); + + real = gtk_text_iter_make_real (iter); + + if (real == NULL) + return 0; + + 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, + 0); + + seg = _gtk_text_iter_get_indexable_segment (&pos); + + while (seg != real->segment) + { + /* This is a pretty expensive call, making the + * whole function pretty lame; we could keep track + * of current invisibility state by looking at toggle + * segments as we loop, and then call this function + * only once per line, in order to speed up the loop + * quite a lot. + */ + if (_gtk_text_btree_char_is_invisible (&pos)) + vis_offset -= seg->byte_count; + + _gtk_text_iter_forward_indexable_segment (&pos); + + seg = _gtk_text_iter_get_indexable_segment (&pos); + } + + if (_gtk_text_btree_char_is_invisible (&pos)) + vis_offset -= real->segment_byte_offset; + + return vis_offset; +} + /* * Dereferencing */ @@ -697,7 +822,7 @@ gtk_text_iter_get_line_index (const GtkTextIter *iter) * @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. @@ -720,7 +845,7 @@ gtk_text_iter_get_char (const GtkTextIter *iter) check_invariants (iter); - if (gtk_text_iter_is_last (iter)) + if (gtk_text_iter_is_end (iter)) return 0; else if (real->segment->type == >k_text_char_type) { @@ -843,9 +968,9 @@ gtk_text_iter_get_visible_text (const GtkTextIter *start, * 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 **/ @@ -873,9 +998,9 @@ gtk_text_iter_get_pixbuf (const GtkTextIter *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 **/ @@ -946,10 +1071,10 @@ gtk_text_iter_get_marks (const GtkTextIter *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 @@ -1004,11 +1129,11 @@ gtk_text_iter_get_toggled_tags (const GtkTextIter *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 * start of the tagged range; * gtk_text_iter_has_tag () tells you whether an iterator is * within a tagged range. @@ -1050,11 +1175,11 @@ gtk_text_iter_begins_tag (const GtkTextIter *iter, /** * 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 * end of the tagged range; * gtk_text_iter_has_tag () tells you whether an iterator is * within a tagged range. @@ -1097,7 +1222,7 @@ gtk_text_iter_ends_tag (const GtkTextIter *iter, /** * 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 @@ -1106,8 +1231,8 @@ gtk_text_iter_ends_tag (const GtkTextIter *iter, * 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; @@ -1141,13 +1266,13 @@ gtk_text_iter_toggles_tag (const GtkTextIter *iter, * @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; @@ -1164,13 +1289,13 @@ gtk_text_iter_has_tag (const GtkTextIter *iter, 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); } } @@ -1201,15 +1326,11 @@ gtk_text_iter_get_tags (const GtkTextIter *iter) /* 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) @@ -1227,14 +1348,21 @@ gtk_text_iter_get_tags (const GtkTextIter *iter) /** * 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 @@ -1244,6 +1372,8 @@ gtk_text_iter_editable (const GtkTextIter *iter, GtkTextAttributes *values; gboolean retval; + g_return_val_if_fail (iter != NULL, FALSE); + values = gtk_text_attributes_new (); values->editable = default_setting; @@ -1257,28 +1387,68 @@ gtk_text_iter_editable (const GtkTextIter *iter, 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 **/ -gchar* +PangoLanguage * gtk_text_iter_get_language (const GtkTextIter *iter) { GtkTextAttributes *values; - gchar *retval; - + PangoLanguage *retval; + values = gtk_text_attributes_new (); gtk_text_iter_get_attributes (iter, values); - retval = g_strdup (values->language); + retval = values->language; gtk_text_attributes_unref (values); @@ -1289,8 +1459,8 @@ gtk_text_iter_get_language (const GtkTextIter *iter) * gtk_text_iter_starts_line: * @iter: an iterator * - * Returns TRUE if @iter begins a newline-terminated line, - * i.e. gtk_text_iter_get_line_offset () would return 0. + * 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 * the offset, it just has to see whether it's 0. @@ -1326,25 +1496,24 @@ gtk_text_iter_starts_line (const GtkTextIter *iter) * 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 @@ -1354,16 +1523,29 @@ gtk_text_iter_ends_line (const GtkTextIter *iter) 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 FALSE; + return TRUE; return gtk_text_iter_get_char (&tmp) != '\r'; } @@ -1372,18 +1554,18 @@ gtk_text_iter_ends_line (const GtkTextIter *iter) } /** - * gtk_text_iter_is_last: + * gtk_text_iter_is_end: * @iter: an iterator * - * Returns TRUE if @iter is the end iterator, i.e. one past the last - * dereferenceable iterator in the buffer. gtk_text_iter_is_last () is + * 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. * * Return value: whether @iter is the end iterator **/ gboolean -gtk_text_iter_is_last (const GtkTextIter *iter) +gtk_text_iter_is_end (const GtkTextIter *iter) { GtkTextRealIter *real; @@ -1396,20 +1578,32 @@ gtk_text_iter_is_last (const GtkTextIter *iter) 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_first: + * 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 **/ gboolean -gtk_text_iter_is_first (const GtkTextIter *iter) +gtk_text_iter_is_start (const GtkTextIter *iter) { return gtk_text_iter_get_offset (iter) == 0; } @@ -1419,7 +1613,7 @@ gtk_text_iter_is_first (const GtkTextIter *iter) * @iter: an iterator * * Returns the number of characters in the line containing @iter, - * including the terminating newline. + * including the paragraph delimiters. * * Return value: number of characters in the line **/ @@ -1430,7 +1624,7 @@ gtk_text_iter_get_chars_in_line (const GtkTextIter *iter) 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); @@ -1443,7 +1637,7 @@ gtk_text_iter_get_chars_in_line (const GtkTextIter *iter) { /* We can start at the segments we've already found. */ count = real->line_char_offset - real->segment_char_offset; - seg = gtk_text_iter_get_indexable_segment (iter); + seg = _gtk_text_iter_get_indexable_segment (iter); } else { @@ -1460,6 +1654,60 @@ gtk_text_iter_get_chars_in_line (const GtkTextIter *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; +} + +/** + * gtk_text_iter_get_bytes_in_line: + * @iter: an iterator + * + * Returns the number of bytes in the line containing @iter, + * including the paragraph delimiters. + * + * Return value: number of bytes in the line + **/ +gint +gtk_text_iter_get_bytes_in_line (const GtkTextIter *iter) +{ + GtkTextRealIter *real; + gint count; + GtkTextLineSegment *seg; + + g_return_val_if_fail (iter != NULL, 0); + + real = gtk_text_iter_make_surreal (iter); + + if (real == NULL) + return 0; + + check_invariants (iter); + + if (real->line_byte_offset >= 0) + { + /* We can start at the segments we've already found. */ + count = real->line_byte_offset - real->segment_byte_offset; + seg = _gtk_text_iter_get_indexable_segment (iter); + } + else + { + /* count whole line. */ + seg = real->line->segments; + count = 0; + } + + while (seg != NULL) + { + count += seg->byte_count; + + 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; } @@ -1470,16 +1718,18 @@ gtk_text_iter_get_chars_in_line (const GtkTextIter *iter) * * Computes the effect of any tags applied to this spot in the * text. The @values parameter should be initialized to the default - * settings you wish to use if no tags are in effect. + * settings you wish to use if no tags are in effect. You'd typically + * obtain the defaults from gtk_text_view_get_default_attributes(). + * * gtk_text_iter_get_attributes () will modify @values, applying the * effects of any tags present at @iter. If any tags affected @values, - * the function returns TRUE. + * the function returns %TRUE. * - * Return value: TRUE if @values was modified + * Return value: %TRUE if @values was modified **/ gboolean gtk_text_iter_get_attributes (const GtkTextIter *iter, - GtkTextAttributes *values) + GtkTextAttributes *values) { GtkTextTag** tags; gint tag_count = 0; @@ -1490,18 +1740,14 @@ gtk_text_iter_get_attributes (const GtkTextIter *iter, /* 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); + _gtk_text_attributes_fill_from_tags (values, + tags, + tag_count); g_free (tags); @@ -1515,18 +1761,23 @@ gtk_text_iter_get_attributes (const GtkTextIter *iter, /* 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; @@ -1545,32 +1796,23 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real) } 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) @@ -1611,6 +1853,7 @@ backward_line_leaving_caches_unmodified (GtkTextRealIter *real) return FALSE; } } +#endif /* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS * DEREFERENCEABLE) @@ -1628,7 +1871,7 @@ forward_char (GtkTextRealIter *real) { /* Need to move to the next segment; if no next segment, need to move to next line. */ - return gtk_text_iter_forward_indexable_segment (iter); + return _gtk_text_iter_forward_indexable_segment (iter); } else { @@ -1664,7 +1907,7 @@ forward_char (GtkTextRealIter *real) check_invariants ((GtkTextIter*)real); - if (gtk_text_iter_is_last ((GtkTextIter*)real)) + if (gtk_text_iter_is_end ((GtkTextIter*)real)) return FALSE; else return TRUE; @@ -1672,7 +1915,7 @@ forward_char (GtkTextRealIter *real) } gboolean -gtk_text_iter_forward_indexable_segment (GtkTextIter *iter) +_gtk_text_iter_forward_indexable_segment (GtkTextIter *iter) { /* Need to move to the next segment; if no next segment, need to move to next line. */ @@ -1736,7 +1979,7 @@ gtk_text_iter_forward_indexable_segment (GtkTextIter *iter) check_invariants (iter); - return TRUE; + return !gtk_text_iter_is_end (iter); } else { @@ -1755,17 +1998,23 @@ gtk_text_iter_forward_indexable_segment (GtkTextIter *iter) check_invariants (iter); - if (gtk_text_iter_is_last (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; } } @@ -1795,7 +2044,7 @@ at_last_indexable_segment (GtkTextRealIter *real) * ends up on a different segment if it returns TRUE) */ gboolean -gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) +_gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) { /* Move to the start of the previous segment; if no previous * segment, to the last segment in the previous line. This is @@ -1838,7 +2087,7 @@ gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) { /* Go forward to last indexable segment in line. */ while (!at_last_indexable_segment (real)) - gtk_text_iter_forward_indexable_segment (iter); + _gtk_text_iter_forward_indexable_segment (iter); check_invariants (iter); @@ -1852,7 +2101,7 @@ gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) * segment just before our current segment. */ g_assert (seg != real->segment); - while (seg != real->segment) + do { prev_seg = seg; prev_any_seg = any_seg; @@ -1862,6 +2111,7 @@ gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) while (seg->char_count == 0) seg = seg->next; } + while (seg != real->segment); g_assert (prev_seg != NULL); g_assert (prev_any_seg != NULL); @@ -1932,10 +2182,10 @@ gtk_text_iter_backward_indexable_segment (GtkTextIter *iter) * 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) @@ -1959,9 +2209,9 @@ 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 @@ -2001,7 +2251,7 @@ gtk_text_iter_backward_char (GtkTextIter *iter) * 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 **/ @@ -2012,6 +2262,8 @@ gtk_text_iter_forward_chars (GtkTextIter *iter, gint count) g_return_val_if_fail (iter != NULL, FALSE); + FIX_OVERFLOWS (count); + real = gtk_text_iter_make_real (iter); if (real == NULL) @@ -2053,7 +2305,7 @@ gtk_text_iter_forward_chars (GtkTextIter *iter, gint count) /* Return FALSE if we're on the non-dereferenceable end * iterator. */ - if (gtk_text_iter_is_last (iter)) + if (gtk_text_iter_is_end (iter)) return FALSE; else return TRUE; @@ -2069,8 +2321,8 @@ gtk_text_iter_forward_chars (GtkTextIter *iter, gint count) * 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 * @@ -2082,6 +2334,8 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, gint count) g_return_val_if_fail (iter != NULL, FALSE); + FIX_OVERFLOWS (count); + real = gtk_text_iter_make_real (iter); if (real == NULL) @@ -2094,34 +2348,35 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, gint count) 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); @@ -2149,6 +2404,7 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, gint 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); @@ -2217,10 +2473,10 @@ gtk_text_iter_backward_text_chars (GtkTextIter *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 **/ @@ -2230,7 +2486,7 @@ gtk_text_iter_forward_line (GtkTextIter *iter) GtkTextRealIter *real; g_return_val_if_fail (iter != NULL, FALSE); - + real = gtk_text_iter_make_real (iter); if (real == NULL) @@ -2245,13 +2501,18 @@ gtk_text_iter_forward_line (GtkTextIter *iter) check_invariants (iter); - if (gtk_text_iter_is_last (iter)) + if (gtk_text_iter_is_end (iter)) return FALSE; else return TRUE; } 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; } @@ -2261,11 +2522,11 @@ gtk_text_iter_forward_line (GtkTextIter *iter) * 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.) * @@ -2317,7 +2578,7 @@ gtk_text_iter_backward_line (GtkTextIter *iter) /* 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); @@ -2332,9 +2593,27 @@ gtk_text_iter_backward_line (GtkTextIter *iter) return TRUE; } + +/** + * gtk_text_iter_forward_lines: + * @iter: a #GtkTextIter + * @count: number of lines to move forward + * + * Moves @count 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 + **/ gboolean gtk_text_iter_forward_lines (GtkTextIter *iter, gint count) { + FIX_OVERFLOWS (count); + if (count < 0) return gtk_text_iter_backward_lines (iter, 0 - count); else if (count == 0) @@ -2348,22 +2627,44 @@ gtk_text_iter_forward_lines (GtkTextIter *iter, gint count) { 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_last (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); } } +/** + * gtk_text_iter_backward_lines: + * @iter: a #GtkTextIter + * @count: number of lines to move backward + * + * Moves @count 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 + **/ gboolean gtk_text_iter_backward_lines (GtkTextIter *iter, gint count) { + FIX_OVERFLOWS (count); + if (count < 0) return gtk_text_iter_forward_lines (iter, 0 - count); else if (count == 0) @@ -2384,20 +2685,186 @@ gtk_text_iter_backward_lines (GtkTextIter *iter, gint count) } } +/** + * 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, gint len, - gint *found_offset); + gint *found_offset, + gboolean already_moved_initially); + +typedef gboolean (* TestLogAttrFunc) (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len); + +/* Word funcs */ static gboolean find_word_end_func (const PangoLogAttr *attrs, gint offset, gint min_offset, gint len, - gint *found_offset) + gint *found_offset, + gboolean already_moved_initially) { - ++offset; /* We always go to the NEXT word end */ + if (!already_moved_initially) + ++offset; /* Find end of next word */ while (offset < min_offset + len && @@ -2413,8 +2880,7 @@ static gboolean is_word_end_func (const PangoLogAttr *attrs, gint offset, gint min_offset, - gint len, - gint *found_offset) + gint len) { return attrs[offset].is_word_end; } @@ -2424,9 +2890,11 @@ find_word_start_func (const PangoLogAttr *attrs, gint offset, gint min_offset, gint len, - gint *found_offset) + gint *found_offset, + gboolean already_moved_initially) { - --offset; /* We always go to the NEXT word start */ + if (!already_moved_initially) + --offset; /* Find start of prev word */ while (offset >= min_offset && @@ -2442,8 +2910,7 @@ static gboolean is_word_start_func (const PangoLogAttr *attrs, gint offset, gint min_offset, - gint len, - gint *found_offset) + gint len) { return attrs[offset].is_word_start; } @@ -2452,23 +2919,99 @@ static gboolean inside_word_func (const PangoLogAttr *attrs, gint offset, gint min_offset, - gint len, - gint *found_offset) + gint len) { /* Find next word start or end */ while (offset >= 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 */ + +static gboolean +find_sentence_end_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len, + gint *found_offset, + gboolean already_moved_initially) +{ + if (!already_moved_initially) + ++offset; + + /* Find end of next sentence */ + while (offset < min_offset + len && + !attrs[offset].is_sentence_end) + ++offset; + + *found_offset = offset; + + return offset < min_offset + len; +} + +static gboolean +is_sentence_end_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len) +{ + return attrs[offset].is_sentence_end; +} + +static gboolean +find_sentence_start_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len, + gint *found_offset, + gboolean already_moved_initially) +{ + if (!already_moved_initially) + --offset; + + /* Find start of prev sentence */ + while (offset >= min_offset && + !attrs[offset].is_sentence_start) + --offset; + + *found_offset = offset; + + return offset >= min_offset; +} + +static gboolean +is_sentence_start_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len) +{ + return attrs[offset].is_sentence_start; +} + +static gboolean +inside_sentence_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len) +{ + /* Find next sentence start or end */ + while (offset >= min_offset && + !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end)) + --offset; + + return attrs[offset].is_sentence_start; } static gboolean test_log_attrs (const GtkTextIter *iter, - FindLogAttrFunc func, - gint *found_offset) + TestLogAttrFunc func) { - gchar *paragraph; gint char_len; const PangoLogAttr *attrs; int offset; @@ -2481,10 +3024,44 @@ test_log_attrs (const GtkTextIter *iter, 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 (attrs && offset <= char_len) + result = (* func) (attrs, offset, 0, char_len); + + return result; +} + +static gboolean +find_line_log_attrs (const GtkTextIter *iter, + FindLogAttrFunc func, + gint *found_offset, + gboolean already_moved_initially) +{ + gint char_len; + const PangoLogAttr *attrs; + int offset; + gboolean result = FALSE; + + g_return_val_if_fail (iter != NULL, FALSE); + + attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter), + iter, &char_len); + + offset = gtk_text_iter_get_line_offset (iter); - if (offset < char_len) - result = (* func) (attrs, offset, 0, char_len, found_offset); + /* 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 (attrs) + result = (* func) (attrs, offset, 0, char_len, found_offset, + already_moved_initially); return result; } @@ -2493,7 +3070,8 @@ test_log_attrs (const GtkTextIter *iter, static gboolean find_by_log_attrs (GtkTextIter *iter, FindLogAttrFunc func, - gboolean forward) + gboolean forward, + gboolean already_moved_initially) { GtkTextIter orig; gint offset = 0; @@ -2502,136 +3080,718 @@ find_by_log_attrs (GtkTextIter *iter, g_return_val_if_fail (iter != NULL, FALSE); orig = *iter; - - found = test_log_attrs (iter, func, &offset); + + found = find_line_log_attrs (iter, func, &offset, already_moved_initially); if (!found) { if (forward) { if (gtk_text_iter_forward_line (iter)) - return find_by_log_attrs (iter, func, forward); + return find_by_log_attrs (iter, func, forward, + TRUE); else return FALSE; } else - { - if (gtk_text_iter_backward_line (iter)) - return find_by_log_attrs (iter, func, forward); + { + /* 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) && - !gtk_text_iter_is_last (iter); + (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 + * + * Moves forward to the next 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 + **/ gboolean gtk_text_iter_forward_word_end (GtkTextIter *iter) { - return find_by_log_attrs (iter, find_word_end_func, TRUE); + return find_by_log_attrs (iter, find_word_end_func, TRUE, FALSE); } +/** + * gtk_text_iter_backward_word_start: + * @iter: a #GtkTextIter + * + * 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 + * algorithms). + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ gboolean gtk_text_iter_backward_word_start (GtkTextIter *iter) { - return find_by_log_attrs (iter, find_word_start_func, FALSE); + return find_by_log_attrs (iter, find_word_start_func, FALSE, FALSE); } /* FIXME a loop around a truly slow function means * a truly spectacularly slow function. */ + +/** + * gtk_text_iter_forward_word_ends: + * @iter: a #GtkTextIter + * @count: number of times to move + * + * Calls gtk_text_iter_forward_word_end() up to @count times. + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ gboolean gtk_text_iter_forward_word_ends (GtkTextIter *iter, gint count) { - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (count > 0, FALSE); + return move_multiple_steps (iter, count, + gtk_text_iter_forward_word_end, + gtk_text_iter_backward_word_starts); +} - if (!gtk_text_iter_forward_word_end (iter)) - return FALSE; - --count; +/** + * gtk_text_iter_backward_word_starts + * @iter: a #GtkTextIter + * @count: number of times to move + * + * Calls gtk_text_iter_backward_word_start() up to @count times. + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ +gboolean +gtk_text_iter_backward_word_starts (GtkTextIter *iter, + gint count) +{ + return move_multiple_steps (iter, count, + gtk_text_iter_backward_word_start, + gtk_text_iter_forward_word_ends); +} - while (count > 0) - { - if (!gtk_text_iter_forward_word_end (iter)) - break; - --count; - } - return TRUE; +/** + * 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); +} + +/** + * 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); +} + +/** + * 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_starts_word: + * @iter: a #GtkTextIter + * + * Determines whether @iter begins a natural-language word. 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 is at the start of a word + **/ +gboolean +gtk_text_iter_starts_word (const GtkTextIter *iter) +{ + return test_log_attrs (iter, is_word_start_func); +} + +/** + * gtk_text_iter_ends_word: + * @iter: a #GtkTextIter + * + * Determines whether @iter ends a natural-language word. 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 is at the end of a word + **/ +gboolean +gtk_text_iter_ends_word (const GtkTextIter *iter) +{ + return test_log_attrs (iter, is_word_end_func); +} + +/** + * gtk_text_iter_inside_word: + * @iter: a #GtkTextIter + * + * Determines whether @iter is inside a natural-language word (as + * opposed to say inside some whitespace). 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 is inside a word + **/ +gboolean +gtk_text_iter_inside_word (const GtkTextIter *iter) +{ + return test_log_attrs (iter, inside_word_func); +} + +/** + * gtk_text_iter_starts_sentence: + * @iter: a #GtkTextIter + * + * Determines whether @iter begins a sentence. 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 boundary + * algorithms). + * + * Return value: %TRUE if @iter is at the start of a sentence. + **/ +gboolean +gtk_text_iter_starts_sentence (const GtkTextIter *iter) +{ + return test_log_attrs (iter, is_sentence_start_func); +} + +/** + * gtk_text_iter_ends_sentence: + * @iter: a #GtkTextIter + * + * Determines whether @iter ends a sentence. 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 boundary + * algorithms). + * + * Return value: %TRUE if @iter is at the end of a sentence. + **/ +gboolean +gtk_text_iter_ends_sentence (const GtkTextIter *iter) +{ + return test_log_attrs (iter, is_sentence_end_func); +} + +/** + * gtk_text_iter_inside_sentence: + * @iter: a #GtkTextIter + * + * Determines whether @iter is inside a sentence (as opposed to in + * between two sentences, e.g. after a period and before the first + * letter of the next sentence). 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 boundary algorithms). + * + * Return value: %TRUE if @iter is inside a sentence. + **/ +gboolean +gtk_text_iter_inside_sentence (const GtkTextIter *iter) +{ + return test_log_attrs (iter, inside_sentence_func); +} + +/** + * gtk_text_iter_forward_sentence_end: + * @iter: a #GtkTextIter + * + * Moves forward to the next sentence end. (If @iter is at the end of + * a sentence, moves to the next end of sentence.) 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 + * boundary algorithms). + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ +gboolean +gtk_text_iter_forward_sentence_end (GtkTextIter *iter) +{ + return find_by_log_attrs (iter, find_sentence_end_func, TRUE, FALSE); +} + +/** + * gtk_text_iter_backward_sentence_start: + * @iter: a #GtkTextIter + * + * 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 + * boundary algorithms). + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ +gboolean +gtk_text_iter_backward_sentence_start (GtkTextIter *iter) +{ + return find_by_log_attrs (iter, find_sentence_start_func, FALSE, FALSE); +} + +/* FIXME a loop around a truly slow function means + * a truly spectacularly slow function. + */ +/** + * gtk_text_iter_forward_sentence_ends: + * @iter: a #GtkTextIter + * @count: number of sentences to move + * + * Calls gtk_text_iter_forward_sentence_end() @count times (or until + * gtk_text_iter_forward_sentence_end() returns %FALSE). If @count is + * negative, moves backward instead of forward. + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ +gboolean +gtk_text_iter_forward_sentence_ends (GtkTextIter *iter, + gint count) +{ + return move_multiple_steps (iter, count, + gtk_text_iter_forward_sentence_end, + gtk_text_iter_backward_sentence_starts); +} + +/** + * gtk_text_iter_backward_sentence_starts: + * @iter: a #GtkTextIter + * @count: number of sentences to move + * + * Calls gtk_text_iter_backward_sentence_start() up to @count times, + * or until it returns %FALSE. If @count is negative, moves forward + * instead of backward. + * + * Return value: %TRUE if @iter moved and is not the end iterator + **/ +gboolean +gtk_text_iter_backward_sentence_starts (GtkTextIter *iter, + gint count) +{ + return move_multiple_steps (iter, count, + gtk_text_iter_backward_sentence_start, + gtk_text_iter_forward_sentence_ends); +} + +static gboolean +find_forward_cursor_pos_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len, + gint *found_offset, + gboolean already_moved_initially) +{ + if (!already_moved_initially) + ++offset; + + while (offset < (min_offset + len) && + !attrs[offset].is_cursor_position) + ++offset; + + *found_offset = offset; + + return offset < (min_offset + len); +} + +static gboolean +find_backward_cursor_pos_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len, + gint *found_offset, + gboolean already_moved_initially) +{ + if (!already_moved_initially) + --offset; + + while (offset > min_offset && + !attrs[offset].is_cursor_position) + --offset; + + *found_offset = offset; + + return offset >= min_offset; +} + +static gboolean +is_cursor_pos_func (const PangoLogAttr *attrs, + gint offset, + gint min_offset, + gint len) +{ + return attrs[offset].is_cursor_position; +} + +/** + * gtk_text_iter_forward_cursor_position: + * @iter: a #GtkTextIter + * + * Moves @iter forward by a single cursor position. Cursor positions + * are (unsurprisingly) positions where the cursor can appear. Perhaps + * surprisingly, there may not be a cursor position between all + * characters. The most common example for European languages would be + * a carriage return/newline sequence. For some Unicode characters, + * the equivalent of say the letter "a" with an accent mark will be + * represented as two characters, first the letter then a "combining + * mark" that causes the accent to be rendered; so the cursor can't go + * between those two characters. See also the #PangoLogAttr structure and + * pango_break() function. + * + * Return value: %TRUE if we moved and the new position is dereferenceable + **/ +gboolean +gtk_text_iter_forward_cursor_position (GtkTextIter *iter) +{ + return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE); +} + +/** + * gtk_text_iter_backward_cursor_position: + * @iter: a #GtkTextIter + * + * Like gtk_text_iter_forward_cursor_position(), but moves backward. + * + * Return value: %TRUE if we moved + **/ +gboolean +gtk_text_iter_backward_cursor_position (GtkTextIter *iter) +{ + return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE); +} + +/** + * gtk_text_iter_forward_cursor_positions: + * @iter: a #GtkTextIter + * @count: number of positions to move + * + * Moves up to @count cursor positions. See + * gtk_text_iter_forward_cursor_position() for details. + * + * Return value: %TRUE if we moved and the new position is dereferenceable + **/ +gboolean +gtk_text_iter_forward_cursor_positions (GtkTextIter *iter, + gint count) +{ + return move_multiple_steps (iter, count, + gtk_text_iter_forward_cursor_position, + gtk_text_iter_backward_cursor_positions); +} + +/** + * gtk_text_iter_backward_cursor_positions: + * @iter: a #GtkTextIter + * @count: number of positions to move + * + * Moves up to @count cursor positions. See + * gtk_text_iter_forward_cursor_position() for details. + * + * Return value: %TRUE if we moved and the new position is dereferenceable + **/ +gboolean +gtk_text_iter_backward_cursor_positions (GtkTextIter *iter, + gint count) +{ + return move_multiple_steps (iter, count, + gtk_text_iter_backward_cursor_position, + gtk_text_iter_forward_cursor_positions); +} + +/** + * 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); } +/** + * 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_word_starts (GtkTextIter *iter, - gint count) +gtk_text_iter_backward_visible_cursor_position (GtkTextIter *iter) { - g_return_val_if_fail (iter != NULL, FALSE); - g_return_val_if_fail (count > 0, FALSE); - - if (!gtk_text_iter_backward_word_start (iter)) - return FALSE; - --count; - - while (count > 0) - { - if (!gtk_text_iter_backward_word_start (iter)) - break; - --count; - } - return TRUE; + return find_visible_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE); } - +/** + * 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_starts_word (const GtkTextIter *iter) +gtk_text_iter_forward_visible_cursor_positions (GtkTextIter *iter, + gint count) { - return test_log_attrs (iter, is_word_start_func, NULL); + 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_ends_word (const GtkTextIter *iter) +gtk_text_iter_backward_visible_cursor_positions (GtkTextIter *iter, + gint count) { - return test_log_attrs (iter, is_word_end_func, NULL); + return move_multiple_steps (iter, count, + gtk_text_iter_backward_visible_cursor_position, + gtk_text_iter_forward_visible_cursor_positions); } +/** + * gtk_text_iter_is_cursor_position: + * @iter: a #GtkTextIter + * + * See gtk_text_iter_forward_cursor_position() or #PangoLogAttr or + * pango_break() for details on what a cursor position is. + * + * Return value: %TRUE if the cursor can be placed at @iter + **/ gboolean -gtk_text_iter_inside_word (const GtkTextIter *iter) +gtk_text_iter_is_cursor_position (const GtkTextIter *iter) { - return test_log_attrs (iter, inside_word_func, NULL); + return test_log_attrs (iter, is_cursor_pos_func); } +/** + * gtk_text_iter_set_line_offset: + * @iter: a #GtkTextIter + * @char_on_line: a character offset relative to the start of @iter's current line + * + * Moves @iter within a line, to a new character + * (not byte) offset. The given character offset must be less than or + * equal to the number of characters in the line; if equal, @iter + * moves to the start of the next line. See + * gtk_text_iter_set_line_index() if you have a byte index rather than + * a character offset. + * + **/ void gtk_text_iter_set_line_offset (GtkTextIter *iter, - gint char_on_line) + gint char_on_line) { GtkTextRealIter *real; - + gint chars_in_line; + g_return_if_fail (iter != NULL); real = gtk_text_iter_make_surreal (iter); if (real == NULL) return; - + check_invariants (iter); - iter_set_from_char_offset (real, real->line, char_on_line); + chars_in_line = gtk_text_iter_get_chars_in_line (iter); + + g_return_if_fail (char_on_line <= chars_in_line); + if (char_on_line < chars_in_line) + 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); } +/** + * gtk_text_iter_set_line_index: + * @iter: a #GtkTextIter + * @byte_on_line: a byte index relative to the start of @iter's current line + * + * Same as gtk_text_iter_set_line_offset(), but works with a + * byte index. The given byte index must be at + * the start of a character, it can't be in the middle of a UTF-8 + * encoded character. + * + **/ void gtk_text_iter_set_line_index (GtkTextIter *iter, gint byte_on_line) { GtkTextRealIter *real; - + gint bytes_in_line; + g_return_if_fail (iter != NULL); real = gtk_text_iter_make_surreal (iter); @@ -2641,7 +3801,14 @@ gtk_text_iter_set_line_index (GtkTextIter *iter, check_invariants (iter); - iter_set_from_byte_offset (real, real->line, byte_on_line); + bytes_in_line = gtk_text_iter_get_bytes_in_line (iter); + + g_return_if_fail (byte_on_line <= bytes_in_line); + + if (byte_on_line < bytes_in_line) + iter_set_from_byte_offset (real, real->line, byte_on_line); + else + gtk_text_iter_forward_line (iter); if (real->segment->type == >k_text_char_type && (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80) @@ -2653,6 +3820,118 @@ gtk_text_iter_set_line_index (GtkTextIter *iter, check_invariants (iter); } + +/** + * gtk_text_iter_set_visible_line_offset: + * @iter: a #GtkTextIter + * @char_on_line: a character offset + * + * Like gtk_text_iter_set_line_offset(), but the offset is in visible + * characters, i.e. text with a tag making it invisible is not + * counted in the offset. + **/ +void +gtk_text_iter_set_visible_line_offset (GtkTextIter *iter, + gint char_on_line) +{ + gint chars_seen = 0; + GtkTextIter pos; + + 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 (chars_seen < char_on_line) + { + if (!_gtk_text_btree_char_is_invisible (&pos)) + ++chars_seen; + + if (!gtk_text_iter_forward_char (&pos)) + break; + + if (chars_seen == char_on_line) + break; + } + + if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter)) + *iter = pos; + else + gtk_text_iter_forward_line (iter); +} + +/** + * gtk_text_iter_set_visible_line_index: + * @iter: a #GtkTextIter + * @byte_on_line: a byte index + * + * Like gtk_text_iter_set_line_index(), but the index is in visible + * bytes, i.e. text with a tag making it invisible is not counted + * in the index. + **/ +void +gtk_text_iter_set_visible_line_index (GtkTextIter *iter, + gint byte_on_line) +{ + GtkTextRealIter *real; + gint offset = 0; + GtkTextIter pos; + GtkTextLineSegment *seg; + + g_return_if_fail (iter != NULL); + + gtk_text_iter_set_line_offset (iter, 0); + + pos = *iter; + + 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)) + { + 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; + } + + offset += seg->byte_count; + _gtk_text_iter_forward_indexable_segment (&pos); + seg = _gtk_text_iter_get_indexable_segment (&pos); + } + + if (byte_on_line == 0) + *iter = pos; + else + gtk_text_iter_forward_line (iter); +} + +/** + * gtk_text_iter_set_line: + * @iter: a #GtkTextIter + * @line_number: line number (counted from 0) + * + * 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 gtk_text_iter_set_line (GtkTextIter *iter, gint line_number) @@ -2670,7 +3949,7 @@ gtk_text_iter_set_line (GtkTextIter *iter, 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); @@ -2680,8 +3959,17 @@ gtk_text_iter_set_line (GtkTextIter *iter, check_invariants (iter); } +/** + * gtk_text_iter_set_offset: + * @iter: a #GtkTextIter + * @char_offset: a character number + * + * 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, gint char_index) +gtk_text_iter_set_offset (GtkTextIter *iter, + gint char_offset) { GtkTextLine *line; GtkTextRealIter *real; @@ -2698,13 +3986,13 @@ gtk_text_iter_set_offset (GtkTextIter *iter, gint char_index) check_invariants (iter); if (real->cached_char_index >= 0 && - real->cached_char_index == char_index) + real->cached_char_index == char_offset) return; - line = __gtk_text_btree_get_line_at_char (real->tree, - char_index, - &line_start, - &real_char_index); + line = _gtk_text_btree_get_line_at_char (real->tree, + char_offset, + &line_start, + &real_char_index); iter_set_from_char_offset (real, line, real_char_index - line_start); @@ -2714,8 +4002,16 @@ gtk_text_iter_set_offset (GtkTextIter *iter, gint char_index) check_invariants (iter); } +/** + * gtk_text_iter_forward_to_end: + * @iter: a #GtkTextIter + * + * 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_iter_forward_to_end (GtkTextIter *iter) { GtkTextBuffer *buffer; GtkTextRealIter *real; @@ -2729,25 +4025,71 @@ gtk_text_iter_forward_to_end (GtkTextIter *iter) buffer = _gtk_text_btree_get_buffer (real->tree); - gtk_text_buffer_get_last_iter (buffer, 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 + * + * Moves the iterator to point to the paragraph delimiter characters, + * which will be either a newline, a carriage return, a carriage + * 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. 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 + **/ gboolean -gtk_text_iter_forward_to_newline (GtkTextIter *iter) +gtk_text_iter_forward_to_line_end (GtkTextIter *iter) { gint current_offset; gint new_offset; + g_return_val_if_fail (iter != NULL, FALSE); current_offset = gtk_text_iter_get_line_offset (iter); - 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 { @@ -2757,9 +4099,9 @@ gtk_text_iter_forward_to_newline (GtkTextIter *iter) /* We don't want to move past all * empty lines. */ - if (gtk_text_iter_get_char (iter) != '\n') - gtk_text_iter_forward_to_newline (iter); - return TRUE; + if (!gtk_text_iter_ends_line (iter)) + gtk_text_iter_forward_to_line_end (iter); + return !gtk_text_iter_is_end (iter); } else return FALSE; @@ -2769,12 +4111,12 @@ gtk_text_iter_forward_to_newline (GtkTextIter *iter) /** * 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. @@ -2799,10 +4141,10 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, check_invariants (iter); current_line = real->line; - next_line = __gtk_text_line_next_could_contain_tag (current_line, - real->tree, tag); + next_line = _gtk_text_line_next_could_contain_tag (current_line, + real->tree, tag); - while (gtk_text_iter_forward_indexable_segment (iter)) + while (_gtk_text_iter_forward_indexable_segment (iter)) { /* If we went forward to a line that couldn't contain a toggle for the tag, then skip forward to a line that could contain @@ -2813,7 +4155,7 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, if (next_line == NULL) { /* End of search. Set to end of buffer. */ - _gtk_text_btree_get_last_iter (real->tree, iter); + _gtk_text_btree_get_end_iter (real->tree, iter); return FALSE; } @@ -2821,9 +4163,9 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, iter_set_from_byte_offset (real, next_line, 0); current_line = real->line; - next_line = __gtk_text_line_next_could_contain_tag (current_line, - real->tree, - tag); + next_line = _gtk_text_line_next_could_contain_tag (current_line, + real->tree, + tag); } if (gtk_text_iter_toggles_tag (iter, tag)) @@ -2851,12 +4193,12 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter, /** * 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. @@ -2881,7 +4223,7 @@ gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter, check_invariants (iter); current_line = real->line; - prev_line = __gtk_text_line_previous_could_contain_tag (current_line, + prev_line = _gtk_text_line_previous_could_contain_tag (current_line, real->tree, tag); @@ -2890,7 +4232,7 @@ gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter, */ if (is_segment_start (real)) { - if (!gtk_text_iter_backward_indexable_segment (iter)) + if (!_gtk_text_iter_backward_indexable_segment (iter)) return FALSE; } else @@ -2925,11 +4267,11 @@ gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter, iter_set_from_byte_offset (real, prev_line, 0); while (!at_last_indexable_segment (real)) - gtk_text_iter_forward_indexable_segment (iter); + _gtk_text_iter_forward_indexable_segment (iter); } current_line = real->line; - prev_line = __gtk_text_line_previous_could_contain_tag (current_line, + prev_line = _gtk_text_line_previous_could_contain_tag (current_line, real->tree, tag); } @@ -2943,7 +4285,7 @@ gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter, return TRUE; } } - while (gtk_text_iter_backward_indexable_segment (iter)); + while (_gtk_text_iter_backward_indexable_segment (iter)); /* Reached front of buffer */ return FALSE; @@ -2999,6 +4341,17 @@ gtk_text_iter_forward_find_char (GtkTextIter *iter, return FALSE; } +/** + * gtk_text_iter_backward_find_char: + * @iter: a #GtkTextIter + * @pred: function to be called on each character + * @user_data: user data for @pred + * @limit: search limit, or %NULL for none + * + * Same as gtk_text_iter_forward_find_char(), but goes backward from @iter. + * + * Return value: whether a match was found + **/ gboolean gtk_text_iter_backward_find_char (GtkTextIter *iter, GtkTextCharPredicate pred, @@ -3217,21 +4570,32 @@ strbreakup (const char *string, * 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 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) @@ -3240,7 +4604,9 @@ gtk_text_iter_forward_search (const GtkTextIter *iter, 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); @@ -3269,6 +4635,9 @@ gtk_text_iter_forward_search (const GtkTextIter *iter, 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); @@ -3292,7 +4661,7 @@ gtk_text_iter_forward_search (const GtkTextIter *iter, { if (limit == NULL || (limit && - gtk_text_iter_compare (&end, limit) < 0)) + gtk_text_iter_compare (&end, limit) <= 0)) { retval = TRUE; @@ -3384,7 +4753,7 @@ lines_window_init (LinesWindow *win, /* If we start on line 1, there are 2 lines to search (0 and 1), so * n_lines can be 2. */ - if (gtk_text_iter_is_first (start) || + if (gtk_text_iter_is_start (start) || gtk_text_iter_get_line (start) + 1 < win->n_lines) { /* Already at the end, or not enough lines to match */ @@ -3494,62 +4863,23 @@ lines_window_free (LinesWindow *win) 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 * - * + * Same as gtk_text_iter_forward_search(), but moves backward. * * Return value: whether a match was found **/ 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) @@ -3559,7 +4889,9 @@ gtk_text_iter_backward_search (const GtkTextIter *iter, 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); @@ -3587,6 +4919,9 @@ gtk_text_iter_backward_search (const GtkTextIter *iter, 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); @@ -3623,7 +4958,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter, * 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)) @@ -3679,6 +5014,19 @@ gtk_text_iter_backward_search (const GtkTextIter *iter, * Comparisons */ +/** + * gtk_text_iter_equal: + * @lhs: a #GtkTextIter + * @rhs: another #GtkTextIter + * + * Tests whether two iterators are equal, using the fastest possible + * mechanism. This function is very fast; you can expect it to perform + * better than e.g. getting the character offset for each iterator and + * comparing the offsets yourself. Also, it's a bit faster than + * gtk_text_iter_compare(). + * + * Return value: %TRUE if the iterators point to the same place in the buffer + **/ gboolean gtk_text_iter_equal (const GtkTextIter *lhs, const GtkTextIter *rhs) @@ -3707,8 +5055,21 @@ gtk_text_iter_equal (const GtkTextIter *lhs, } } +/** + * gtk_text_iter_compare: + * @lhs: a #GtkTextIter + * @rhs: another #GtkTextIter + * + * A qsort()-style function that returns negative if @lhs is less than + * @rhs, positive if @lhs is greater than @rhs, and 0 if they're equal. + * Ordering is in character offset order, i.e. the first character in the buffer + * is less than the second character in the buffer. + * + * Return value: -1 if @lhs is less than @rhs, 1 if @lhs is greater, 0 if they are equal + **/ gint -gtk_text_iter_compare (const GtkTextIter *lhs, const GtkTextIter *rhs) +gtk_text_iter_compare (const GtkTextIter *lhs, + const GtkTextIter *rhs) { GtkTextRealIter *real_lhs; GtkTextRealIter *real_rhs; @@ -3716,13 +5077,13 @@ gtk_text_iter_compare (const GtkTextIter *lhs, const GtkTextIter *rhs) real_lhs = gtk_text_iter_make_surreal (lhs); real_rhs = gtk_text_iter_make_surreal (rhs); - check_invariants (lhs); - check_invariants (rhs); - if (real_lhs == NULL || real_rhs == NULL) return -1; /* why not */ + check_invariants (lhs); + check_invariants (rhs); + if (real_lhs->line == real_rhs->line) { gint left_index, right_index; @@ -3765,18 +5126,47 @@ gtk_text_iter_compare (const GtkTextIter *lhs, const GtkTextIter *rhs) } } +/** + * gtk_text_iter_in_range: + * @iter: a #GtkTextIter + * @start: start of range + * @end: end of range + * + * 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 + **/ gboolean gtk_text_iter_in_range (const GtkTextIter *iter, 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; } +/** + * gtk_text_iter_order: + * @first: a #GtkTextIter + * @second: another #GtkTextIter + * + * Swaps the value of @first and @second if @second comes before + * @first in the buffer. That is, ensures that @first and @second are + * in sequence. Most text buffer functions that take a range call this + * automatically on your behalf, so there's no real reason to call it yourself + * in those cases. There are some exceptions, such as gtk_text_iter_in_range(), + * that expect a pre-sorted range. + * + **/ void -gtk_text_iter_reorder (GtkTextIter *first, - GtkTextIter *second) +gtk_text_iter_order (GtkTextIter *first, + GtkTextIter *second) { g_return_if_fail (first != NULL); g_return_if_fail (second != NULL); @@ -3797,8 +5187,8 @@ gtk_text_iter_reorder (GtkTextIter *first, void _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree, - GtkTextIter *iter, - gint char_index) + GtkTextIter *iter, + gint char_index) { GtkTextRealIter *real = (GtkTextRealIter*)iter; gint real_char_index; @@ -3808,8 +5198,8 @@ _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree, g_return_if_fail (iter != NULL); g_return_if_fail (tree != NULL); - line = __gtk_text_btree_get_line_at_char (tree, char_index, - &line_start, &real_char_index); + line = _gtk_text_btree_get_line_at_char (tree, char_index, + &line_start, &real_char_index); iter_init_from_char_offset (iter, tree, line, real_char_index - line_start); @@ -3819,10 +5209,10 @@ _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree, } void -__gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree, - GtkTextIter *iter, - gint line_number, - gint char_on_line) +_gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree, + GtkTextIter *iter, + gint line_number, + gint char_on_line) { GtkTextRealIter *real = (GtkTextRealIter*)iter; GtkTextLine *line; @@ -3831,8 +5221,8 @@ __gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree, 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); /* We might as well cache this, since we know it. */ @@ -3842,10 +5232,10 @@ __gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree, } void -__gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree, - GtkTextIter *iter, - gint line_number, - gint byte_index) +_gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree, + GtkTextIter *iter, + gint line_number, + gint byte_index) { GtkTextRealIter *real = (GtkTextRealIter*)iter; GtkTextLine *line; @@ -3854,28 +5244,21 @@ __gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree, 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); /* We might as well cache this, since we know it. */ real->cached_line_number = real_line; - if (real->segment->type == >k_text_char_type && - (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80) - g_warning ("%s: Incorrect byte offset %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_index); - check_invariants (iter); } void _gtk_text_btree_get_iter_at_line (GtkTextBTree *tree, - GtkTextIter *iter, - GtkTextLine *line, - gint byte_offset) + GtkTextIter *iter, + GtkTextLine *line, + gint byte_offset) { g_return_if_fail (iter != NULL); g_return_if_fail (tree != NULL); @@ -3888,8 +5271,8 @@ _gtk_text_btree_get_iter_at_line (GtkTextBTree *tree, gboolean _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree, - GtkTextIter *iter, - GtkTextTag *tag) + GtkTextIter *iter, + GtkTextTag *tag) { GtkTextLine *line; @@ -3901,14 +5284,17 @@ _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree, if (line == NULL) { /* Set iter to last in tree */ - _gtk_text_btree_get_last_iter (tree, iter); + _gtk_text_btree_get_end_iter (tree, iter); check_invariants (iter); return FALSE; } 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; } @@ -3916,36 +5302,23 @@ _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree, gboolean _gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree, - GtkTextIter *iter, - GtkTextTag *tag) + GtkTextIter *iter, + GtkTextTag *tag) { - GtkTextLine *line; - g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (tree != NULL, FALSE); - line = _gtk_text_btree_last_could_contain_tag (tree, tag); - - if (line == NULL) - { - /* Set iter to first in tree */ - __gtk_text_btree_get_iter_at_line_char (tree, iter, 0, 0); - check_invariants (iter); - return FALSE; - } - else - { - iter_init_from_byte_offset (iter, tree, line, -1); - gtk_text_iter_backward_to_tag_toggle (iter, tag); - check_invariants (iter); - return TRUE; - } + _gtk_text_btree_get_end_iter (tree, iter); + gtk_text_iter_backward_to_tag_toggle (iter, tag); + check_invariants (iter); + + return TRUE; } gboolean -__gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree, - GtkTextIter *iter, - const gchar *mark_name) +_gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree, + GtkTextIter *iter, + const gchar *mark_name) { GtkTextMark *mark; @@ -3966,8 +5339,8 @@ __gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree, void _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree, - GtkTextIter *iter, - GtkTextMark *mark) + GtkTextIter *iter, + GtkTextMark *mark) { GtkTextLineSegment *seg; @@ -3979,31 +5352,33 @@ _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree, iter_init_from_segment (iter, tree, seg->body.mark.line, seg); - g_assert (seg->body.mark.line == gtk_text_iter_get_text_line (iter)); + g_assert (seg->body.mark.line == _gtk_text_iter_get_text_line (iter)); check_invariants (iter); } 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_assert (seg->body.child.line == gtk_text_iter_get_text_line (iter)); + g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter)); check_invariants (iter); } void -_gtk_text_btree_get_last_iter (GtkTextBTree *tree, +_gtk_text_btree_get_end_iter (GtkTextBTree *tree, GtkTextIter *iter) { g_return_if_fail (iter != NULL); @@ -4016,29 +5391,7 @@ _gtk_text_btree_get_last_iter (GtkTextBTree *tree, } void -gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc) -{ - GtkTextRealIter *real = (GtkTextRealIter*)iter; - - g_return_if_fail (iter != NULL); - - if (real->chars_changed_stamp != _gtk_text_btree_get_chars_changed_stamp (real->tree)) - g_print (" %20s: \n", desc); - else - { - check_invariants (iter); - g_print (" %20s: line %d / char %d / line char %d / line byte %d\n", - desc, - gtk_text_iter_get_line (iter), - gtk_text_iter_get_offset (iter), - gtk_text_iter_get_line_offset (iter), - gtk_text_iter_get_line_index (iter)); - check_invariants (iter); - } -} - -void -gtk_text_iter_check (const GtkTextIter *iter) +_gtk_text_iter_check (const GtkTextIter *iter) { const GtkTextRealIter *real = (const GtkTextRealIter*)iter; gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset; @@ -4095,8 +5448,8 @@ gtk_text_iter_check (const GtkTextIter *iter) if (real->line_byte_offset >= 0) { _gtk_text_line_byte_locate (real->line, real->line_byte_offset, - &byte_segment, &byte_any_segment, - &seg_byte_offset, &line_byte_offset); + &byte_segment, &byte_any_segment, + &seg_byte_offset, &line_byte_offset); if (line_byte_offset != real->line_byte_offset) g_error ("wrong byte offset was stored in iterator"); @@ -4126,8 +5479,8 @@ gtk_text_iter_check (const GtkTextIter *iter) if (real->line_char_offset >= 0) { _gtk_text_line_char_locate (real->line, real->line_char_offset, - &char_segment, &char_any_segment, - &seg_char_offset, &line_char_offset); + &char_segment, &char_any_segment, + &seg_char_offset, &line_char_offset); if (line_char_offset != real->line_char_offset) g_error ("wrong char offset was stored in iterator"); @@ -4215,5 +5568,10 @@ gtk_text_iter_check (const GtkTextIter *iter) 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"