]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextiter.c
Updated Bulgarian translation by Alexander Shopov <ash@contact.bg>
[~andy/gtk] / gtk / gtktextiter.c
index 4d980b7c32d0a48885e60330694b27f2ebfe3f62..4ba37f9cf5ee66ce02cd49d7e0458e95369af6ec 100644 (file)
  */
 
 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <config.h>
 #include "gtktextiter.h"
 #include "gtktextbtree.h"
 #include "gtktextiterprivate.h"
 #include "gtkdebug.h"
+#include "gtkalias.h"
 #include <string.h>
 
 #define FIX_OVERFLOWS(varname) if ((varname) == G_MININT) (varname) = G_MININT + 1
@@ -385,7 +387,7 @@ 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)
 {
@@ -393,7 +395,7 @@ check_invariants (const GtkTextIter *iter)
     _gtk_text_iter_check (iter);
 }
 #else
-#define check_invariants (x)
+#define check_invariants(x)
 #endif
 
 /**
@@ -439,7 +441,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;
 
@@ -460,7 +462,7 @@ gtk_text_iter_free (GtkTextIter *iter)
 {
   g_return_if_fail (iter != NULL);
 
-  g_free (iter);
+  g_slice_free (GtkTextIter, iter);
 }
 
 GType
@@ -469,7 +471,7 @@ gtk_text_iter_get_type (void)
   static GType our_type = 0;
   
   if (our_type == 0)
-    our_type = g_boxed_type_register_static ("GtkTypeTextIter",
+    our_type = g_boxed_type_register_static ("GtkTextIter",
                                             (GBoxedCopyFunc) gtk_text_iter_copy,
                                             (GBoxedFreeFunc) gtk_text_iter_free);
 
@@ -481,7 +483,7 @@ _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);
 
@@ -500,7 +502,7 @@ _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);
 
@@ -559,7 +561,7 @@ _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;
 
@@ -573,7 +575,7 @@ _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;
 
@@ -853,7 +855,7 @@ gtk_text_iter_get_visible_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.
@@ -999,8 +1001,8 @@ gtk_text_iter_get_visible_text (const GtkTextIter  *start,
  * gtk_text_iter_get_pixbuf:
  * @iter: an iterator
  *
- * If the location pointed to by @iter contains a pixbuf, the pixbuf
- * is returned (with no new reference count added). Otherwise,
+ * 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
@@ -1029,7 +1031,7 @@ 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.
  *
@@ -1545,13 +1547,10 @@ gtk_text_iter_starts_line (const GtkTextIter   *iter)
 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
@@ -1565,10 +1564,23 @@ gtk_text_iter_ends_line (const GtkTextIter   *iter)
     return TRUE;
   else if (wc == '\n')
     {
+      GtkTextIter tmp = *iter;
+
       /* need to determine if a \r precedes the \n, in which case
-       * we aren't the end of the line
+       * we aren't the end of the line.
+       * Note however that if \r and \n are on different lines, they
+       * both are terminators. This for instance may happen after
+       * deleting some text:
+
+          1 some text\r    delete 'a'    1 some text\r
+          2 a\n            --------->    2 \n
+          3 ...                          3 ...
+
        */
-      GtkTextIter tmp = *iter;
+
+      if (gtk_text_iter_get_line_offset (&tmp) == 0)
+        return TRUE;
+
       if (!gtk_text_iter_backward_char (&tmp))
         return TRUE;
 
@@ -1649,7 +1661,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);
 
@@ -1701,7 +1713,7 @@ gtk_text_iter_get_bytes_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);
 
@@ -1834,10 +1846,14 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
     }
 }
 
-
+#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)
@@ -1878,6 +1894,7 @@ backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
       return FALSE;
     }
 }
