]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextiter.c
Fix bug #486369, by passing the selection info along when pasting.
[~andy/gtk] / gtk / gtktextiter.c
index fae5b5dc63aaad28a58da52c5cd09149b666c101..715f7e596496632f3a87bf52e8b0c80105c2cdc2 100644 (file)
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
  */
 
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <config.h>
 #include "gtktextiter.h"
 #include "gtktextbtree.h"
 #include "gtktextiterprivate.h"
+#include "gtkintl.h"
 #include "gtkdebug.h"
+#include "gtkalias.h"
 #include <string.h>
-#include <ctype.h>
+
+#define FIX_OVERFLOWS(varname) if ((varname) == G_MININT) (varname) = G_MININT + 1
 
 typedef struct _GtkTextRealIter GtkTextRealIter;
 
@@ -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
@@ -73,7 +82,7 @@ iter_set_common (GtkTextRealIter *iter,
 {
   /* Update segments stamp */
   iter->segments_changed_stamp =
-    gtk_text_btree_get_segments_changed_stamp (iter->tree);
+    _gtk_text_btree_get_segments_changed_stamp (iter->tree);
 
   iter->line = line;
 
@@ -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
@@ -147,7 +159,7 @@ gtk_text_iter_make_surreal (const GtkTextIter *_iter)
   GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
 
   if (iter->chars_changed_stamp !=
-      gtk_text_btree_get_chars_changed_stamp (iter->tree))
+      _gtk_text_btree_get_chars_changed_stamp (iter->tree))
     {
       g_warning ("Invalid text buffer iterator: either the iterator "
                  "is uninitialized, or the characters/pixbufs/widgets "
@@ -168,7 +180,7 @@ gtk_text_iter_make_surreal (const GtkTextIter *_iter)
      should have used make_real. */
 
   if (iter->segments_changed_stamp !=
-      gtk_text_btree_get_segments_changed_stamp (iter->tree))
+      _gtk_text_btree_get_segments_changed_stamp (iter->tree))
     {
       iter->segment = NULL;
       iter->any_segment = NULL;
@@ -188,7 +200,7 @@ gtk_text_iter_make_real (const GtkTextIter *_iter)
   iter = gtk_text_iter_make_surreal (_iter);
 
   if (iter->segments_changed_stamp !=
-      gtk_text_btree_get_segments_changed_stamp (iter->tree))
+      _gtk_text_btree_get_segments_changed_stamp (iter->tree))
     {
       if (iter->line_byte_offset >= 0)
         {
@@ -225,7 +237,7 @@ iter_init_common (GtkTextIter *_iter,
   iter->tree = tree;
 
   iter->chars_changed_stamp =
-    gtk_text_btree_get_chars_changed_stamp (iter->tree);
+    _gtk_text_btree_get_chars_changed_stamp (iter->tree);
 
   return iter;
 }
@@ -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 == &gtk_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
  **/
@@ -402,14 +387,14 @@ gtk_text_iter_get_buffer (const GtkTextIter *iter)
 
   check_invariants (iter);
 
-  return gtk_text_btree_get_buffer (real->tree);
+  return _gtk_text_btree_get_buffer (real->tree);
 }
 
 /**
  * gtk_text_iter_copy:
  * @iter: an iterator
  *
- * Create a dynamically-allocated copy of an iterator. This function
+ * Creates a dynamically-allocated copy of an iterator. This function
  * is not useful in applications, because iterators can be copied with a
  * simple assignment (<literal>GtkTextIter i = j;</literal>). The
  * function is used by language bindings.
@@ -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;
 
@@ -587,7 +584,7 @@ gtk_text_iter_get_offset (const GtkTextIter *iter)
       ensure_char_offsets (real);
       
       real->cached_char_index =
-        gtk_text_line_char_index (real->line);
+        _gtk_text_line_char_index (real->line);
       real->cached_char_index += real->line_char_offset;
     }
 
@@ -620,7 +617,7 @@ gtk_text_iter_get_line (const GtkTextIter *iter)
 
   if (real->cached_line_number < 0)
     real->cached_line_number =
-      gtk_text_line_get_number (real->line);
+      _gtk_text_line_get_number (real->line);
 
   check_invariants (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 == &gtk_text_char_type)
     {
@@ -762,7 +887,7 @@ gtk_text_iter_get_slice       (const GtkTextIter *start,
   check_invariants (start);
   check_invariants (end);
 
-  return gtk_text_btree_get_text (start, end, TRUE, TRUE);
+  return _gtk_text_btree_get_text (start, end, TRUE, TRUE);
 }
 
 /**
@@ -788,7 +913,7 @@ gtk_text_iter_get_text       (const GtkTextIter *start,
   check_invariants (start);
   check_invariants (end);
 
-  return gtk_text_btree_get_text (start, end, TRUE, FALSE);
+  return _gtk_text_btree_get_text (start, end, TRUE, FALSE);
 }
 
 /**
@@ -812,7 +937,7 @@ gtk_text_iter_get_visible_slice (const GtkTextIter  *start,
   check_invariants (start);
   check_invariants (end);
 
-  return gtk_text_btree_get_text (start, end, FALSE, TRUE);
+  return _gtk_text_btree_get_text (start, end, FALSE, TRUE);
 }
 
 /**
@@ -836,16 +961,16 @@ gtk_text_iter_get_visible_text (const GtkTextIter  *start,
   check_invariants (start);
   check_invariants (end);
 
-  return gtk_text_btree_get_text (start, end, FALSE, FALSE);
+  return _gtk_text_btree_get_text (start, end, FALSE, FALSE);
 }
 
 /**
  * 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
  * <emphasis>start</emphasis> of the tagged range;
  * gtk_text_iter_has_tag () tells you whether an iterator is
  * <emphasis>within</emphasis> 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
  * <emphasis>end</emphasis> of the tagged range;
  * gtk_text_iter_has_tag () tells you whether an iterator is
  * <emphasis>within</emphasis> 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;
 
@@ -1163,14 +1288,14 @@ 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);
+      return _gtk_text_line_byte_has_tag (real->line, real->tree,
+                                          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);
+      return _gtk_text_line_char_has_tag (real->line, real->tree,
+                                          real->line_char_offset, tag);
     }
 }
 
@@ -1196,19 +1321,18 @@ gtk_text_iter_get_tags (const GtkTextIter *iter)
   g_return_val_if_fail (iter != NULL, NULL);
   
   /* Get the tags at this spot */
-  tags = gtk_text_btree_get_tags (iter, &tag_count);
+  tags = _gtk_text_btree_get_tags (iter, &tag_count);
 
   /* 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);
+  _gtk_text_tag_array_sort (tags, tag_count);
 
   retval = NULL;
   i = 0;
@@ -1227,14 +1351,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 +1375,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 +1390,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
  **/
-static 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 +1462,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 +1499,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 +1526,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_prev_char (&tmp))
-        return FALSE;
+
+      if (gtk_text_iter_get_line_offset (&tmp) == 0)
+        return TRUE;
+
+      if (!gtk_text_iter_backward_char (&tmp))
+        return TRUE;
 
       return gtk_text_iter_get_char (&tmp) != '\r';
     }
@@ -1372,18 +1557,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 +1581,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 +1616,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 +1627,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 +1640,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 +1657,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,38 +1721,39 @@ 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;
 
   /* Get the tags at this spot */
-  tags = gtk_text_btree_get_tags (iter, &tag_count);
+  tags = _gtk_text_btree_get_tags (iter, &tag_count);
 
   /* 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_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 +1767,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,39 +1802,30 @@ 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 == &gtk_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)
 {
   GtkTextLine *new_line;
 
-  new_line = gtk_text_line_previous (real->line);
+  new_line = _gtk_text_line_previous (real->line);
 
   g_assert (new_line != real->line);
 
@@ -1611,6 +1859,7 @@ backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
       return FALSE;
     }
 }
+#endif 
 
 /* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
  * DEREFERENCEABLE)
@@ -1628,7 +1877,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 +1913,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 +1921,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 +1985,7 @@ gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
 
       check_invariants (iter);
 
-      return TRUE;
+      return !gtk_text_iter_is_end (iter);
     }
   else
     {
@@ -1755,17 +2004,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 +2050,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 +2093,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 +2107,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 +2117,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);
@@ -1924,21 +2180,21 @@ gtk_text_iter_backward_indexable_segment (GtkTextIter *iter)
 }
 
 /**
- * gtk_text_iter_next_char:
+ * gtk_text_iter_forward_char:
  * @iter: an iterator
  *
  * Moves @iter forward by one character offset. Note that images
  * embedded in the buffer occupy 1 character slot, so
- * gtk_text_iter_next_char () may actually move onto an image instead
+ * 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_next_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_next_char (GtkTextIter *iter)
+gtk_text_iter_forward_char (GtkTextIter *iter)
 {
   GtkTextRealIter *real;
 
@@ -1956,18 +2212,18 @@ gtk_text_iter_next_char (GtkTextIter *iter)
 }
 
 /**
- * gtk_text_iter_prev_char:
+ * 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_prev_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
  **/
 gboolean
-gtk_text_iter_prev_char (GtkTextIter *iter)
+gtk_text_iter_backward_char (GtkTextIter *iter)
 {
   g_return_val_if_fail (iter != NULL, FALSE);
 
@@ -2001,7 +2257,7 @@ gtk_text_iter_prev_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 +2268,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)
@@ -2042,7 +2300,7 @@ gtk_text_iter_forward_chars (GtkTextIter *iter, gint count)
 
       current_char_index = gtk_text_iter_get_offset (iter);
 
-      if (current_char_index == gtk_text_btree_char_count (real->tree))
+      if (current_char_index == _gtk_text_btree_char_count (real->tree))
         return FALSE; /* can't move forward */
 
       new_char_index = current_char_index + count;
@@ -2053,7 +2311,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 +2327,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 +2340,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 +2354,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 == &gtk_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 +2410,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 +2479,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 +2492,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 +2507,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 +2528,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.)
  *
