]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktextbtree.c
fix an incorrect assertion that the "valid" flag is always correct in a
[~andy/gtk] / gtk / gtktextbtree.c
index 322344e896f2106b437dba4e5f3aee28ff75ef3a..3cabfc72725b1e7c962ef3da0abad14d3f1ba3b7 100644 (file)
@@ -52,6 +52,7 @@
  *
  */
 
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
 #include "gtktextbtree.h"
 #include <string.h>
 #include <ctype.h>
@@ -102,7 +103,12 @@ struct _NodeData {
   gint height;
   gint width : 24;
 
-  /* boolean indicating whether the height/width need to be recomputed */
+  /* 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.
+   */
   gint valid : 8;
 };
 
@@ -178,24 +184,34 @@ struct _GtkTextBTree {
   BTreeView *views;
   GSList *tag_infos;
   guint tag_changed_handler;
-  guint tag_removed_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;
 };
 
@@ -280,6 +296,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 +315,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);
@@ -347,7 +362,7 @@ chars_changed (GtkTextBTree *tree)
 
 GtkTextBTree*
 _gtk_text_btree_new (GtkTextTagTable *table,
-                    GtkTextBuffer *buffer)
+                     GtkTextBuffer *buffer)
 {
   GtkTextBTree *tree;
   GtkTextBTreeNode *root_node;
@@ -402,21 +417,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 (G_OBJECT (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 (G_OBJECT (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,7 +446,7 @@ _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,
@@ -475,16 +490,6 @@ _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)
 {
@@ -497,21 +502,16 @@ _gtk_text_btree_unref (GtkTextBTree *tree)
     {
       gtk_text_btree_node_destroy (tree, tree->root_node);
 
-      g_hash_table_foreach (tree->mark_table,
-                            mark_destroy_foreach,
-                            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);
+      g_signal_handler_disconnect (G_OBJECT (tree->table),
+                                   tree->tag_changed_handler);
 
-      gtk_object_unref (GTK_OBJECT (tree->table));
+      g_object_unref (G_OBJECT (tree->table));
 
       g_free (tree);
     }
@@ -565,14 +565,18 @@ _gtk_text_btree_delete (GtkTextIter *start,
 
   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));
+  g_return_if_fail (_gtk_text_iter_get_btree (start) ==
+                    _gtk_text_iter_get_btree (end));
 
-  gtk_text_iter_reorder (start, end);
-
-  tree = gtk_text_iter_get_btree (start);
+  gtk_text_iter_order (start, end);
 
+  tree = _gtk_text_iter_get_btree (start);
+  if (gtk_debug_flags & GTK_DEBUG_TEXT)
+    _gtk_text_btree_check (tree);
+  
   {
+    /* FIXME this code should no longer be required */
     /*
      * 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
@@ -610,7 +614,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
           }
 
         tags = _gtk_text_btree_get_tags (end,
-                                        &array_size);
+                                         &array_size);
 
         if (tags != NULL)
           {
@@ -635,8 +639,8 @@ _gtk_text_btree_delete (GtkTextIter *start,
   /* 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
@@ -700,6 +704,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 +742,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 +752,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
@@ -790,21 +797,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--;
@@ -880,6 +898,9 @@ _gtk_text_btree_delete (GtkTextIter *start,
           view = view->next;
         }
 
+      /* avoid dangling pointer */
+      deleted_lines = NULL;
+      
       gtk_text_btree_rebalance (tree, curnode);
     }
 
@@ -910,8 +931,8 @@ _gtk_text_btree_delete (GtkTextIter *start,
 
 void
 _gtk_text_btree_insert (GtkTextIter *iter,
-                       const gchar *text,
-                       gint len)
+                        const gchar *text,
+                        gint         len)
 {
   GtkTextLineSegment *prev_seg;     /* The segment just before the first
                                      * new segment (NULL means new segment
@@ -946,19 +967,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
@@ -971,6 +997,8 @@ _gtk_text_btree_insert (GtkTextIter *iter,
   char_count_delta = 0;
   while (eol < len)
     {
+      sol = eol;
+      
       pango_find_paragraph_boundary (text + sol,
                                      len - sol,
                                      &delim,
@@ -979,9 +1007,16 @@ _gtk_text_btree_insert (GtkTextIter *iter,
       /* 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;
@@ -998,8 +1033,11 @@ _gtk_text_btree_insert (GtkTextIter *iter,
         }
 
       if (delim == eol)
-        /* chunk didn't end with a paragraph separator */
-        break;
+        {
+          /* chunk didn't end with a paragraph separator */
+          g_assert (eol == len);
+          break;
+        }
 
       /*
        * The chunk ended with a newline, so create a new GtkTextLine
@@ -1015,8 +1053,6 @@ _gtk_text_btree_insert (GtkTextIter *iter,
       line = newline;
       cur_seg = NULL;
       line_count_delta++;
-
-      sol = eol;
     }
 
   /*
@@ -1040,9 +1076,9 @@ _gtk_text_btree_insert (GtkTextIter *iter,
 
 
     _gtk_text_btree_get_iter_at_line (tree,
-                                     &start,
-                                     start_line,
-                                     start_byte_index);
+                                      &start,
+                                      start_line,
+                                      start_byte_index);
     end = start;
 
     /* We could almost certainly be more efficient here
@@ -1070,8 +1106,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);
@@ -1102,7 +1138,7 @@ insert_pixbuf_or_widget_segment (GtkTextIter        *iter,
 }
      
 void
-__gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
+_gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
                               GdkPixbuf   *pixbuf)
 {
   GtkTextLineSegment *seg;
@@ -1112,15 +1148,22 @@ __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);
+  tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
   
   insert_pixbuf_or_widget_segment (iter, seg);
 
@@ -1130,8 +1173,6 @@ _gtk_text_btree_create_child_anchor (GtkTextIter *iter)
   g_hash_table_insert (tree->child_anchor_table,
                        seg->body.child.obj,
                        seg->body.child.obj);
-  
-  return seg->body.child.obj;
 }
 
 void
@@ -1215,9 +1256,9 @@ 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)
+                                gpointer      view_id,
+                                gint          ypixel,
+                                gint         *line_top_out)
 {
   GtkTextLine *line;
   BTreeView *view;
@@ -1345,7 +1386,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;
@@ -1354,6 +1395,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
@@ -1374,14 +1421,14 @@ _gtk_text_btree_add_view (GtkTextBTree *tree,
 
 void
 _gtk_text_btree_remove_view (GtkTextBTree *tree,
-                            gpointer view_id)
+                             gpointer      view_id)
 {
   BTreeView *view;
   GtkTextLine *last_line;
   GtkTextLineData *line_data;
 
   g_return_if_fail (tree != NULL);
-
+  
   view = tree->views;
 
   while (view != NULL)
@@ -1412,6 +1459,9 @@ _gtk_text_btree_remove_view (GtkTextBTree *tree,
 
   gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
 
+  view->layout = (gpointer) 0xdeadbeef;
+  view->view_id = (gpointer) 0xdeadbeef;
+  
   g_free (view);
 }
 
@@ -1526,12 +1576,11 @@ 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);
-
     }
-  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);
@@ -1542,9 +1591,9 @@ queue_tag_redisplay (GtkTextBTree      *tree,
 
 void
 _gtk_text_btree_tag (const GtkTextIter *start_orig,
-                    const GtkTextIter *end_orig,
-                    GtkTextTag *tag,
-                    gboolean add)
+                     const GtkTextIter *end_orig,
+                     GtkTextTag        *tag,
+                     gboolean           add)
 {
   GtkTextLineSegment *seg, *prev;
   GtkTextLine *cleanupline;
@@ -1560,9 +1609,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->table == _gtk_text_iter_get_btree (start_orig)->table);
+  
 #if 0
   printf ("%s tag %s from %d to %d\n",
           add ? "Adding" : "Removing",
@@ -1577,16 +1627,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
@@ -1594,8 +1644,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_forward_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)
@@ -1657,9 +1710,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);
@@ -1796,10 +1849,11 @@ _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;
@@ -1807,7 +1861,9 @@ _gtk_text_btree_get_line (GtkTextBTree *tree,
   int line_count;
 
   line_count = _gtk_text_btree_line_count (tree);
-
+  if (!include_last)
+    line_count -= 1;
+  
   if (line_number < 0)
     {
       line_number = line_count;
@@ -1863,10 +1919,35 @@ _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;
@@ -1877,10 +1958,13 @@ __gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
 
   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;
@@ -1961,8 +2045,8 @@ _gtk_text_btree_get_tags (const GtkTextIter *iter,
 
 #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);
+  tree = _gtk_text_iter_get_btree (iter);
   byte_index = gtk_text_iter_get_line_index (iter);
 
   tagInfo.numTags = 0;
@@ -2072,8 +2156,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)
     {
@@ -2090,12 +2174,12 @@ copy_segment (GString *string,
           /* 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;
         }
@@ -2158,29 +2242,29 @@ _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);
+  gtk_text_iter_order (&start, &end);
 
   retval = g_string_new ("");
 
-  tree = gtk_text_iter_get_btree (&start);
+  tree = _gtk_text_iter_get_btree (&start);
 
-  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);
@@ -2201,8 +2285,10 @@ _gtk_text_btree_line_count (GtkTextBTree *tree)
 gint
 _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
@@ -2225,11 +2311,11 @@ _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)
@@ -2373,8 +2459,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)
@@ -2451,7 +2537,7 @@ 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;
@@ -2474,7 +2560,7 @@ real_set_mark (GtkTextBTree      *tree,
   iter = *where;
 
   if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+    _gtk_text_iter_check (&iter);
   
   if (mark != NULL)
     {
@@ -2506,8 +2592,8 @@ 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 */
@@ -2518,7 +2604,7 @@ real_set_mark (GtkTextBTree      *tree,
                                     left_gravity,
                                     name);
 
-      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,
@@ -2527,7 +2613,7 @@ real_set_mark (GtkTextBTree      *tree,
     }
 
   if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+    _gtk_text_iter_check (&iter);
   
   /* Link mark into new location */
   gtk_text_btree_link_segment (mark, &iter);
@@ -2542,7 +2628,7 @@ real_set_mark (GtkTextBTree      *tree,
   redisplay_mark_if_visible (mark);
 
   if (gtk_debug_flags & GTK_DEBUG_TEXT)
-    gtk_text_iter_check (&iter);
+    _gtk_text_iter_check (&iter);
 
   if (gtk_debug_flags & GTK_DEBUG_TEXT)
     _gtk_text_btree_check (tree);
@@ -2592,7 +2678,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;
@@ -2621,7 +2707,7 @@ _gtk_text_btree_place_cursor (GtkTextBTree      *tree,
 }
 
 void
-__gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
+_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
                                     const gchar *name)
 {
   GtkTextMark *mark;
@@ -2635,9 +2721,26 @@ __gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
   _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 (G_OBJECT (segment->body.mark.obj));
+}
+
 void
 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
-                            GtkTextMark *mark)
+                             GtkTextMark *mark)
 {
   GtkTextLineSegment *segment;
 
@@ -2654,34 +2757,27 @@ _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)
+                                GtkTextMark *segment)
 {
   return segment == tree->insert_mark;
 }
 
 gboolean
 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
-                                        GtkTextMark *segment)
+                                         GtkTextMark *segment)
 {
   return segment == tree->selection_bound_mark;
 }
 
 GtkTextMark*
 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