+#endif 
 
 /* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
  * DEREFERENCEABLE)
@@ -2382,28 +2399,25 @@ gtk_text_iter_backward_chars (GtkTextIter *iter, gint count)
       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);
@@ -2712,6 +2726,162 @@ gtk_text_iter_backward_lines (GtkTextIter *iter, gint count)
     }
 }
 
+/**
+ * gtk_text_iter_forward_visible_line:
+ * @iter: an iterator
+ *
+ * Moves @iter to the start of the next visible line. Returns %TRUE if there
+ * was a next line to move to, and %FALSE if @iter was simply moved to
+ * the end of the buffer and is now not dereferenceable, or if @iter was
+ * already at the end of the buffer.
+ *
+ * Return value: whether @iter can be dereferenced
+ * 
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_forward_visible_line (GtkTextIter *iter)
+{
+  while (gtk_text_iter_forward_line (iter))
+    {
+      if (!_gtk_text_btree_char_is_invisible (iter))
+        return TRUE;
+      else
+        {
+          do
+            {
+              if (!gtk_text_iter_forward_char (iter))
+                return FALSE;
+          
+              if (!_gtk_text_btree_char_is_invisible (iter))
+                return TRUE;
+            }
+          while (!gtk_text_iter_ends_line (iter));
+        }
+    }
+    
+  return FALSE;
+}
+
+/**
+ * gtk_text_iter_backward_visible_line:
+ * @iter: an iterator
+ *
+ * Moves @iter to the start of the previous visible line. Returns %TRUE if
+ * @iter could be moved; i.e. if @iter was at character offset 0, this
+ * function returns %FALSE. Therefore if @iter was already on line 0,
+ * but not at the start of the line, @iter is snapped to the start of
+ * the line and the function returns %TRUE. (Note that this implies that
+ * in a loop calling this function, the line number may not change on
+ * every iteration, if your first iteration is on line 0.)
+ *
+ * Return value: whether @iter moved
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_backward_visible_line (GtkTextIter *iter)
+{
+  while (gtk_text_iter_backward_line (iter))
+    {
+      if (!_gtk_text_btree_char_is_invisible (iter))
+        return TRUE;
+      else
+        {
+          do
+            {
+              if (!gtk_text_iter_backward_char (iter))
+                return FALSE;
+          
+              if (!_gtk_text_btree_char_is_invisible (iter))
+                return TRUE;
+            }
+          while (!gtk_text_iter_starts_line (iter));
+        }
+    }
+    
+  return FALSE;
+}
+
+/**
+ * gtk_text_iter_forward_visible_lines:
+ * @iter: a #GtkTextIter
+ * @count: number of lines to move forward
+ *
+ * Moves @count visible lines forward, if possible (if @count would move
+ * past the start or end of the buffer, moves to the start or end of
+ * the buffer).  The return value indicates whether the iterator moved
+ * onto a dereferenceable position; if the iterator didn't move, or
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
+ * moves backward by 0 - @count lines.
+ *
+ * Return value: whether @iter moved and is dereferenceable
+ * 
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_forward_visible_lines (GtkTextIter *iter,
+                                     gint         count)
+{
+  FIX_OVERFLOWS (count);
+  
+  if (count < 0)
+    return gtk_text_iter_backward_visible_lines (iter, 0 - count);
+  else if (count == 0)
+    return FALSE;
+  else if (count == 1)
+    {
+      check_invariants (iter);
+      return gtk_text_iter_forward_visible_line (iter);
+    }
+  else
+    {
+      while (gtk_text_iter_forward_visible_line (iter) && count > 0)
+        count--;
+      return count == 0;
+    }    
+}
+
+/**
+ * gtk_text_iter_backward_visible_lines:
+ * @iter: a #GtkTextIter
+ * @count: number of lines to move backward
+ *
+ * Moves @count visible lines backward, if possible (if @count would move
+ * past the start or end of the buffer, moves to the start or end of
+ * the buffer).  The return value indicates whether the iterator moved
+ * onto a dereferenceable position; if the iterator didn't move, or
+ * moved onto the end iterator, then %FALSE is returned. If @count is 0,
+ * the function does nothing and returns %FALSE. If @count is negative,
+ * moves forward by 0 - @count lines.
+ *
+ * Return value: whether @iter moved and is dereferenceable
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_text_iter_backward_visible_lines (GtkTextIter *iter,
+                                      gint         count)
+{
+  FIX_OVERFLOWS (count);
+  
+  if (count < 0)
+    return gtk_text_iter_forward_visible_lines (iter, 0 - count);
+  else if (count == 0)
+    return FALSE;
+  else if (count == 1)
+    {
+      return gtk_text_iter_backward_visible_line (iter);
+    }
+  else
+    {
+      while (gtk_text_iter_backward_visible_line (iter) && count > 0)
+        count--;
+      return count == 0;
+    }
+}
+
 typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
                                       gint                offset,
                                       gint                min_offset,
@@ -2797,7 +2967,10 @@ inside_word_func (const PangoLogAttr *attrs,
          !(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 */