@@ -2288,7 +2555,7 @@ gtk_text_iter_backward_line (GtkTextIter *iter)
 
   check_invariants (iter);
 
-  new_line = gtk_text_line_previous (real->line);
+  new_line = _gtk_text_line_previous (real->line);
 
   offset_will_change = FALSE;
   if (real->line_char_offset > 0)
@@ -2316,8 +2583,8 @@ 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);
+  real->segment = _gtk_text_line_byte_to_segment (real->line,
+                                                  0, &offset);
 
   g_assert (offset == 0);
 
@@ -2332,9 +2599,27 @@ gtk_text_iter_backward_line (GtkTextIter *iter)
   return TRUE;
 }
 
-gboolean
-gtk_text_iter_forward_lines (GtkTextIter *iter, gint count)
-{
+
+/**
+ * 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 +2633,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 +2691,186 @@ gtk_text_iter_backward_lines (GtkTextIter *iter, gint count)
     }
 }
 
-typedef gboolean (* FindLogAttrFunc) (PangoLogAttr *attrs,
-                                      gint          offset,
-                                      gint          min_offset,
-                                      gint          len,
-                                      gint         *found_offset);
+/**
+ * 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,
+                                      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 (PangoLogAttr *attrs,
+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 &&
@@ -2410,23 +2883,24 @@ find_word_end_func (PangoLogAttr *attrs,
 }
 
 static gboolean
-is_word_end_func (PangoLogAttr *attrs,
+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;
 }
 
 static gboolean
-find_word_start_func (PangoLogAttr *attrs,
+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 &&
@@ -2439,77 +2913,161 @@ find_word_start_func (PangoLogAttr *attrs,
 }
 
 static gboolean
-is_word_start_func (PangoLogAttr *attrs,
+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;
 }
 
 static gboolean
-inside_word_func (PangoLogAttr *attrs,
+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
-test_log_attrs (GtkTextIter       *iter,
-                FindLogAttrFunc    func,
-                gint              *found_offset)
+find_sentence_end_func (const PangoLogAttr *attrs,
+                        gint          offset,
+                        gint          min_offset,
+                        gint          len,
+                        gint         *found_offset,
+                        gboolean      already_moved_initially)
 {
-  GtkTextIter start;
-  GtkTextIter end;
-  gchar *paragraph;
-  gint char_len, byte_len;
-  PangoLogAttr *attrs;
-  int offset;
-  gboolean result = FALSE;
+  if (!already_moved_initially)
+    ++offset;
 
-  g_return_val_if_fail (iter != NULL, FALSE);
+  /* Find end of next sentence */
+  while (offset < min_offset + len &&
+         !attrs[offset].is_sentence_end)
+    ++offset;
 
-  start = *iter;
-  end = *iter;
+  *found_offset = offset;
 
-  gtk_text_iter_set_line_offset (&start, 0);
-  gtk_text_iter_forward_line (&end);
+  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;
+}
 
