]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextbtree.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtktextbtree.c
index 8654c292350ef9a3454ba9730c3acf74953d5e18..2757c8ca40a51094186ef7a6b129a86c6affd0c1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * gtktextbtree.c --
+ * Gtktextbtree.c --
  *
  *      This file contains code that manages the B-tree representation
  *      of text for the text buffer and implements character and
  *
  */
 
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include "config.h"
 #include "gtktextbtree.h"
 #include <string.h>
-#include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include "gtksignal.h"
 #include "gtktexttag.h"
+#include "gtktexttagprivate.h"
 #include "gtktexttagtable.h"
 #include "gtktextlayout.h"
 #include "gtktextiterprivate.h"
@@ -72,7 +73,7 @@
 
 /*
  * The structure below is used to pass information between
- * gtk_text_btree_get_tags and inc_count:
+ * _gtk_text_btree_get_tags and inc_count:
  */
 
 typedef struct TagInfo {
@@ -100,10 +101,15 @@ struct _NodeData {
 
   /* Height and width of this node */
   gint height;
-  gint width : 24;
+  signed int width : 24;
 
-  /* boolean indicating whether the height/width need to be recomputed */
-  gint valid : 8;
+  /* boolean indicating whether the lines below this node are in need of validation.
+   * However, width/height should always represent the current total width and
+   * max height for lines below this node; the valid flag indicates whether the
+   * width/height on the lines needs recomputing, not whether the totals
+   * need recomputing.
+   */
+  guint valid : 8;             /* Actually a boolean */
 };
 
 
@@ -137,14 +143,14 @@ struct _GtkTextBTreeNode {
   int level;                            /* Level of this node in the B-tree.
                                          * 0 refers to the bottom of the tree
                                          * (children are lines, not nodes). */
+  int num_lines;                        /* Total number of lines (leaves) in
+                                         * the subtree rooted here. */
+  int num_chars;                        /* Number of chars below here */
+  int num_children;                     /* Number of children of this node. */
   union {                               /* First in linked list of children. */
     struct _GtkTextBTreeNode *node;         /* Used if level > 0. */
     GtkTextLine *line;         /* Used if level == 0. */
   } children;
-  int num_children;                     /* Number of children of this node. */
-  int num_lines;                        /* Total number of lines (leaves) in
-                                         * the subtree rooted here. */
-  int num_chars;                        /* Number of chars below here */
 
   NodeData *node_data;
 };
@@ -177,25 +183,35 @@ struct _GtkTextBTree {
   GtkTextBuffer *buffer;
   BTreeView *views;
   GSList *tag_infos;
-  guint tag_changed_handler;
-  guint tag_removed_handler;
+  gulong tag_changed_handler;
+
   /* Incremented when a segment with a byte size > 0
-     is added to or removed from the tree (i.e. the
-     length of a line may have changed, and lines may
-     have been added or removed). This invalidates
-     all outstanding iterators.
-  */
+   * is added to or removed from the tree (i.e. the
+   * length of a line may have changed, and lines may
+   * have been added or removed). This invalidates
+   * all outstanding iterators.
+   */
   guint chars_changed_stamp;
   /* Incremented when any segments are added or deleted;
-     this makes outstanding iterators recalculate their
-     pointed-to segment and segment offset.
-  */
+   * this makes outstanding iterators recalculate their
+   * pointed-to segment and segment offset.
+   */
   guint segments_changed_stamp;
 
-  GtkTextLine *end_iter_line;
+  /* Cache the last line in the buffer */
+  GtkTextLine *last_line;
+  guint last_line_stamp;
 
+  /* Cache the next-to-last line in the buffer,
+   * containing the end iterator
+   */
+  GtkTextLine *end_iter_line;
+  GtkTextLineSegment *end_iter_segment;
+  int end_iter_segment_byte_index;
+  int end_iter_segment_char_offset;
   guint end_iter_line_stamp;
-
+  guint end_iter_segment_stamp;
+  
   GHashTable *child_anchor_table;
 };
 
@@ -265,7 +281,9 @@ static NodeData         *node_data_find         (NodeData *nd,
                                                  gpointer  view_id);
 
 static GtkTextBTreeNode     *gtk_text_btree_node_new                  (void);
+#if 0
 static void                  gtk_text_btree_node_invalidate_downward  (GtkTextBTreeNode *node);
+#endif
 static void                  gtk_text_btree_node_invalidate_upward    (GtkTextBTreeNode *node,
                                                                        gpointer          view_id);
 static NodeData *            gtk_text_btree_node_check_valid          (GtkTextBTreeNode *node,
@@ -280,6 +298,8 @@ static void                  gtk_text_btree_node_remove_view         (BTreeView
                                                                       gpointer          view_id);
 static void                  gtk_text_btree_node_destroy             (GtkTextBTree     *tree,
                                                                       GtkTextBTreeNode *node);
+static void                  gtk_text_btree_node_free_empty          (GtkTextBTree *tree,
+                                                                      GtkTextBTreeNode *node);
 static NodeData         *    gtk_text_btree_node_ensure_data         (GtkTextBTreeNode *node,
                                                                       gpointer          view_id);
 static void                  gtk_text_btree_node_remove_data         (GtkTextBTreeNode *node,
@@ -297,9 +317,6 @@ static void tag_changed_cb        (GtkTextTagTable  *table,
                                    GtkTextTag       *tag,
                                    gboolean          size_changed,
                                    GtkTextBTree     *tree);
-static void tag_removed_cb        (GtkTextTagTable  *table,
-                                   GtkTextTag       *tag,
-                                   GtkTextBTree     *tree);
 static void cleanup_line          (GtkTextLine      *line);
 static void recompute_node_counts (GtkTextBTree     *tree,
                                    GtkTextBTreeNode *node);
@@ -325,7 +342,8 @@ static void            gtk_text_btree_remove_tag_info       (GtkTextBTree   *tre
 
 static void redisplay_region (GtkTextBTree      *tree,
                               const GtkTextIter *start,
-                              const GtkTextIter *end);
+                              const GtkTextIter *end,
+                              gboolean           cursors_only);
 
 /* Inline thingies */
 
@@ -346,8 +364,8 @@ chars_changed (GtkTextBTree *tree)
  */
 
 GtkTextBTree*
-gtk_text_btree_new (GtkTextTagTable *table,
-                    GtkTextBuffer *buffer)
+_gtk_text_btree_new (GtkTextTagTable *table,
+                     GtkTextBuffer *buffer)
 {
   GtkTextBTree *tree;
   GtkTextBTreeNode *root_node;
@@ -402,21 +420,21 @@ gtk_text_btree_new (GtkTextTagTable *table,
   tree->chars_changed_stamp = g_random_int ();
   tree->segments_changed_stamp = g_random_int ();
 
+  tree->last_line_stamp = tree->chars_changed_stamp - 1;
+  tree->last_line = NULL;
+
   tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
+  tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
   tree->end_iter_line = NULL;
+  tree->end_iter_segment_byte_index = 0;
+  tree->end_iter_segment_char_offset = 0;
+  
+  g_object_ref (tree->table);
 
-  gtk_object_ref (GTK_OBJECT (tree->table));
-  gtk_object_sink (GTK_OBJECT (tree->table));
-
-  tree->tag_changed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
-                                                  "tag_changed",
-                                                  GTK_SIGNAL_FUNC (tag_changed_cb),
-                                                  tree);
-
-  tree->tag_removed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
-                                                  "tag_removed",
-                                                  GTK_SIGNAL_FUNC (tag_removed_cb),
-                                                  tree);
+  tree->tag_changed_handler = g_signal_connect (tree->table,
+                                               "tag-changed",
+                                               G_CALLBACK (tag_changed_cb),
+                                               tree);
 
   tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
   tree->child_anchor_table = NULL;
@@ -431,10 +449,10 @@ gtk_text_btree_new (GtkTextTagTable *table,
     GtkTextIter start;
     GtkTextLineSegment *seg;
 
-    gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
+    _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
 
 
-    tree->insert_mark = gtk_text_btree_set_mark (tree,
+    tree->insert_mark = _gtk_text_btree_set_mark (tree,
                                                  NULL,
                                                  "insert",
                                                  FALSE,
@@ -446,7 +464,7 @@ gtk_text_btree_new (GtkTextTagTable *table,
     seg->body.mark.not_deleteable = TRUE;
     seg->body.mark.visible = TRUE;
 
-    tree->selection_bound_mark = gtk_text_btree_set_mark (tree,
+    tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
                                                           NULL,
                                                           "selection_bound",
                                                           FALSE,
@@ -457,8 +475,8 @@ gtk_text_btree_new (GtkTextTagTable *table,
 
     seg->body.mark.not_deleteable = TRUE;
 
-    g_object_ref (G_OBJECT (tree->insert_mark));
-    g_object_ref (G_OBJECT (tree->selection_bound_mark));
+    g_object_ref (tree->insert_mark);
+    g_object_ref (tree->selection_bound_mark);
   }
 
   tree->refcount = 1;
@@ -467,7 +485,7 @@ gtk_text_btree_new (GtkTextTagTable *table,
 }
 
 void
-gtk_text_btree_ref (GtkTextBTree *tree)
+_gtk_text_btree_ref (GtkTextBTree *tree)
 {
   g_return_if_fail (tree != NULL);
   g_return_if_fail (tree->refcount > 0);
@@ -475,18 +493,8 @@ gtk_text_btree_ref (GtkTextBTree *tree)
   tree->refcount += 1;
 }
 
-static void
-mark_destroy_foreach (gpointer key, gpointer value, gpointer user_data)
-{
-  GtkTextLineSegment *seg = value;
-
-  g_return_if_fail (seg->body.mark.tree == NULL);
-
-  g_object_unref (G_OBJECT (seg->body.mark.obj));
-}
-
 void
-gtk_text_btree_unref (GtkTextBTree *tree)
+_gtk_text_btree_unref (GtkTextBTree *tree)
 {
   g_return_if_fail (tree != NULL);
   g_return_if_fail (tree->refcount > 0);
@@ -494,49 +502,54 @@ gtk_text_btree_unref (GtkTextBTree *tree)
   tree->refcount -= 1;
 
   if (tree->refcount == 0)
-    {
-      gtk_text_btree_node_destroy (tree, tree->root_node);
+    {      
+      g_signal_handler_disconnect (tree->table,
+                                   tree->tag_changed_handler);
 
-      g_hash_table_foreach (tree->mark_table,
-                            mark_destroy_foreach,
-                            NULL);
+      g_object_unref (tree->table);
+      tree->table = NULL;
+      
+      gtk_text_btree_node_destroy (tree, tree->root_node);
+      tree->root_node = NULL;
+      
+      g_assert (g_hash_table_size (tree->mark_table) == 0);
       g_hash_table_destroy (tree->mark_table);
-
-      g_object_unref (G_OBJECT (tree->insert_mark));
-      g_object_unref (G_OBJECT (tree->selection_bound_mark));
-
-      gtk_signal_disconnect (GTK_OBJECT (tree->table),
-                             tree->tag_changed_handler);
-
-      gtk_signal_disconnect (GTK_OBJECT (tree->table),
-                             tree->tag_removed_handler);
-
-      gtk_object_unref (GTK_OBJECT (tree->table));
+      tree->mark_table = NULL;
+      if (tree->child_anchor_table != NULL) 
+       {
+         g_hash_table_destroy (tree->child_anchor_table);
+         tree->child_anchor_table = NULL;
+       }
+
+      g_object_unref (tree->insert_mark);
+      tree->insert_mark = NULL;
+      g_object_unref (tree->selection_bound_mark);
+      tree->selection_bound_mark = NULL;
 
       g_free (tree);
     }
 }
 
 GtkTextBuffer*
-gtk_text_btree_get_buffer (GtkTextBTree *tree)
+_gtk_text_btree_get_buffer (GtkTextBTree *tree)
 {
   return tree->buffer;
 }
 
 guint
-gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
+_gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
 {
   return tree->chars_changed_stamp;
 }
 
 guint
-gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
+_gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
 {
   return tree->segments_changed_stamp;
 }
 
 void
-gtk_text_btree_segments_changed (GtkTextBTree *tree)
+_gtk_text_btree_segments_changed (GtkTextBTree *tree)
 {
   g_return_if_fail (tree != NULL);
   segments_changed (tree);
@@ -546,97 +559,213 @@ gtk_text_btree_segments_changed (GtkTextBTree *tree)
  * Indexable segment mutation
  */
 
-void
-gtk_text_btree_delete (GtkTextIter *start,
-                       GtkTextIter *end)
+/*
+ *  The following function is responsible for resolving the bidi direction
+ *  for the lines between start and end. But it also calculates any
+ *  dependent bidi direction for surrounding lines that change as a result
+ *  of the bidi direction decisions within the range. The function is
+ *  trying to do as little propagation as is needed.
+ */
+static void
+gtk_text_btree_resolve_bidi (GtkTextIter *start,
+                            GtkTextIter *end)
 {
-  GtkTextLineSegment *prev_seg;             /* The segment just before the start
-                                             * of the deletion range. */
-  GtkTextLineSegment *last_seg;             /* The segment just after the end
-                                             * of the deletion range. */
-  GtkTextLineSegment *seg, *next;
-  GtkTextLine *curline;
-  GtkTextBTreeNode *curnode, *node;
-  GtkTextBTree *tree;
-  GtkTextLine *start_line;
-  GtkTextLine *end_line;
-  GtkTextLine *deleted_lines = NULL;        /* List of lines we've deleted */
-  gint start_byte_offset;
+  GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
+  GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
+  PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
 
-  g_return_if_fail (start != NULL);
-  g_return_if_fail (end != NULL);
-  g_return_if_fail (gtk_text_iter_get_btree (start) ==
-                    gtk_text_iter_get_btree (end));
+  /* Resolve the strong bidi direction for all lines between
+   * start and end.
+  */
+  start_line = _gtk_text_iter_get_text_line (start);
+  start_line_prev = _gtk_text_line_previous (start_line);
+  end_line = _gtk_text_iter_get_text_line (end);
+  end_line_next = _gtk_text_line_next (end_line);
+  
+  line = start_line;
+  while (line && line != end_line_next)
+    {
+      /* Loop through the segments and search for a strong character
+       */
+      GtkTextLineSegment *seg = line->segments;
+      line->dir_strong = PANGO_DIRECTION_NEUTRAL;
+      
+      while (seg)
+        {
+          if (seg->type == &gtk_text_char_type && seg->byte_count > 0)
+            {
+             PangoDirection pango_dir;
 
-  gtk_text_iter_reorder (start, end);
+              pango_dir = pango_find_base_dir (seg->body.chars,
+                                              seg->byte_count);
+             
+              if (pango_dir != PANGO_DIRECTION_NEUTRAL)
+                {
+                  line->dir_strong = pango_dir;
+                  break;
+                }
+            }
+          seg = seg->next;
+        }
 
-  tree = gtk_text_iter_get_btree (start);
+      line = _gtk_text_line_next (line);
+    }
 
-  {
-    /*
-     * The code below is ugly, but it's needed to make sure there
-     * is always a dummy empty line at the end of the text.  If the
-     * final newline of the file (just before the dummy line) is being
-     * deleted, then back up index to just before the newline.  If
-     * there is a newline just before the first character being deleted,
-     * then back up the first index too, so that an even number of lines
-     * gets deleted.  Furthermore, remove any tags that are present on
-     * the newline that isn't going to be deleted after all (this simulates
-     * deleting the newline and then adding a "clean" one back again).
-     */
+  /* Sweep forward */
 
-    gint line1;
-    gint line2;
+  /* The variable dir_above_propagated contains the forward propagated
+   * direction before start. It is neutral if start is in the beginning
+   * of the buffer.
+   */
+  dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
+  if (start_line_prev)
+    dir_above_propagated = start_line_prev->dir_propagated_forward;
 
-    line1 = gtk_text_iter_get_line (start);
-    line2 = gtk_text_iter_get_line (end);
+  /* Loop forward and propagate the direction of each paragraph 
+   * to all neutral lines.
+   */
+  line = start_line;
+  last_strong = dir_above_propagated;
+  while (line != end_line_next)
+    {
+      if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
+        last_strong = line->dir_strong;
+      
+      line->dir_propagated_forward = last_strong;
+      
+      line = _gtk_text_line_next (line);
+    }
 
-    if (line2 == gtk_text_btree_line_count (tree))
+  /* Continue propagating as long as the previous resolved forward
+   * is different from last_strong.
+   */
+  {
+    GtkTextIter end_propagate;
+    
+    while (line &&
+          line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
+          line->dir_propagated_forward != last_strong)
       {
-        GtkTextTag** tags;
-        int array_size;
-        GtkTextIter orig_end;
+        GtkTextLine *prev = line;
+        line->dir_propagated_forward = last_strong;
+        
+        line = _gtk_text_line_next(line);
+        if (!line)
+          {
+            line = prev;
+            break;
+          }
+      }
 
-        orig_end = *end;
-        gtk_text_iter_prev_char (end);
+    /* The last line to invalidate is the last line before the
+     * line with the strong character. Or in case of the end of the
+     * buffer, the last line of the buffer. (There seems to be an
+     * extra "virtual" last line in the buffer that must not be used
+     * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
+     * _gtk_text_line_previous is ok in that case as well.)
+     */
+    line = _gtk_text_line_previous (line);
+    _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
+    _gtk_text_btree_invalidate_region (tree, end, &end_propagate, FALSE);
+  }
+  
+  /* Sweep backward */
 
-        --line2;
+  /* The variable dir_below_propagated contains the backward propagated
+   * direction after end. It is neutral if end is at the end of
+   * the buffer.
+  */
+  dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
+  if (end_line_next)
+    dir_below_propagated = end_line_next->dir_propagated_back;
 
-        if (gtk_text_iter_get_line_offset (start) == 0 &&
-            line1 != 0)
-          {
-            gtk_text_iter_prev_char (start);
-            --line1;
-          }
+  /* Loop backward and propagate the direction of each paragraph 
+   * to all neutral lines.
+   */
+  line = end_line;
+  last_strong = dir_below_propagated;
+  while (line != start_line_prev)
+    {
+      if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
+        last_strong = line->dir_strong;
 
-        tags = gtk_text_btree_get_tags (end,
-                                        &array_size);
+      line->dir_propagated_back = last_strong;
 
-        if (tags != NULL)
-          {
-            int i;
+      line = _gtk_text_line_previous (line);
+    }
 
-            i = 0;
-            while (i < array_size)
-              {
-                gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
+  /* Continue propagating as long as the resolved backward dir
+   * is different from last_strong.
+   */
+  {
+    GtkTextIter start_propagate;
 
-                ++i;
-              }
+    while (line &&
+          line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
+          line->dir_propagated_back != last_strong)
+      {
+        GtkTextLine *prev = line;
+        line->dir_propagated_back = last_strong;
 
-            g_free (tags);
+        line = _gtk_text_line_previous (line);
+        if (!line)
+          {
+            line = prev;
+            break;
           }
       }
+
+    /* We only need to invalidate for backwards propagation if the
+     * line we ended up on didn't get a direction from forwards
+     * propagation.
+     */
+    if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
+      {
+        _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
+        _gtk_text_btree_invalidate_region (tree, &start_propagate, start, FALSE);
+      }
   }
+}
 
+void
+_gtk_text_btree_delete (GtkTextIter *start,
+                        GtkTextIter *end)
+{
+  GtkTextLineSegment *prev_seg;             /* The segment just before the start
+                                             * of the deletion range. */
+  GtkTextLineSegment *last_seg;             /* The segment just after the end
+                                             * of the deletion range. */
+  GtkTextLineSegment *seg, *next, *next2;
+  GtkTextLine *curline;
+  GtkTextBTreeNode *curnode, *node;
+  GtkTextBTree *tree;
+  GtkTextLine *start_line;
+  GtkTextLine *end_line;
+  GtkTextLine *line;
+  GtkTextLine *deleted_lines = NULL;        /* List of lines we've deleted */
+  gint start_byte_offset;
+
+  g_return_if_fail (start != NULL);
+  g_return_if_fail (end != NULL);
+  g_return_if_fail (_gtk_text_iter_get_btree (start) ==
+                    _gtk_text_iter_get_btree (end));
+
+  gtk_text_iter_order (start, end);
+
+  tree = _gtk_text_iter_get_btree (start);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
+  
   /* Broadcast the need for redisplay before we break the iterators */
-  gtk_text_btree_invalidate_region (tree, start, end);
+  DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
+  _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
 
   /* Save the byte offset so we can reset the iterators */
   start_byte_offset = gtk_text_iter_get_line_index (start);
 
-  start_line = gtk_text_iter_get_text_line (start);
-  end_line = gtk_text_iter_get_text_line (end);
+  start_line = _gtk_text_iter_get_text_line (start);
+  end_line = _gtk_text_iter_get_text_line (end);
 
   /*
    * Split the start and end segments, so we have a place
@@ -689,7 +818,7 @@ gtk_text_btree_delete (GtkTextIter *start,
            * (unless it's the starting line for the range).
            */
 
-          nextline = gtk_text_line_next (curline);
+          nextline = _gtk_text_line_next (curline);
           if (curline != start_line)
             {
               if (curnode == start_line->parent)
@@ -700,6 +829,9 @@ gtk_text_btree_delete (GtkTextIter *start,
               for (node = curnode; node != NULL;
                    node = node->parent)
                 {
+                  /* Don't update node->num_chars, because
+                   * that was done when we deleted the segments.
+                   */
                   node->num_lines -= 1;
                 }
 
@@ -735,7 +867,7 @@ gtk_text_btree_delete (GtkTextIter *start,
                   prevnode->next = curnode->next;
                 }
               parent->num_children--;
-              g_free (curnode);
+              gtk_text_btree_node_free_empty (tree, curnode);
               curnode = parent;
             }
           curnode = curline->parent;
@@ -745,7 +877,7 @@ gtk_text_btree_delete (GtkTextIter *start,
       next = seg->next;
       char_count = seg->char_count;
 
-      if ((*seg->type->deleteFunc)(seg, curline, 0) != 0)
+      if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
         {
           /*
            * This segment refuses to die.  Move it to prev_seg and
@@ -757,12 +889,30 @@ gtk_text_btree_delete (GtkTextIter *start,
               seg->next = start_line->segments;
               start_line->segments = seg;
             }
-          else
-            {
+          else if (prev_seg->next &&
+                  prev_seg->next != last_seg &&
+                  seg->type == &gtk_text_toggle_off_type &&
+                  prev_seg->next->type == &gtk_text_toggle_on_type &&
+                  seg->body.toggle.info == prev_seg->next->body.toggle.info)
+           {
+             /* Try to match an off toggle with the matching on toggle
+              * if it immediately follows. This is a common case, and
+              * handling it here prevents quadratic blowup in
+              * cleanup_line() below. See bug 317125.
+              */
+             next2 = prev_seg->next->next;
+             g_free ((char *)prev_seg->next);
+             prev_seg->next = next2;
+             g_free ((char *)seg);
+             seg = NULL;
+           }
+         else
+           {
               seg->next = prev_seg->next;
               prev_seg->next = seg;
             }
-          if (seg->type->leftGravity)
+
+          if (seg && seg->type->leftGravity)
             {
               prev_seg = seg;
             }
@@ -790,21 +940,32 @@ gtk_text_btree_delete (GtkTextIter *start,
     {
       BTreeView *view;
       GtkTextBTreeNode *ancestor_node;
-
       GtkTextLine *prevline;
+      int chars_moved;      
 
+      /* last_seg was appended to start_line up at the top of this function */
+      chars_moved = 0;
       for (seg = last_seg; seg != NULL;
            seg = seg->next)
         {
+          chars_moved += seg->char_count;
           if (seg->type->lineChangeFunc != NULL)
             {
               (*seg->type->lineChangeFunc)(seg, end_line);
             }
         }
+
+      for (node = start_line->parent; node != NULL;
+           node = node->parent)
+        {
+          node->num_chars += chars_moved;
+        }
+      
       curnode = end_line->parent;
       for (node = curnode; node != NULL;
            node = node->parent)
         {
+          node->num_chars -= chars_moved;
           node->num_lines--;
         }
       curnode->num_children--;
@@ -832,7 +993,6 @@ gtk_text_btree_delete (GtkTextIter *start,
       view = tree->views;
       while (view)
         {
-          GtkTextLine *line;
           GtkTextLineData *ld;
 
           gint deleted_width = 0;
@@ -842,7 +1002,7 @@ gtk_text_btree_delete (GtkTextIter *start,
           while (line)
             {
               GtkTextLine *next_line = line->next;
-              ld = gtk_text_line_get_data (line, view->view_id);
+              ld = _gtk_text_line_get_data (line, view->view_id);
 
               if (ld)
                 {
@@ -850,24 +1010,27 @@ gtk_text_btree_delete (GtkTextIter *start,
                   deleted_height += ld->height;
                 }
 
-              if (!view->next)
-                gtk_text_line_destroy (tree, line);
-
               line = next_line;
             }
 
           if (deleted_width > 0 || deleted_height > 0)
             {
-              ld = gtk_text_line_get_data (start_line, view->view_id);
-
-              /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
-               * no way to add ld without also validating the node, which would
-               * be improper at this point.
-               */
-              /* This assertion does actually fail sometimes, must
-                 fix before stable release -hp */
-              g_assert (ld);
-
+              ld = _gtk_text_line_get_data (start_line, view->view_id);
+              
+              if (ld == NULL)
+                {
+                  /* This means that start_line has never been validated.
+                   * We don't really want to do the validation here but
+                   * we do need to store our temporary sizes. So we
+                   * create the line data and assume a line w/h of 0.
+                   */
+                  ld = _gtk_text_line_data_new (view->layout, start_line);
+                  _gtk_text_line_add_data (start_line, ld);
+                  ld->width = 0;
+                  ld->height = 0;
+                  ld->valid = FALSE;
+                }
+              
               ld->width = MAX (deleted_width, ld->width);
               ld->height += deleted_height;
               ld->valid = FALSE;
@@ -880,6 +1043,19 @@ gtk_text_btree_delete (GtkTextIter *start,
           view = view->next;
         }
 
+      line = deleted_lines;
+      while (line)
+        {
+          GtkTextLine *next_line = line->next;
+
+          gtk_text_line_destroy (tree, line);
+
+          line = next_line;
+        }
+
+      /* avoid dangling pointer */
+      deleted_lines = NULL;
+      
       gtk_text_btree_rebalance (tree, curnode);
     }
 
@@ -900,18 +1076,20 @@ gtk_text_btree_delete (GtkTextIter *start,
   chars_changed (tree);
   segments_changed (tree);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 
   /* Re-initialize our iterators */
-  gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
+  _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
   *end = *start;
+
+  gtk_text_btree_resolve_bidi (start, end);
 }
 
 void
-gtk_text_btree_insert (GtkTextIter *iter,
-                       const gchar *text,
-                       gint len)
+_gtk_text_btree_insert (GtkTextIter *iter,
+                        const gchar *text,
+                        gint         len)
 {
   GtkTextLineSegment *prev_seg;     /* The segment just before the first
                                      * new segment (NULL means new segment
@@ -924,12 +1102,15 @@ gtk_text_btree_insert (GtkTextIter *iter,
                                 * added to this line). */
   GtkTextLineSegment *seg;
   GtkTextLine *newline;
-  int chunkSize;                        /* # characters in current chunk. */
-  guint sol; /* start of line */
-  guint eol;                      /* Pointer to character just after last
-                                   * one in current chunk. */
+  int chunk_len;                        /* # characters in current chunk. */
+  gint sol;                           /* start of line */
+  gint eol;                           /* Pointer to character just after last
+                                       * one in current chunk.
+                                       */
+  gint delim;                          /* index of paragraph delimiter */
   int line_count_delta;                /* Counts change to total number of
-                                        * lines in file. */
+                                        * lines in file.
+                                        */
 
   int char_count_delta;                /* change to number of chars */
   GtkTextBTree *tree;
@@ -943,19 +1124,24 @@ gtk_text_btree_insert (GtkTextIter *iter,
     len = strlen (text);
 
   /* extract iterator info */
-  tree = gtk_text_iter_get_btree (iter);
-  line = gtk_text_iter_get_text_line (iter);
+  tree = _gtk_text_iter_get_btree (iter);
+  line = _gtk_text_iter_get_text_line (iter);
+  
   start_line = line;
   start_byte_index = gtk_text_iter_get_line_index (iter);
 
-  /* Get our insertion segment split */
+  /* Get our insertion segment split. Note this assumes line allows
+   * char insertions, which isn't true of the "last" line. But iter
+   * should not be on that line, as we assert here.
+   */
+  g_assert (!_gtk_text_line_is_last (line, tree));
   prev_seg = gtk_text_line_segment_split (iter);
   cur_seg = prev_seg;
 
   /* Invalidate all iterators */
   chars_changed (tree);
   segments_changed (tree);
-
+  
   /*
    * Chop the text up into lines and create a new segment for
    * each line, plus a new line for the leftovers from the
@@ -968,17 +1154,27 @@ gtk_text_btree_insert (GtkTextIter *iter,
   char_count_delta = 0;
   while (eol < len)
     {
-      for (; eol < len; eol++)
-        {
-          if (text[eol] == '\n')
-            {
-              eol++;
-              break;
-            }
-        }
-      chunkSize = eol - sol;
-
-      seg = _gtk_char_segment_new (&text[sol], chunkSize);
+      sol = eol;
+      
+      pango_find_paragraph_boundary (text + sol,
+                                     len - sol,
+                                     &delim,
+                                     &eol);      
+
+      /* make these relative to the start of the text */
+      delim += sol;
+      eol += sol;
+
+      g_assert (eol >= sol);
+      g_assert (delim >= sol);
+      g_assert (eol >= delim);
+      g_assert (sol >= 0);
+      g_assert (eol <= len);
+      
+      chunk_len = eol - sol;
+
+      g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
+      seg = _gtk_char_segment_new (&text[sol], chunk_len);
 
       char_count_delta += seg->char_count;
 
@@ -993,8 +1189,10 @@ gtk_text_btree_insert (GtkTextIter *iter,
           cur_seg->next = seg;
         }
 
-      if (text[eol-1] != '\n')
+      if (delim == eol)
         {
+          /* chunk didn't end with a paragraph separator */
+          g_assert (eol == len);
           break;
         }
 
@@ -1012,8 +1210,6 @@ gtk_text_btree_insert (GtkTextIter *iter,
       line = newline;
       cur_seg = NULL;
       line_count_delta++;
-
-      sol = eol;
     }
 
   /*
@@ -1036,10 +1232,10 @@ gtk_text_btree_insert (GtkTextIter *iter,
     GtkTextIter end;
 
 
-    gtk_text_btree_get_iter_at_line (tree,
-                                     &start,
-                                     start_line,
-                                     start_byte_index);
+    _gtk_text_btree_get_iter_at_line (tree,
+                                      &start,
+                                      start_line,
+                                      start_byte_index);
     end = start;
 
     /* We could almost certainly be more efficient here
@@ -1047,12 +1243,14 @@ gtk_text_btree_insert (GtkTextIter *iter,
        above. FIXME */
     gtk_text_iter_forward_chars (&end, char_count_delta);
 
-    gtk_text_btree_invalidate_region (tree,
-                                      &start, &end);
+    DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
+    _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
 
 
     /* Convenience for the user */
     *iter = end;
+
+    gtk_text_btree_resolve_bidi (&start, &end);
   }
 }
 
@@ -1067,8 +1265,8 @@ insert_pixbuf_or_widget_segment (GtkTextIter        *iter,
   GtkTextBTree *tree;
   gint start_byte_offset;
 
-  line = gtk_text_iter_get_text_line (iter);
-  tree = gtk_text_iter_get_btree (iter);
+  line = _gtk_text_iter_get_text_line (iter);
+  tree = _gtk_text_iter_get_btree (iter);
   start_byte_offset = gtk_text_iter_get_line_index (iter);
 
   prevPtr = gtk_text_line_segment_split (iter);
@@ -1090,16 +1288,17 @@ insert_pixbuf_or_widget_segment (GtkTextIter        *iter,
 
   /* reset *iter for the user, and invalidate tree nodes */
 
-  gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
+  _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
 
   *iter = start;
-  gtk_text_iter_next_char (iter); /* skip forward past the segment */
+  gtk_text_iter_forward_char (iter); /* skip forward past the segment */
 
-  gtk_text_btree_invalidate_region (tree, &start, iter);
+  DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
+  _gtk_text_btree_invalidate_region (tree, &start, iter, FALSE);
 }
      
 void
-gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
+_gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
                               GdkPixbuf   *pixbuf)
 {
   GtkTextLineSegment *seg;
@@ -1109,30 +1308,36 @@ gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
   insert_pixbuf_or_widget_segment (iter, seg);
 }
 
-GtkTextChildAnchor*
-gtk_text_btree_create_child_anchor (GtkTextIter *iter)
+void
+_gtk_text_btree_insert_child_anchor (GtkTextIter        *iter,
+                                     GtkTextChildAnchor *anchor)
 {
   GtkTextLineSegment *seg;
   GtkTextBTree *tree;
+
+  if (anchor->segment != NULL)
+    {
+      g_warning (G_STRLOC": Same child anchor can't be inserted twice");
+      return;
+    }
   
-  seg = _gtk_widget_segment_new ();
+  seg = _gtk_widget_segment_new (anchor);
 
+  tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
+  seg->body.child.line = _gtk_text_iter_get_text_line (iter);
+  
   insert_pixbuf_or_widget_segment (iter, seg);
 
-  tree = seg->body.child.tree;
-
   if (tree->child_anchor_table == NULL)
     tree->child_anchor_table = g_hash_table_new (NULL, NULL);
 
   g_hash_table_insert (tree->child_anchor_table,
                        seg->body.child.obj,
                        seg->body.child.obj);
-  
-  return seg->body.child.obj;
 }
 
 void
-gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
+_gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
 {
   GtkTextLineSegment *seg;
 
@@ -1153,8 +1358,8 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view,
 {
   gint current_y = 0;
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 
   if (node->level == 0)
     {
@@ -1166,7 +1371,7 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view,
         {
           GtkTextLineData *ld;
 
-          ld = gtk_text_line_get_data (line, view->view_id);
+          ld = _gtk_text_line_get_data (line, view->view_id);
 
           if (ld)
             {
@@ -1211,10 +1416,10 @@ find_line_by_y (GtkTextBTree *tree, BTreeView *view,
 }
 
 GtkTextLine *
-gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
-                               gpointer      view_id,
-                               gint          ypixel,
-                               gint         *line_top_out)
+_gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
+                                gpointer      view_id,
+                                gint          ypixel,
+                                gint         *line_top_out)
 {
   GtkTextLine *line;
   BTreeView *view;
@@ -1249,7 +1454,7 @@ find_line_top_in_line_list (GtkTextBTree *tree,
       if (line == target_line)
         return y;
 
-      ld = gtk_text_line_get_data (line, view->view_id);
+      ld = _gtk_text_line_get_data (line, view->view_id);
       if (ld)
         y += ld->height;
 
@@ -1263,7 +1468,7 @@ find_line_top_in_line_list (GtkTextBTree *tree,
 }
 
 gint
-gtk_text_btree_find_line_top (GtkTextBTree *tree,
+_gtk_text_btree_find_line_top (GtkTextBTree *tree,
                               GtkTextLine *target_line,
                               gpointer view_id)
 {
@@ -1334,7 +1539,7 @@ gtk_text_btree_find_line_top (GtkTextBTree *tree,
 }
 
 void
-gtk_text_btree_add_view (GtkTextBTree *tree,
+_gtk_text_btree_add_view (GtkTextBTree *tree,
                          GtkTextLayout *layout)
 {
   BTreeView *view;
@@ -1342,7 +1547,7 @@ gtk_text_btree_add_view (GtkTextBTree *tree,
   GtkTextLineData *line_data;
 
   g_return_if_fail (tree != NULL);
-
+  
   view = g_new (BTreeView, 1);
 
   view->view_id = layout;
@@ -1351,6 +1556,12 @@ gtk_text_btree_add_view (GtkTextBTree *tree,
   view->next = tree->views;
   view->prev = NULL;
 
+  if (tree->views)
+    {
+      g_assert (tree->views->prev == NULL);
+      tree->views->prev = view;
+    }
+  
   tree->views = view;
 
   /* The last line in the buffer has identity values for the per-view
@@ -1366,19 +1577,19 @@ gtk_text_btree_add_view (GtkTextBTree *tree,
   line_data->height = 0;
   line_data->valid = TRUE;
 
-  gtk_text_line_add_data (last_line, line_data);
+  _gtk_text_line_add_data (last_line, line_data);
 }
 
 void
-gtk_text_btree_remove_view (GtkTextBTree *tree,
-                            gpointer view_id)
+_gtk_text_btree_remove_view (GtkTextBTree *tree,
+                             gpointer      view_id)
 {
   BTreeView *view;
   GtkTextLine *last_line;
   GtkTextLineData *line_data;
 
   g_return_if_fail (tree != NULL);
-
+  
   view = tree->views;
 
   while (view != NULL)
@@ -1404,18 +1615,22 @@ gtk_text_btree_remove_view (GtkTextBTree *tree,
    * (Do this first, so that we don't try to call the view's line data destructor on it.)
    */
   last_line = get_last_line (tree);
-  line_data = gtk_text_line_remove_data (last_line, view_id);
+  line_data = _gtk_text_line_remove_data (last_line, view_id);
   g_free (line_data);
 
   gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
 
+  view->layout = (gpointer) 0xdeadbeef;
+  view->view_id = (gpointer) 0xdeadbeef;
+  
   g_free (view);
 }
 
 void
-gtk_text_btree_invalidate_region (GtkTextBTree *tree,
-                                  const GtkTextIter *start,
-                                  const GtkTextIter *end)
+_gtk_text_btree_invalidate_region (GtkTextBTree      *tree,
+                                   const GtkTextIter *start,
+                                   const GtkTextIter *end,
+                                   gboolean           cursors_only)
 {
   BTreeView *view;
 
@@ -1423,14 +1638,17 @@ gtk_text_btree_invalidate_region (GtkTextBTree *tree,
 
   while (view != NULL)
     {
-      gtk_text_layout_invalidate (view->layout, start, end);
+      if (cursors_only)
+       gtk_text_layout_invalidate_cursors (view->layout, start, end);
+      else
+       gtk_text_layout_invalidate (view->layout, start, end);
 
       view = view->next;
     }
 }
 
 void
-gtk_text_btree_get_view_size (GtkTextBTree *tree,
+_gtk_text_btree_get_view_size (GtkTextBTree *tree,
                               gpointer view_id,
                               gint *width,
                               gint *height)
@@ -1456,7 +1674,7 @@ static IterStack*
 iter_stack_new (void)
 {
   IterStack *stack;
-  stack = g_new (IterStack, 1);
+  stack = g_slice_new (IterStack);
   stack->iters = NULL;
   stack->count = 0;
   stack->alloced = 0;
@@ -1464,20 +1682,22 @@ iter_stack_new (void)
 }
 
 static void
-iter_stack_push (IterStack *stack, const GtkTextIter *iter)
+iter_stack_push (IterStack         *stack, 
+                const GtkTextIter *iter)
 {
   stack->count += 1;
   if (stack->count > stack->alloced)
     {
       stack->alloced = stack->count*2;
       stack->iters = g_realloc (stack->iters,
-                                stack->alloced*sizeof (GtkTextIter));
+                                stack->alloced * sizeof (GtkTextIter));
     }
   stack->iters[stack->count-1] = *iter;
 }
 
 static gboolean
-iter_stack_pop (IterStack *stack, GtkTextIter *iter)
+iter_stack_pop (IterStack   *stack, 
+               GtkTextIter *iter)
 {
   if (stack->count == 0)
     return FALSE;
@@ -1493,7 +1713,7 @@ static void
 iter_stack_free (IterStack *stack)
 {
   g_free (stack->iters);
-  g_free (stack);
+  g_slice_free (IterStack, stack);
 }
 
 static void
@@ -1523,25 +1743,25 @@ queue_tag_redisplay (GtkTextBTree      *tree,
                      const GtkTextIter *start,
                      const GtkTextIter *end)
 {
-  if (gtk_text_tag_affects_size (tag))
+  if (_gtk_text_tag_affects_size (tag))
     {
-      gtk_text_btree_invalidate_region (tree, start, end);
-
+      DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
+      _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
     }
-  else if (gtk_text_tag_affects_nonsize_appearance (tag))
+  else if (_gtk_text_tag_affects_nonsize_appearance (tag))
     {
       /* We only need to queue a redraw, not a relayout */
-      redisplay_region (tree, start, end);
+      redisplay_region (tree, start, end, FALSE);
     }
 
   /* We don't need to do anything if the tag doesn't affect display */
 }
 
 void
-gtk_text_btree_tag (const GtkTextIter *start_orig,
-                    const GtkTextIter *end_orig,
-                    GtkTextTag *tag,
-                    gboolean add)
+_gtk_text_btree_tag (const GtkTextIter *start_orig,
+                     const GtkTextIter *end_orig,
+                     GtkTextTag        *tag,
+                     gboolean           add)
 {
   GtkTextLineSegment *seg, *prev;
   GtkTextLine *cleanupline;
@@ -1557,9 +1777,10 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
   g_return_if_fail (start_orig != NULL);
   g_return_if_fail (end_orig != NULL);
   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
-  g_return_if_fail (gtk_text_iter_get_btree (start_orig) ==
-                    gtk_text_iter_get_btree (end_orig));
-
+  g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
+                    _gtk_text_iter_get_btree (end_orig));
+  g_return_if_fail (tag->priv->table == _gtk_text_iter_get_btree (start_orig)->table);
+  
 #if 0
   printf ("%s tag %s from %d to %d\n",
           add ? "Adding" : "Removing",
@@ -1574,16 +1795,16 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
   start = *start_orig;
   end = *end_orig;
 
-  gtk_text_iter_reorder (&start, &end);
+  gtk_text_iter_order (&start, &end);
 
-  tree = gtk_text_iter_get_btree (&start);
+  tree = _gtk_text_iter_get_btree (&start);
 
   queue_tag_redisplay (tree, tag, &start, &end);
 
   info = gtk_text_btree_get_tag_info (tree, tag);
 
-  start_line = gtk_text_iter_get_text_line (&start);
-  end_line = gtk_text_iter_get_text_line (&end);
+  start_line = _gtk_text_iter_get_text_line (&start);
+  end_line = _gtk_text_iter_get_text_line (&end);
 
   /* Find all tag toggles in the region; we are going to delete them.
      We need to find them in advance, because
@@ -1591,8 +1812,11 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
      with the tree. */
   stack = iter_stack_new ();
   iter = start;
-  /* We don't want to delete a toggle that's at the start iterator. */
-  gtk_text_iter_next_char (&iter);
+
+  /* forward_to_tag_toggle() skips a toggle at the start iterator,
+   * which is deliberate - we don't want to delete a toggle at the
+   * start.
+   */
   while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
     {
       if (gtk_text_iter_compare (&iter, &end) >= 0)
@@ -1654,9 +1878,9 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
       GtkTextLineSegment *indexable_seg;
       GtkTextLine *line;
 
-      line = gtk_text_iter_get_text_line (&iter);
-      seg = gtk_text_iter_get_any_segment (&iter);
-      indexable_seg = gtk_text_iter_get_indexable_segment (&iter);
+      line = _gtk_text_iter_get_text_line (&iter);
+      seg = _gtk_text_iter_get_any_segment (&iter);
+      indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
 
       g_assert (seg != NULL);
       g_assert (indexable_seg != NULL);
@@ -1670,7 +1894,7 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
           g_assert (seg != NULL);
           g_assert (indexable_seg != NULL);
           g_assert (seg != indexable_seg);
-
+          
           if ( (seg->type == &gtk_text_toggle_on_type ||
                 seg->type == &gtk_text_toggle_off_type) &&
                (seg->body.toggle.info == info) )
@@ -1784,8 +2008,10 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
 
   segments_changed (tree);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  queue_tag_redisplay (tree, tag, &start, &end);
+
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 }
 
 
@@ -1793,18 +2019,21 @@ gtk_text_btree_tag (const GtkTextIter *start_orig,
  * "Getters"
  */
 
-GtkTextLine*
-gtk_text_btree_get_line (GtkTextBTree *tree,
-                         gint  line_number,
-                         gint *real_line_number)
+static GtkTextLine*
+get_line_internal (GtkTextBTree *tree,
+                   gint          line_number,
+                   gint         *real_line_number,
+                   gboolean      include_last)
 {
   GtkTextBTreeNode *node;
   GtkTextLine *line;
   int lines_left;
   int line_count;
 
-  line_count = gtk_text_btree_line_count (tree);
-
+  line_count = _gtk_text_btree_line_count (tree);
+  if (!include_last)
+    line_count -= 1;
+  
   if (line_number < 0)
     {
       line_number = line_count;
@@ -1860,24 +2089,51 @@ gtk_text_btree_get_line (GtkTextBTree *tree,
 }
 
 GtkTextLine*
-gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
-                                 gint                char_index,
-                                 gint               *line_start_index,
-                                 gint               *real_char_index)
+_gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
+{
+  return
+    _gtk_text_btree_get_line (tree,
+                              _gtk_text_btree_line_count (tree) - 1,
+                              NULL);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line (GtkTextBTree *tree,
+                          gint          line_number,
+                          gint         *real_line_number)
+{
+  return get_line_internal (tree, line_number, real_line_number, TRUE);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line_no_last (GtkTextBTree      *tree,
+                                  gint               line_number,
+                                  gint              *real_line_number)
+{
+  return get_line_internal (tree, line_number, real_line_number, FALSE);
+}
+
+GtkTextLine*
+_gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
+                                  gint               char_index,
+                                  gint              *line_start_index,
+                                  gint              *real_char_index)
 {
   GtkTextBTreeNode *node;
   GtkTextLine *line;
   GtkTextLineSegment *seg;
   int chars_left;
   int chars_in_line;
-  int bytes_in_line;
 
   node = tree->root_node;
 
-  /* Clamp to valid indexes (-1 is magic for "highest index") */
-  if (char_index < 0 || char_index >= node->num_chars)
+  /* Clamp to valid indexes (-1 is magic for "highest index"),
+   * node->num_chars includes the two newlines that aren't really
+   * in the buffer.
+   */
+  if (char_index < 0 || char_index >= (node->num_chars - 1))
     {
-      char_index = node->num_chars - 1;
+      char_index = node->num_chars - 2;
     }
 
   *real_char_index = char_index;
@@ -1913,7 +2169,6 @@ gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
    */
 
   chars_in_line = 0;
-  bytes_in_line = 0;
   seg = NULL;
   for (line = node->children.line; line != NULL; line = line->next)
     {
@@ -1943,8 +2198,10 @@ gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
   return line;
 }
 
+/* It returns an array sorted by tags priority, ready to pass to
+ * _gtk_text_attributes_fill_from_tags() */
 GtkTextTag**
-gtk_text_btree_get_tags (const GtkTextIter *iter,
+_gtk_text_btree_get_tags (const GtkTextIter *iter,
                          gint *num_tags)
 {
   GtkTextBTreeNode *node;
@@ -1953,13 +2210,11 @@ gtk_text_btree_get_tags (const GtkTextIter *iter,
   int src, dst, index;
   TagInfo tagInfo;
   GtkTextLine *line;
-  GtkTextBTree *tree;
   gint byte_index;
 
 #define NUM_TAG_INFOS 10
 
-  line = gtk_text_iter_get_text_line (iter);
-  tree = gtk_text_iter_get_btree (iter);
+  line = _gtk_text_iter_get_text_line (iter);
   byte_index = gtk_text_iter_get_line_index (iter);
 
   tagInfo.numTags = 0;
@@ -2053,6 +2308,10 @@ gtk_text_btree_get_tags (const GtkTextIter *iter,
       g_free (tagInfo.tags);
       return NULL;
     }
+
+  /* Sort tags in ascending order of priority */
+  _gtk_text_tag_array_sort (tagInfo.tags, dst);
+
   return tagInfo.tags;
 }
 
@@ -2069,8 +2328,8 @@ copy_segment (GString *string,
   if (gtk_text_iter_equal (start, end))
     return;
 
-  seg = gtk_text_iter_get_indexable_segment (start);
-  end_seg = gtk_text_iter_get_indexable_segment (end);
+  seg = _gtk_text_iter_get_indexable_segment (start);
+  end_seg = _gtk_text_iter_get_indexable_segment (end);
 
   if (seg->type == &gtk_text_char_type)
     {
@@ -2081,18 +2340,18 @@ copy_segment (GString *string,
       /* Don't copy if we're invisible; segments are invisible/not
          as a whole, no need to check each char */
       if (!include_hidden &&
-          gtk_text_btree_char_is_invisible (start))
+          _gtk_text_btree_char_is_invisible (start))
         {
           copy = FALSE;
           /* printf (" <invisible>\n"); */
         }
 
-      copy_start = gtk_text_iter_get_segment_byte (start);
+      copy_start = _gtk_text_iter_get_segment_byte (start);
 
       if (seg == end_seg)
         {
           /* End is in the same segment; need to copy fewer bytes. */
-          gint end_byte = gtk_text_iter_get_segment_byte (end);
+          gint end_byte = _gtk_text_iter_get_segment_byte (end);
 
           copy_bytes = end_byte - copy_start;
         }
@@ -2123,7 +2382,7 @@ copy_segment (GString *string,
           copy = FALSE;
         }
       else if (!include_hidden &&
-               gtk_text_btree_char_is_invisible (start))
+               _gtk_text_btree_char_is_invisible (start))
         {
           copy = FALSE;
         }
@@ -2131,15 +2390,15 @@ copy_segment (GString *string,
       if (copy)
         {
           g_string_append_len (string,
-                               gtk_text_unknown_char_utf8,
-                               3);
+                               _gtk_text_unknown_char_utf8,
+                               GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN);
 
         }
     }
 }
 
 gchar*
-gtk_text_btree_get_text (const GtkTextIter *start_orig,
+_gtk_text_btree_get_text (const GtkTextIter *start_orig,
                          const GtkTextIter *end_orig,
                          gboolean include_hidden,
                          gboolean include_nonchars)
@@ -2147,7 +2406,6 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig,
   GtkTextLineSegment *seg;
   GtkTextLineSegment *end_seg;
   GString *retval;
-  GtkTextBTree *tree;
   gchar *str;
   GtkTextIter iter;
   GtkTextIter start;
@@ -2155,29 +2413,27 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig,
 
   g_return_val_if_fail (start_orig != NULL, NULL);
   g_return_val_if_fail (end_orig != NULL, NULL);
-  g_return_val_if_fail (gtk_text_iter_get_btree (start_orig) ==
-                        gtk_text_iter_get_btree (end_orig), NULL);
+  g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
+                        _gtk_text_iter_get_btree (end_orig), NULL);
 
   start = *start_orig;
   end = *end_orig;
 
-  gtk_text_iter_reorder (&start, &end);
-
-  retval = g_string_new ("");
+  gtk_text_iter_order (&start, &end);
 
-  tree = gtk_text_iter_get_btree (&start);
+  retval = g_string_new (NULL);
 
-  end_seg = gtk_text_iter_get_indexable_segment (&end);
+  end_seg = _gtk_text_iter_get_indexable_segment (&end);
   iter = start;
-  seg = gtk_text_iter_get_indexable_segment (&iter);
+  seg = _gtk_text_iter_get_indexable_segment (&iter);
   while (seg != end_seg)
     {
       copy_segment (retval, include_hidden, include_nonchars,
                     &iter, &end);
 
-      gtk_text_iter_forward_indexable_segment (&iter);
+      _gtk_text_iter_forward_indexable_segment (&iter);
 
-      seg = gtk_text_iter_get_indexable_segment (&iter);
+      seg = _gtk_text_iter_get_indexable_segment (&iter);
     }
 
   copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
@@ -2188,7 +2444,7 @@ gtk_text_btree_get_text (const GtkTextIter *start_orig,
 }
 
 gint
-gtk_text_btree_line_count (GtkTextBTree *tree)
+_gtk_text_btree_line_count (GtkTextBTree *tree)
 {
   /* Subtract bogus line at the end; we return a count
      of usable lines. */
@@ -2196,19 +2452,21 @@ gtk_text_btree_line_count (GtkTextBTree *tree)
 }
 
 gint
-gtk_text_btree_char_count (GtkTextBTree *tree)
+_gtk_text_btree_char_count (GtkTextBTree *tree)
 {
-  /* Exclude newline in bogus last line */
-  return tree->root_node->num_chars - 1;
+  /* Exclude newline in bogus last line and the
+   * one in the last line that is after the end iterator
+   */
+  return tree->root_node->num_chars - 2;
 }
 
 #define LOTSA_TAGS 1000
 gboolean
-gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
+_gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
 {
   gboolean invisible = FALSE;  /* if nobody says otherwise, it's visible */
 
-  int deftagCnts[LOTSA_TAGS];
+  int deftagCnts[LOTSA_TAGS] = { 0, };
   int *tagCnts = deftagCnts;
   GtkTextTag *deftags[LOTSA_TAGS];
   GtkTextTag **tags = deftags;
@@ -2222,24 +2480,19 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
   GtkTextBTree *tree;
   gint byte_index;
 
-  line = gtk_text_iter_get_text_line (iter);
-  tree = gtk_text_iter_get_btree (iter);
+  line = _gtk_text_iter_get_text_line (iter);
+  tree = _gtk_text_iter_get_btree (iter);
   byte_index = gtk_text_iter_get_line_index (iter);
 
-  numTags = gtk_text_tag_table_size (tree->table);
+  numTags = gtk_text_tag_table_get_size (tree->table);
 
   /* almost always avoid malloc, so stay out of system calls */
   if (LOTSA_TAGS < numTags)
     {
-      tagCnts = g_new (int, numTags);
+      tagCnts = g_new0 (int, numTags);
       tags = g_new (GtkTextTag*, numTags);
     }
 
-  for (i=0; i<numTags; i++)
-    {
-      tagCnts[i] = 0;
-    }
-
   /*
    * Record tag toggles within the line of indexPtr but preceding
    * indexPtr.
@@ -2253,10 +2506,10 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
           || (seg->type == &gtk_text_toggle_off_type))
         {
           tag = seg->body.toggle.info->tag;
-          if (tag->invisible_set && tag->values->invisible)
+          if (tag->priv->invisible_set)
             {
-              tags[tag->priority] = tag;
-              tagCnts[tag->priority]++;
+              tags[tag->priv->priority] = tag;
+              tagCnts[tag->priv->priority]++;
             }
         }
     }
@@ -2277,10 +2530,10 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
               || (seg->type == &gtk_text_toggle_off_type))
             {
               tag = seg->body.toggle.info->tag;
-              if (tag->invisible_set && tag->values->invisible)
+              if (tag->priv->invisible_set)
                 {
-                  tags[tag->priority] = tag;
-                  tagCnts[tag->priority]++;
+                  tags[tag->priv->priority] = tag;
+                  tagCnts[tag->priv->priority]++;
                 }
             }
         }
@@ -2306,10 +2559,10 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
               if (summary->toggle_count & 1)
                 {
                   tag = summary->info->tag;
-                  if (tag->invisible_set && tag->values->invisible)
+                  if (tag->priv->invisible_set)
                     {
-                      tags[tag->priority] = tag;
-                      tagCnts[tag->priority] += summary->toggle_count;
+                      tags[tag->priv->priority] = tag;
+                      tagCnts[tag->priv->priority] += summary->toggle_count;
                     }
                 }
             }
@@ -2336,7 +2589,7 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
             }
 #endif
 #endif
-          invisible = tags[i]->values->invisible;
+          invisible = tags[i]->priv->values->invisible;
           break;
         }
     }
@@ -2358,7 +2611,8 @@ gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
 static void
 redisplay_region (GtkTextBTree      *tree,
                   const GtkTextIter *start,
-                  const GtkTextIter *end)
+                  const GtkTextIter *end,
+                  gboolean           cursors_only)
 {
   BTreeView *view;
   GtkTextLine *start_line, *end_line;
@@ -2370,8 +2624,8 @@ redisplay_region (GtkTextBTree      *tree,
       end = tmp;
     }
 
-  start_line = gtk_text_iter_get_text_line (start);
-  end_line = gtk_text_iter_get_text_line (end);
+  start_line = _gtk_text_iter_get_text_line (start);
+  end_line = _gtk_text_iter_get_text_line (end);
 
   view = tree->views;
   while (view != NULL)
@@ -2379,20 +2633,25 @@ redisplay_region (GtkTextBTree      *tree,
       gint start_y, end_y;
       GtkTextLineData *ld;
 
-      start_y = gtk_text_btree_find_line_top (tree, start_line, view->view_id);
+      start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
 
       if (end_line == start_line)
         end_y = start_y;
       else
-        end_y = gtk_text_btree_find_line_top (tree, end_line, view->view_id);
+        end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
 
-      ld = gtk_text_line_get_data (end_line, view->view_id);
+      ld = _gtk_text_line_get_data (end_line, view->view_id);
       if (ld)
         end_y += ld->height;
 
-      gtk_text_layout_changed (view->layout, start_y,
-                               end_y - start_y,
-                               end_y - start_y);
+      if (cursors_only)
+       gtk_text_layout_cursors_changed (view->layout, start_y,
+                                        end_y - start_y,
+                                         end_y - start_y);
+      else
+       gtk_text_layout_changed (view->layout, start_y,
+                                end_y - start_y,
+                                end_y - start_y);
 
       view = view->next;
     }
@@ -2403,16 +2662,18 @@ redisplay_mark (GtkTextLineSegment *mark)
 {
   GtkTextIter iter;
   GtkTextIter end;
+  gboolean cursor_only;
 
-  gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
+  _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
                                    &iter,
                                    mark->body.mark.obj);
 
   end = iter;
-  gtk_text_iter_next_char (&end);
+  gtk_text_iter_forward_char (&end);
 
-  gtk_text_btree_invalidate_region (mark->body.mark.tree,
-                                    &iter, &end);
+  DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
+  cursor_only = mark == mark->body.mark.tree->insert_mark->segment;
+  _gtk_text_btree_invalidate_region (mark->body.mark.tree, &iter, &end, cursor_only);
 }
 
 static void
@@ -2429,9 +2690,8 @@ ensure_not_off_end (GtkTextBTree *tree,
                     GtkTextLineSegment *mark,
                     GtkTextIter *iter)
 {
-  if (gtk_text_iter_get_line (iter) ==
-      gtk_text_btree_line_count (tree))
-    gtk_text_iter_prev_char (iter);
+  if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
+    gtk_text_iter_backward_char (iter);
 }
 
 static GtkTextLineSegment*
@@ -2448,10 +2708,15 @@ real_set_mark (GtkTextBTree      *tree,
 
   g_return_val_if_fail (tree != NULL, NULL);
   g_return_val_if_fail (where != NULL, NULL);
-  g_return_val_if_fail (gtk_text_iter_get_btree (where) == tree, NULL);
+  g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
 
   if (existing_mark)
-    mark = existing_mark->segment;
+    {
+      if (gtk_text_mark_get_buffer (existing_mark) != NULL)
+       mark = existing_mark->segment;
+      else
+       mark = NULL;
+    }
   else if (name != NULL)
     mark = g_hash_table_lookup (tree->mark_table,
                                 name);
@@ -2470,8 +2735,8 @@ real_set_mark (GtkTextBTree      *tree,
   
   iter = *where;
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_iter_check (&iter);
   
   if (mark != NULL)
     {
@@ -2481,9 +2746,9 @@ real_set_mark (GtkTextBTree      *tree,
         {
           GtkTextIter old_pos;
 
-          gtk_text_btree_get_iter_at_mark (tree, &old_pos,
+          _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
                                            mark->body.mark.obj);
-          redisplay_region (tree, &old_pos, where);
+          redisplay_region (tree, &old_pos, where, TRUE);
         }
 
       /*
@@ -2503,19 +2768,23 @@ real_set_mark (GtkTextBTree      *tree,
          This could hose our iterator... */
       gtk_text_btree_unlink_segment (tree, mark,
                                      mark->body.mark.line);
-      mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
-      g_assert (mark->body.mark.line == gtk_text_iter_get_text_line (&iter));
+      mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
+      g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
 
       segments_changed (tree); /* make sure the iterator recomputes its
                                   segment */
     }
   else
     {
-      mark = _gtk_mark_segment_new (tree,
-                                    left_gravity,
-                                    name);
+      if (existing_mark)
+       g_object_ref (existing_mark);
+      else
+       existing_mark = gtk_text_mark_new (name, left_gravity);
+
+      mark = existing_mark->segment;
+      _gtk_mark_segment_set_tree (mark, tree);
 
-      mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
+      mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
 
       if (mark->body.mark.name)
         g_hash_table_insert (tree->mark_table,
@@ -2523,8 +2792,8 @@ real_set_mark (GtkTextBTree      *tree,
                              mark);
     }
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_iter_check (&iter);
   
   /* Link mark into new location */
   gtk_text_btree_link_segment (mark, &iter);
@@ -2538,18 +2807,18 @@ real_set_mark (GtkTextBTree      *tree,
 
   redisplay_mark_if_visible (mark);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_iter_check (&iter);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
   
   return mark;
 }
 
 
 GtkTextMark*
-gtk_text_btree_set_mark (GtkTextBTree *tree,
+_gtk_text_btree_set_mark (GtkTextBTree *tree,
                          GtkTextMark  *existing_mark,
                          const gchar *name,
                          gboolean left_gravity,
@@ -2566,15 +2835,15 @@ gtk_text_btree_set_mark (GtkTextBTree *tree,
 }
 
 gboolean
-gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
+_gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
                                      GtkTextIter  *start,
                                      GtkTextIter  *end)
 {
   GtkTextIter tmp_start, tmp_end;
 
-  gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
+  _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
                                    tree->insert_mark);
-  gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
+  _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
                                    tree->selection_bound_mark);
 
   if (gtk_text_iter_equal (&tmp_start, &tmp_end))
@@ -2589,7 +2858,7 @@ gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
     }
   else
     {
-      gtk_text_iter_reorder (&tmp_start, &tmp_end);
+      gtk_text_iter_order (&tmp_start, &tmp_end);
 
       if (start)
         *start = tmp_start;
@@ -2602,23 +2871,44 @@ gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
 }
 
 void
-gtk_text_btree_place_cursor (GtkTextBTree      *tree,
+_gtk_text_btree_place_cursor (GtkTextBTree      *tree,
                              const GtkTextIter *iter)
 {
-  GtkTextIter start, end;
+  _gtk_text_btree_select_range (tree, iter, iter);
+}
+
+void
+_gtk_text_btree_select_range (GtkTextBTree      *tree,
+                             const GtkTextIter *ins,
+                              const GtkTextIter *bound)
+{
+  GtkTextIter old_ins, old_bound;
+
+  _gtk_text_btree_get_iter_at_mark (tree, &old_ins,
+                                    tree->insert_mark);
+  _gtk_text_btree_get_iter_at_mark (tree, &old_bound,
+                                    tree->selection_bound_mark);
+
+  /* Check if it's no-op since gtk_text_buffer_place_cursor()
+   * also calls this, and this will redraw the cursor line. */
+  if (!gtk_text_iter_equal (&old_ins, ins) ||
+      !gtk_text_iter_equal (&old_bound, bound))
+    {
+      redisplay_region (tree, &old_ins, &old_bound, TRUE);
 
-  if (gtk_text_btree_get_selection_bounds (tree, &start, &end))
-    redisplay_region (tree, &start, &end);
+      /* Move insert AND selection_bound before we redisplay */
+      real_set_mark (tree, tree->insert_mark,
+                    "insert", FALSE, ins, TRUE, FALSE);
+      real_set_mark (tree, tree->selection_bound_mark,
+                    "selection_bound", FALSE, bound, TRUE, FALSE);
 
-  /* Move insert AND selection_bound before we redisplay */
-  real_set_mark (tree, tree->insert_mark,
-                 "insert", FALSE, iter, TRUE, FALSE);
-  real_set_mark (tree, tree->selection_bound_mark,
-                 "selection_bound", FALSE, iter, TRUE, FALSE);
+      redisplay_region (tree, ins, bound, TRUE);
+    }
 }
 
+
 void
-gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
+_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
                                     const gchar *name)
 {
   GtkTextMark *mark;
@@ -2629,12 +2919,29 @@ gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
   mark = g_hash_table_lookup (tree->mark_table,
                               name);
 
-  gtk_text_btree_remove_mark (tree, mark);
+  _gtk_text_btree_remove_mark (tree, mark);
+}
+
+void
+_gtk_text_btree_release_mark_segment (GtkTextBTree       *tree,
+                                      GtkTextLineSegment *segment)
+{
+
+  if (segment->body.mark.name)
+    g_hash_table_remove (tree->mark_table, segment->body.mark.name);
+
+  segment->body.mark.tree = NULL;
+  segment->body.mark.line = NULL;
+  
+  /* Remove the ref on the mark, which frees segment as a side effect
+   * if this is the last reference.
+   */
+  g_object_unref (segment->body.mark.obj);
 }
 
 void
-gtk_text_btree_remove_mark (GtkTextBTree *tree,
-                            GtkTextMark *mark)
+_gtk_text_btree_remove_mark (GtkTextBTree *tree,
+                             GtkTextMark *mark)
 {
   GtkTextLineSegment *segment;
 
@@ -2651,34 +2958,39 @@ gtk_text_btree_remove_mark (GtkTextBTree *tree,
 
   /* This calls cleanup_line and segments_changed */
   gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
-
-  if (segment->body.mark.name)
-    g_hash_table_remove (tree->mark_table, segment->body.mark.name);
-
-  /* Remove the ref on the mark that belonged to the segment. */
-  g_object_unref (G_OBJECT (mark));
-
-  segment->body.mark.tree = NULL;
-  segment->body.mark.line = NULL;
+  
+  _gtk_text_btree_release_mark_segment (tree, segment);
 }
 
 gboolean
-gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
-                               GtkTextMark *segment)
+_gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
+                                GtkTextMark *segment)
 {
   return segment == tree->insert_mark;
 }
 
 gboolean
-gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
-                                        GtkTextMark *segment)
+_gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
+                                         GtkTextMark *segment)
 {
   return segment == tree->selection_bound_mark;
 }
 
+GtkTextMark *
+_gtk_text_btree_get_insert (GtkTextBTree *tree)
+{
+  return tree->insert_mark;
+}
+
+GtkTextMark *
+_gtk_text_btree_get_selection_bound (GtkTextBTree *tree)
+{
+  return tree->selection_bound_mark;
+}
+
 GtkTextMark*
-gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
-                                 const gchar *name)
+_gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
+                                  const gchar *name)
 {
   GtkTextLineSegment *seg;
 
@@ -2690,6 +3002,18 @@ gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
   return seg ? seg->body.mark.obj : NULL;
 }
 
+/**
+ * gtk_text_mark_set_visible:
+ * @mark: a #GtkTextMark
+ * @setting: visibility of mark
+ * 
+ * Sets the visibility of @mark; the insertion point is normally
+ * visible, i.e. you can see it as a vertical bar. Also, the text
+ * widget uses a visible mark to indicate where a drop will occur when
+ * dragging-and-dropping text. Most other marks are not visible.
+ * Marks are not visible by default.
+ * 
+ **/
 void
 gtk_text_mark_set_visible (GtkTextMark       *mark,
                            gboolean           setting)
@@ -2706,12 +3030,13 @@ gtk_text_mark_set_visible (GtkTextMark       *mark,
     {
       seg->body.mark.visible = setting;
 
-      redisplay_mark (seg);
+      if (seg->body.mark.tree)
+       redisplay_mark (seg);
     }
 }
 
 GtkTextLine*
-gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
+_gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
                                         GtkTextTag *tag)
 {
   GtkTextBTreeNode *node;
@@ -2763,12 +3088,12 @@ gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
          Unfortunately this can't be done in a simple and efficient way
          right now; so I'm just going to return the
          first line in the btree. FIXME */
-      return gtk_text_btree_get_line (tree, 0, NULL);
+      return _gtk_text_btree_get_line (tree, 0, NULL);
     }
 }
 
 GtkTextLine*
-gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
+_gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
                                        GtkTextTag *tag)
 {
   GtkTextBTreeNode *node;
@@ -2822,7 +3147,7 @@ gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
          at least not without complexity.
          So, we just return the last line.
       */
-      return gtk_text_btree_get_line (tree, -1, NULL);
+      return _gtk_text_btree_get_end_iter_line (tree);
     }
 }
 
@@ -2832,7 +3157,7 @@ gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
  */
 
 gint
-gtk_text_line_get_number (GtkTextLine *line)
+_gtk_text_line_get_number (GtkTextLine *line)
 {
   GtkTextLine *line2;
   GtkTextBTreeNode *node, *parent, *node2;
@@ -3017,10 +3342,10 @@ find_toggle_outside_current_line (GtkTextLine *line,
 
 /* FIXME this function is far too slow, for no good reason. */
 gboolean
-gtk_text_line_char_has_tag (GtkTextLine *line,
-                            GtkTextBTree *tree,
-                            gint char_in_line,
-                            GtkTextTag *tag)
+_gtk_text_line_char_has_tag (GtkTextLine *line,
+                             GtkTextBTree *tree,
+                             gint char_in_line,
+                             GtkTextTag *tag)
 {
   GtkTextLineSegment *toggle_seg;
 
@@ -3041,10 +3366,10 @@ gtk_text_line_char_has_tag (GtkTextLine *line,
 }
 
 gboolean
-gtk_text_line_byte_has_tag (GtkTextLine *line,
-                            GtkTextBTree *tree,
-                            gint byte_in_line,
-                            GtkTextTag *tag)
+_gtk_text_line_byte_has_tag (GtkTextLine *line,
+                             GtkTextBTree *tree,
+                             gint byte_in_line,
+                             GtkTextTag *tag)
 {
   GtkTextLineSegment *toggle_seg;
 
@@ -3065,14 +3390,96 @@ gtk_text_line_byte_has_tag (GtkTextLine *line,
 }
 
 gboolean
-gtk_text_line_is_last (GtkTextLine *line,
-                       GtkTextBTree *tree)
+_gtk_text_line_is_last (GtkTextLine *line,
+                        GtkTextBTree *tree)
 {
   return line == get_last_line (tree);
 }
 
+static void
+ensure_end_iter_line (GtkTextBTree *tree)
+{
+  if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+    {
+      gint real_line;
+       
+       /* n_lines is without the magic line at the end */
+      g_assert (_gtk_text_btree_line_count (tree) >= 1);
+
+      tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
+      
+      tree->end_iter_line_stamp = tree->chars_changed_stamp;
+    }
+}
+
+static void
+ensure_end_iter_segment (GtkTextBTree *tree)
+{
+  if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
+    {
+      GtkTextLineSegment *seg;
+      GtkTextLineSegment *last_with_chars;
+
+      ensure_end_iter_line (tree);
+
+      last_with_chars = NULL;
+      
+      seg = tree->end_iter_line->segments;
+      while (seg != NULL)
+        {
+          if (seg->char_count > 0)
+            last_with_chars = seg;
+          seg = seg->next;
+        }
+
+      tree->end_iter_segment = last_with_chars;
+
+      /* We know the last char in the last line is '\n' */
+      tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
+      tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
+      
+      tree->end_iter_segment_stamp = tree->segments_changed_stamp;
+
+      g_assert (tree->end_iter_segment->type == &gtk_text_char_type);
+      g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
+    }
+}
+
+gboolean
+_gtk_text_line_contains_end_iter (GtkTextLine  *line,
+                                  GtkTextBTree *tree)
+{
+  ensure_end_iter_line (tree);
+
+  return line == tree->end_iter_line;
+}
+
+gboolean
+_gtk_text_btree_is_end (GtkTextBTree       *tree,
+                        GtkTextLine        *line,
+                        GtkTextLineSegment *seg,
+                        int                 byte_index,
+                        int                 char_offset)
+{
+  g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
+  
+  /* Do this first to avoid walking segments in most cases */
+  if (!_gtk_text_line_contains_end_iter (line, tree))
+    return FALSE;
+
+  ensure_end_iter_segment (tree);
+
+  if (seg != tree->end_iter_segment)
+    return FALSE;
+
+  if (byte_index >= 0)
+    return byte_index == tree->end_iter_segment_byte_index;
+  else
+    return char_offset == tree->end_iter_segment_char_offset;
+}
+
 GtkTextLine*
-gtk_text_line_next (GtkTextLine *line)
+_gtk_text_line_next (GtkTextLine *line)
 {
   GtkTextBTreeNode *node;
 
@@ -3107,7 +3514,24 @@ gtk_text_line_next (GtkTextLine *line)
 }
 
 GtkTextLine*
-gtk_text_line_previous (GtkTextLine *line)
+_gtk_text_line_next_excluding_last (GtkTextLine *line)
+{
+  GtkTextLine *next;
+  
+  next = _gtk_text_line_next (line);
+
+  /* If we were on the end iter line, we can't go to
+   * the last line
+   */
+  if (next && next->next == NULL && /* these checks are optimization only */
+      _gtk_text_line_next (next) == NULL)
+    return NULL;
+
+  return next;
+}
+
+GtkTextLine*
+_gtk_text_line_previous (GtkTextLine *line)
 {
   GtkTextBTreeNode *node;
   GtkTextBTreeNode *node2;
@@ -3163,9 +3587,27 @@ gtk_text_line_previous (GtkTextLine *line)
   return NULL;
 }
 
+
+GtkTextLineData*
+_gtk_text_line_data_new (GtkTextLayout *layout,
+                         GtkTextLine   *line)
+{
+  GtkTextLineData *line_data;
+
+  line_data = g_new (GtkTextLineData, 1);
+
+  line_data->view_id = layout;
+  line_data->next = NULL;
+  line_data->width = 0;
+  line_data->height = 0;
+  line_data->valid = FALSE;
+
+  return line_data;
+}
+
 void
-gtk_text_line_add_data (GtkTextLine     *line,
-                        GtkTextLineData *data)
+_gtk_text_line_add_data (GtkTextLine     *line,
+                         GtkTextLineData *data)
 {
   g_return_if_fail (line != NULL);
   g_return_if_fail (data != NULL);
@@ -3183,7 +3625,7 @@ gtk_text_line_add_data (GtkTextLine     *line,
 }
 
 gpointer
-gtk_text_line_remove_data (GtkTextLine *line,
+_gtk_text_line_remove_data (GtkTextLine *line,
                            gpointer view_id)
 {
   GtkTextLineData *prev;
@@ -3216,8 +3658,8 @@ gtk_text_line_remove_data (GtkTextLine *line,
 }
 
 gpointer
-gtk_text_line_get_data (GtkTextLine *line,
-                        gpointer view_id)
+_gtk_text_line_get_data (GtkTextLine *line,
+                         gpointer view_id)
 {
   GtkTextLineData *iter;
 
@@ -3236,8 +3678,8 @@ gtk_text_line_get_data (GtkTextLine *line,
 }
 
 void
-gtk_text_line_invalidate_wrap (GtkTextLine *line,
-                               GtkTextLineData *ld)
+_gtk_text_line_invalidate_wrap (GtkTextLine *line,
+                                GtkTextLineData *ld)
 {
   /* For now this is totally unoptimized. FIXME?
 
@@ -3245,15 +3687,15 @@ gtk_text_line_invalidate_wrap (GtkTextLine *line,
      is less than the max width for the parent node,
      and the case where the height is unchanged when we re-wrap.
   */
-
+  
   g_return_if_fail (ld != NULL);
-
+  
   ld->valid = FALSE;
   gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
 }
 
 gint
-gtk_text_line_char_count (GtkTextLine *line)
+_gtk_text_line_char_count (GtkTextLine *line)
 {
   GtkTextLineSegment *seg;
   gint size;
@@ -3269,7 +3711,7 @@ gtk_text_line_char_count (GtkTextLine *line)
 }
 
 gint
-gtk_text_line_byte_count (GtkTextLine *line)
+_gtk_text_line_byte_count (GtkTextLine *line)
 {
   GtkTextLineSegment *seg;
   gint size;
@@ -3286,7 +3728,7 @@ gtk_text_line_byte_count (GtkTextLine *line)
 }
 
 gint
-gtk_text_line_char_index (GtkTextLine *target_line)
+_gtk_text_line_char_index (GtkTextLine *target_line)
 {
   GSList *node_stack = NULL;
   GtkTextBTreeNode *iter;
@@ -3360,7 +3802,7 @@ gtk_text_line_char_index (GtkTextLine *target_line)
     {
       g_assert (line != NULL);
 
-      num_chars += gtk_text_line_char_count (line);
+      num_chars += _gtk_text_line_char_count (line);
 
       line = line->next;
     }
@@ -3371,7 +3813,7 @@ gtk_text_line_char_index (GtkTextLine *target_line)
 }
 
 GtkTextLineSegment*
-gtk_text_line_byte_to_segment (GtkTextLine *line,
+_gtk_text_line_byte_to_segment (GtkTextLine *line,
                                gint byte_offset,
                                gint *seg_offset)
 {
@@ -3385,9 +3827,9 @@ gtk_text_line_byte_to_segment (GtkTextLine *line,
 
   while (offset >= seg->byte_count)
     {
-      g_assert (seg != NULL); /* means an invalid byte index */
       offset -= seg->byte_count;
       seg = seg->next;
+      g_assert (seg != NULL); /* means an invalid byte index */
     }
 
   if (seg_offset)
@@ -3397,7 +3839,7 @@ gtk_text_line_byte_to_segment (GtkTextLine *line,
 }
 
 GtkTextLineSegment*
-gtk_text_line_char_to_segment (GtkTextLine *line,
+_gtk_text_line_char_to_segment (GtkTextLine *line,
                                gint char_offset,
                                gint *seg_offset)
 {
@@ -3411,9 +3853,9 @@ gtk_text_line_char_to_segment (GtkTextLine *line,
 
   while (offset >= seg->char_count)
     {
-      g_assert (seg != NULL); /* means an invalid char index */
       offset -= seg->char_count;
       seg = seg->next;
+      g_assert (seg != NULL); /* means an invalid char index */
     }
 
   if (seg_offset)
@@ -3423,7 +3865,7 @@ gtk_text_line_char_to_segment (GtkTextLine *line,
 }
 
 GtkTextLineSegment*
-gtk_text_line_byte_to_any_segment (GtkTextLine *line,
+_gtk_text_line_byte_to_any_segment (GtkTextLine *line,
                                    gint byte_offset,
                                    gint *seg_offset)
 {
@@ -3437,9 +3879,9 @@ gtk_text_line_byte_to_any_segment (GtkTextLine *line,
 
   while (offset > 0 && offset >= seg->byte_count)
     {
-      g_assert (seg != NULL); /* means an invalid byte index */
       offset -= seg->byte_count;
       seg = seg->next;
+      g_assert (seg != NULL); /* means an invalid byte index */
     }
 
   if (seg_offset)
@@ -3449,7 +3891,7 @@ gtk_text_line_byte_to_any_segment (GtkTextLine *line,
 }
 
 GtkTextLineSegment*
-gtk_text_line_char_to_any_segment (GtkTextLine *line,
+_gtk_text_line_char_to_any_segment (GtkTextLine *line,
                                    gint char_offset,
                                    gint *seg_offset)
 {
@@ -3463,9 +3905,9 @@ gtk_text_line_char_to_any_segment (GtkTextLine *line,
 
   while (offset > 0 && offset >= seg->char_count)
     {
-      g_assert (seg != NULL); /* means an invalid byte index */
       offset -= seg->char_count;
       seg = seg->next;
+      g_assert (seg != NULL); /* means an invalid byte index */
     }
 
   if (seg_offset)
@@ -3475,7 +3917,7 @@ gtk_text_line_char_to_any_segment (GtkTextLine *line,
 }
 
 gint
-gtk_text_line_byte_to_char (GtkTextLine *line,
+_gtk_text_line_byte_to_char (GtkTextLine *line,
                             gint byte_offset)
 {
   gint char_offset;
@@ -3489,12 +3931,10 @@ gtk_text_line_byte_to_char (GtkTextLine *line,
   while (byte_offset >= seg->byte_count) /* while (we need to go farther than
                                             the next segment) */
     {
-      g_assert (seg != NULL); /* our byte_index was bogus if this happens */
-
       byte_offset -= seg->byte_count;
       char_offset += seg->char_count;
-
       seg = seg->next;
+      g_assert (seg != NULL); /* our byte_index was bogus if this happens */
     }
 
   g_assert (seg != NULL);
@@ -3519,7 +3959,7 @@ gtk_text_line_byte_to_char (GtkTextLine *line,
 }
 
 gint
-gtk_text_line_char_to_byte (GtkTextLine *line,
+_gtk_text_line_char_to_byte (GtkTextLine *line,
                             gint         char_offset)
 {
   g_warning ("FIXME not implemented");
@@ -3529,31 +3969,22 @@ gtk_text_line_char_to_byte (GtkTextLine *line,
 
 /* FIXME sync with char_locate (or figure out a clean
    way to merge the two functions) */
-void
-gtk_text_line_byte_locate (GtkTextLine *line,
-                           gint byte_offset,
-                           GtkTextLineSegment **segment,
-                           GtkTextLineSegment **any_segment,
-                           gint *seg_byte_offset,
-                           gint *line_byte_offset)
+gboolean
+_gtk_text_line_byte_locate (GtkTextLine *line,
+                            gint byte_offset,
+                            GtkTextLineSegment **segment,
+                            GtkTextLineSegment **any_segment,
+                            gint *seg_byte_offset,
+                            gint *line_byte_offset)
 {
   GtkTextLineSegment *seg;
-  GtkTextLineSegment *after_prev_indexable;
   GtkTextLineSegment *after_last_indexable;
   GtkTextLineSegment *last_indexable;
   gint offset;
   gint bytes_in_line;
 
-  g_return_if_fail (line != NULL);
-
-  if (byte_offset < 0)
-    {
-      /* -1 means end of line; we here assume no line is
-         longer than 1 bazillion bytes, of course we assumed
-         that anyway since we'd wrap around... */
-
-      byte_offset = G_MAXINT;
-    }
+  g_return_val_if_fail (line != NULL, FALSE);
+  g_return_val_if_fail (byte_offset >= 0, FALSE);
 
   *segment = NULL;
   *any_segment = NULL;
@@ -3563,7 +3994,6 @@ gtk_text_line_byte_locate (GtkTextLine *line,
 
   last_indexable = NULL;
   after_last_indexable = line->segments;
-  after_prev_indexable = line->segments;
   seg = line->segments;
 
   /* The loop ends when we're inside a segment;
@@ -3576,7 +4006,6 @@ gtk_text_line_byte_locate (GtkTextLine *line,
           offset -= seg->byte_count;
           bytes_in_line += seg->byte_count;
           last_indexable = seg;
-          after_prev_indexable = after_last_indexable;
           after_last_indexable = last_indexable->next;
         }
 
@@ -3586,11 +4015,10 @@ gtk_text_line_byte_locate (GtkTextLine *line,
   if (seg == NULL)
     {
       /* We went off the end of the line */
-      *segment = last_indexable;
-      *any_segment = after_prev_indexable;
-      /* subtracting 1 is OK, we know it's a newline at the end. */
-      offset = (*segment)->byte_count - 1;
-      bytes_in_line -= (*segment)->byte_count;
+      if (offset != 0)
+        g_warning ("%s: byte index off the end of the line", G_STRLOC);
+
+      return FALSE;
     }
   else
     {
@@ -3612,36 +4040,29 @@ gtk_text_line_byte_locate (GtkTextLine *line,
   g_assert (*seg_byte_offset < (*segment)->byte_count);
 
   *line_byte_offset = bytes_in_line + *seg_byte_offset;
+
+  return TRUE;
 }
 
 /* FIXME sync with byte_locate (or figure out a clean
    way to merge the two functions) */
-void
-gtk_text_line_char_locate     (GtkTextLine     *line,
-                               gint              char_offset,
-                               GtkTextLineSegment **segment,
-                               GtkTextLineSegment **any_segment,
-                               gint             *seg_char_offset,
-                               gint             *line_char_offset)
+gboolean
+_gtk_text_line_char_locate     (GtkTextLine     *line,
+                                gint              char_offset,
+                                GtkTextLineSegment **segment,
+                                GtkTextLineSegment **any_segment,
+                                gint             *seg_char_offset,
+                                gint             *line_char_offset)
 {
   GtkTextLineSegment *seg;
-  GtkTextLineSegment *after_prev_indexable;
   GtkTextLineSegment *after_last_indexable;
   GtkTextLineSegment *last_indexable;
   gint offset;
   gint chars_in_line;
 
-  g_return_if_fail (line != NULL);
-
-  if (char_offset < 0)
-    {
-      /* -1 means end of line; we here assume no line is
-         longer than 1 bazillion chars, of course we assumed
-         that anyway since we'd wrap around... */
-
-      char_offset = G_MAXINT;
-    }
-
+  g_return_val_if_fail (line != NULL, FALSE);
+  g_return_val_if_fail (char_offset >= 0, FALSE);
+  
   *segment = NULL;
   *any_segment = NULL;
   chars_in_line = 0;
@@ -3650,7 +4071,6 @@ gtk_text_line_char_locate     (GtkTextLine     *line,
 
   last_indexable = NULL;
   after_last_indexable = line->segments;
-  after_prev_indexable = line->segments;
   seg = line->segments;
 
   /* The loop ends when we're inside a segment;
@@ -3663,7 +4083,6 @@ gtk_text_line_char_locate     (GtkTextLine     *line,
           offset -= seg->char_count;
           chars_in_line += seg->char_count;
           last_indexable = seg;
-          after_prev_indexable = after_last_indexable;
           after_last_indexable = last_indexable->next;
         }
 
@@ -3672,12 +4091,11 @@ gtk_text_line_char_locate     (GtkTextLine     *line,
 
   if (seg == NULL)
     {
-      /* We went off the end of the line */
-      *segment = last_indexable;
-      *any_segment = after_prev_indexable;
-      /* subtracting 1 is OK, we know it's a newline at the end. */
-      offset = (*segment)->char_count - 1;
-      chars_in_line -= (*segment)->char_count;
+      /* end of the line */
+      if (offset != 0)
+        g_warning ("%s: char offset off the end of the line", G_STRLOC);
+
+      return FALSE;
     }
   else
     {
@@ -3699,10 +4117,12 @@ gtk_text_line_char_locate     (GtkTextLine     *line,
   g_assert (*seg_char_offset < (*segment)->char_count);
 
   *line_char_offset = chars_in_line + *seg_char_offset;
+
+  return TRUE;
 }
 
 void
-gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
+_gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
                                     gint byte_offset,
                                     gint *line_char_offset,
                                     gint *seg_char_offset)
@@ -3748,7 +4168,7 @@ gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
 }
 
 void
-gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
+_gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
                                     gint char_offset,
                                     gint *line_byte_offset,
                                     gint *seg_byte_offset)
@@ -3779,16 +4199,16 @@ gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
 
   if (seg->type == &gtk_text_char_type)
     {
-      *seg_byte_offset = 0;
-      while (offset > 0)
-        {
-          gint bytes;
-          const char * start = seg->body.chars + *seg_byte_offset;
+      const char *p;
 
-          bytes = g_utf8_next_char (start) - start;
-          *seg_byte_offset += bytes;
-          offset -= 1;
-        }
+      /* if in the last fourth of the segment walk backwards */
+      if (seg->char_count - offset < seg->char_count / 4)
+        p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count, 
+                                      offset - seg->char_count);
+      else
+        p = g_utf8_offset_to_pointer (seg->body.chars, offset);
+
+      *seg_byte_offset = p - seg->body.chars;
 
       g_assert (*seg_byte_offset < seg->byte_count);
 
@@ -3898,9 +4318,9 @@ node_compare (GtkTextBTreeNode *lhs,
 
 /* remember that tag == NULL means "any tag" */
 GtkTextLine*
-gtk_text_line_next_could_contain_tag (GtkTextLine *line,
-                                      GtkTextBTree *tree,
-                                      GtkTextTag  *tag)
+_gtk_text_line_next_could_contain_tag (GtkTextLine  *line,
+                                       GtkTextBTree *tree,
+                                       GtkTextTag   *tag)
 {
   GtkTextBTreeNode *node;
   GtkTextTagInfo *info;
@@ -3908,25 +4328,25 @@ gtk_text_line_next_could_contain_tag (GtkTextLine *line,
 
   g_return_val_if_fail (line != NULL, NULL);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 
   if (tag == NULL)
     {
       /* Right now we can only offer linear-search if the user wants
        * to know about any tag toggle at all.
        */
-      return gtk_text_line_next (line);
+      return _gtk_text_line_next_excluding_last (line);
     }
 
   /* Our tag summaries only have node precision, not line
-     precision. This means that if any line under a node could contain a
-     tag, then any of the others could also contain a tag.
-
-     In the future we could have some mechanism to keep track of how
-     many toggles we've found under a node so far, since we have a
-     count of toggles under the node. But for now I'm going with KISS.
-  */
+   * precision. This means that if any line under a node could contain a
+   * tag, then any of the others could also contain a tag.
+   * 
+   * In the future we could have some mechanism to keep track of how
+   * many toggles we've found under a node so far, since we have a
+   * count of toggles under the node. But for now I'm going with KISS.
+   */
 
   /* return same-node line, if any. */
   if (line->next)
@@ -4053,7 +4473,7 @@ prev_line_under_node (GtkTextBTreeNode *node,
 }
 
 GtkTextLine*
-gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
+_gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
                                           GtkTextBTree *tree,
                                           GtkTextTag   *tag)
 {
@@ -4071,15 +4491,15 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
 
   g_return_val_if_fail (line != NULL, NULL);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 
   if (tag == NULL)
     {
       /* Right now we can only offer linear-search if the user wants
        * to know about any tag toggle at all.
        */
-      return gtk_text_line_previous (line);
+      return _gtk_text_line_previous (line);
     }
 
   /* Return same-node line, if any. */
@@ -4126,9 +4546,7 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
       line_ancestor = line->parent;
       line_ancestor_parent = line->parent->parent;
 
-      node = line_ancestor_parent->children.node;
-      while (node != line_ancestor &&
-             line_ancestor != info->tag_root)
+      while (line_ancestor != info->tag_root)
         {
           GSList *child_nodes = NULL;
           GSList *tmp;
@@ -4136,8 +4554,12 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
           /* Create reverse-order list of nodes before
            * line_ancestor
            */
-          while (node != line_ancestor
-                 && node != NULL)
+          if (line_ancestor_parent != NULL)
+           node = line_ancestor_parent->children.node;
+         else
+           node = line_ancestor;
+
+          while (node != line_ancestor && node != NULL)
             {
               child_nodes = g_slist_prepend (child_nodes, node);
 
@@ -4167,8 +4589,6 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
           /* Didn't find anything on this level; go up one level. */
           line_ancestor = line_ancestor_parent;
           line_ancestor_parent = line_ancestor->parent;
-
-          node = line_ancestor_parent->children.node;
         }
 
       /* No dice. */
@@ -4266,35 +4686,29 @@ gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
 static void
 summary_list_destroy (Summary *summary)
 {
-  Summary *next;
-  while (summary != NULL)
-    {
-      next = summary->next;
-      summary_destroy (summary);
-      summary = next;
-    }
+  g_slice_free_chain (Summary, summary, next);
 }
 
 static GtkTextLine*
 get_last_line (GtkTextBTree *tree)
 {
-  if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+  if (tree->last_line_stamp != tree->chars_changed_stamp)
     {
       gint n_lines;
       GtkTextLine *line;
       gint real_line;
 
-      n_lines = gtk_text_btree_line_count (tree);
+      n_lines = _gtk_text_btree_line_count (tree);
 
       g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
 
-      line = gtk_text_btree_get_line (tree, n_lines, &real_line);
+      line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
 
-      tree->end_iter_line_stamp = tree->chars_changed_stamp;
-      tree->end_iter_line = line;
+      tree->last_line_stamp = tree->chars_changed_stamp;
+      tree->last_line = line;
     }
 
-  return tree->end_iter_line;
+  return tree->last_line;
 }
 
 /*
@@ -4306,7 +4720,10 @@ gtk_text_line_new (void)
 {
   GtkTextLine *line;
 
-  line = g_new0(GtkTextLine, 1);
+  line = g_slice_new0 (GtkTextLine);
+  line->dir_strong = PANGO_DIRECTION_NEUTRAL;
+  line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
+  line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
 
   return line;
 }
@@ -4334,7 +4751,7 @@ gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
       ld = next;
     }
 
-  g_free (line);
+  g_slice_free (GtkTextLine, line);
 }
 
 static void
@@ -4367,16 +4784,20 @@ cleanup_line (GtkTextLine *line)
   while (changed)
     {
       changed = FALSE;
-      for (prev_p = &line->segments, seg = *prev_p;
-           seg != NULL;
-           prev_p = &(*prev_p)->next, seg = *prev_p)
+      prev_p = &line->segments;
+      for (seg = *prev_p; seg != NULL; seg = *prev_p)
         {
           if (seg->type->cleanupFunc != NULL)
             {
               *prev_p = (*seg->type->cleanupFunc)(seg, line);
               if (seg != *prev_p)
-                changed = TRUE;
+               {
+                 changed = TRUE;
+                 continue;
+               }
             }
+
+         prev_p = &(*prev_p)->next;
         }
     }
 }
@@ -4389,8 +4810,8 @@ static NodeData*
 node_data_new (gpointer view_id)
 {
   NodeData *nd;
-
-  nd = g_new (NodeData, 1);
+  
+  nd = g_slice_new (NodeData);
 
   nd->view_id = view_id;
   nd->next = NULL;
@@ -4404,27 +4825,18 @@ node_data_new (gpointer view_id)
 static void
 node_data_destroy (NodeData *nd)
 {
-
-  g_free (nd);
+  g_slice_free (NodeData, nd);
 }
 
 static void
 node_data_list_destroy (NodeData *nd)
 {
-  NodeData *iter;
-  NodeData *next;
-
-  iter = nd;
-  while (iter != NULL)
-    {
-      next = iter->next;
-      node_data_destroy (iter);
-      iter = next;
-    }
+  g_slice_free_chain (NodeData, nd, next);
 }
 
 static NodeData*
-node_data_find (NodeData *nd, gpointer view_id)
+node_data_find (NodeData *nd, 
+               gpointer  view_id)
 {
   while (nd != NULL)
     {
@@ -4442,7 +4854,7 @@ summary_destroy (Summary *summary)
   summary->info = (void*)0x1;
   summary->toggle_count = 567;
   summary->next = (void*)0x1;
-  g_free (summary);
+  g_slice_free (Summary, summary);
 }
 
 static GtkTextBTreeNode*
@@ -4450,7 +4862,7 @@ gtk_text_btree_node_new (void)
 {
   GtkTextBTreeNode *node;
 
-  node = g_new (GtkTextBTreeNode, 1);
+  node = g_slice_new (GtkTextBTreeNode);
 
   node->node_data = NULL;
 
@@ -4480,7 +4892,7 @@ gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode  *node,
     {
       /* didn't find a summary for our tag. */
       g_return_if_fail (adjust > 0);
-      summary = g_new (Summary, 1);
+      summary = g_slice_new (Summary);
       summary->info = info;
       summary->toggle_count = adjust;
       summary->next = node->summary;
@@ -4510,6 +4922,7 @@ gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
 }
 
 /* Add node and all children to the damage region. */
+#if 0
 static void
 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
 {
@@ -4555,6 +4968,7 @@ gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
         }
     }
 }
+#endif
 
 static void
 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
@@ -4602,7 +5016,7 @@ gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
 
 
 /**
- * gtk_text_btree_is_valid:
+ * _gtk_text_btree_is_valid:
  * @tree: a #GtkTextBTree
  * @view_id: ID for the view
  *
@@ -4612,7 +5026,7 @@ gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
  * Return value: %TRUE if the entire #GtkTextBTree is valid
  **/
 gboolean
-gtk_text_btree_is_valid (GtkTextBTree *tree,
+_gtk_text_btree_is_valid (GtkTextBTree *tree,
                          gpointer      view_id)
 {
   NodeData *nd;
@@ -4654,7 +5068,7 @@ gtk_text_btree_node_validate (BTreeView         *view,
       /* Iterate over leading valid lines */
       while (line != NULL)
         {
-          ld = gtk_text_line_get_data (line, view_id);
+          ld = _gtk_text_line_get_data (line, view_id);
 
           if (!ld || !ld->valid)
             break;
@@ -4678,7 +5092,7 @@ gtk_text_btree_node_validate (BTreeView         *view,
       /* Iterate over invalid lines */
       while (line != NULL)
         {
-          ld = gtk_text_line_get_data (line, view_id);
+          ld = _gtk_text_line_get_data (line, view_id);
 
           if (ld && ld->valid)
             break;
@@ -4706,7 +5120,7 @@ gtk_text_btree_node_validate (BTreeView         *view,
       /* Iterate over the remaining lines */
       while (line != NULL)
         {
-          ld = gtk_text_line_get_data (line, view_id);
+          ld = _gtk_text_line_get_data (line, view_id);
           state->in_validation = FALSE;
 
           if (!ld || !ld->valid)
@@ -4798,7 +5212,7 @@ gtk_text_btree_node_validate (BTreeView         *view,
 }
 
 /**
- * gtk_text_btree_validate:
+ * _gtk_text_btree_validate:
  * @tree: a #GtkTextBTree
  * @view_id: view id
  * @max_pixels: the maximum number of pixels to validate. (No more
@@ -4814,7 +5228,7 @@ gtk_text_btree_node_validate (BTreeView         *view,
  * entire tree was already valid.
  **/
 gboolean
-gtk_text_btree_validate (GtkTextBTree *tree,
+_gtk_text_btree_validate (GtkTextBTree *tree,
                          gpointer      view_id,
                          gint          max_pixels,
                          gint         *y,
@@ -4828,7 +5242,7 @@ gtk_text_btree_validate (GtkTextBTree *tree,
   view = gtk_text_btree_get_view (tree, view_id);
   g_return_val_if_fail (view != NULL, FALSE);
 
-  if (!gtk_text_btree_is_valid (tree, view_id))
+  if (!_gtk_text_btree_is_valid (tree, view_id))
     {
       ValidateState state;
 
@@ -4849,8 +5263,8 @@ gtk_text_btree_validate (GtkTextBTree *tree,
       if (new_height)
         *new_height = state.new_height;
 
-      if (gtk_debug_flags & GTK_DEBUG_TEXT)
-        gtk_text_btree_check (tree);
+      if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+        _gtk_text_btree_check (tree);
 
       return TRUE;
     }
@@ -4875,7 +5289,7 @@ gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
 
       while (line != NULL)
         {
-          GtkTextLineData *ld = gtk_text_line_get_data (line, view_id);
+          GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
 
           if (!ld || !ld->valid)
             valid = FALSE;
@@ -4984,7 +5398,7 @@ gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
 
 
 /**
- * gtk_text_btree_validate_line:
+ * _gtk_text_btree_validate_line:
  * @tree: a #GtkTextBTree
  * @line: line to validate
  * @view_id: view ID for the view to validate
@@ -4993,12 +5407,11 @@ gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
  * results up through the entire tree.
  **/
 void
-gtk_text_btree_validate_line (GtkTextBTree     *tree,
-                              GtkTextLine      *line,
-                              gpointer          view_id)
+_gtk_text_btree_validate_line (GtkTextBTree     *tree,
+                               GtkTextLine      *line,
+                               gpointer          view_id)
 {
   GtkTextLineData *ld;
-  GtkTextLine *last_line;
   BTreeView *view;
 
   g_return_if_fail (tree != NULL);
@@ -5006,13 +5419,12 @@ gtk_text_btree_validate_line (GtkTextBTree     *tree,
 
   view = gtk_text_btree_get_view (tree, view_id);
   g_return_if_fail (view != NULL);
-
-  ld = gtk_text_line_get_data (line, view_id);
+  
+  ld = _gtk_text_line_get_data (line, view_id);
   if (!ld || !ld->valid)
     {
       ld = gtk_text_layout_wrap (view->layout, line, ld);
-      last_line = get_last_line (tree);
-
+      
       gtk_text_btree_node_check_valid_upward (line->parent, view_id);
     }
 }
@@ -5029,7 +5441,7 @@ gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpoint
         {
           GtkTextLineData *ld;
 
-          ld = gtk_text_line_remove_data (line, view_id);
+          ld = _gtk_text_line_remove_data (line, view_id);
 
           if (ld)
             gtk_text_layout_free_line_data (view->layout, line, ld);
@@ -5072,14 +5484,7 @@ gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
               seg = line->segments;
               line->segments = seg->next;
 
-              if (GTK_IS_TEXT_MARK_SEGMENT (seg))
-                {
-                  /* Set the mark as deleted */
-                  seg->body.mark.tree = NULL;
-                  seg->body.mark.line = NULL;
-                }
-
-              (*seg->type->deleteFunc) (seg, line, 1);
+              (*seg->type->deleteFunc) (seg, line, TRUE);
             }
           gtk_text_line_destroy (tree, line);
         }
@@ -5096,9 +5501,19 @@ gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
         }
     }
 
+  gtk_text_btree_node_free_empty (tree, node);
+}
+
+static void
+gtk_text_btree_node_free_empty (GtkTextBTree *tree,
+                                GtkTextBTreeNode *node)
+{
+  g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
+                    (node->level == 0 && node->children.line == NULL));
+
   summary_list_destroy (node->summary);
   node_data_list_destroy (node->node_data);
-  g_free (node);
+  g_slice_free (GtkTextBTreeNode, node);
 }
 
 static NodeData*
@@ -5115,14 +5530,15 @@ gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
       nd = nd->next;
     }
 
-  if (nd == NULL)    {
-    nd = node_data_new (view_id);
-
-    if (node->node_data)
-      nd->next = node->node_data;
-
-    node->node_data = nd;
-  }
+  if (nd == NULL)
+    {
+      nd = node_data_new (view_id);
+      
+      if (node->node_data)
+        nd->next = node->node_data;
+      
+      node->node_data = nd;
+    }
 
   return nd;
 }
@@ -5222,8 +5638,8 @@ get_tree_bounds (GtkTextBTree *tree,
                  GtkTextIter *start,
                  GtkTextIter *end)
 {
-  gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
-  gtk_text_btree_get_last_iter (tree, end);
+  _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
+  _gtk_text_btree_get_end_iter (tree, end);
 }
 
 static void
@@ -5241,12 +5657,12 @@ tag_changed_cb (GtkTextTagTable *table,
       GtkTextIter start;
       GtkTextIter end;
 
-      if (gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
+      if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
         {
           /* Must be a last toggle if there was a first one. */
-          gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
-          gtk_text_btree_invalidate_region (tree,
-                                            &start, &end);
+          _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
+          DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
+          _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
 
         }
     }
@@ -5261,7 +5677,7 @@ tag_changed_cb (GtkTextTagTable *table,
         {
           gint width, height;
 
-          gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
+          _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
           gtk_text_layout_changed (view->layout, 0, height, height);
 
           view = view->next;
@@ -5269,10 +5685,9 @@ tag_changed_cb (GtkTextTagTable *table,
     }
 }
 
-static void
-tag_removed_cb (GtkTextTagTable *table,
-                GtkTextTag *tag,
-                GtkTextBTree *tree)
+void
+_gtk_text_btree_notify_will_remove_tag (GtkTextBTree    *tree,
+                                        GtkTextTag      *tag)
 {
   /* Remove the tag from the tree */
 
@@ -5281,7 +5696,7 @@ tag_removed_cb (GtkTextTagTable *table,
 
   get_tree_bounds (tree, &start, &end);
 
-  gtk_text_btree_tag (&start, &end, tag, FALSE);
+  _gtk_text_btree_tag (&start, &end, tag, FALSE);
   gtk_text_btree_remove_tag_info (tree, tag);
 }
 
@@ -5392,8 +5807,9 @@ gtk_text_btree_rebalance (GtkTextBTree *tree,
                 {
                   tree->root_node = node->children.node;
                   tree->root_node->parent = NULL;
-                  summary_list_destroy (node->summary);
-                  g_free (node);
+
+                  node->children.node = NULL;
+                  gtk_text_btree_node_free_empty (tree, node);
                 }
               return;
             }
@@ -5497,8 +5913,10 @@ gtk_text_btree_rebalance (GtkTextBTree *tree,
               recompute_node_counts (tree, node);
               node->next = other->next;
               node->parent->num_children--;
-              summary_list_destroy (other->summary);
-              g_free (other);
+
+              other->children.node = NULL;
+              other->children.line = NULL;
+              gtk_text_btree_node_free_empty (tree, other);
               continue;
             }
 
@@ -5554,8 +5972,8 @@ post_insert_fixup (GtkTextBTree *tree,
       gtk_text_btree_rebalance (tree, node);
     }
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 }
 
 static GtkTextTagInfo*
@@ -5591,14 +6009,20 @@ gtk_text_btree_get_tag_info (GtkTextBTree *tree,
     {
       /* didn't find it, create. */
 
-      info = g_new (GtkTextTagInfo, 1);
+      info = g_slice_new (GtkTextTagInfo);
 
       info->tag = tag;
-      gtk_object_ref (GTK_OBJECT (tag));
+      g_object_ref (tag);
       info->tag_root = NULL;
       info->toggle_count = 0;
 
       tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
+
+#if 0
+      g_print ("Created tag info %p for tag %s(%p)\n",
+               info, info->tag->name ? info->tag->name : "anon",
+               info->tag);
+#endif
     }
 
   return info;
@@ -5619,6 +6043,12 @@ gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
       info = list->data;
       if (info->tag == tag)
         {
+#if 0
+          g_print ("Removing tag info %p for tag %s(%p)\n",
+                   info, info->tag->name ? info->tag->name : "anon",
+                   info->tag);
+#endif
+          
           if (prev != NULL)
             {
               prev->next = list->next;
@@ -5630,17 +6060,15 @@ gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
           list->next = NULL;
           g_slist_free (list);
 
-          gtk_object_unref (GTK_OBJECT (info->tag));
+          g_object_unref (info->tag);
 
-          g_free (info);
+          g_slice_free (GtkTextTagInfo, info);
           return;
         }
 
+      prev = list;
       list = g_slist_next (list);
     }
-
-  g_assert_not_reached ();
-  return;
 }
 
 static void
@@ -5784,7 +6212,7 @@ recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
       gtk_text_btree_node_check_valid (node, view->view_id);
       view = view->next;
     }
-
+  
   /*
    * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
    * records that still have a zero count, or that have all the toggles.
@@ -5934,7 +6362,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
                */
 
               GtkTextBTreeNode *rootnode = info->tag_root;
-              summary = (Summary *) g_malloc (sizeof (Summary));
+              summary = g_slice_new (Summary);
               summary->info = info;
               summary->toggle_count = info->toggle_count - delta;
               summary->next = rootnode->summary;
@@ -5943,7 +6371,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
               rootLevel = rootnode->level;
               info->tag_root = rootnode;
             }
-          summary = (Summary *) g_malloc (sizeof (Summary));
+          summary = g_slice_new (Summary);
           summary->info = info;
           summary->toggle_count = delta;
           summary->next = node->summary;
@@ -6024,7 +6452,7 @@ _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
  *
  * inc_count --
  *
- *      This is a utility procedure used by gtk_text_btree_get_tags.  It
+ *      This is a utility procedure used by _gtk_text_btree_get_tags.  It
  *      increments the count for a particular tag, adding a new
  *      entry for that tag if there wasn't one previously.
  *
@@ -6093,8 +6521,8 @@ gtk_text_btree_link_segment (GtkTextLineSegment *seg,
   GtkTextLine *line;
   GtkTextBTree *tree;
 
-  line = gtk_text_iter_get_text_line (iter);
-  tree = gtk_text_iter_get_btree (iter);
+  line = _gtk_text_iter_get_text_line (iter);
+  tree = _gtk_text_iter_get_btree (iter);
 
   prev = gtk_text_line_segment_split (iter);
   if (prev == NULL)
@@ -6110,8 +6538,8 @@ gtk_text_btree_link_segment (GtkTextLineSegment *seg,
   cleanup_line (line);
   segments_changed (tree);
 
-  if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_btree_check (tree);
+  if (gtk_get_debug_flags () & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
 }
 
 static void
@@ -6207,18 +6635,45 @@ _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
  */
 
 static void
-gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
+gtk_text_btree_node_view_check_consistency (GtkTextBTree     *tree,
+                                            GtkTextBTreeNode *node,
                                             NodeData         *nd)
 {
   gint width;
   gint height;
   gboolean valid;
+  BTreeView *view;
+  
+  view = tree->views;
 
+  while (view != NULL)
+    {
+      if (view->view_id == nd->view_id)
+        break;
+
+      view = view->next;
+    }
+
+  if (view == NULL)
+    g_error ("Node has data for a view %p no longer attached to the tree",
+             nd->view_id);
+  
   gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
                                                &width, &height, &valid);
+
+  /* valid aggregate not checked the same as width/height, because on
+   * btree rebalance we can have invalid nodes where all lines below
+   * them are actually valid, due to moving lines around between
+   * nodes.
+   *
+   * The guarantee is that if there are invalid lines the node is
+   * invalid - we don't guarantee that if the node is invalid there
+   * are invalid lines.
+   */
+  
   if (nd->width != width ||
       nd->height != height ||
-      !nd->valid != !valid)
+      (nd->valid && !valid))
     {
       g_error ("Node aggregates for view %p are invalid:\n"
                "Are (%d,%d,%s), should be (%d,%d,%s)",
@@ -6229,7 +6684,8 @@ gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
 }
 
 static void
-gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
+gtk_text_btree_node_check_consistency (GtkTextBTree     *tree,
+                                       GtkTextBTreeNode *node)
 {
   GtkTextBTreeNode *childnode;
   Summary *summary, *summary2;
@@ -6260,7 +6716,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
   nd = node->node_data;
   while (nd != NULL)
     {
-      gtk_text_btree_node_view_check_consistency (node, nd);
+      gtk_text_btree_node_view_check_consistency (tree, node, nd);
       nd = nd->next;
     }
 
@@ -6329,7 +6785,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
               g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
                        node->level, childnode->level);
             }
-          gtk_text_btree_node_check_consistency (childnode);
+          gtk_text_btree_node_check_consistency (tree, childnode);
           for (summary = childnode->summary; summary != NULL;
                summary = summary->next)
             {
@@ -6343,7 +6799,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
                           break;
                         }
                       g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
-                               summary->info->tag->name,
+                               summary->info->tag->priv->name,
                                "present in parent summaries");
                     }
                   if (summary->info == summary2->info)
@@ -6379,7 +6835,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
       if (summary->info->toggle_count == summary->toggle_count)
         {
           g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
-                   summary->info->tag->name);
+                   summary->info->tag->priv->name);
         }
       toggle_count = 0;
       if (node->level == 0)
@@ -6433,7 +6889,7 @@ gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
           if (summary2->info == summary->info)
             {
               g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
-                       summary->info->tag->name);
+                       summary->info->tag->priv->name);
             }
         }
     }
@@ -6458,22 +6914,22 @@ list_of_tags (GtkTextTagTable *table)
 }
 
 void
-gtk_text_btree_check (GtkTextBTree *tree)
+_gtk_text_btree_check (GtkTextBTree *tree)
 {
   Summary *summary;
   GtkTextBTreeNode *node;
   GtkTextLine *line;
   GtkTextLineSegment *seg;
   GtkTextTag *tag;
-  GSList *taglist = NULL;
+  GSList *all_tags, *taglist = NULL;
   int count;
   GtkTextTagInfo *info;
 
   /*
    * Make sure that the tag toggle counts and the tag root pointers are OK.
    */
-  for (taglist = list_of_tags (tree->table);
-       taglist != NULL ; taglist = taglist->next)
+  all_tags = list_of_tags (tree->table);
+  for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
     {
       tag = taglist->data;
       info = gtk_text_btree_get_existing_tag_info (tree, tag);
@@ -6484,27 +6940,27 @@ gtk_text_btree_check (GtkTextBTree *tree)
             {
               if (info->toggle_count != 0)
                 {
-                  g_error ("gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
-                           tag->name, info->toggle_count);
+                  g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
+                           tag->priv->name, info->toggle_count);
                 }
               continue;         /* no ranges for the tag */
             }
           else if (info->toggle_count == 0)
             {
-              g_error ("gtk_text_btree_check found root for \"%s\" with no toggles",
-                       tag->name);
+              g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
+                       tag->priv->name);
             }
           else if (info->toggle_count & 1)
             {
-              g_error ("gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
-                       tag->name, info->toggle_count);
+              g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
+                       tag->priv->name, info->toggle_count);
             }
           for (summary = node->summary; summary != NULL;
                summary = summary->next)
             {
               if (summary->info->tag == tag)
                 {
-                  g_error ("gtk_text_btree_check found root GtkTextBTreeNode with summary info");
+                  g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
                 }
             }
           count = 0;
@@ -6525,7 +6981,7 @@ gtk_text_btree_check (GtkTextBTree *tree)
             }
           else
             {
-              GtkTextLineSegmentClass * last = NULL;
+              const GtkTextLineSegmentClass *last = NULL;
 
               for (line = node->children.line ; line != NULL ;
                    line = line->next)
@@ -6551,21 +7007,20 @@ gtk_text_btree_check (GtkTextBTree *tree)
             }
           if (count != info->toggle_count)
             {
-              g_error ("gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
-                       info->toggle_count, tag->name, count);
+              g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
+                       info->toggle_count, tag->priv->name, count);
             }
         }
     }
 
-  g_slist_free (taglist);
-  taglist = NULL;
+  g_slist_free (all_tags);
 
   /*
    * Call a recursive procedure to do the main body of checks.
    */
 
   node = tree->root_node;
-  gtk_text_btree_node_check_consistency (tree->root_node);
+  gtk_text_btree_node_check_consistency (tree, tree->root_node);
 
   /*
    * Make sure that there are at least two lines in the text and
@@ -6574,11 +7029,11 @@ gtk_text_btree_check (GtkTextBTree *tree)
 
   if (node->num_lines < 2)
     {
-      g_error ("gtk_text_btree_check: less than 2 lines in tree");
+      g_error ("_gtk_text_btree_check: less than 2 lines in tree");
     }
   if (node->num_chars < 2)
     {
-      g_error ("gtk_text_btree_check: less than 2 chars in tree");
+      g_error ("_gtk_text_btree_check: less than 2 chars in tree");
     }
   while (node->level > 0)
     {
@@ -6608,44 +7063,44 @@ gtk_text_btree_check (GtkTextBTree *tree)
     }
   if (seg->type != &gtk_text_char_type)
     {
-      g_error ("gtk_text_btree_check: last line has bogus segment type");
+      g_error ("_gtk_text_btree_check: last line has bogus segment type");
     }
   if (seg->next != NULL)
     {
-      g_error ("gtk_text_btree_check: last line has too many segments");
+      g_error ("_gtk_text_btree_check: last line has too many segments");
     }
   if (seg->byte_count != 1)
     {
-      g_error ("gtk_text_btree_check: last line has wrong # characters: %d",
+      g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
                seg->byte_count);
     }
   if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
     {
-      g_error ("gtk_text_btree_check: last line had bad value: %s",
+      g_error ("_gtk_text_btree_check: last line had bad value: %s",
                seg->body.chars);
     }
 }
 
-void gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
-void gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
-void gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
-void gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
+void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
+void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
+void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
+void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
 
 void
-gtk_text_btree_spew (GtkTextBTree *tree)
+_gtk_text_btree_spew (GtkTextBTree *tree)
 {
   GtkTextLine * line;
   int real_line;
 
   printf ("%d lines in tree %p\n",
-          gtk_text_btree_line_count (tree), tree);
+          _gtk_text_btree_line_count (tree), tree);
 
-  line = gtk_text_btree_get_line (tree, 0, &real_line);
+  line = _gtk_text_btree_get_line (tree, 0, &real_line);
 
   while (line != NULL)
     {
-      gtk_text_btree_spew_line (tree, line);
-      line = gtk_text_line_next (line);
+      _gtk_text_btree_spew_line (tree, line);
+      line = _gtk_text_line_next (line);
     }
 
   printf ("=================== Tag information\n");
@@ -6662,7 +7117,7 @@ gtk_text_btree_spew (GtkTextBTree *tree)
         info = list->data;
 
         printf ("  tag `%s': root at %p, toggle count %d\n",
-                info->tag->name, info->tag_root, info->toggle_count);
+                info->tag->priv->name, info->tag_root, info->toggle_count);
 
         list = g_slist_next (list);
       }
@@ -6676,12 +7131,12 @@ gtk_text_btree_spew (GtkTextBTree *tree)
   printf ("=================== Tree nodes\n");
 
   {
-    gtk_text_btree_spew_node (tree->root_node, 0);
+    _gtk_text_btree_spew_node (tree->root_node, 0);
   }
 }
 
 void
-gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
+_gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
 {
   gchar * spaces;
   GtkTextLineSegment *seg;
@@ -6690,8 +7145,8 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
 
   printf ("%sline %p chars %d bytes %d\n",
           spaces, line,
-          gtk_text_line_char_count (line),
-          gtk_text_line_byte_count (line));
+          _gtk_text_line_char_count (line),
+          _gtk_text_line_byte_count (line));
 
   seg = line->segments;
   while (seg != NULL)
@@ -6703,7 +7158,7 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
           s = str;
           while (*s)
             {
-              if (*s == '\n')
+              if (*s == '\n' || *s == '\r')
                 *s = '\\';
               ++s;
             }
@@ -6728,7 +7183,7 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
                seg->type == &gtk_text_toggle_off_type)
         {
           printf ("%s tag `%s' %s\n",
-                  spaces, seg->body.toggle.info->tag->name,
+                  spaces, seg->body.toggle.info->tag->priv->name,
                   seg->type == &gtk_text_toggle_off_type ? "off" : "on");
         }
 
@@ -6739,7 +7194,7 @@ gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
 }
 
 void
-gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
+_gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
 {
   gchar * spaces;
   GtkTextBTreeNode *iter;
@@ -6755,7 +7210,7 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
   while (s)
     {
       printf ("%s %d toggles of `%s' below this node\n",
-              spaces, s->toggle_count, s->info->tag->name);
+              spaces, s->toggle_count, s->info->tag->priv->name);
       s = s->next;
     }
 
@@ -6766,7 +7221,7 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
       iter = node->children.node;
       while (iter != NULL)
         {
-          gtk_text_btree_spew_node (iter, indent + 2);
+          _gtk_text_btree_spew_node (iter, indent + 2);
 
           iter = iter->next;
         }
@@ -6776,7 +7231,7 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
       GtkTextLine *line = node->children.line;
       while (line != NULL)
         {
-          gtk_text_btree_spew_line_short (line, indent + 2);
+          _gtk_text_btree_spew_line_short (line, indent + 2);
 
           line = line->next;
         }
@@ -6784,24 +7239,24 @@ gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
 }
 
 void
-gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
+_gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
 {
   GtkTextLineSegment * seg;
 
   printf ("%4d| line: %p parent: %p next: %p\n",
-          gtk_text_line_get_number (line), line, line->parent, line->next);
+          _gtk_text_line_get_number (line), line, line->parent, line->next);
 
   seg = line->segments;
 
   while (seg != NULL)
     {
-      gtk_text_btree_spew_segment (tree, seg);
+      _gtk_text_btree_spew_segment (tree, seg);
       seg = seg->next;
     }
 }
 
 void
-gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
+_gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
 {
   printf ("     segment: %p type: %s bytes: %d chars: %d\n",
           seg, seg->type->name, seg->byte_count, seg->char_count);
@@ -6830,9 +7285,7 @@ gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
            seg->type == &gtk_text_toggle_off_type)
     {
       printf ("       tag `%s' priority %d\n",
-              seg->body.toggle.info->tag->name,
-              seg->body.toggle.info->tag->priority);
+              seg->body.toggle.info->tag->priv->name,
+              seg->body.toggle.info->tag->priv->priority);
     }
 }
-
-