@@ -2990,6 +3163,64 @@ find_by_log_attrs (GtkTextIter    *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
@@ -3043,28 +3274,9 @@ gboolean
 gtk_text_iter_forward_word_ends (GtkTextIter      *iter,
                                  gint              count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
-
-  FIX_OVERFLOWS (count);
-  
-  if (count == 0)
-    return FALSE;
-
-  if (count < 0)
-    return gtk_text_iter_backward_word_starts (iter, -count);
-
-  if (!gtk_text_iter_forward_word_end (iter))
-    return FALSE;
-  --count;
-
-  while (count > 0)
-    {
-      if (!gtk_text_iter_forward_word_end (iter))
-        break;
-      --count;
-    }
-  
-  return !gtk_text_iter_is_end (iter);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_forward_word_end,
+                             gtk_text_iter_backward_word_starts);
 }
 
 /**
@@ -3080,25 +3292,89 @@ gboolean
 gtk_text_iter_backward_word_starts (GtkTextIter      *iter,
                                     gint               count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_backward_word_start,
+                             gtk_text_iter_forward_word_ends);
+}
 
-  FIX_OVERFLOWS (count);
-  
-  if (count < 0)
-    return gtk_text_iter_forward_word_ends (iter, -count);
+/**
+ * gtk_text_iter_forward_visible_word_end:
+ * @iter: a #GtkTextIter
+ * 
+ * Moves forward to the next visible word end. (If @iter is currently on a
+ * word end, moves forward to the next one after that.) Word breaks
+ * are determined by Pango and should be correct for nearly any
+ * language (if not, the correct fix would be to the Pango word break
+ * algorithms).
+ * 
+ * Return value: %TRUE if @iter moved and is not the end iterator 
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_word_end (GtkTextIter *iter)
+{
+  return find_visible_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
+}
 
-  if (!gtk_text_iter_backward_word_start (iter))
-    return FALSE;
-  --count;
+/**
+ * gtk_text_iter_backward_visible_word_start:
+ * @iter: a #GtkTextIter
+ * 
+ * Moves backward to the previous visible word start. (If @iter is currently 
+ * on a word start, moves backward to the next one after that.) Word breaks
+ * are determined by Pango and should be correct for nearly any
+ * language (if not, the correct fix would be to the Pango word break
+ * algorithms).
+ * 
+ * Return value: %TRUE if @iter moved and is not the end iterator 
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_word_start (GtkTextIter      *iter)
+{
+  return find_visible_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
+}
 
-  while (count > 0)
-    {
-      if (!gtk_text_iter_backward_word_start (iter))
-        break;
-      --count;
-    }
+/**
+ * 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);
+}
 
-  return !gtk_text_iter_is_end (iter);
+/**
+ * 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);
 }
 
 /**
@@ -3258,26 +3534,9 @@ gboolean
 gtk_text_iter_forward_sentence_ends (GtkTextIter      *iter,
                                      gint              count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
-
-  if (count == 0)
-    return FALSE;
-
-  if (count < 0)
-    return gtk_text_iter_backward_sentence_starts (iter, -count);
-
-  if (!gtk_text_iter_forward_sentence_end (iter))
-    return FALSE;
-  --count;
-
-  while (count > 0)
-    {
-      if (!gtk_text_iter_forward_sentence_end (iter))
-        break;
-      --count;
-    }
-
-  return !gtk_text_iter_is_end (iter);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_forward_sentence_end,
+                             gtk_text_iter_backward_sentence_starts);
 }
 
 /**
@@ -3295,23 +3554,9 @@ gboolean
 gtk_text_iter_backward_sentence_starts (GtkTextIter      *iter,
                                         gint               count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
-
-  if (count < 0)
-    return gtk_text_iter_forward_sentence_ends (iter, -count);
-
-  if (!gtk_text_iter_backward_sentence_start (iter))
-    return FALSE;
-  --count;
-
-  while (count > 0)
-    {
-      if (!gtk_text_iter_backward_sentence_start (iter))
-        break;
-      --count;
-    }
-
-  return !gtk_text_iter_is_end (iter);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_backward_sentence_start,
+                             gtk_text_iter_forward_sentence_ends);
 }
 
 static gboolean
@@ -3392,7 +3637,7 @@ gtk_text_iter_forward_cursor_position (GtkTextIter *iter)
  * 
  * Like gtk_text_iter_forward_cursor_position(), but moves backward.
  * 
- * Return value: %TRUE if we moved and the new position is dereferenceable
+ * Return value: %TRUE if we moved
  **/
 gboolean
 gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