-  paragraph = gtk_text_iter_get_slice (&start, &end);
-  char_len = g_utf8_strlen (paragraph, -1);
-  byte_len = strlen (paragraph);
+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;
 
-  offset = gtk_text_iter_get_line_offset (iter);
+  /* Find start of prev sentence */
+  while (offset >= min_offset &&
+         !attrs[offset].is_sentence_start)
+    --offset;
 
-  if (char_len > 0 && offset < char_len)
-    {
-      gchar *lang;
+  *found_offset = offset;
 
-      attrs = g_new (PangoLogAttr, char_len);
+  return offset >= min_offset;
+}
 
-      lang = gtk_text_iter_get_language (iter);
+static gboolean
+is_sentence_start_func (const PangoLogAttr *attrs,
+                        gint          offset,
+                        gint          min_offset,
+                        gint          len)
+{
+  return attrs[offset].is_sentence_start;
+}
 
-      pango_get_log_attrs (paragraph, byte_len, -1,
-                           lang,
-                           attrs);
+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;
 
-      g_free (lang);
+  return attrs[offset].is_sentence_start;
+}
 
-      result = (* func) (attrs, offset, 0, char_len, found_offset);
+static gboolean
+test_log_attrs (const GtkTextIter *iter,
+                TestLogAttrFunc    func)
+{
+  gint char_len;
+  const PangoLogAttr *attrs;
+  int offset;
+  gboolean result = FALSE;
 
-      g_free (attrs);
-    }
+  g_return_val_if_fail (iter != NULL, FALSE);
 