-                                 const gchar *name)
+                                  const gchar *name)
 {
   GtkTextLineSegment *seg;
 
@@ -2693,6 +2789,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)
@@ -2825,7 +2933,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);
     }
 }
 
@@ -3021,9 +3129,9 @@ 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)
+                             GtkTextBTree *tree,
+                             gint char_in_line,
+                             GtkTextTag *tag)
 {
   GtkTextLineSegment *toggle_seg;
 
@@ -3045,9 +3153,9 @@ _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)
+                             GtkTextBTree *tree,
+                             gint byte_in_line,
+                             GtkTextTag *tag)
 {
   GtkTextLineSegment *toggle_seg;
 
@@ -3069,11 +3177,96 @@ _gtk_text_line_byte_has_tag (GtkTextLine *line,
 
 gboolean
 _gtk_text_line_is_last (GtkTextLine *line,
-                       GtkTextBTree *tree)
+                        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)
+    {
+      int n_lines;
+      int real_line;
+
+      /* n_lines is without the magic line at the end */
+      n_lines = _gtk_text_btree_line_count (tree);
+      g_assert (n_lines >= 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)
 {
@@ -3109,6 +3302,23 @@ _gtk_text_line_next (GtkTextLine *line)
     }
 }
 
+GtkTextLine*
+_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)
 {
@@ -3220,7 +3430,7 @@ _gtk_text_line_remove_data (GtkTextLine *line,
 
 gpointer
 _gtk_text_line_get_data (GtkTextLine *line,
-                        gpointer view_id)
+                         gpointer view_id)
 {
   GtkTextLineData *iter;
 
@@ -3240,7 +3450,7 @@ _gtk_text_line_get_data (GtkTextLine *line,
 
 void
 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
-                               GtkTextLineData *ld)
+                                GtkTextLineData *ld)
 {
   /* For now this is totally unoptimized. FIXME?
 
@@ -3248,9 +3458,9 @@ _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);
 }
@@ -3532,13 +3742,13 @@ _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
+gboolean
 _gtk_text_line_byte_locate (GtkTextLine *line,
-                           gint byte_offset,
-                           GtkTextLineSegment **segment,
-                           GtkTextLineSegment **any_segment,
-                           gint *seg_byte_offset,
-                           gint *line_byte_offset)
+                            gint byte_offset,
+                            GtkTextLineSegment **segment,
+                            GtkTextLineSegment **any_segment,
+                            gint *seg_byte_offset,
+                            gint *line_byte_offset)
 {
   GtkTextLineSegment *seg;
   GtkTextLineSegment *after_prev_indexable;
@@ -3547,16 +3757,8 @@ _gtk_text_line_byte_locate (GtkTextLine *line,
   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;
@@ -3589,11 +3791,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
     {
@@ -3615,17 +3816,19 @@ _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
+gboolean
 _gtk_text_line_char_locate     (GtkTextLine     *line,
-                               gint              char_offset,
-                               GtkTextLineSegment **segment,
-                               GtkTextLineSegment **any_segment,
-                               gint             *seg_char_offset,
-                               gint             *line_char_offset)
+                                gint              char_offset,
+                                GtkTextLineSegment **segment,
+                                GtkTextLineSegment **any_segment,
+                                gint             *seg_char_offset,
+                                gint             *line_char_offset)
 {
   GtkTextLineSegment *seg;
   GtkTextLineSegment *after_prev_indexable;
@@ -3634,17 +3837,9 @@ _gtk_text_line_char_locate     (GtkTextLine     *line,
   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;
@@ -3675,12 +3870,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
     {
@@ -3702,10 +3896,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)
@@ -3751,7 +3947,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)
@@ -3901,9 +4097,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;
@@ -3919,17 +4115,17 @@ __gtk_text_line_next_could_contain_tag (GtkTextLine *line,
       /* 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)
@@ -4056,7 +4252,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)
 {
@@ -4281,7 +4477,7 @@ summary_list_destroy (Summary *summary)
 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;
@@ -4293,11 +4489,11 @@ get_last_line (GtkTextBTree *tree)
 
       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;
 }
 
 /*
@@ -4392,7 +4588,7 @@ static NodeData*
 node_data_new (gpointer view_id)
 {
   NodeData *nd;
-
+  
   nd = g_new (NodeData, 1);
 
   nd->view_id = view_id;
@@ -4407,7 +4603,6 @@ node_data_new (gpointer view_id)
 static void
 node_data_destroy (NodeData *nd)
 {
-
   g_free (nd);
 }
 
@@ -4987,7 +5182,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
@@ -4996,12 +5191,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);
@@ -5009,13 +5203,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);
   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);
     }
 }
@@ -5075,14 +5268,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);
         }
@@ -5099,6 +5285,16 @@ 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);
@@ -5118,14 +5314,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;
 }
@@ -5225,8 +5422,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
@@ -5272,10 +5469,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 */
 
@@ -5395,8 +5591,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;
             }
@@ -5500,8 +5697,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;
             }
 
@@ -5597,7 +5796,7 @@ gtk_text_btree_get_tag_info (GtkTextBTree *tree,
       info = g_new (GtkTextTagInfo, 1);
 
       info->tag = tag;
-      gtk_object_ref (GTK_OBJECT (tag));
+      g_object_ref (G_OBJECT (tag));
       info->tag_root = NULL;
       info->toggle_count = 0;
 
@@ -5633,7 +5832,7 @@ 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 (G_OBJECT (info->tag));
 
           g_free (info);
           return;
@@ -5641,9 +5840,6 @@ gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
 
       list = g_slist_next (list);
     }
-
-  g_assert_not_reached ();
-  return;
 }
 
 static void
@@ -5787,7 +5983,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.
@@ -6096,8 +6292,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)
@@ -6210,18 +6406,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)",
@@ -6232,7 +6455,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;
@@ -6263,7 +6487,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;
     }
 
@@ -6332,7 +6556,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)
             {
@@ -6568,7 +6792,7 @@ _gtk_text_btree_check (GtkTextBTree *tree)
    */
 
   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