@@ -3414,28 +3659,9 @@ gboolean
 gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
                                         gint         count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
-
-  FIX_OVERFLOWS (count);
-  
-  if (count == 0)
-    return FALSE;
-  
-  if (count < 0)
-    return gtk_text_iter_backward_cursor_positions (iter, -count);
-  
-  if (!gtk_text_iter_forward_cursor_position (iter))
-    return FALSE;
-  --count;
-
-  while (count > 0)
-    {
-      if (!gtk_text_iter_forward_cursor_position (iter))
-        break;
-      --count;
-    }
-  
-  return !gtk_text_iter_is_end (iter);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_forward_cursor_position,
+                             gtk_text_iter_backward_cursor_positions);
 }
 
 /**
@@ -3452,28 +3678,85 @@ gboolean
 gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
                                          gint         count)
 {
-  g_return_val_if_fail (iter != NULL, FALSE);
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_backward_cursor_position,
+                             gtk_text_iter_forward_cursor_positions);
+}
 
-  FIX_OVERFLOWS (count);
-  
-  if (count == 0)
-    return FALSE;
+/**
+ * gtk_text_iter_forward_visible_cursor_position:
+ * @iter: a #GtkTextIter
+ * 
+ * Moves @iter forward to the next visible cursor position. See 
+ * gtk_text_iter_forward_cursor_position() for details.
+ * 
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_cursor_position (GtkTextIter *iter)
+{
+  return find_visible_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
+}
 
-  if (count < 0)
-    return gtk_text_iter_forward_cursor_positions (iter, -count);
-  
-  if (!gtk_text_iter_backward_cursor_position (iter))
-    return FALSE;
-  --count;
+/**
+ * gtk_text_iter_backward_visible_cursor_position:
+ * @iter: a #GtkTextIter
+ * 
+ * Moves @iter forward to the previous visible cursor position. See 
+ * gtk_text_iter_backward_cursor_position() for details.
+ * 
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_cursor_position (GtkTextIter *iter)
+{
+  return find_visible_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
+}
 
-  while (count > 0)
-    {
-      if (!gtk_text_iter_backward_cursor_position (iter))
-        break;
-      --count;
-    }
+/**
+ * gtk_text_iter_forward_visible_cursor_positions:
+ * @iter: a #GtkTextIter
+ * @count: number of positions to move
+ * 
+ * Moves up to @count visible cursor positions. See
+ * gtk_text_iter_forward_cursor_position() for details.
+ * 
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_forward_visible_cursor_positions (GtkTextIter *iter,
+                                               gint         count)
+{
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_forward_visible_cursor_position,
+                             gtk_text_iter_backward_visible_cursor_positions);
+}
 
-  return !gtk_text_iter_is_end (iter);
+/**
+ * gtk_text_iter_backward_visible_cursor_positions:
+ * @iter: a #GtkTextIter
+ * @count: number of positions to move
+ *
+ * Moves up to @count visible cursor positions. See
+ * gtk_text_iter_backward_cursor_position() for details.
+ * 
+ * Return value: %TRUE if we moved and the new position is dereferenceable
+ * 
+ * Since: 2.4
+ **/
+gboolean
+gtk_text_iter_backward_visible_cursor_positions (GtkTextIter *iter,
+                                                gint         count)
+{
+  return move_multiple_steps (iter, count, 
+                             gtk_text_iter_backward_visible_cursor_position,
+                             gtk_text_iter_forward_visible_cursor_positions);
 }
 
 /**
@@ -3597,6 +3880,8 @@ gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
 
   g_return_if_fail (iter != NULL);
   
+  gtk_text_iter_set_line_offset (iter, 0);
+
   pos = *iter;
 
   /* For now we use a ludicrously slow implementation */