-  g_free (paragraph);
+  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);
+
+  /* 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);
+  
+  /* 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;
 }
@@ -2518,7 +3076,8 @@ test_log_attrs (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;
@@ -2527,136 +3086,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 <emphasis>character</emphasis>
+ * (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
+ * <emphasis>byte</emphasis> 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);
@@ -2666,7 +3807,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 == &gtk_text_char_type &&
       (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
@@ -2678,6 +3826,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)
@@ -2695,7 +3955,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);
 
@@ -2705,8 +3965,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;
@@ -2723,13 +3992,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);
 
@@ -2739,8 +4008,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;
@@ -2752,27 +4029,73 @@ gtk_text_iter_forward_to_end  (GtkTextIter       *iter)
   if (real == NULL)
     return;
 
-  buffer = gtk_text_btree_get_buffer (real->tree);
+  buffer = _gtk_text_btree_get_buffer (real->tree);
+
+  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);
+    }
 
-  gtk_text_buffer_get_last_iter (buffer, iter);
+  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
     {
@@ -2782,9 +4105,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;
@@ -2794,12 +4117,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.
@@ -2824,10 +4147,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
@@ -2838,7 +4161,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;
             }
 
@@ -2846,9 +4169,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))
@@ -2876,12 +4199,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.
@@ -2906,7 +4229,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);
 
 
@@ -2915,7 +4238,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
@@ -2938,7 +4261,7 @@ gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
           if (prev_line == NULL)
             {
               /* End of search. Set to start of buffer. */