@@ -3618,12 +3903,6 @@ gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
     gtk_text_iter_forward_line (iter);
 }
 
-static gint
-bytes_in_char (GtkTextIter *iter)
-{
-  return g_unichar_to_utf8 (gtk_text_iter_get_char (iter), NULL);
-}
-
 /**
  * gtk_text_iter_set_visible_line_index:
  * @iter: a #GtkTextIter
@@ -3634,36 +3913,54 @@ bytes_in_char (GtkTextIter *iter)
  * in the index.
  **/
 void
-gtk_text_iter_set_visible_line_index  (GtkTextIter *iter,
-                                       gint         byte_on_line)
+gtk_text_iter_set_visible_line_index (GtkTextIter *iter,
+                                      gint         byte_on_line)
 {
-  gint bytes_seen = 0;
+  GtkTextRealIter *real;
+  gint bytes_in_line = 0;
+  gint offset = 0;
   GtkTextIter pos;
-
-  g_return_if_fail (iter != NULL);
+  GtkTextLineSegment *seg;
   
+  g_return_if_fail (iter != NULL);
+
+  gtk_text_iter_set_line_offset (iter, 0);
+
+  bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
+
   pos = *iter;
 
-  /* For now we use a ludicrously slow implementation */
-  while (bytes_seen < byte_on_line)
+  real = gtk_text_iter_make_real (&pos);
+
+  if (real == NULL)
+    return;
+
+  ensure_byte_offsets (real);
+
+  check_invariants (&pos);
+
+  seg = _gtk_text_iter_get_indexable_segment (&pos);
+
+  while (seg != NULL && byte_on_line > 0)
     {
       if (!_gtk_text_btree_char_is_invisible (&pos))
-        bytes_seen += bytes_in_char (&pos);
-
-      if (!gtk_text_iter_forward_char (&pos))
-        break;
+        {
+          if (byte_on_line < seg->byte_count)
+            {
+              iter_set_from_byte_offset (real, real->line, offset + byte_on_line);
+              byte_on_line = 0;
+              break;
+            }
+          else
+            byte_on_line -= seg->byte_count;
+        }
 
-      if (bytes_seen >= byte_on_line)
-        break;
+      offset += seg->byte_count;
+      _gtk_text_iter_forward_indexable_segment (&pos);
+      seg = _gtk_text_iter_get_indexable_segment (&pos);
     }
 
-  if (bytes_seen > byte_on_line)
-    g_warning ("%s: Incorrect visible byte index %d falls in the middle of a UTF-8 "
-               "character; this will crash the text buffer. "
-               "Byte indexes must refer to the start of a character.",
-               G_STRLOC, byte_on_line);
-  
-  if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
+  if (byte_on_line == 0)
     *iter = pos;
   else
     gtk_text_iter_forward_line (iter);
@@ -4408,7 +4705,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;
               
@@ -4610,43 +4907,6 @@ 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
@@ -4742,7 +5002,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))
@@ -5075,7 +5335,10 @@ _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
   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;
     }
@@ -5354,3 +5617,5 @@ _gtk_text_iter_check (const GtkTextIter *iter)
     g_error ("Iterator was on last line (past the end iterator)");
 }
 
+#define __GTK_TEXT_ITER_C__
+#include "gtkaliasdef.c"