-              gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
+              _gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
               return FALSE;
             }
 
@@ -2950,11 +4273,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);
         }
@@ -2968,7 +4291,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;
@@ -3015,7 +4338,7 @@ gtk_text_iter_forward_find_char (GtkTextIter         *iter,
   
   while ((limit == NULL ||
           !gtk_text_iter_equal (limit, iter)) &&
-         gtk_text_iter_next_char (iter))
+         gtk_text_iter_forward_char (iter))
     {      
       if (matches_pred (iter, pred, user_data))
         return TRUE;
@@ -3024,6 +4347,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,
@@ -3039,7 +4373,7 @@ gtk_text_iter_backward_find_char (GtkTextIter         *iter,
   
   while ((limit == NULL ||
           !gtk_text_iter_equal (limit, iter)) &&
-         gtk_text_iter_prev_char (iter))
+         gtk_text_iter_backward_char (iter))
     {
       if (matches_pred (iter, pred, user_data))
         return TRUE;
@@ -3071,10 +4405,10 @@ forward_chars_with_skipping (GtkTextIter *iter,
 
       if (!ignored &&
           skip_invisible &&
-          gtk_text_btree_char_is_invisible (iter))
+          _gtk_text_btree_char_is_invisible (iter))
         ignored = TRUE;
 
-      gtk_text_iter_next_char (iter);
+      gtk_text_iter_forward_char (iter);
 
       if (!ignored)
         --i;
@@ -3242,21 +4576,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)
@@ -3265,7 +4610,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);
 
@@ -3278,7 +4625,7 @@ gtk_text_iter_forward_search (const GtkTextIter *iter,
       /* If we can move one char, return the empty string there */
       match = *iter;
       
-      if (gtk_text_iter_next_char (&match))
+      if (gtk_text_iter_forward_char (&match))
         {
           if (limit &&
               gtk_text_iter_equal (&match, limit))
@@ -3294,6 +4641,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);
@@ -3317,7 +4667,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;
               
@@ -3409,7 +4759,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 */
@@ -3519,62 +4869,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)
@@ -3584,7 +4895,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);
 
@@ -3600,7 +4913,7 @@ gtk_text_iter_backward_search (const GtkTextIter *iter,
       if (limit && gtk_text_iter_equal (limit, &match))
         return FALSE;
       
-      if (gtk_text_iter_prev_char (&match))
+      if (gtk_text_iter_backward_char (&match))
         {
           if (match_start)
             *match_start = match;
@@ -3612,6 +4925,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);
@@ -3648,7 +4964,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))
@@ -3704,6 +5020,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)
@@ -3732,8 +5061,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;
@@ -3741,13 +5083,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;
@@ -3790,18 +5132,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);
@@ -3821,9 +5192,9 @@ gtk_text_iter_reorder         (GtkTextIter *first,
  */
 
 void
-gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
-                                 GtkTextIter *iter,
-                                 gint char_index)
+_gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
+                                  GtkTextIter *iter,
+                                  gint char_index)
 {
   GtkTextRealIter *real = (GtkTextRealIter*)iter;
   gint real_char_index;
@@ -3833,8 +5204,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);
 
@@ -3844,10 +5215,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;
@@ -3856,8 +5227,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. */
@@ -3867,10 +5238,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;
@@ -3879,28 +5250,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 == &gtk_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)
+_gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
+                                       GtkTextIter    *iter,
+                                       GtkTextLine    *line,
+                                       gint            byte_offset)
 {
   g_return_if_fail (iter != NULL);
   g_return_if_fail (tree != NULL);
@@ -3912,87 +5276,77 @@ gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
 }
 
 gboolean
-gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
-                                         GtkTextIter    *iter,
-                                         GtkTextTag     *tag)
+_gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
+                                          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_first_could_contain_tag (tree, tag);
+  line = _gtk_text_btree_first_could_contain_tag (tree, tag);
 
   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;
     }
 }
 
 gboolean
-gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
-                                         GtkTextIter    *iter,
-                                         GtkTextTag     *tag)
+_gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
+                                          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;
 
   g_return_val_if_fail (iter != NULL, FALSE);
   g_return_val_if_fail (tree != NULL, FALSE);
 
-  mark = gtk_text_btree_get_mark_by_name (tree, mark_name);
+  mark = _gtk_text_btree_get_mark_by_name (tree, mark_name);
 
   if (mark == NULL)
     return FALSE;
   else
     {
-      gtk_text_btree_get_iter_at_mark (tree, iter, mark);
+      _gtk_text_btree_get_iter_at_mark (tree, iter, mark);
       check_invariants (iter);
       return TRUE;
     }
 }
 
 void
-gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
-                                 GtkTextIter *iter,
-                                 GtkTextMark *mark)
+_gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
+                                  GtkTextIter *iter,
+                                  GtkTextMark *mark)
 {
   GtkTextLineSegment *seg;
 
@@ -4004,66 +5358,46 @@ 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)
+_gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree       *tree,
+                                          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);
   g_return_if_fail (tree != NULL);
 
-  gtk_text_btree_get_iter_at_char (tree,
+  _gtk_text_btree_get_iter_at_char (tree,
                                    iter,
-                                   gtk_text_btree_char_count (tree));
+                                   _gtk_text_btree_char_count (tree));
   check_invariants (iter);
 }
 
 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: <invalidated iterator>\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;
@@ -4078,14 +5412,14 @@ gtk_text_iter_check (const GtkTextIter *iter)
   g_assert (sizeof (GtkTextIter) == sizeof (GtkTextRealIter));
 
   if (real->chars_changed_stamp !=
-      gtk_text_btree_get_chars_changed_stamp (real->tree))
+      _gtk_text_btree_get_chars_changed_stamp (real->tree))
     g_error ("iterator check failed: invalid iterator");
 
   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
     g_error ("iterator check failed: both char and byte offsets are invalid");
 
   segments_updated = (real->segments_changed_stamp ==
-                      gtk_text_btree_get_segments_changed_stamp (real->tree));
+                      _gtk_text_btree_get_segments_changed_stamp (real->tree));
 
 #if 0
   printf ("checking iter, segments %s updated, byte %d char %d\n",
@@ -4119,9 +5453,9 @@ 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);
+      _gtk_text_line_byte_locate (real->line, real->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");
@@ -4150,9 +5484,9 @@ 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);
+      _gtk_text_line_char_locate (real->line, real->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");
@@ -4220,7 +5554,7 @@ gtk_text_iter_check (const GtkTextIter *iter)
     {
       gint should_be;
 
-      should_be = gtk_text_line_get_number (real->line);
+      should_be = _gtk_text_line_get_number (real->line);
       if (real->cached_line_number != should_be)
         g_error ("wrong line number was cached");
     }
@@ -4233,12 +5567,17 @@ gtk_text_iter_check (const GtkTextIter *iter)
         {
           gint char_index;
 
-          char_index = gtk_text_line_char_index (real->line);
+          char_index = _gtk_text_line_char_index (real->line);
           char_index += real->line_char_offset;
 
           if (real->cached_char_index != char_index)
             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"