]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbtree.c
Get widgets working on the btree/buffer side. Display of them still
[~andy/gtk] / gtk / gtktextbtree.c
1 /*
2  * gtktextbtree.c --
3  *
4  *      This file contains code that manages the B-tree representation
5  *      of text for the text buffer and implements character and
6  *      toggle segment types.
7  *
8  * Copyright (c) 1992-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  * Copyright (c) 2000      Red Hat, Inc.
11  * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
12  *
13  * This software is copyrighted by the Regents of the University of
14  * California, Sun Microsystems, Inc., and other parties.  The
15  * following terms apply to all files associated with the software
16  * unless explicitly disclaimed in individual files.
17  *
18  * The authors hereby grant permission to use, copy, modify,
19  * distribute, and license this software and its documentation for any
20  * purpose, provided that existing copyright notices are retained in
21  * all copies and that this notice is included verbatim in any
22  * distributions. No written agreement, license, or royalty fee is
23  * required for any of the authorized uses.  Modifications to this
24  * software may be copyrighted by their authors and need not follow
25  * the licensing terms described here, provided that the new terms are
26  * clearly indicated on the first page of each file where they apply.
27  *
28  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32  * OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
40  *
41  * GOVERNMENT USE: If you are acquiring this software on behalf of the
42  * U.S. government, the Government shall have only "Restricted Rights"
43  * in the software and related documentation as defined in the Federal
44  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
45  * are acquiring the software on behalf of the Department of Defense,
46  * the software shall be classified as "Commercial Computer Software"
47  * and the Government shall have only "Restricted Rights" as defined
48  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
49  * foregoing, the authors grant the U.S. Government and others acting
50  * in its behalf permission to use and distribute the software in
51  * accordance with the terms specified in this license.
52  *
53  */
54
55 #include "gtktextbtree.h"
56 #include <string.h>
57 #include <ctype.h>
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include "gtksignal.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
65 #include "gtkdebug.h"
66 #include "gtktextmarkprivate.h"
67
68 /*
69  * Types
70  */
71
72
73 /*
74  * The structure below is used to pass information between
75  * gtk_text_btree_get_tags and inc_count:
76  */
77
78 typedef struct TagInfo {
79   int numTags;                  /* Number of tags for which there
80                                  * is currently information in
81                                  * tags and counts. */
82   int arraySize;                        /* Number of entries allocated for
83                                          * tags and counts. */
84   GtkTextTag **tags;           /* Array of tags seen so far.
85                                 * Malloc-ed. */
86   int *counts;                  /* Toggle count (so far) for each
87                                  * entry in tags.  Malloc-ed. */
88 } TagInfo;
89
90
91 /*
92  * This is used to store per-view width/height info at the tree nodes.
93  */
94
95 typedef struct _NodeData NodeData;
96
97 struct _NodeData {
98   gpointer view_id;
99   NodeData *next;
100
101   /* Height and width of this node */
102   gint height;
103   gint width : 24;
104
105   /* boolean indicating whether the height/width need to be recomputed */
106   gint valid : 8;
107 };
108
109
110 /*
111  * The data structure below keeps summary information about one tag as part
112  * of the tag information in a node.
113  */
114
115 typedef struct Summary {
116   GtkTextTagInfo *info;                     /* Handle for tag. */
117   int toggle_count;                     /* Number of transitions into or
118                                          * out of this tag that occur in
119                                          * the subtree rooted at this node. */
120   struct Summary *next;         /* Next in list of all tags for same
121                                  * node, or NULL if at end of list. */
122 } Summary;
123
124 /*
125  * The data structure below defines a node in the B-tree.
126  */
127
128 struct _GtkTextBTreeNode {
129   GtkTextBTreeNode *parent;         /* Pointer to parent node, or NULL if
130                                      * this is the root. */
131   GtkTextBTreeNode *next;           /* Next in list of siblings with the
132                                      * same parent node, or NULL for end
133                                      * of list. */
134   Summary *summary;             /* First in malloc-ed list of info
135                                  * about tags in this subtree (NULL if
136                                  * no tag info in the subtree). */
137   int level;                            /* Level of this node in the B-tree.
138                                          * 0 refers to the bottom of the tree
139                                          * (children are lines, not nodes). */
140   union {                               /* First in linked list of children. */
141     struct _GtkTextBTreeNode *node;         /* Used if level > 0. */
142     GtkTextLine *line;         /* Used if level == 0. */
143   } children;
144   int num_children;                     /* Number of children of this node. */
145   int num_lines;                        /* Total number of lines (leaves) in
146                                          * the subtree rooted here. */
147   int num_chars;                        /* Number of chars below here */
148
149   NodeData *node_data;
150 };
151
152
153 /*
154  * Used to store the list of views in our btree
155  */
156
157 typedef struct _BTreeView BTreeView;
158
159 struct _BTreeView {
160   gpointer view_id;
161   GtkTextLayout *layout;
162   BTreeView *next;
163   BTreeView *prev;
164 };
165
166 /*
167  * And the tree itself
168  */
169
170 struct _GtkTextBTree {
171   GtkTextBTreeNode *root_node;          /* Pointer to root of B-tree. */
172   GtkTextTagTable *table;
173   GHashTable *mark_table;
174   guint refcount;
175   GtkTextMark *insert_mark;
176   GtkTextMark *selection_bound_mark;
177   GtkTextBuffer *buffer;
178   BTreeView *views;
179   GSList *tag_infos;
180   guint tag_changed_handler;
181   guint tag_removed_handler;
182   /* Incremented when a segment with a byte size > 0
183      is added to or removed from the tree (i.e. the
184      length of a line may have changed, and lines may
185      have been added or removed). This invalidates
186      all outstanding iterators.
187   */
188   guint chars_changed_stamp;
189   /* Incremented when any segments are added or deleted;
190      this makes outstanding iterators recalculate their
191      pointed-to segment and segment offset.
192   */
193   guint segments_changed_stamp;
194
195   GtkTextLine *end_iter_line;
196
197   guint end_iter_line_stamp;
198
199   GHashTable *child_anchor_table;
200 };
201
202
203 /*
204  * Upper and lower bounds on how many children a node may have:
205  * rebalance when either of these limits is exceeded.  MAX_CHILDREN
206  * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
207  */
208
209 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
210    shallow. It appears to be faster to locate a particular line number
211    if the tree is narrow and deep, since it is more finely sorted.  I
212    guess this may increase memory use though, and make it slower to
213    walk the tree in order, or locate a particular byte index (which
214    is done by walking the tree in order).
215
216    There's basically a tradeoff here. However I'm thinking we want to
217    add pixels, byte counts, and char counts to the tree nodes,
218    at that point narrow and deep should speed up all operations,
219    not just the line number searches.
220 */
221
222 #if 1
223 #define MAX_CHILDREN 12
224 #define MIN_CHILDREN 6
225 #else
226 #define MAX_CHILDREN 6
227 #define MIN_CHILDREN 3
228 #endif
229
230 /*
231  * Prototypes
232  */
233
234 static BTreeView        *gtk_text_btree_get_view                 (GtkTextBTree     *tree,
235                                                                   gpointer          view_id);
236 static void              gtk_text_btree_rebalance                (GtkTextBTree     *tree,
237                                                                   GtkTextBTreeNode *node);
238 static GtkTextLine     * get_last_line                           (GtkTextBTree     *tree);
239 static void              post_insert_fixup                       (GtkTextBTree     *tree,
240                                                                   GtkTextLine      *insert_line,
241                                                                   gint              char_count_delta,
242                                                                   gint              line_count_delta);
243 static void              gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
244                                                                   GtkTextTagInfo   *info,
245                                                                   gint              adjust);
246 static gboolean          gtk_text_btree_node_has_tag             (GtkTextBTreeNode *node,
247                                                                   GtkTextTag       *tag);
248
249 static void             segments_changed                (GtkTextBTree     *tree);
250 static void             chars_changed                   (GtkTextBTree     *tree);
251 static void             summary_list_destroy            (Summary          *summary);
252 static GtkTextLine     *gtk_text_line_new               (void);
253 static void             gtk_text_line_destroy           (GtkTextBTree     *tree,
254                                                          GtkTextLine      *line);
255 static void             gtk_text_line_set_parent        (GtkTextLine      *line,
256                                                          GtkTextBTreeNode *node);
257 static void             gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
258                                                          gpointer          view_id);
259
260
261 static NodeData         *node_data_new          (gpointer  view_id);
262 static void              node_data_destroy      (NodeData *nd);
263 static void              node_data_list_destroy (NodeData *nd);
264 static NodeData         *node_data_find         (NodeData *nd,
265                                                  gpointer  view_id);
266
267 static GtkTextBTreeNode     *gtk_text_btree_node_new                  (void);
268 static void                  gtk_text_btree_node_invalidate_downward  (GtkTextBTreeNode *node);
269 static void                  gtk_text_btree_node_invalidate_upward    (GtkTextBTreeNode *node,
270                                                                        gpointer          view_id);
271 static NodeData *            gtk_text_btree_node_check_valid          (GtkTextBTreeNode *node,
272                                                                        gpointer          view_id);
273 static NodeData *            gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
274                                                                        gpointer          view_id);
275 static void                  gtk_text_btree_node_check_valid_upward   (GtkTextBTreeNode *node,
276                                                                        gpointer          view_id);
277
278 static void                  gtk_text_btree_node_remove_view         (BTreeView        *view,
279                                                                       GtkTextBTreeNode *node,
280                                                                       gpointer          view_id);
281 static void                  gtk_text_btree_node_destroy             (GtkTextBTree     *tree,
282                                                                       GtkTextBTreeNode *node);
283 static NodeData         *    gtk_text_btree_node_ensure_data         (GtkTextBTreeNode *node,
284                                                                       gpointer          view_id);
285 static void                  gtk_text_btree_node_remove_data         (GtkTextBTreeNode *node,
286                                                                       gpointer          view_id);
287 static void                  gtk_text_btree_node_get_size            (GtkTextBTreeNode *node,
288                                                                       gpointer          view_id,
289                                                                       gint             *width,
290                                                                       gint             *height);
291 static GtkTextBTreeNode *    gtk_text_btree_node_common_parent       (GtkTextBTreeNode *node1,
292                                                                       GtkTextBTreeNode *node2);
293 static void get_tree_bounds       (GtkTextBTree     *tree,
294                                    GtkTextIter      *start,
295                                    GtkTextIter      *end);
296 static void tag_changed_cb        (GtkTextTagTable  *table,
297                                    GtkTextTag       *tag,
298                                    gboolean          size_changed,
299                                    GtkTextBTree     *tree);
300 static void tag_removed_cb        (GtkTextTagTable  *table,
301                                    GtkTextTag       *tag,
302                                    GtkTextBTree     *tree);
303 static void cleanup_line          (GtkTextLine      *line);
304 static void recompute_node_counts (GtkTextBTree     *tree,
305                                    GtkTextBTreeNode *node);
306 static void inc_count             (GtkTextTag       *tag,
307                                    int               inc,
308                                    TagInfo          *tagInfoPtr);
309
310 static void summary_destroy       (Summary          *summary);
311
312 static void gtk_text_btree_link_segment   (GtkTextLineSegment *seg,
313                                            const GtkTextIter  *iter);
314 static void gtk_text_btree_unlink_segment (GtkTextBTree       *tree,
315                                            GtkTextLineSegment *seg,
316                                            GtkTextLine        *line);
317
318
319 static GtkTextTagInfo *gtk_text_btree_get_tag_info          (GtkTextBTree   *tree,
320                                                              GtkTextTag     *tag);
321 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree   *tree,
322                                                              GtkTextTag     *tag);
323 static void            gtk_text_btree_remove_tag_info       (GtkTextBTree   *tree,
324                                                              GtkTextTag     *tag);
325
326 static void redisplay_region (GtkTextBTree      *tree,
327                               const GtkTextIter *start,
328                               const GtkTextIter *end);
329
330 /* Inline thingies */
331
332 static inline void
333 segments_changed (GtkTextBTree *tree)
334 {
335   tree->segments_changed_stamp += 1;
336 }
337
338 static inline void
339 chars_changed (GtkTextBTree *tree)
340 {
341   tree->chars_changed_stamp += 1;
342 }
343
344 /*
345  * BTree operations
346  */
347
348 GtkTextBTree*
349 gtk_text_btree_new (GtkTextTagTable *table,
350                     GtkTextBuffer *buffer)
351 {
352   GtkTextBTree *tree;
353   GtkTextBTreeNode *root_node;
354   GtkTextLine *line, *line2;
355
356   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
357   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
358
359   /*
360    * The tree will initially have two empty lines.  The second line
361    * isn't actually part of the tree's contents, but its presence
362    * makes several operations easier.  The tree will have one GtkTextBTreeNode,
363    * which is also the root of the tree.
364    */
365
366   /* Create the root node. */
367
368   root_node = gtk_text_btree_node_new ();
369
370   line = gtk_text_line_new ();
371   line2 = gtk_text_line_new ();
372
373   root_node->parent = NULL;
374   root_node->next = NULL;
375   root_node->summary = NULL;
376   root_node->level = 0;
377   root_node->children.line = line;
378   root_node->num_children = 2;
379   root_node->num_lines = 2;
380   root_node->num_chars = 2;
381
382   line->parent = root_node;
383   line->next = line2;
384
385   line->segments = _gtk_char_segment_new ("\n", 1);
386
387   line2->parent = root_node;
388   line2->next = NULL;
389   line2->segments = _gtk_char_segment_new ("\n", 1);
390
391   /* Create the tree itself */
392
393   tree = g_new0(GtkTextBTree, 1);
394   tree->root_node = root_node;
395   tree->table = table;
396   tree->views = NULL;
397
398   /* Set these to values that are unlikely to be found
399    * in random memory garbage, and also avoid
400    * duplicates between tree instances.
401    */
402   tree->chars_changed_stamp = g_random_int ();
403   tree->segments_changed_stamp = g_random_int ();
404
405   tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
406   tree->end_iter_line = NULL;
407
408   gtk_object_ref (GTK_OBJECT (tree->table));
409   gtk_object_sink (GTK_OBJECT (tree->table));
410
411   tree->tag_changed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
412                                                   "tag_changed",
413                                                   GTK_SIGNAL_FUNC (tag_changed_cb),
414                                                   tree);
415
416   tree->tag_removed_handler = gtk_signal_connect (GTK_OBJECT (tree->table),
417                                                   "tag_removed",
418                                                   GTK_SIGNAL_FUNC (tag_removed_cb),
419                                                   tree);
420
421   tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
422   tree->child_anchor_table = NULL;
423   
424   /* We don't ref the buffer, since the buffer owns us;
425    * we'd have some circularity issues. The buffer always
426    * lasts longer than the BTree
427    */
428   tree->buffer = buffer;
429
430   {
431     GtkTextIter start;
432     GtkTextLineSegment *seg;
433
434     gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
435
436
437     tree->insert_mark = gtk_text_btree_set_mark (tree,
438                                                  NULL,
439                                                  "insert",
440                                                  FALSE,
441                                                  &start,
442                                                  FALSE);
443
444     seg = tree->insert_mark->segment;
445
446     seg->body.mark.not_deleteable = TRUE;
447     seg->body.mark.visible = TRUE;
448
449     tree->selection_bound_mark = gtk_text_btree_set_mark (tree,
450                                                           NULL,
451                                                           "selection_bound",
452                                                           FALSE,
453                                                           &start,
454                                                           FALSE);
455
456     seg = tree->selection_bound_mark->segment;
457
458     seg->body.mark.not_deleteable = TRUE;
459
460     g_object_ref (G_OBJECT (tree->insert_mark));
461     g_object_ref (G_OBJECT (tree->selection_bound_mark));
462   }
463
464   tree->refcount = 1;
465
466   return tree;
467 }
468
469 void
470 gtk_text_btree_ref (GtkTextBTree *tree)
471 {
472   g_return_if_fail (tree != NULL);
473   g_return_if_fail (tree->refcount > 0);
474
475   tree->refcount += 1;
476 }
477
478 static void
479 mark_destroy_foreach (gpointer key, gpointer value, gpointer user_data)
480 {
481   GtkTextLineSegment *seg = value;
482
483   g_return_if_fail (seg->body.mark.tree == NULL);
484
485   g_object_unref (G_OBJECT (seg->body.mark.obj));
486 }
487
488 void
489 gtk_text_btree_unref (GtkTextBTree *tree)
490 {
491   g_return_if_fail (tree != NULL);
492   g_return_if_fail (tree->refcount > 0);
493
494   tree->refcount -= 1;
495
496   if (tree->refcount == 0)
497     {
498       gtk_text_btree_node_destroy (tree, tree->root_node);
499
500       g_hash_table_foreach (tree->mark_table,
501                             mark_destroy_foreach,
502                             NULL);
503       g_hash_table_destroy (tree->mark_table);
504
505       g_object_unref (G_OBJECT (tree->insert_mark));
506       g_object_unref (G_OBJECT (tree->selection_bound_mark));
507
508       gtk_signal_disconnect (GTK_OBJECT (tree->table),
509                              tree->tag_changed_handler);
510
511       gtk_signal_disconnect (GTK_OBJECT (tree->table),
512                              tree->tag_removed_handler);
513
514       gtk_object_unref (GTK_OBJECT (tree->table));
515
516       g_free (tree);
517     }
518 }
519
520 GtkTextBuffer*
521 gtk_text_btree_get_buffer (GtkTextBTree *tree)
522 {
523   return tree->buffer;
524 }
525
526 guint
527 gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
528 {
529   return tree->chars_changed_stamp;
530 }
531
532 guint
533 gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
534 {
535   return tree->segments_changed_stamp;
536 }
537
538 void
539 gtk_text_btree_segments_changed (GtkTextBTree *tree)
540 {
541   g_return_if_fail (tree != NULL);
542   segments_changed (tree);
543 }
544
545 /*
546  * Indexable segment mutation
547  */
548
549 void
550 gtk_text_btree_delete (GtkTextIter *start,
551                        GtkTextIter *end)
552 {
553   GtkTextLineSegment *prev_seg;             /* The segment just before the start
554                                              * of the deletion range. */
555   GtkTextLineSegment *last_seg;             /* The segment just after the end
556                                              * of the deletion range. */
557   GtkTextLineSegment *seg, *next;
558   GtkTextLine *curline;
559   GtkTextBTreeNode *curnode, *node;
560   GtkTextBTree *tree;
561   GtkTextLine *start_line;
562   GtkTextLine *end_line;
563   GtkTextLine *deleted_lines = NULL;        /* List of lines we've deleted */
564   gint start_byte_offset;
565
566   g_return_if_fail (start != NULL);
567   g_return_if_fail (end != NULL);
568   g_return_if_fail (gtk_text_iter_get_btree (start) ==
569                     gtk_text_iter_get_btree (end));
570
571   gtk_text_iter_reorder (start, end);
572
573   tree = gtk_text_iter_get_btree (start);
574
575   {
576     /*
577      * The code below is ugly, but it's needed to make sure there
578      * is always a dummy empty line at the end of the text.  If the
579      * final newline of the file (just before the dummy line) is being
580      * deleted, then back up index to just before the newline.  If
581      * there is a newline just before the first character being deleted,
582      * then back up the first index too, so that an even number of lines
583      * gets deleted.  Furthermore, remove any tags that are present on
584      * the newline that isn't going to be deleted after all (this simulates
585      * deleting the newline and then adding a "clean" one back again).
586      */
587
588     gint line1;
589     gint line2;
590
591     line1 = gtk_text_iter_get_line (start);
592     line2 = gtk_text_iter_get_line (end);
593
594     if (line2 == gtk_text_btree_line_count (tree))
595       {
596         GtkTextTag** tags;
597         int array_size;
598         GtkTextIter orig_end;
599
600         orig_end = *end;
601         gtk_text_iter_prev_char (end);
602
603         --line2;
604
605         if (gtk_text_iter_get_line_offset (start) == 0 &&
606             line1 != 0)
607           {
608             gtk_text_iter_prev_char (start);
609             --line1;
610           }
611
612         tags = gtk_text_btree_get_tags (end,
613                                         &array_size);
614
615         if (tags != NULL)
616           {
617             int i;
618
619             i = 0;
620             while (i < array_size)
621               {
622                 gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
623
624                 ++i;
625               }
626
627             g_free (tags);
628           }
629       }
630   }
631
632   /* Broadcast the need for redisplay before we break the iterators */
633   gtk_text_btree_invalidate_region (tree, start, end);
634
635   /* Save the byte offset so we can reset the iterators */
636   start_byte_offset = gtk_text_iter_get_line_index (start);
637
638   start_line = gtk_text_iter_get_text_line (start);
639   end_line = gtk_text_iter_get_text_line (end);
640
641   /*
642    * Split the start and end segments, so we have a place
643    * to insert our new text.
644    *
645    * Tricky point:  split at end first;  otherwise the split
646    * at end may invalidate seg and/or prev_seg. This allows
647    * us to avoid invalidating segments for start.
648    */
649
650   last_seg = gtk_text_line_segment_split (end);
651   if (last_seg != NULL)
652     last_seg = last_seg->next;
653   else
654     last_seg = end_line->segments;
655
656   prev_seg = gtk_text_line_segment_split (start);
657   if (prev_seg != NULL)
658     {
659       seg = prev_seg->next;
660       prev_seg->next = last_seg;
661     }
662   else
663     {
664       seg = start_line->segments;
665       start_line->segments = last_seg;
666     }
667
668   /* notify iterators that their segments need recomputation,
669      just for robustness. */
670   segments_changed (tree);
671
672   /*
673    * Delete all of the segments between prev_seg and last_seg.
674    */
675
676   curline = start_line;
677   curnode = curline->parent;
678   while (seg != last_seg)
679     {
680       gint char_count = 0;
681
682       if (seg == NULL)
683         {
684           GtkTextLine *nextline;
685
686           /*
687            * We just ran off the end of a line.  First find the
688            * next line, then go back to the old line and delete it
689            * (unless it's the starting line for the range).
690            */
691
692           nextline = gtk_text_line_next (curline);
693           if (curline != start_line)
694             {
695               if (curnode == start_line->parent)
696                 start_line->next = curline->next;
697               else
698                 curnode->children.line = curline->next;
699
700               for (node = curnode; node != NULL;
701                    node = node->parent)
702                 {
703                   node->num_lines -= 1;
704                 }
705
706               curnode->num_children -= 1;
707               curline->next = deleted_lines;
708               deleted_lines = curline;
709             }
710
711           curline = nextline;
712           seg = curline->segments;
713
714           /*
715            * If the GtkTextBTreeNode is empty then delete it and its parents,
716            * recursively upwards until a non-empty GtkTextBTreeNode is found.
717            */
718
719           while (curnode->num_children == 0)
720             {
721               GtkTextBTreeNode *parent;
722
723               parent = curnode->parent;
724               if (parent->children.node == curnode)
725                 {
726                   parent->children.node = curnode->next;
727                 }
728               else
729                 {
730                   GtkTextBTreeNode *prevnode = parent->children.node;
731                   while (prevnode->next != curnode)
732                     {
733                       prevnode = prevnode->next;
734                     }
735                   prevnode->next = curnode->next;
736                 }
737               parent->num_children--;
738               g_free (curnode);
739               curnode = parent;
740             }
741           curnode = curline->parent;
742           continue;
743         }
744
745       next = seg->next;
746       char_count = seg->char_count;
747
748       if ((*seg->type->deleteFunc)(seg, curline, 0) != 0)
749         {
750           /*
751            * This segment refuses to die.  Move it to prev_seg and
752            * advance prev_seg if the segment has left gravity.
753            */
754
755           if (prev_seg == NULL)
756             {
757               seg->next = start_line->segments;
758               start_line->segments = seg;
759             }
760           else
761             {
762               seg->next = prev_seg->next;
763               prev_seg->next = seg;
764             }
765           if (seg->type->leftGravity)
766             {
767               prev_seg = seg;
768             }
769         }
770       else
771         {
772           /* Segment is gone. Decrement the char count of the node and
773              all its parents. */
774           for (node = curnode; node != NULL;
775                node = node->parent)
776             {
777               node->num_chars -= char_count;
778             }
779         }
780
781       seg = next;
782     }
783
784   /*
785    * If the beginning and end of the deletion range are in different
786    * lines, join the two lines together and discard the ending line.
787    */
788
789   if (start_line != end_line)
790     {
791       BTreeView *view;
792       GtkTextBTreeNode *ancestor_node;
793
794       GtkTextLine *prevline;
795
796       for (seg = last_seg; seg != NULL;
797            seg = seg->next)
798         {
799           if (seg->type->lineChangeFunc != NULL)
800             {
801               (*seg->type->lineChangeFunc)(seg, end_line);
802             }
803         }
804       curnode = end_line->parent;
805       for (node = curnode; node != NULL;
806            node = node->parent)
807         {
808           node->num_lines--;
809         }
810       curnode->num_children--;
811       prevline = curnode->children.line;
812       if (prevline == end_line)
813         {
814           curnode->children.line = end_line->next;
815         }
816       else
817         {
818           while (prevline->next != end_line)
819             {
820               prevline = prevline->next;
821             }
822           prevline->next = end_line->next;
823         }
824       end_line->next = deleted_lines;
825       deleted_lines = end_line;
826
827       /* We now fix up the per-view aggregates. We add all the height and
828        * width for the deleted lines to the start line, so that when revalidation
829        * occurs, the correct change in size is seen.
830        */
831       ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
832       view = tree->views;
833       while (view)
834         {
835           GtkTextLine *line;
836           GtkTextLineData *ld;
837
838           gint deleted_width = 0;
839           gint deleted_height = 0;
840
841           line = deleted_lines;
842           while (line)
843             {
844               GtkTextLine *next_line = line->next;
845               ld = gtk_text_line_get_data (line, view->view_id);
846
847               if (ld)
848                 {
849                   deleted_width = MAX (deleted_width, ld->width);
850                   deleted_height += ld->height;
851                 }
852
853               if (!view->next)
854                 gtk_text_line_destroy (tree, line);
855
856               line = next_line;
857             }
858
859           if (deleted_width > 0 || deleted_height > 0)
860             {
861               ld = gtk_text_line_get_data (start_line, view->view_id);
862
863               /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
864                * no way to add ld without also validating the node, which would
865                * be improper at this point.
866                */
867               /* This assertion does actually fail sometimes, must
868                  fix before stable release -hp */
869               g_assert (ld);
870
871               ld->width = MAX (deleted_width, ld->width);
872               ld->height += deleted_height;
873               ld->valid = FALSE;
874             }
875
876           gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
877           if (ancestor_node->parent)
878             gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
879
880           view = view->next;
881         }
882
883       gtk_text_btree_rebalance (tree, curnode);
884     }
885
886   /*
887    * Cleanup the segments in the new line.
888    */
889
890   cleanup_line (start_line);
891
892   /*
893    * Lastly, rebalance the first GtkTextBTreeNode of the range.
894    */
895
896   gtk_text_btree_rebalance (tree, start_line->parent);
897
898   /* Notify outstanding iterators that they
899      are now hosed */
900   chars_changed (tree);
901   segments_changed (tree);
902
903   if (gtk_debug_flags & GTK_DEBUG_TEXT)
904     gtk_text_btree_check (tree);
905
906   /* Re-initialize our iterators */
907   gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
908   *end = *start;
909 }
910
911 void
912 gtk_text_btree_insert (GtkTextIter *iter,
913                        const gchar *text,
914                        gint len)
915 {
916   GtkTextLineSegment *prev_seg;     /* The segment just before the first
917                                      * new segment (NULL means new segment
918                                      * is at beginning of line). */
919   GtkTextLineSegment *cur_seg;              /* Current segment;  new characters
920                                              * are inserted just after this one.
921                                              * NULL means insert at beginning of
922                                              * line. */
923   GtkTextLine *line;           /* Current line (new segments are
924                                 * added to this line). */
925   GtkTextLineSegment *seg;
926   GtkTextLine *newline;
927   int chunkSize;                        /* # characters in current chunk. */
928   guint sol; /* start of line */
929   guint eol;                      /* Pointer to character just after last
930                                    * one in current chunk. */
931   int line_count_delta;                /* Counts change to total number of
932                                         * lines in file. */
933
934   int char_count_delta;                /* change to number of chars */
935   GtkTextBTree *tree;
936   gint start_byte_index;
937   GtkTextLine *start_line;
938
939   g_return_if_fail (text != NULL);
940   g_return_if_fail (iter != NULL);
941
942   if (len < 0)
943     len = strlen (text);
944
945   /* extract iterator info */
946   tree = gtk_text_iter_get_btree (iter);
947   line = gtk_text_iter_get_text_line (iter);
948   start_line = line;
949   start_byte_index = gtk_text_iter_get_line_index (iter);
950
951   /* Get our insertion segment split */
952   prev_seg = gtk_text_line_segment_split (iter);
953   cur_seg = prev_seg;
954
955   /* Invalidate all iterators */
956   chars_changed (tree);
957   segments_changed (tree);
958
959   /*
960    * Chop the text up into lines and create a new segment for
961    * each line, plus a new line for the leftovers from the
962    * previous line.
963    */
964
965   eol = 0;
966   sol = 0;
967   line_count_delta = 0;
968   char_count_delta = 0;
969   while (eol < len)
970     {
971       for (; eol < len; eol++)
972         {
973           if (text[eol] == '\n')
974             {
975               eol++;
976               break;
977             }
978         }
979       chunkSize = eol - sol;
980
981       seg = _gtk_char_segment_new (&text[sol], chunkSize);
982
983       char_count_delta += seg->char_count;
984
985       if (cur_seg == NULL)
986         {
987           seg->next = line->segments;
988           line->segments = seg;
989         }
990       else
991         {
992           seg->next = cur_seg->next;
993           cur_seg->next = seg;
994         }
995
996       if (text[eol-1] != '\n')
997         {
998           break;
999         }
1000
1001       /*
1002        * The chunk ended with a newline, so create a new GtkTextLine
1003        * and move the remainder of the old line to it.
1004        */
1005
1006       newline = gtk_text_line_new ();
1007       gtk_text_line_set_parent (newline, line->parent);
1008       newline->next = line->next;
1009       line->next = newline;
1010       newline->segments = seg->next;
1011       seg->next = NULL;
1012       line = newline;
1013       cur_seg = NULL;
1014       line_count_delta++;
1015
1016       sol = eol;
1017     }
1018
1019   /*
1020    * Cleanup the starting line for the insertion, plus the ending
1021    * line if it's different.
1022    */
1023
1024   cleanup_line (start_line);
1025   if (line != start_line)
1026     {
1027       cleanup_line (line);
1028     }
1029
1030   post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1031
1032   /* Invalidate our region, and reset the iterator the user
1033      passed in to point to the end of the inserted text. */
1034   {
1035     GtkTextIter start;
1036     GtkTextIter end;
1037
1038
1039     gtk_text_btree_get_iter_at_line (tree,
1040                                      &start,
1041                                      start_line,
1042                                      start_byte_index);
1043     end = start;
1044
1045     /* We could almost certainly be more efficient here
1046        by saving the information from the insertion loop
1047        above. FIXME */
1048     gtk_text_iter_forward_chars (&end, char_count_delta);
1049
1050     gtk_text_btree_invalidate_region (tree,
1051                                       &start, &end);
1052
1053
1054     /* Convenience for the user */
1055     *iter = end;
1056   }
1057 }
1058
1059 static void
1060 insert_pixbuf_or_widget_segment (GtkTextIter        *iter,
1061                                  GtkTextLineSegment *seg)
1062
1063 {
1064   GtkTextIter start;
1065   GtkTextLineSegment *prevPtr;
1066   GtkTextLine *line;
1067   GtkTextBTree *tree;
1068   gint start_byte_offset;
1069
1070   line = gtk_text_iter_get_text_line (iter);
1071   tree = gtk_text_iter_get_btree (iter);
1072   start_byte_offset = gtk_text_iter_get_line_index (iter);
1073
1074   prevPtr = gtk_text_line_segment_split (iter);
1075   if (prevPtr == NULL)
1076     {
1077       seg->next = line->segments;
1078       line->segments = seg;
1079     }
1080   else
1081     {
1082       seg->next = prevPtr->next;
1083       prevPtr->next = seg;
1084     }
1085
1086   post_insert_fixup (tree, line, 0, seg->char_count);
1087
1088   chars_changed (tree);
1089   segments_changed (tree);
1090
1091   /* reset *iter for the user, and invalidate tree nodes */
1092
1093   gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1094
1095   *iter = start;
1096   gtk_text_iter_next_char (iter); /* skip forward past the segment */
1097
1098   gtk_text_btree_invalidate_region (tree, &start, iter);
1099 }
1100      
1101 void
1102 gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1103                               GdkPixbuf   *pixbuf)
1104 {
1105   GtkTextLineSegment *seg;
1106   
1107   seg = _gtk_pixbuf_segment_new (pixbuf);
1108
1109   insert_pixbuf_or_widget_segment (iter, seg);
1110 }
1111
1112 GtkTextChildAnchor*
1113 gtk_text_btree_create_child_anchor (GtkTextIter *iter)
1114 {
1115   GtkTextLineSegment *seg;
1116   GtkTextBTree *tree;
1117   
1118   seg = _gtk_widget_segment_new ();
1119
1120   insert_pixbuf_or_widget_segment (iter, seg);
1121
1122   tree = seg->body.child.tree;
1123
1124   if (tree->child_anchor_table == NULL)
1125     tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1126
1127   g_hash_table_insert (tree->child_anchor_table,
1128                        seg->body.child.obj,
1129                        seg->body.child.obj);
1130   
1131   return seg->body.child.obj;
1132 }
1133
1134 void
1135 gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1136 {
1137   GtkTextLineSegment *seg;
1138
1139   seg = anchor->segment;
1140   
1141   g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1142                        anchor);
1143 }
1144
1145 /*
1146  * View stuff
1147  */
1148
1149 static GtkTextLine*
1150 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1151                 GtkTextBTreeNode *node, gint y, gint *line_top,
1152                 GtkTextLine *last_line)
1153 {
1154   gint current_y = 0;
1155
1156   if (gtk_debug_flags & GTK_DEBUG_TEXT)
1157     gtk_text_btree_check (tree);
1158
1159   if (node->level == 0)
1160     {
1161       GtkTextLine *line;
1162
1163       line = node->children.line;
1164
1165       while (line != NULL && line != last_line)
1166         {
1167           GtkTextLineData *ld;
1168
1169           ld = gtk_text_line_get_data (line, view->view_id);
1170
1171           if (ld)
1172             {
1173               if (y < (current_y + (ld ? ld->height : 0)))
1174                 return line;
1175
1176               current_y += ld->height;
1177               *line_top += ld->height;
1178             }
1179
1180           line = line->next;
1181         }
1182       return NULL;
1183     }
1184   else
1185     {
1186       GtkTextBTreeNode *child;
1187
1188       child = node->children.node;
1189
1190       while (child != NULL)
1191         {
1192           gint width;
1193           gint height;
1194
1195           gtk_text_btree_node_get_size (child, view->view_id,
1196                                         &width, &height);
1197
1198           if (y < (current_y + height))
1199             return find_line_by_y (tree, view, child,
1200                                    y - current_y, line_top,
1201                                    last_line);
1202
1203           current_y += height;
1204           *line_top += height;
1205
1206           child = child->next;
1207         }
1208
1209       return NULL;
1210     }
1211 }
1212
1213 GtkTextLine *
1214 gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1215                                gpointer      view_id,
1216                                gint          ypixel,
1217                                gint         *line_top_out)
1218 {
1219   GtkTextLine *line;
1220   BTreeView *view;
1221   GtkTextLine *last_line;
1222   gint line_top = 0;
1223
1224   view = gtk_text_btree_get_view (tree, view_id);
1225   g_return_val_if_fail (view != NULL, NULL);
1226
1227   last_line = get_last_line (tree);
1228
1229   line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1230                          last_line);
1231
1232   if (line_top_out)
1233     *line_top_out = line_top;
1234
1235   return line;
1236 }
1237
1238 static gint
1239 find_line_top_in_line_list (GtkTextBTree *tree,
1240                             BTreeView *view,
1241                             GtkTextLine *line,
1242                             GtkTextLine *target_line,
1243                             gint y)
1244 {
1245   while (line != NULL)
1246     {
1247       GtkTextLineData *ld;
1248
1249       if (line == target_line)
1250         return y;
1251
1252       ld = gtk_text_line_get_data (line, view->view_id);
1253       if (ld)
1254         y += ld->height;
1255
1256       line = line->next;
1257     }
1258
1259   g_assert_not_reached (); /* If we get here, our
1260                               target line didn't exist
1261                               under its parent node */
1262   return 0;
1263 }
1264
1265 gint
1266 gtk_text_btree_find_line_top (GtkTextBTree *tree,
1267                               GtkTextLine *target_line,
1268                               gpointer view_id)
1269 {
1270   gint y = 0;
1271   BTreeView *view;
1272   GSList *nodes;
1273   GSList *iter;
1274   GtkTextBTreeNode *node;
1275
1276   view = gtk_text_btree_get_view (tree, view_id);
1277
1278   g_return_val_if_fail (view != NULL, 0);
1279
1280   nodes = NULL;
1281   node = target_line->parent;
1282   while (node != NULL)
1283     {
1284       nodes = g_slist_prepend (nodes, node);
1285       node = node->parent;
1286     }
1287
1288   iter = nodes;
1289   while (iter != NULL)
1290     {
1291       node = iter->data;
1292
1293       if (node->level == 0)
1294         {
1295           g_slist_free (nodes);
1296           return find_line_top_in_line_list (tree, view,
1297                                              node->children.line,
1298                                              target_line, y);
1299         }
1300       else
1301         {
1302           GtkTextBTreeNode *child;
1303           GtkTextBTreeNode *target_node;
1304
1305           g_assert (iter->next != NULL); /* not at level 0 */
1306           target_node = iter->next->data;
1307
1308           child = node->children.node;
1309
1310           while (child != NULL)
1311             {
1312               gint width;
1313               gint height;
1314
1315               if (child == target_node)
1316                 break;
1317               else
1318                 {
1319                   gtk_text_btree_node_get_size (child, view->view_id,
1320                                                 &width, &height);
1321                   y += height;
1322                 }
1323               child = child->next;
1324             }
1325           g_assert (child != NULL); /* should have broken out before we
1326                                        ran out of nodes */
1327         }
1328
1329       iter = g_slist_next (iter);
1330     }
1331
1332   g_assert_not_reached (); /* we return when we find the target line */
1333   return 0;
1334 }
1335
1336 void
1337 gtk_text_btree_add_view (GtkTextBTree *tree,
1338                          GtkTextLayout *layout)
1339 {
1340   BTreeView *view;
1341   GtkTextLine *last_line;
1342   GtkTextLineData *line_data;
1343
1344   g_return_if_fail (tree != NULL);
1345
1346   view = g_new (BTreeView, 1);
1347
1348   view->view_id = layout;
1349   view->layout = layout;
1350
1351   view->next = tree->views;
1352   view->prev = NULL;
1353
1354   tree->views = view;
1355
1356   /* The last line in the buffer has identity values for the per-view
1357    * data so that we can avoid special case checks for it in a large
1358    * number of loops
1359    */
1360   last_line = get_last_line (tree);
1361
1362   line_data = g_new (GtkTextLineData, 1);
1363   line_data->view_id = layout;
1364   line_data->next = NULL;
1365   line_data->width = 0;
1366   line_data->height = 0;
1367   line_data->valid = TRUE;
1368
1369   gtk_text_line_add_data (last_line, line_data);
1370 }
1371
1372 void
1373 gtk_text_btree_remove_view (GtkTextBTree *tree,
1374                             gpointer view_id)
1375 {
1376   BTreeView *view;
1377   GtkTextLine *last_line;
1378   GtkTextLineData *line_data;
1379
1380   g_return_if_fail (tree != NULL);
1381
1382   view = tree->views;
1383
1384   while (view != NULL)
1385     {
1386       if (view->view_id == view_id)
1387         break;
1388
1389       view = view->next;
1390     }
1391
1392   g_return_if_fail (view != NULL);
1393
1394   if (view->next)
1395     view->next->prev = view->prev;
1396
1397   if (view->prev)
1398     view->prev->next = view->next;
1399
1400   if (view == tree->views)
1401     tree->views = view->next;
1402
1403   /* Remove the line data for the last line which we added ourselves.
1404    * (Do this first, so that we don't try to call the view's line data destructor on it.)
1405    */
1406   last_line = get_last_line (tree);
1407   line_data = gtk_text_line_remove_data (last_line, view_id);
1408   g_free (line_data);
1409
1410   gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1411
1412   g_free (view);
1413 }
1414
1415 void
1416 gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1417                                   const GtkTextIter *start,
1418                                   const GtkTextIter *end)
1419 {
1420   BTreeView *view;
1421
1422   view = tree->views;
1423
1424   while (view != NULL)
1425     {
1426       gtk_text_layout_invalidate (view->layout, start, end);
1427
1428       view = view->next;
1429     }
1430 }
1431
1432 void
1433 gtk_text_btree_get_view_size (GtkTextBTree *tree,
1434                               gpointer view_id,
1435                               gint *width,
1436                               gint *height)
1437 {
1438   g_return_if_fail (tree != NULL);
1439   g_return_if_fail (view_id != NULL);
1440
1441   gtk_text_btree_node_get_size (tree->root_node, view_id,
1442                                 width, height);
1443 }
1444
1445 /*
1446  * Tag
1447  */
1448
1449 typedef struct {
1450   GtkTextIter *iters;
1451   guint count;
1452   guint alloced;
1453 } IterStack;
1454
1455 static IterStack*
1456 iter_stack_new (void)
1457 {
1458   IterStack *stack;
1459   stack = g_new (IterStack, 1);
1460   stack->iters = NULL;
1461   stack->count = 0;
1462   stack->alloced = 0;
1463   return stack;
1464 }
1465
1466 static void
1467 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1468 {
1469   stack->count += 1;
1470   if (stack->count > stack->alloced)
1471     {
1472       stack->alloced = stack->count*2;
1473       stack->iters = g_realloc (stack->iters,
1474                                 stack->alloced*sizeof (GtkTextIter));
1475     }
1476   stack->iters[stack->count-1] = *iter;
1477 }
1478
1479 static gboolean
1480 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1481 {
1482   if (stack->count == 0)
1483     return FALSE;
1484   else
1485     {
1486       stack->count -= 1;
1487       *iter = stack->iters[stack->count];
1488       return TRUE;
1489     }
1490 }
1491
1492 static void
1493 iter_stack_free (IterStack *stack)
1494 {
1495   g_free (stack->iters);
1496   g_free (stack);
1497 }
1498
1499 static void
1500 iter_stack_invert (IterStack *stack)
1501 {
1502   if (stack->count > 0)
1503     {
1504       guint i = 0;
1505       guint j = stack->count - 1;
1506       while (i < j)
1507         {
1508           GtkTextIter tmp;
1509
1510           tmp = stack->iters[i];
1511           stack->iters[i] = stack->iters[j];
1512           stack->iters[j] = tmp;
1513
1514           ++i;
1515           --j;
1516         }
1517     }
1518 }
1519
1520 static void
1521 queue_tag_redisplay (GtkTextBTree      *tree,
1522                      GtkTextTag        *tag,
1523                      const GtkTextIter *start,
1524                      const GtkTextIter *end)
1525 {
1526   if (gtk_text_tag_affects_size (tag))
1527     {
1528       gtk_text_btree_invalidate_region (tree, start, end);
1529
1530     }
1531   else if (gtk_text_tag_affects_nonsize_appearance (tag))
1532     {
1533       /* We only need to queue a redraw, not a relayout */
1534       redisplay_region (tree, start, end);
1535     }
1536
1537   /* We don't need to do anything if the tag doesn't affect display */
1538 }
1539
1540 void
1541 gtk_text_btree_tag (const GtkTextIter *start_orig,
1542                     const GtkTextIter *end_orig,
1543                     GtkTextTag *tag,
1544                     gboolean add)
1545 {
1546   GtkTextLineSegment *seg, *prev;
1547   GtkTextLine *cleanupline;
1548   gboolean toggled_on;
1549   GtkTextLine *start_line;
1550   GtkTextLine *end_line;
1551   GtkTextIter iter;
1552   GtkTextIter start, end;
1553   GtkTextBTree *tree;
1554   IterStack *stack;
1555   GtkTextTagInfo *info;
1556
1557   g_return_if_fail (start_orig != NULL);
1558   g_return_if_fail (end_orig != NULL);
1559   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1560   g_return_if_fail (gtk_text_iter_get_btree (start_orig) ==
1561                     gtk_text_iter_get_btree (end_orig));
1562
1563 #if 0
1564   printf ("%s tag %s from %d to %d\n",
1565           add ? "Adding" : "Removing",
1566           tag->name,
1567           gtk_text_buffer_get_offset (start_orig),
1568           gtk_text_buffer_get_offset (end_orig));
1569 #endif
1570
1571   if (gtk_text_iter_equal (start_orig, end_orig))
1572     return;
1573
1574   start = *start_orig;
1575   end = *end_orig;
1576
1577   gtk_text_iter_reorder (&start, &end);
1578
1579   tree = gtk_text_iter_get_btree (&start);
1580
1581   queue_tag_redisplay (tree, tag, &start, &end);
1582
1583   info = gtk_text_btree_get_tag_info (tree, tag);
1584
1585   start_line = gtk_text_iter_get_text_line (&start);
1586   end_line = gtk_text_iter_get_text_line (&end);
1587
1588   /* Find all tag toggles in the region; we are going to delete them.
1589      We need to find them in advance, because
1590      forward_find_tag_toggle () won't work once we start playing around
1591      with the tree. */
1592   stack = iter_stack_new ();
1593   iter = start;
1594   /* We don't want to delete a toggle that's at the start iterator. */
1595   gtk_text_iter_next_char (&iter);
1596   while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1597     {
1598       if (gtk_text_iter_compare (&iter, &end) >= 0)
1599         break;
1600       else
1601         iter_stack_push (stack, &iter);
1602     }
1603
1604   /* We need to traverse the toggles in order. */
1605   iter_stack_invert (stack);
1606
1607   /*
1608    * See whether the tag is present at the start of the range.  If
1609    * the state doesn't already match what we want then add a toggle
1610    * there.
1611    */
1612
1613   toggled_on = gtk_text_iter_has_tag (&start, tag);
1614   if ( (add && !toggled_on) ||
1615        (!add && toggled_on) )
1616     {
1617       /* This could create a second toggle at the start position;
1618          cleanup_line () will remove it if so. */
1619       seg = _gtk_toggle_segment_new (info, add);
1620
1621       prev = gtk_text_line_segment_split (&start);
1622       if (prev == NULL)
1623         {
1624           seg->next = start_line->segments;
1625           start_line->segments = seg;
1626         }
1627       else
1628         {
1629           seg->next = prev->next;
1630           prev->next = seg;
1631         }
1632
1633       /* cleanup_line adds the new toggle to the node counts. */
1634 #if 0
1635       printf ("added toggle at start\n");
1636 #endif
1637       /* we should probably call segments_changed, but in theory
1638          any still-cached segments in the iters we are about to
1639          use are still valid, since they're in front
1640          of this spot. */
1641     }
1642
1643   /*
1644    *
1645    * Scan the range of characters and delete any internal tag
1646    * transitions.  Keep track of what the old state was at the end
1647    * of the range, and add a toggle there if it's needed.
1648    *
1649    */
1650
1651   cleanupline = start_line;
1652   while (iter_stack_pop (stack, &iter))
1653     {
1654       GtkTextLineSegment *indexable_seg;
1655       GtkTextLine *line;
1656
1657       line = gtk_text_iter_get_text_line (&iter);
1658       seg = gtk_text_iter_get_any_segment (&iter);
1659       indexable_seg = gtk_text_iter_get_indexable_segment (&iter);
1660
1661       g_assert (seg != NULL);
1662       g_assert (indexable_seg != NULL);
1663       g_assert (seg != indexable_seg);
1664
1665       prev = line->segments;
1666
1667       /* Find the segment that actually toggles this tag. */
1668       while (seg != indexable_seg)
1669         {
1670           g_assert (seg != NULL);
1671           g_assert (indexable_seg != NULL);
1672           g_assert (seg != indexable_seg);
1673
1674           if ( (seg->type == &gtk_text_toggle_on_type ||
1675                 seg->type == &gtk_text_toggle_off_type) &&
1676                (seg->body.toggle.info == info) )
1677             break;
1678           else
1679             seg = seg->next;
1680         }
1681
1682       g_assert (seg != NULL);
1683       g_assert (indexable_seg != NULL);
1684
1685       g_assert (seg != indexable_seg); /* If this happens, then
1686                                           forward_to_tag_toggle was
1687                                           full of shit. */
1688       g_assert (seg->body.toggle.info->tag == tag);
1689
1690       /* If this happens, when previously tagging we didn't merge
1691          overlapping tags. */
1692       g_assert ( (toggled_on && seg->type == &gtk_text_toggle_off_type) ||
1693                  (!toggled_on && seg->type == &gtk_text_toggle_on_type) );
1694
1695       toggled_on = !toggled_on;
1696
1697 #if 0
1698       printf ("deleting %s toggle\n",
1699               seg->type == &gtk_text_toggle_on_type ? "on" : "off");
1700 #endif
1701       /* Remove toggle segment from the list. */
1702       if (prev == seg)
1703         {
1704           line->segments = seg->next;
1705         }
1706       else
1707         {
1708           while (prev->next != seg)
1709             {
1710               prev = prev->next;
1711             }
1712           prev->next = seg->next;
1713         }
1714
1715       /* Inform iterators we've hosed them. This actually reflects a
1716          bit of inefficiency; if you have the same tag toggled on and
1717          off a lot in a single line, we keep having the rescan from
1718          the front of the line. Of course we have to do that to get
1719          "prev" anyway, but here we are doing it an additional
1720          time. FIXME */
1721       segments_changed (tree);
1722
1723       /* Update node counts */
1724       if (seg->body.toggle.inNodeCounts)
1725         {
1726           _gtk_change_node_toggle_count (line->parent,
1727                                          info, -1);
1728           seg->body.toggle.inNodeCounts = FALSE;
1729         }
1730
1731       g_free (seg);
1732
1733       /* We only clean up lines when we're done with them, saves some
1734          gratuitous line-segment-traversals */
1735
1736       if (cleanupline != line)
1737         {
1738           cleanup_line (cleanupline);
1739           cleanupline = line;
1740         }
1741     }
1742
1743   iter_stack_free (stack);
1744
1745   /* toggled_on now reflects the toggle state _just before_ the
1746      end iterator. The end iterator could already have a toggle
1747      on or a toggle off. */
1748   if ( (add && !toggled_on) ||
1749        (!add && toggled_on) )
1750     {
1751       /* This could create a second toggle at the start position;
1752          cleanup_line () will remove it if so. */
1753
1754       seg = _gtk_toggle_segment_new (info, !add);
1755
1756       prev = gtk_text_line_segment_split (&end);
1757       if (prev == NULL)
1758         {
1759           seg->next = end_line->segments;
1760           end_line->segments = seg;
1761         }
1762       else
1763         {
1764           seg->next = prev->next;
1765           prev->next = seg;
1766         }
1767       /* cleanup_line adds the new toggle to the node counts. */
1768       g_assert (seg->body.toggle.inNodeCounts == FALSE);
1769 #if 0
1770       printf ("added toggle at end\n");
1771 #endif
1772     }
1773
1774   /*
1775    * Cleanup cleanupline and the last line of the range, if
1776    * these are different.
1777    */
1778
1779   cleanup_line (cleanupline);
1780   if (cleanupline != end_line)
1781     {
1782       cleanup_line (end_line);
1783     }
1784
1785   segments_changed (tree);
1786
1787   if (gtk_debug_flags & GTK_DEBUG_TEXT)
1788     gtk_text_btree_check (tree);
1789 }
1790
1791
1792 /*
1793  * "Getters"
1794  */
1795
1796 GtkTextLine*
1797 gtk_text_btree_get_line (GtkTextBTree *tree,
1798                          gint  line_number,
1799                          gint *real_line_number)
1800 {
1801   GtkTextBTreeNode *node;
1802   GtkTextLine *line;
1803   int lines_left;
1804   int line_count;
1805
1806   line_count = gtk_text_btree_line_count (tree);
1807
1808   if (line_number < 0)
1809     {
1810       line_number = line_count;
1811     }
1812   else if (line_number > line_count)
1813     {
1814       line_number = line_count;
1815     }
1816
1817   if (real_line_number)
1818     *real_line_number = line_number;
1819
1820   node = tree->root_node;
1821   lines_left = line_number;
1822
1823   /*
1824    * Work down through levels of the tree until a GtkTextBTreeNode is found at
1825    * level 0.
1826    */
1827
1828   while (node->level != 0)
1829     {
1830       for (node = node->children.node;
1831            node->num_lines <= lines_left;
1832            node = node->next)
1833         {
1834 #if 0
1835           if (node == NULL)
1836             {
1837               g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1838             }
1839 #endif
1840           lines_left -= node->num_lines;
1841         }
1842     }
1843
1844   /*
1845    * Work through the lines attached to the level-0 GtkTextBTreeNode.
1846    */
1847
1848   for (line = node->children.line; lines_left > 0;
1849        line = line->next)
1850     {
1851 #if 0
1852       if (line == NULL)
1853         {
1854           g_error ("gtk_text_btree_find_line ran out of lines");
1855         }
1856 #endif
1857       lines_left -= 1;
1858     }
1859   return line;
1860 }
1861
1862 GtkTextLine*
1863 gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
1864                                  gint                char_index,
1865                                  gint               *line_start_index,
1866                                  gint               *real_char_index)
1867 {
1868   GtkTextBTreeNode *node;
1869   GtkTextLine *line;
1870   GtkTextLineSegment *seg;
1871   int chars_left;
1872   int chars_in_line;
1873   int bytes_in_line;
1874
1875   node = tree->root_node;
1876
1877   /* Clamp to valid indexes (-1 is magic for "highest index") */
1878   if (char_index < 0 || char_index >= node->num_chars)
1879     {
1880       char_index = node->num_chars - 1;
1881     }
1882
1883   *real_char_index = char_index;
1884
1885   /*
1886    * Work down through levels of the tree until a GtkTextBTreeNode is found at
1887    * level 0.
1888    */
1889
1890   chars_left = char_index;
1891   while (node->level != 0)
1892     {
1893       for (node = node->children.node;
1894            chars_left >= node->num_chars;
1895            node = node->next)
1896         {
1897           chars_left -= node->num_chars;
1898
1899           g_assert (chars_left >= 0);
1900         }
1901     }
1902
1903   if (chars_left == 0)
1904     {
1905       /* Start of a line */
1906
1907       *line_start_index = char_index;
1908       return node->children.line;
1909     }
1910
1911   /*
1912    * Work through the lines attached to the level-0 GtkTextBTreeNode.
1913    */
1914
1915   chars_in_line = 0;
1916   bytes_in_line = 0;
1917   seg = NULL;
1918   for (line = node->children.line; line != NULL; line = line->next)
1919     {
1920       seg = line->segments;
1921       while (seg != NULL)
1922         {
1923           if (chars_in_line + seg->char_count > chars_left)
1924             goto found; /* found our line/segment */
1925
1926           chars_in_line += seg->char_count;
1927
1928           seg = seg->next;
1929         }
1930
1931       chars_left -= chars_in_line;
1932
1933       chars_in_line = 0;
1934       seg = NULL;
1935     }
1936
1937  found:
1938
1939   g_assert (line != NULL); /* hosage, ran out of lines */
1940   g_assert (seg != NULL);
1941
1942   *line_start_index = char_index - chars_left;
1943   return line;
1944 }
1945
1946 GtkTextTag**
1947 gtk_text_btree_get_tags (const GtkTextIter *iter,
1948                          gint *num_tags)
1949 {
1950   GtkTextBTreeNode *node;
1951   GtkTextLine *siblingline;
1952   GtkTextLineSegment *seg;
1953   int src, dst, index;
1954   TagInfo tagInfo;
1955   GtkTextLine *line;
1956   GtkTextBTree *tree;
1957   gint byte_index;
1958
1959 #define NUM_TAG_INFOS 10
1960
1961   line = gtk_text_iter_get_text_line (iter);
1962   tree = gtk_text_iter_get_btree (iter);
1963   byte_index = gtk_text_iter_get_line_index (iter);
1964
1965   tagInfo.numTags = 0;
1966   tagInfo.arraySize = NUM_TAG_INFOS;
1967   tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
1968   tagInfo.counts = g_new (int, NUM_TAG_INFOS);
1969
1970   /*
1971    * Record tag toggles within the line of indexPtr but preceding
1972    * indexPtr. Note that if this loop segfaults, your
1973    * byte_index probably points past the sum of all
1974    * seg->byte_count */
1975
1976   for (index = 0, seg = line->segments;
1977        (index + seg->byte_count) <= byte_index;
1978        index += seg->byte_count, seg = seg->next)
1979     {
1980       if ((seg->type == &gtk_text_toggle_on_type)
1981           || (seg->type == &gtk_text_toggle_off_type))
1982         {
1983           inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
1984         }
1985     }
1986
1987   /*
1988    * Record toggles for tags in lines that are predecessors of
1989    * line but under the same level-0 GtkTextBTreeNode.
1990    */
1991
1992   for (siblingline = line->parent->children.line;
1993        siblingline != line;
1994        siblingline = siblingline->next)
1995     {
1996       for (seg = siblingline->segments; seg != NULL;
1997            seg = seg->next)
1998         {
1999           if ((seg->type == &gtk_text_toggle_on_type)
2000               || (seg->type == &gtk_text_toggle_off_type))
2001             {
2002               inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2003             }
2004         }
2005     }
2006
2007   /*
2008    * For each GtkTextBTreeNode in the ancestry of this line, record tag
2009    * toggles for all siblings that precede that GtkTextBTreeNode.
2010    */
2011
2012   for (node = line->parent; node->parent != NULL;
2013        node = node->parent)
2014     {
2015       GtkTextBTreeNode *siblingPtr;
2016       Summary *summary;
2017
2018       for (siblingPtr = node->parent->children.node;
2019            siblingPtr != node; siblingPtr = siblingPtr->next)
2020         {
2021           for (summary = siblingPtr->summary; summary != NULL;
2022                summary = summary->next)
2023             {
2024               if (summary->toggle_count & 1)
2025                 {
2026                   inc_count (summary->info->tag, summary->toggle_count,
2027                              &tagInfo);
2028                 }
2029             }
2030         }
2031     }
2032
2033   /*
2034    * Go through the tag information and squash out all of the tags
2035    * that have even toggle counts (these tags exist before the point
2036    * of interest, but not at the desired character itself).
2037    */
2038
2039   for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2040     {
2041       if (tagInfo.counts[src] & 1)
2042         {
2043           g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2044           tagInfo.tags[dst] = tagInfo.tags[src];
2045           dst++;
2046         }
2047     }
2048
2049   *num_tags = dst;
2050   g_free (tagInfo.counts);
2051   if (dst == 0)
2052     {
2053       g_free (tagInfo.tags);
2054       return NULL;
2055     }
2056   return tagInfo.tags;
2057 }
2058
2059 static void
2060 copy_segment (GString *string,
2061               gboolean include_hidden,
2062               gboolean include_nonchars,
2063               const GtkTextIter *start,
2064               const GtkTextIter *end)
2065 {
2066   GtkTextLineSegment *end_seg;
2067   GtkTextLineSegment *seg;
2068
2069   if (gtk_text_iter_equal (start, end))
2070     return;
2071
2072   seg = gtk_text_iter_get_indexable_segment (start);
2073   end_seg = gtk_text_iter_get_indexable_segment (end);
2074
2075   if (seg->type == &gtk_text_char_type)
2076     {
2077       gboolean copy = TRUE;
2078       gint copy_bytes = 0;
2079       gint copy_start = 0;
2080
2081       /* Don't copy if we're invisible; segments are invisible/not
2082          as a whole, no need to check each char */
2083       if (!include_hidden &&
2084           gtk_text_btree_char_is_invisible (start))
2085         {
2086           copy = FALSE;
2087           /* printf (" <invisible>\n"); */
2088         }
2089
2090       copy_start = gtk_text_iter_get_segment_byte (start);
2091
2092       if (seg == end_seg)
2093         {
2094           /* End is in the same segment; need to copy fewer bytes. */
2095           gint end_byte = gtk_text_iter_get_segment_byte (end);
2096
2097           copy_bytes = end_byte - copy_start;
2098         }
2099       else
2100         copy_bytes = seg->byte_count - copy_start;
2101
2102       g_assert (copy_bytes != 0); /* Due to iter equality check at
2103                                      front of this function. */
2104
2105       if (copy)
2106         {
2107           g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2108
2109           g_string_append_len (string,
2110                                seg->body.chars + copy_start,
2111                                copy_bytes);
2112         }
2113
2114       /* printf ("  :%s\n", string->str); */
2115     }
2116   else if (seg->type == &gtk_text_pixbuf_type ||
2117            seg->type == &gtk_text_child_type)
2118     {
2119       gboolean copy = TRUE;
2120
2121       if (!include_nonchars)
2122         {
2123           copy = FALSE;
2124         }
2125       else if (!include_hidden &&
2126                gtk_text_btree_char_is_invisible (start))
2127         {
2128           copy = FALSE;
2129         }
2130
2131       if (copy)
2132         {
2133           g_string_append_len (string,
2134                                gtk_text_unknown_char_utf8,
2135                                3);
2136
2137         }
2138     }
2139 }
2140
2141 gchar*
2142 gtk_text_btree_get_text (const GtkTextIter *start_orig,
2143                          const GtkTextIter *end_orig,
2144                          gboolean include_hidden,
2145                          gboolean include_nonchars)
2146 {
2147   GtkTextLineSegment *seg;
2148   GtkTextLineSegment *end_seg;
2149   GString *retval;
2150   GtkTextBTree *tree;
2151   gchar *str;
2152   GtkTextIter iter;
2153   GtkTextIter start;
2154   GtkTextIter end;
2155
2156   g_return_val_if_fail (start_orig != NULL, NULL);
2157   g_return_val_if_fail (end_orig != NULL, NULL);
2158   g_return_val_if_fail (gtk_text_iter_get_btree (start_orig) ==
2159                         gtk_text_iter_get_btree (end_orig), NULL);
2160
2161   start = *start_orig;
2162   end = *end_orig;
2163
2164   gtk_text_iter_reorder (&start, &end);
2165
2166   retval = g_string_new ("");
2167
2168   tree = gtk_text_iter_get_btree (&start);
2169
2170   end_seg = gtk_text_iter_get_indexable_segment (&end);
2171   iter = start;
2172   seg = gtk_text_iter_get_indexable_segment (&iter);
2173   while (seg != end_seg)
2174     {
2175       copy_segment (retval, include_hidden, include_nonchars,
2176                     &iter, &end);
2177
2178       gtk_text_iter_forward_indexable_segment (&iter);
2179
2180       seg = gtk_text_iter_get_indexable_segment (&iter);
2181     }
2182
2183   copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2184
2185   str = retval->str;
2186   g_string_free (retval, FALSE);
2187   return str;
2188 }
2189
2190 gint
2191 gtk_text_btree_line_count (GtkTextBTree *tree)
2192 {
2193   /* Subtract bogus line at the end; we return a count
2194      of usable lines. */
2195   return tree->root_node->num_lines - 1;
2196 }
2197
2198 gint
2199 gtk_text_btree_char_count (GtkTextBTree *tree)
2200 {
2201   /* Exclude newline in bogus last line */
2202   return tree->root_node->num_chars - 1;
2203 }
2204
2205 #define LOTSA_TAGS 1000
2206 gboolean
2207 gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2208 {
2209   gboolean invisible = FALSE;  /* if nobody says otherwise, it's visible */
2210
2211   int deftagCnts[LOTSA_TAGS];
2212   int *tagCnts = deftagCnts;
2213   GtkTextTag *deftags[LOTSA_TAGS];
2214   GtkTextTag **tags = deftags;
2215   int numTags;
2216   GtkTextBTreeNode *node;
2217   GtkTextLine *siblingline;
2218   GtkTextLineSegment *seg;
2219   GtkTextTag *tag;
2220   int i, index;
2221   GtkTextLine *line;
2222   GtkTextBTree *tree;
2223   gint byte_index;
2224
2225   line = gtk_text_iter_get_text_line (iter);
2226   tree = gtk_text_iter_get_btree (iter);
2227   byte_index = gtk_text_iter_get_line_index (iter);
2228
2229   numTags = gtk_text_tag_table_size (tree->table);
2230
2231   /* almost always avoid malloc, so stay out of system calls */
2232   if (LOTSA_TAGS < numTags)
2233     {
2234       tagCnts = g_new (int, numTags);
2235       tags = g_new (GtkTextTag*, numTags);
2236     }
2237
2238   for (i=0; i<numTags; i++)
2239     {
2240       tagCnts[i] = 0;
2241     }
2242
2243   /*
2244    * Record tag toggles within the line of indexPtr but preceding
2245    * indexPtr.
2246    */
2247
2248   for (index = 0, seg = line->segments;
2249        (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2250        index += seg->byte_count, seg = seg->next)
2251     {
2252       if ((seg->type == &gtk_text_toggle_on_type)
2253           || (seg->type == &gtk_text_toggle_off_type))
2254         {
2255           tag = seg->body.toggle.info->tag;
2256           if (tag->invisible_set && tag->values->invisible)
2257             {
2258               tags[tag->priority] = tag;
2259               tagCnts[tag->priority]++;
2260             }
2261         }
2262     }
2263
2264   /*
2265    * Record toggles for tags in lines that are predecessors of
2266    * line but under the same level-0 GtkTextBTreeNode.
2267    */
2268
2269   for (siblingline = line->parent->children.line;
2270        siblingline != line;
2271        siblingline = siblingline->next)
2272     {
2273       for (seg = siblingline->segments; seg != NULL;
2274            seg = seg->next)
2275         {
2276           if ((seg->type == &gtk_text_toggle_on_type)
2277               || (seg->type == &gtk_text_toggle_off_type))
2278             {
2279               tag = seg->body.toggle.info->tag;
2280               if (tag->invisible_set && tag->values->invisible)
2281                 {
2282                   tags[tag->priority] = tag;
2283                   tagCnts[tag->priority]++;
2284                 }
2285             }
2286         }
2287     }
2288
2289   /*
2290    * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2291    * for all siblings that precede that GtkTextBTreeNode.
2292    */
2293
2294   for (node = line->parent; node->parent != NULL;
2295        node = node->parent)
2296     {
2297       GtkTextBTreeNode *siblingPtr;
2298       Summary *summary;
2299
2300       for (siblingPtr = node->parent->children.node;
2301            siblingPtr != node; siblingPtr = siblingPtr->next)
2302         {
2303           for (summary = siblingPtr->summary; summary != NULL;
2304                summary = summary->next)
2305             {
2306               if (summary->toggle_count & 1)
2307                 {
2308                   tag = summary->info->tag;
2309                   if (tag->invisible_set && tag->values->invisible)
2310                     {
2311                       tags[tag->priority] = tag;
2312                       tagCnts[tag->priority] += summary->toggle_count;
2313                     }
2314                 }
2315             }
2316         }
2317     }
2318
2319   /*
2320    * Now traverse from highest priority to lowest,
2321    * take invisible value from first odd count (= on)
2322    */
2323
2324   for (i = numTags-1; i >=0; i--)
2325     {
2326       if (tagCnts[i] & 1)
2327         {
2328           /* FIXME not sure this should be if 0 */
2329 #if 0
2330 #ifndef ALWAYS_SHOW_SELECTION
2331           /* who would make the selection invisible? */
2332           if ((tag == tkxt->seltag)
2333               && !(tkxt->flags & GOT_FOCUS))
2334             {
2335               continue;
2336             }
2337 #endif
2338 #endif
2339           invisible = tags[i]->values->invisible;
2340           break;
2341         }
2342     }
2343
2344   if (LOTSA_TAGS < numTags)
2345     {
2346       g_free (tagCnts);
2347       g_free (tags);
2348     }
2349
2350   return invisible;
2351 }
2352
2353
2354 /*
2355  * Manipulate marks
2356  */
2357
2358 static void
2359 redisplay_region (GtkTextBTree      *tree,
2360                   const GtkTextIter *start,
2361                   const GtkTextIter *end)
2362 {
2363   BTreeView *view;
2364   GtkTextLine *start_line, *end_line;
2365
2366   if (gtk_text_iter_compare (start, end) > 0)
2367     {
2368       const GtkTextIter *tmp = start;
2369       start = end;
2370       end = tmp;
2371     }
2372
2373   start_line = gtk_text_iter_get_text_line (start);
2374   end_line = gtk_text_iter_get_text_line (end);
2375
2376   view = tree->views;
2377   while (view != NULL)
2378     {
2379       gint start_y, end_y;
2380       GtkTextLineData *ld;
2381
2382       start_y = gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2383
2384       if (end_line == start_line)
2385         end_y = start_y;
2386       else
2387         end_y = gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2388
2389       ld = gtk_text_line_get_data (end_line, view->view_id);
2390       if (ld)
2391         end_y += ld->height;
2392
2393       gtk_text_layout_changed (view->layout, start_y,
2394                                end_y - start_y,
2395                                end_y - start_y);
2396
2397       view = view->next;
2398     }
2399 }
2400
2401 static void
2402 redisplay_mark (GtkTextLineSegment *mark)
2403 {
2404   GtkTextIter iter;
2405   GtkTextIter end;
2406
2407   gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2408                                    &iter,
2409                                    mark->body.mark.obj);
2410
2411   end = iter;
2412   gtk_text_iter_next_char (&end);
2413
2414   gtk_text_btree_invalidate_region (mark->body.mark.tree,
2415                                     &iter, &end);
2416 }
2417
2418 static void
2419 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2420 {
2421   if (!mark->body.mark.visible)
2422     return;
2423   else
2424     redisplay_mark (mark);
2425 }
2426
2427 static void
2428 ensure_not_off_end (GtkTextBTree *tree,
2429                     GtkTextLineSegment *mark,
2430                     GtkTextIter *iter)
2431 {
2432   if (gtk_text_iter_get_line (iter) ==
2433       gtk_text_btree_line_count (tree))
2434     gtk_text_iter_prev_char (iter);
2435 }
2436
2437 static GtkTextLineSegment*
2438 real_set_mark (GtkTextBTree *tree,
2439                GtkTextMark *existing_mark,
2440                const gchar *name,
2441                gboolean left_gravity,
2442                const GtkTextIter *where,
2443                gboolean should_exist,
2444                gboolean redraw_selections)
2445 {
2446   GtkTextLineSegment *mark;
2447   GtkTextIter iter;
2448
2449   g_return_val_if_fail (tree != NULL, NULL);
2450   g_return_val_if_fail (where != NULL, NULL);
2451   g_return_val_if_fail (gtk_text_iter_get_btree (where) == tree, NULL);
2452
2453   if (existing_mark)
2454     mark = existing_mark->segment;
2455   else if (name != NULL)
2456     mark = g_hash_table_lookup (tree->mark_table,
2457                                 name);
2458   else
2459     mark = NULL;
2460
2461   if (should_exist && mark == NULL)
2462     {
2463       g_warning ("No mark `%s' exists!", name);
2464       return NULL;
2465     }
2466
2467   /* OK if !should_exist and it does already exist, in that case
2468      we just move it. */
2469
2470   iter = *where;
2471
2472   if (mark != NULL)
2473     {
2474       if (redraw_selections &&
2475           (mark == tree->insert_mark->segment ||
2476            mark == tree->selection_bound_mark->segment))
2477         {
2478           GtkTextIter old_pos;
2479
2480           gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2481                                            mark->body.mark.obj);
2482           redisplay_region (tree, &old_pos, where);
2483         }
2484
2485       /*
2486        * don't let visible marks be after the final newline of the
2487        *  file.
2488        */
2489
2490       if (mark->body.mark.visible)
2491         {
2492           ensure_not_off_end (tree, mark, &iter);
2493         }
2494
2495       /* Redraw the mark's old location. */
2496       redisplay_mark_if_visible (mark);
2497
2498       /* Unlink mark from its current location.
2499          This could hose our iterator... */
2500       gtk_text_btree_unlink_segment (tree, mark,
2501                                      mark->body.mark.line);
2502       mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
2503       g_assert (mark->body.mark.line == gtk_text_iter_get_text_line (&iter));
2504
2505       segments_changed (tree); /* make sure the iterator recomputes its
2506                                   segment */
2507     }
2508   else
2509     {
2510       mark = _gtk_mark_segment_new (tree,
2511                                     left_gravity,
2512                                     name);
2513
2514       mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
2515
2516       if (mark->body.mark.name)
2517         g_hash_table_insert (tree->mark_table,
2518                              mark->body.mark.name,
2519                              mark);
2520     }
2521
2522   /* Link mark into new location */
2523   gtk_text_btree_link_segment (mark, &iter);
2524
2525   /* Invalidate some iterators. */
2526   segments_changed (tree);
2527
2528   /*
2529    * update the screen at the mark's new location.
2530    */
2531
2532   redisplay_mark_if_visible (mark);
2533
2534   return mark;
2535 }
2536
2537
2538 GtkTextMark*
2539 gtk_text_btree_set_mark (GtkTextBTree *tree,
2540                          GtkTextMark  *existing_mark,
2541                          const gchar *name,
2542                          gboolean left_gravity,
2543                          const GtkTextIter *iter,
2544                          gboolean should_exist)
2545 {
2546   GtkTextLineSegment *seg;
2547
2548   seg = real_set_mark (tree, existing_mark,
2549                        name, left_gravity, iter, should_exist,
2550                        TRUE);
2551
2552   return seg ? seg->body.mark.obj : NULL;
2553 }
2554
2555 gboolean
2556 gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2557                                      GtkTextIter  *start,
2558                                      GtkTextIter  *end)
2559 {
2560   GtkTextIter tmp_start, tmp_end;
2561
2562   gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2563                                    tree->insert_mark);
2564   gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2565                                    tree->selection_bound_mark);
2566
2567   if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2568     {
2569       if (start)
2570         *start = tmp_start;
2571
2572       if (end)
2573         *end = tmp_end;
2574
2575       return FALSE;
2576     }
2577   else
2578     {
2579       gtk_text_iter_reorder (&tmp_start, &tmp_end);
2580
2581       if (start)
2582         *start = tmp_start;
2583
2584       if (end)
2585         *end = tmp_end;
2586
2587       return TRUE;
2588     }
2589 }
2590
2591 void
2592 gtk_text_btree_place_cursor (GtkTextBTree      *tree,
2593                              const GtkTextIter *iter)
2594 {
2595   GtkTextIter start, end;
2596
2597   if (gtk_text_btree_get_selection_bounds (tree, &start, &end))
2598     redisplay_region (tree, &start, &end);
2599
2600   /* Move insert AND selection_bound before we redisplay */
2601   real_set_mark (tree, tree->insert_mark,
2602                  "insert", FALSE, iter, TRUE, FALSE);
2603   real_set_mark (tree, tree->selection_bound_mark,
2604                  "selection_bound", FALSE, iter, TRUE, FALSE);
2605 }
2606
2607 void
2608 gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2609                                     const gchar *name)
2610 {
2611   GtkTextMark *mark;
2612
2613   g_return_if_fail (tree != NULL);
2614   g_return_if_fail (name != NULL);
2615
2616   mark = g_hash_table_lookup (tree->mark_table,
2617                               name);
2618
2619   gtk_text_btree_remove_mark (tree, mark);
2620 }
2621
2622 void
2623 gtk_text_btree_remove_mark (GtkTextBTree *tree,
2624                             GtkTextMark *mark)
2625 {
2626   GtkTextLineSegment *segment;
2627
2628   g_return_if_fail (mark != NULL);
2629   g_return_if_fail (tree != NULL);
2630
2631   segment = mark->segment;
2632
2633   if (segment->body.mark.not_deleteable)
2634     {
2635       g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2636       return;
2637     }
2638
2639   /* This calls cleanup_line and segments_changed */
2640   gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2641
2642   if (segment->body.mark.name)
2643     g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2644
2645   /* Remove the ref on the mark that belonged to the segment. */
2646   g_object_unref (G_OBJECT (mark));
2647
2648   segment->body.mark.tree = NULL;
2649   segment->body.mark.line = NULL;
2650 }
2651
2652 gboolean
2653 gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2654                                GtkTextMark *segment)
2655 {
2656   return segment == tree->insert_mark;
2657 }
2658
2659 gboolean
2660 gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2661                                         GtkTextMark *segment)
2662 {
2663   return segment == tree->selection_bound_mark;
2664 }
2665
2666 GtkTextMark*
2667 gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2668                                  const gchar *name)
2669 {
2670   GtkTextLineSegment *seg;
2671
2672   g_return_val_if_fail (tree != NULL, NULL);
2673   g_return_val_if_fail (name != NULL, NULL);
2674
2675   seg = g_hash_table_lookup (tree->mark_table, name);
2676
2677   return seg ? seg->body.mark.obj : NULL;
2678 }
2679
2680 void
2681 gtk_text_mark_set_visible (GtkTextMark       *mark,
2682                            gboolean           setting)
2683 {
2684   GtkTextLineSegment *seg;
2685
2686   g_return_if_fail (mark != NULL);
2687
2688   seg = mark->segment;
2689
2690   if (seg->body.mark.visible == setting)
2691     return;
2692   else
2693     {
2694       seg->body.mark.visible = setting;
2695
2696       redisplay_mark (seg);
2697     }
2698 }
2699
2700 GtkTextLine*
2701 gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2702                                         GtkTextTag *tag)
2703 {
2704   GtkTextBTreeNode *node;
2705   GtkTextTagInfo *info;
2706
2707   g_return_val_if_fail (tree != NULL, NULL);
2708
2709   if (tag != NULL)
2710     {
2711       info = gtk_text_btree_get_existing_tag_info (tree, tag);
2712
2713       if (info == NULL)
2714         return NULL;
2715
2716       if (info->tag_root == NULL)
2717         return NULL;
2718
2719       node = info->tag_root;
2720
2721       /* We know the tag root has instances of the given
2722          tag below it */
2723
2724     continue_outer_loop:
2725       g_assert (node != NULL);
2726       while (node->level > 0)
2727         {
2728           g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2729           node = node->children.node;
2730           while (node != NULL)
2731             {
2732               if (gtk_text_btree_node_has_tag (node, tag))
2733                 goto continue_outer_loop;
2734
2735               node = node->next;
2736             }
2737           g_assert (node != NULL);
2738         }
2739
2740       g_assert (node != NULL); /* The tag summaries said some node had
2741                                   tag toggles... */
2742
2743       g_assert (node->level == 0);
2744
2745       return node->children.line;
2746     }
2747   else
2748     {
2749       /* Looking for any tag at all (tag == NULL).
2750          Unfortunately this can't be done in a simple and efficient way
2751          right now; so I'm just going to return the
2752          first line in the btree. FIXME */
2753       return gtk_text_btree_get_line (tree, 0, NULL);
2754     }
2755 }
2756
2757 GtkTextLine*
2758 gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2759                                        GtkTextTag *tag)
2760 {
2761   GtkTextBTreeNode *node;
2762   GtkTextBTreeNode *last_node;
2763   GtkTextLine *line;
2764   GtkTextTagInfo *info;
2765
2766   g_return_val_if_fail (tree != NULL, NULL);
2767
2768   if (tag != NULL)
2769     {
2770       info = gtk_text_btree_get_existing_tag_info (tree, tag);
2771
2772       if (info->tag_root == NULL)
2773         return NULL;
2774
2775       node = info->tag_root;
2776       /* We know the tag root has instances of the given
2777          tag below it */
2778
2779       while (node->level > 0)
2780         {
2781           g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2782           last_node = NULL;
2783           node = node->children.node;
2784           while (node != NULL)
2785             {
2786               if (gtk_text_btree_node_has_tag (node, tag))
2787                 last_node = node;
2788               node = node->next;
2789             }
2790
2791           node = last_node;
2792         }
2793
2794       g_assert (node != NULL); /* The tag summaries said some node had
2795                                   tag toggles... */
2796
2797       g_assert (node->level == 0);
2798
2799       /* Find the last line in this node */
2800       line = node->children.line;
2801       while (line->next != NULL)
2802         line = line->next;
2803
2804       return line;
2805     }
2806   else
2807     {
2808       /* This search can't be done efficiently at the moment,
2809          at least not without complexity.
2810          So, we just return the last line.
2811       */
2812       return gtk_text_btree_get_line (tree, -1, NULL);
2813     }
2814 }
2815
2816
2817 /*
2818  * Lines
2819  */
2820
2821 gint
2822 gtk_text_line_get_number (GtkTextLine *line)
2823 {
2824   GtkTextLine *line2;
2825   GtkTextBTreeNode *node, *parent, *node2;
2826   int index;
2827
2828   /*
2829    * First count how many lines precede this one in its level-0
2830    * GtkTextBTreeNode.
2831    */
2832
2833   node = line->parent;
2834   index = 0;
2835   for (line2 = node->children.line; line2 != line;
2836        line2 = line2->next)
2837     {
2838       if (line2 == NULL)
2839         {
2840           g_error ("gtk_text_btree_line_number couldn't find line");
2841         }
2842       index += 1;
2843     }
2844
2845   /*
2846    * Now work up through the levels of the tree one at a time,
2847    * counting how many lines are in GtkTextBTreeNodes preceding the current
2848    * GtkTextBTreeNode.
2849    */
2850
2851   for (parent = node->parent ; parent != NULL;
2852        node = parent, parent = parent->parent)
2853     {
2854       for (node2 = parent->children.node; node2 != node;
2855            node2 = node2->next)
2856         {
2857           if (node2 == NULL)
2858             {
2859               g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2860             }
2861           index += node2->num_lines;
2862         }
2863     }
2864   return index;
2865 }
2866
2867 static GtkTextLineSegment*
2868 find_toggle_segment_before_char (GtkTextLine *line,
2869                                  gint char_in_line,
2870                                  GtkTextTag *tag)
2871 {
2872   GtkTextLineSegment *seg;
2873   GtkTextLineSegment *toggle_seg;
2874   int index;
2875
2876   toggle_seg = NULL;
2877   index = 0;
2878   seg = line->segments;
2879   while ( (index + seg->char_count) <= char_in_line )
2880     {
2881       if (((seg->type == &gtk_text_toggle_on_type)
2882            || (seg->type == &gtk_text_toggle_off_type))
2883           && (seg->body.toggle.info->tag == tag))
2884         toggle_seg = seg;
2885
2886       index += seg->char_count;
2887       seg = seg->next;
2888     }
2889
2890   return toggle_seg;
2891 }
2892
2893 static GtkTextLineSegment*
2894 find_toggle_segment_before_byte (GtkTextLine *line,
2895                                  gint byte_in_line,
2896                                  GtkTextTag *tag)
2897 {
2898   GtkTextLineSegment *seg;
2899   GtkTextLineSegment *toggle_seg;
2900   int index;
2901
2902   toggle_seg = NULL;
2903   index = 0;
2904   seg = line->segments;
2905   while ( (index + seg->byte_count) <= byte_in_line )
2906     {
2907       if (((seg->type == &gtk_text_toggle_on_type)
2908            || (seg->type == &gtk_text_toggle_off_type))
2909           && (seg->body.toggle.info->tag == tag))
2910         toggle_seg = seg;
2911
2912       index += seg->byte_count;
2913       seg = seg->next;
2914     }
2915
2916   return toggle_seg;
2917 }
2918
2919 static gboolean
2920 find_toggle_outside_current_line (GtkTextLine *line,
2921                                   GtkTextBTree *tree,
2922                                   GtkTextTag *tag)
2923 {
2924   GtkTextBTreeNode *node;
2925   GtkTextLine *sibling_line;
2926   GtkTextLineSegment *seg;
2927   GtkTextLineSegment *toggle_seg;
2928   int toggles;
2929   GtkTextTagInfo *info = NULL;
2930
2931   /*
2932    * No toggle in this line.  Look for toggles for the tag in lines
2933    * that are predecessors of line but under the same
2934    * level-0 GtkTextBTreeNode.
2935    */
2936   toggle_seg = NULL;
2937   sibling_line = line->parent->children.line;
2938   while (sibling_line != line)
2939     {
2940       seg = sibling_line->segments;
2941       while (seg != NULL)
2942         {
2943           if (((seg->type == &gtk_text_toggle_on_type)
2944                || (seg->type == &gtk_text_toggle_off_type))
2945               && (seg->body.toggle.info->tag == tag))
2946             toggle_seg = seg;
2947
2948           seg = seg->next;
2949         }
2950
2951       sibling_line = sibling_line->next;
2952     }
2953
2954   if (toggle_seg != NULL)
2955     return (toggle_seg->type == &gtk_text_toggle_on_type);
2956
2957   /*
2958    * No toggle in this GtkTextBTreeNode.  Scan upwards through the ancestors of
2959    * this GtkTextBTreeNode, counting the number of toggles of the given tag in
2960    * siblings that precede that GtkTextBTreeNode.
2961    */
2962
2963   info = gtk_text_btree_get_existing_tag_info (tree, tag);
2964
2965   if (info == NULL)
2966     return FALSE;
2967
2968   toggles = 0;
2969   node = line->parent;
2970   while (node->parent != NULL)
2971     {
2972       GtkTextBTreeNode *sibling_node;
2973
2974       sibling_node = node->parent->children.node;
2975       while (sibling_node != node)
2976         {
2977           Summary *summary;
2978
2979           summary = sibling_node->summary;
2980           while (summary != NULL)
2981             {
2982               if (summary->info == info)
2983                 toggles += summary->toggle_count;
2984
2985               summary = summary->next;
2986             }
2987
2988           sibling_node = sibling_node->next;
2989         }
2990
2991       if (node == info->tag_root)
2992         break;
2993
2994       node = node->parent;
2995     }
2996
2997   /*
2998    * An odd number of toggles means that the tag is present at the
2999    * given point.
3000    */
3001
3002   return (toggles & 1) != 0;
3003 }
3004
3005 /* FIXME this function is far too slow, for no good reason. */
3006 gboolean
3007 gtk_text_line_char_has_tag (GtkTextLine *line,
3008                             GtkTextBTree *tree,
3009                             gint char_in_line,
3010                             GtkTextTag *tag)
3011 {
3012   GtkTextLineSegment *toggle_seg;
3013
3014   g_return_val_if_fail (line != NULL, FALSE);
3015
3016   /*
3017    * Check for toggles for the tag in the line but before
3018    * the char.  If there is one, its type indicates whether or
3019    * not the character is tagged.
3020    */
3021
3022   toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3023
3024   if (toggle_seg != NULL)
3025     return (toggle_seg->type == &gtk_text_toggle_on_type);
3026   else
3027     return find_toggle_outside_current_line (line, tree, tag);
3028 }
3029
3030 gboolean
3031 gtk_text_line_byte_has_tag (GtkTextLine *line,
3032                             GtkTextBTree *tree,
3033                             gint byte_in_line,
3034                             GtkTextTag *tag)
3035 {
3036   GtkTextLineSegment *toggle_seg;
3037
3038   g_return_val_if_fail (line != NULL, FALSE);
3039
3040   /*
3041    * Check for toggles for the tag in the line but before
3042    * the char.  If there is one, its type indicates whether or
3043    * not the character is tagged.
3044    */
3045
3046   toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3047
3048   if (toggle_seg != NULL)
3049     return (toggle_seg->type == &gtk_text_toggle_on_type);
3050   else
3051     return find_toggle_outside_current_line (line, tree, tag);
3052 }
3053
3054 gboolean
3055 gtk_text_line_is_last (GtkTextLine *line,
3056                        GtkTextBTree *tree)
3057 {
3058   return line == get_last_line (tree);
3059 }
3060
3061 GtkTextLine*
3062 gtk_text_line_next (GtkTextLine *line)
3063 {
3064   GtkTextBTreeNode *node;
3065
3066   if (line->next != NULL)
3067     return line->next;
3068   else
3069     {
3070       /*
3071        * This was the last line associated with the particular parent
3072        * GtkTextBTreeNode.  Search up the tree for the next GtkTextBTreeNode,
3073        * then search down from that GtkTextBTreeNode to find the first
3074        * line.
3075        */
3076
3077       node = line->parent;
3078       while (node != NULL && node->next == NULL)
3079         node = node->parent;
3080
3081       if (node == NULL)
3082         return NULL;
3083
3084       node = node->next;
3085       while (node->level > 0)
3086         {
3087           node = node->children.node;
3088         }
3089
3090       g_assert (node->children.line != line);
3091
3092       return node->children.line;
3093     }
3094 }
3095
3096 GtkTextLine*
3097 gtk_text_line_previous (GtkTextLine *line)
3098 {
3099   GtkTextBTreeNode *node;
3100   GtkTextBTreeNode *node2;
3101   GtkTextLine *prev;
3102
3103   /*
3104    * Find the line under this GtkTextBTreeNode just before the starting line.
3105    */
3106   prev = line->parent->children.line;        /* First line at leaf */
3107   while (prev != line)
3108     {
3109       if (prev->next == line)
3110         return prev;
3111
3112       prev = prev->next;
3113
3114       if (prev == NULL)
3115         g_error ("gtk_text_btree_previous_line ran out of lines");
3116     }
3117
3118   /*
3119    * This was the first line associated with the particular parent
3120    * GtkTextBTreeNode.  Search up the tree for the previous GtkTextBTreeNode,
3121    * then search down from that GtkTextBTreeNode to find its last line.
3122    */
3123   for (node = line->parent; ; node = node->parent)
3124     {
3125       if (node == NULL || node->parent == NULL)
3126         return NULL;
3127       else if (node != node->parent->children.node)
3128         break;
3129     }
3130
3131   for (node2 = node->parent->children.node; ;
3132        node2 = node2->children.node)
3133     {
3134       while (node2->next != node)
3135         node2 = node2->next;
3136
3137       if (node2->level == 0)
3138         break;
3139
3140       node = NULL;
3141     }
3142
3143   for (prev = node2->children.line ; ; prev = prev->next)
3144     {
3145       if (prev->next == NULL)
3146         return prev;
3147     }
3148
3149   g_assert_not_reached ();
3150   return NULL;
3151 }
3152
3153 void
3154 gtk_text_line_add_data (GtkTextLine     *line,
3155                         GtkTextLineData *data)
3156 {
3157   g_return_if_fail (line != NULL);
3158   g_return_if_fail (data != NULL);
3159   g_return_if_fail (data->view_id != NULL);
3160
3161   if (line->views)
3162     {
3163       data->next = line->views;
3164       line->views = data;
3165     }
3166   else
3167     {
3168       line->views = data;
3169     }
3170 }
3171
3172 gpointer
3173 gtk_text_line_remove_data (GtkTextLine *line,
3174                            gpointer view_id)
3175 {
3176   GtkTextLineData *prev;
3177   GtkTextLineData *iter;
3178
3179   g_return_val_if_fail (line != NULL, NULL);
3180   g_return_val_if_fail (view_id != NULL, NULL);
3181
3182   prev = NULL;
3183   iter = line->views;
3184   while (iter != NULL)
3185     {
3186       if (iter->view_id == view_id)
3187         break;
3188       prev = iter;
3189       iter = iter->next;
3190     }
3191
3192   if (iter)
3193     {
3194       if (prev)
3195         prev->next = iter->next;
3196       else
3197         line->views = iter->next;
3198
3199       return iter;
3200     }
3201   else
3202     return NULL;
3203 }
3204
3205 gpointer
3206 gtk_text_line_get_data (GtkTextLine *line,
3207                         gpointer view_id)
3208 {
3209   GtkTextLineData *iter;
3210
3211   g_return_val_if_fail (line != NULL, NULL);
3212   g_return_val_if_fail (view_id != NULL, NULL);
3213
3214   iter = line->views;
3215   while (iter != NULL)
3216     {
3217       if (iter->view_id == view_id)
3218         break;
3219       iter = iter->next;
3220     }
3221
3222   return iter;
3223 }
3224
3225 void
3226 gtk_text_line_invalidate_wrap (GtkTextLine *line,
3227                                GtkTextLineData *ld)
3228 {
3229   /* For now this is totally unoptimized. FIXME?
3230
3231      We could probably optimize the case where the width removed
3232      is less than the max width for the parent node,
3233      and the case where the height is unchanged when we re-wrap.
3234   */
3235
3236   g_return_if_fail (ld != NULL);
3237
3238   ld->valid = FALSE;
3239   gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3240 }
3241
3242 gint
3243 gtk_text_line_char_count (GtkTextLine *line)
3244 {
3245   GtkTextLineSegment *seg;
3246   gint size;
3247
3248   size = 0;
3249   seg = line->segments;
3250   while (seg != NULL)
3251     {
3252       size += seg->char_count;
3253       seg = seg->next;
3254     }
3255   return size;
3256 }
3257
3258 gint
3259 gtk_text_line_byte_count (GtkTextLine *line)
3260 {
3261   GtkTextLineSegment *seg;
3262   gint size;
3263
3264   size = 0;
3265   seg = line->segments;
3266   while (seg != NULL)
3267     {
3268       size += seg->byte_count;
3269       seg = seg->next;
3270     }
3271
3272   return size;
3273 }
3274
3275 gint
3276 gtk_text_line_char_index (GtkTextLine *target_line)
3277 {
3278   GSList *node_stack = NULL;
3279   GtkTextBTreeNode *iter;
3280   GtkTextLine *line;
3281   gint num_chars;
3282
3283   /* Push all our parent nodes onto a stack */
3284   iter = target_line->parent;
3285
3286   g_assert (iter != NULL);
3287
3288   while (iter != NULL)
3289     {
3290       node_stack = g_slist_prepend (node_stack, iter);
3291
3292       iter = iter->parent;
3293     }
3294
3295   /* Check that we have the root node on top of the stack. */
3296   g_assert (node_stack != NULL &&
3297             node_stack->data != NULL &&
3298             ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3299
3300   /* Add up chars in all nodes before the nodes in our stack.
3301    */
3302
3303   num_chars = 0;
3304   iter = node_stack->data;
3305   while (iter != NULL)
3306     {
3307       GtkTextBTreeNode *child_iter;
3308       GtkTextBTreeNode *next_node;
3309
3310       next_node = node_stack->next ?
3311         node_stack->next->data : NULL;
3312       node_stack = g_slist_remove (node_stack, node_stack->data);
3313
3314       if (iter->level == 0)
3315         {
3316           /* stack should be empty when we're on the last node */
3317           g_assert (node_stack == NULL);
3318           break; /* Our children are now lines */
3319         }
3320
3321       g_assert (next_node != NULL);
3322       g_assert (iter != NULL);
3323       g_assert (next_node->parent == iter);
3324
3325       /* Add up chars before us in the tree */
3326       child_iter = iter->children.node;
3327       while (child_iter != next_node)
3328         {
3329           g_assert (child_iter != NULL);
3330
3331           num_chars += child_iter->num_chars;
3332
3333           child_iter = child_iter->next;
3334         }
3335
3336       iter = next_node;
3337     }
3338
3339   g_assert (iter != NULL);
3340   g_assert (iter == target_line->parent);
3341
3342   /* Since we don't store char counts in lines, only in segments, we
3343      have to iterate over the lines adding up segment char counts
3344      until we find our line.  */
3345   line = iter->children.line;
3346   while (line != target_line)
3347     {
3348       g_assert (line != NULL);
3349
3350       num_chars += gtk_text_line_char_count (line);
3351
3352       line = line->next;
3353     }
3354
3355   g_assert (line == target_line);
3356
3357   return num_chars;
3358 }
3359
3360 GtkTextLineSegment*
3361 gtk_text_line_byte_to_segment (GtkTextLine *line,
3362                                gint byte_offset,
3363                                gint *seg_offset)
3364 {
3365   GtkTextLineSegment *seg;
3366   int offset;
3367
3368   g_return_val_if_fail (line != NULL, NULL);
3369
3370   offset = byte_offset;
3371   seg = line->segments;
3372
3373   while (offset >= seg->byte_count)
3374     {
3375       g_assert (seg != NULL); /* means an invalid byte index */
3376       offset -= seg->byte_count;
3377       seg = seg->next;
3378     }
3379
3380   if (seg_offset)
3381     *seg_offset = offset;
3382
3383   return seg;
3384 }
3385
3386 GtkTextLineSegment*
3387 gtk_text_line_char_to_segment (GtkTextLine *line,
3388                                gint char_offset,
3389                                gint *seg_offset)
3390 {
3391   GtkTextLineSegment *seg;
3392   int offset;
3393
3394   g_return_val_if_fail (line != NULL, NULL);
3395
3396   offset = char_offset;
3397   seg = line->segments;
3398
3399   while (offset >= seg->char_count)
3400     {
3401       g_assert (seg != NULL); /* means an invalid char index */
3402       offset -= seg->char_count;
3403       seg = seg->next;
3404     }
3405
3406   if (seg_offset)
3407     *seg_offset = offset;
3408
3409   return seg;
3410 }
3411
3412 GtkTextLineSegment*
3413 gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3414                                    gint byte_offset,
3415                                    gint *seg_offset)
3416 {
3417   GtkTextLineSegment *seg;
3418   int offset;
3419
3420   g_return_val_if_fail (line != NULL, NULL);
3421
3422   offset = byte_offset;
3423   seg = line->segments;
3424
3425   while (offset > 0 && offset >= seg->byte_count)
3426     {
3427       g_assert (seg != NULL); /* means an invalid byte index */
3428       offset -= seg->byte_count;
3429       seg = seg->next;
3430     }
3431
3432   if (seg_offset)
3433     *seg_offset = offset;
3434
3435   return seg;
3436 }
3437
3438 GtkTextLineSegment*
3439 gtk_text_line_char_to_any_segment (GtkTextLine *line,
3440                                    gint char_offset,
3441                                    gint *seg_offset)
3442 {
3443   GtkTextLineSegment *seg;
3444   int offset;
3445
3446   g_return_val_if_fail (line != NULL, NULL);
3447
3448   offset = char_offset;
3449   seg = line->segments;
3450
3451   while (offset > 0 && offset >= seg->char_count)
3452     {
3453       g_assert (seg != NULL); /* means an invalid byte index */
3454       offset -= seg->char_count;
3455       seg = seg->next;
3456     }
3457
3458   if (seg_offset)
3459     *seg_offset = offset;
3460
3461   return seg;
3462 }
3463
3464 gint
3465 gtk_text_line_byte_to_char (GtkTextLine *line,
3466                             gint byte_offset)
3467 {
3468   gint char_offset;
3469   GtkTextLineSegment *seg;
3470
3471   g_return_val_if_fail (line != NULL, 0);
3472   g_return_val_if_fail (byte_offset >= 0, 0);
3473
3474   char_offset = 0;
3475   seg = line->segments;
3476   while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3477                                             the next segment) */
3478     {
3479       g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3480
3481       byte_offset -= seg->byte_count;
3482       char_offset += seg->char_count;
3483
3484       seg = seg->next;
3485     }
3486
3487   g_assert (seg != NULL);
3488
3489   /* Now byte_offset is the offset into the current segment,
3490      and char_offset is the start of the current segment.
3491      Optimize the case where no chars use > 1 byte */
3492   if (seg->byte_count == seg->char_count)
3493     return char_offset + byte_offset;
3494   else
3495     {
3496       if (seg->type == &gtk_text_char_type)
3497         return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3498       else
3499         {
3500           g_assert (seg->char_count == 1);
3501           g_assert (byte_offset == 0);
3502
3503           return char_offset;
3504         }
3505     }
3506 }
3507
3508 gint
3509 gtk_text_line_char_to_byte (GtkTextLine *line,
3510                             gint         char_offset)
3511 {
3512   g_warning ("FIXME not implemented");
3513
3514   return 0;
3515 }
3516
3517 /* FIXME sync with char_locate (or figure out a clean
3518    way to merge the two functions) */
3519 void
3520 gtk_text_line_byte_locate (GtkTextLine *line,
3521                            gint byte_offset,
3522                            GtkTextLineSegment **segment,
3523                            GtkTextLineSegment **any_segment,
3524                            gint *seg_byte_offset,
3525                            gint *line_byte_offset)
3526 {
3527   GtkTextLineSegment *seg;
3528   GtkTextLineSegment *after_prev_indexable;
3529   GtkTextLineSegment *after_last_indexable;
3530   GtkTextLineSegment *last_indexable;
3531   gint offset;
3532   gint bytes_in_line;
3533
3534   g_return_if_fail (line != NULL);
3535
3536   if (byte_offset < 0)
3537     {
3538       /* -1 means end of line; we here assume no line is
3539          longer than 1 bazillion bytes, of course we assumed
3540          that anyway since we'd wrap around... */
3541
3542       byte_offset = G_MAXINT;
3543     }
3544
3545   *segment = NULL;
3546   *any_segment = NULL;
3547   bytes_in_line = 0;
3548
3549   offset = byte_offset;
3550
3551   last_indexable = NULL;
3552   after_last_indexable = line->segments;
3553   after_prev_indexable = line->segments;
3554   seg = line->segments;
3555
3556   /* The loop ends when we're inside a segment;
3557      last_indexable refers to the last segment
3558      we passed entirely. */
3559   while (seg && offset >= seg->byte_count)
3560     {
3561       if (seg->char_count > 0)
3562         {
3563           offset -= seg->byte_count;
3564           bytes_in_line += seg->byte_count;
3565           last_indexable = seg;
3566           after_prev_indexable = after_last_indexable;
3567           after_last_indexable = last_indexable->next;
3568         }
3569
3570       seg = seg->next;
3571     }
3572
3573   if (seg == NULL)
3574     {
3575       /* We went off the end of the line */
3576       *segment = last_indexable;
3577       *any_segment = after_prev_indexable;
3578       /* subtracting 1 is OK, we know it's a newline at the end. */
3579       offset = (*segment)->byte_count - 1;
3580       bytes_in_line -= (*segment)->byte_count;
3581     }
3582   else
3583     {
3584       *segment = seg;
3585       if (after_last_indexable != NULL)
3586         *any_segment = after_last_indexable;
3587       else
3588         *any_segment = *segment;
3589     }
3590
3591   /* Override any_segment if we're in the middle of a segment. */
3592   if (offset > 0)
3593     *any_segment = *segment;
3594
3595   *seg_byte_offset = offset;
3596
3597   g_assert (*segment != NULL);
3598   g_assert (*any_segment != NULL);
3599   g_assert (*seg_byte_offset < (*segment)->byte_count);
3600
3601   *line_byte_offset = bytes_in_line + *seg_byte_offset;
3602 }
3603
3604 /* FIXME sync with byte_locate (or figure out a clean
3605    way to merge the two functions) */
3606 void
3607 gtk_text_line_char_locate     (GtkTextLine     *line,
3608                                gint              char_offset,
3609                                GtkTextLineSegment **segment,
3610                                GtkTextLineSegment **any_segment,
3611                                gint             *seg_char_offset,
3612                                gint             *line_char_offset)
3613 {
3614   GtkTextLineSegment *seg;
3615   GtkTextLineSegment *after_prev_indexable;
3616   GtkTextLineSegment *after_last_indexable;
3617   GtkTextLineSegment *last_indexable;
3618   gint offset;
3619   gint chars_in_line;
3620
3621   g_return_if_fail (line != NULL);
3622
3623   if (char_offset < 0)
3624     {
3625       /* -1 means end of line; we here assume no line is
3626          longer than 1 bazillion chars, of course we assumed
3627          that anyway since we'd wrap around... */
3628
3629       char_offset = G_MAXINT;
3630     }
3631
3632   *segment = NULL;
3633   *any_segment = NULL;
3634   chars_in_line = 0;
3635
3636   offset = char_offset;
3637
3638   last_indexable = NULL;
3639   after_last_indexable = line->segments;
3640   after_prev_indexable = line->segments;
3641   seg = line->segments;
3642
3643   /* The loop ends when we're inside a segment;
3644      last_indexable refers to the last segment
3645      we passed entirely. */
3646   while (seg && offset >= seg->char_count)
3647     {
3648       if (seg->char_count > 0)
3649         {
3650           offset -= seg->char_count;
3651           chars_in_line += seg->char_count;
3652           last_indexable = seg;
3653           after_prev_indexable = after_last_indexable;
3654           after_last_indexable = last_indexable->next;
3655         }
3656
3657       seg = seg->next;
3658     }
3659
3660   if (seg == NULL)
3661     {
3662       /* We went off the end of the line */
3663       *segment = last_indexable;
3664       *any_segment = after_prev_indexable;
3665       /* subtracting 1 is OK, we know it's a newline at the end. */
3666       offset = (*segment)->char_count - 1;
3667       chars_in_line -= (*segment)->char_count;
3668     }
3669   else
3670     {
3671       *segment = seg;
3672       if (after_last_indexable != NULL)
3673         *any_segment = after_last_indexable;
3674       else
3675         *any_segment = *segment;
3676     }
3677
3678   /* Override any_segment if we're in the middle of a segment. */
3679   if (offset > 0)
3680     *any_segment = *segment;
3681
3682   *seg_char_offset = offset;
3683
3684   g_assert (*segment != NULL);
3685   g_assert (*any_segment != NULL);
3686   g_assert (*seg_char_offset < (*segment)->char_count);
3687
3688   *line_char_offset = chars_in_line + *seg_char_offset;
3689 }
3690
3691 void
3692 gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3693                                     gint byte_offset,
3694                                     gint *line_char_offset,
3695                                     gint *seg_char_offset)
3696 {
3697   GtkTextLineSegment *seg;
3698   int offset;
3699
3700   g_return_if_fail (line != NULL);
3701   g_return_if_fail (byte_offset >= 0);
3702
3703   *line_char_offset = 0;
3704
3705   offset = byte_offset;
3706   seg = line->segments;
3707
3708   while (offset >= seg->byte_count)
3709     {
3710       offset -= seg->byte_count;
3711       *line_char_offset += seg->char_count;
3712       seg = seg->next;
3713       g_assert (seg != NULL); /* means an invalid char offset */
3714     }
3715
3716   g_assert (seg->char_count > 0); /* indexable. */
3717
3718   /* offset is now the number of bytes into the current segment we
3719      want to go. Count chars into the current segment. */
3720
3721   if (seg->type == &gtk_text_char_type)
3722     {
3723       *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3724
3725       g_assert (*seg_char_offset < seg->char_count);
3726
3727       *line_char_offset += *seg_char_offset;
3728     }
3729   else
3730     {
3731       g_assert (offset == 0);
3732       *seg_char_offset = 0;
3733     }
3734 }
3735
3736 void
3737 gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3738                                     gint char_offset,
3739                                     gint *line_byte_offset,
3740                                     gint *seg_byte_offset)
3741 {
3742   GtkTextLineSegment *seg;
3743   int offset;
3744
3745   g_return_if_fail (line != NULL);
3746   g_return_if_fail (char_offset >= 0);
3747
3748   *line_byte_offset = 0;
3749
3750   offset = char_offset;
3751   seg = line->segments;
3752
3753   while (offset >= seg->char_count)
3754     {
3755       offset -= seg->char_count;
3756       *line_byte_offset += seg->byte_count;
3757       seg = seg->next;
3758       g_assert (seg != NULL); /* means an invalid char offset */
3759     }
3760
3761   g_assert (seg->char_count > 0); /* indexable. */
3762
3763   /* offset is now the number of chars into the current segment we
3764      want to go. Count bytes into the current segment. */
3765
3766   if (seg->type == &gtk_text_char_type)
3767     {
3768       *seg_byte_offset = 0;
3769       while (offset > 0)
3770         {
3771           gint bytes;
3772           const char * start = seg->body.chars + *seg_byte_offset;
3773
3774           bytes = g_utf8_next_char (start) - start;
3775           *seg_byte_offset += bytes;
3776           offset -= 1;
3777         }
3778
3779       g_assert (*seg_byte_offset < seg->byte_count);
3780
3781       *line_byte_offset += *seg_byte_offset;
3782     }
3783   else
3784     {
3785       g_assert (offset == 0);
3786       *seg_byte_offset = 0;
3787     }
3788 }
3789
3790 static gint
3791 node_compare (GtkTextBTreeNode *lhs,
3792               GtkTextBTreeNode *rhs)
3793 {
3794   GtkTextBTreeNode *iter;
3795   GtkTextBTreeNode *node;
3796   GtkTextBTreeNode *common_parent;
3797   GtkTextBTreeNode *parent_of_lower;
3798   GtkTextBTreeNode *parent_of_higher;
3799   gboolean lhs_is_lower;
3800   GtkTextBTreeNode *lower;
3801   GtkTextBTreeNode *higher;
3802
3803   /* This function assumes that lhs and rhs are not underneath each
3804    * other.
3805    */
3806
3807   if (lhs == rhs)
3808     return 0;
3809
3810   if (lhs->level < rhs->level)
3811     {
3812       lhs_is_lower = TRUE;
3813       lower = lhs;
3814       higher = rhs;
3815     }
3816   else
3817     {
3818       lhs_is_lower = FALSE;
3819       lower = rhs;
3820       higher = lhs;
3821     }
3822
3823   /* Algorithm: find common parent of lhs/rhs. Save the child nodes
3824    * of the common parent we used to reach the common parent; the
3825    * ordering of these child nodes in the child list is the ordering
3826    * of lhs and rhs.
3827    */
3828
3829   /* Get on the same level (may be on same level already) */
3830   node = lower;
3831   while (node->level < higher->level)
3832     node = node->parent;
3833
3834   g_assert (node->level == higher->level);
3835
3836   g_assert (node != higher); /* Happens if lower is underneath higher */
3837
3838   /* Go up until we have two children with a common parent.
3839    */
3840   parent_of_lower = node;
3841   parent_of_higher = higher;
3842
3843   while (parent_of_lower->parent != parent_of_higher->parent)
3844     {
3845       parent_of_lower = parent_of_lower->parent;
3846       parent_of_higher = parent_of_higher->parent;
3847     }
3848
3849   g_assert (parent_of_lower->parent == parent_of_higher->parent);
3850
3851   common_parent = parent_of_lower->parent;
3852
3853   g_assert (common_parent != NULL);
3854
3855   /* See which is first in the list of common_parent's children */
3856   iter = common_parent->children.node;
3857   while (iter != NULL)
3858     {
3859       if (iter == parent_of_higher)
3860         {
3861           /* higher is less than lower */
3862
3863           if (lhs_is_lower)
3864             return 1; /* lhs > rhs */
3865           else
3866             return -1;
3867         }
3868       else if (iter == parent_of_lower)
3869         {
3870           /* lower is less than higher */
3871
3872           if (lhs_is_lower)
3873             return -1; /* lhs < rhs */
3874           else
3875             return 1;
3876         }
3877
3878       iter = iter->next;
3879     }
3880
3881   g_assert_not_reached ();
3882   return 0;
3883 }
3884
3885 /* remember that tag == NULL means "any tag" */
3886 GtkTextLine*
3887 gtk_text_line_next_could_contain_tag (GtkTextLine *line,
3888                                       GtkTextBTree *tree,
3889                                       GtkTextTag  *tag)
3890 {
3891   GtkTextBTreeNode *node;
3892   GtkTextTagInfo *info;
3893   gboolean below_tag_root;
3894
3895   g_return_val_if_fail (line != NULL, NULL);
3896
3897   if (gtk_debug_flags & GTK_DEBUG_TEXT)
3898     gtk_text_btree_check (tree);
3899
3900   if (tag == NULL)
3901     {
3902       /* Right now we can only offer linear-search if the user wants
3903        * to know about any tag toggle at all.
3904        */
3905       return gtk_text_line_next (line);
3906     }
3907
3908   /* Our tag summaries only have node precision, not line
3909      precision. This means that if any line under a node could contain a
3910      tag, then any of the others could also contain a tag.
3911
3912      In the future we could have some mechanism to keep track of how
3913      many toggles we've found under a node so far, since we have a
3914      count of toggles under the node. But for now I'm going with KISS.
3915   */
3916
3917   /* return same-node line, if any. */
3918   if (line->next)
3919     return line->next;
3920
3921   info = gtk_text_btree_get_existing_tag_info (tree, tag);
3922   if (info == NULL)
3923     return NULL;
3924
3925   if (info->tag_root == NULL)
3926     return NULL;
3927
3928   if (info->tag_root == line->parent)
3929     return NULL; /* we were at the last line under the tag root */
3930
3931   /* We need to go up out of this node, and on to the next one with
3932      toggles for the target tag. If we're below the tag root, we need to
3933      find the next node below the tag root that has tag summaries. If
3934      we're not below the tag root, we need to see if the tag root is
3935      after us in the tree, and if so, return the first line underneath
3936      the tag root. */
3937
3938   node = line->parent;
3939   below_tag_root = FALSE;
3940   while (node != NULL)
3941     {
3942       if (node == info->tag_root)
3943         {
3944           below_tag_root = TRUE;
3945           break;
3946         }
3947
3948       node = node->parent;
3949     }
3950
3951   if (below_tag_root)
3952     {
3953       node = line->parent;
3954       while (node != info->tag_root)
3955         {
3956           if (node->next == NULL)
3957             node = node->parent;
3958           else
3959             {
3960               node = node->next;
3961
3962               if (gtk_text_btree_node_has_tag (node, tag))
3963                 goto found;
3964             }
3965         }
3966       return NULL;
3967     }
3968   else
3969     {
3970       gint ordering;
3971
3972       ordering = node_compare (line->parent, info->tag_root);
3973
3974       if (ordering < 0)
3975         {
3976           /* Tag root is ahead of us, so search there. */
3977           node = info->tag_root;
3978           goto found;
3979         }
3980       else
3981         {
3982           /* Tag root is after us, so no more lines that
3983            * could contain the tag.
3984            */
3985           return NULL;
3986         }
3987
3988       g_assert_not_reached ();
3989     }
3990
3991  found:
3992
3993   g_assert (node != NULL);
3994
3995   /* We have to find the first sub-node of this node that contains
3996    * the target tag.
3997    */
3998
3999   while (node->level > 0)
4000     {
4001       g_assert (node != NULL); /* If this fails, it likely means an
4002                                   incorrect tag summary led us on a
4003                                   wild goose chase down this branch of
4004                                   the tree. */
4005       node = node->children.node;
4006       while (node != NULL)
4007         {
4008           if (gtk_text_btree_node_has_tag (node, tag))
4009             break;
4010           node = node->next;
4011         }
4012     }
4013
4014   g_assert (node != NULL);
4015   g_assert (node->level == 0);
4016
4017   return node->children.line;
4018 }
4019
4020 static GtkTextLine*
4021 prev_line_under_node (GtkTextBTreeNode *node,
4022                       GtkTextLine      *line)
4023 {
4024   GtkTextLine *prev;
4025
4026   prev = node->children.line;
4027
4028   g_assert (prev);
4029
4030   if (prev != line)
4031     {
4032       while (prev->next != line)
4033         prev = prev->next;
4034
4035       return prev;
4036     }
4037
4038   return NULL;
4039 }
4040
4041 GtkTextLine*
4042 gtk_text_line_previous_could_contain_tag (GtkTextLine  *line,
4043                                           GtkTextBTree *tree,
4044                                           GtkTextTag   *tag)
4045 {
4046   GtkTextBTreeNode *node;
4047   GtkTextBTreeNode *found_node = NULL;
4048   GtkTextTagInfo *info;
4049   gboolean below_tag_root;
4050   GtkTextLine *prev;
4051   GtkTextBTreeNode *line_ancestor;
4052   GtkTextBTreeNode *line_ancestor_parent;
4053
4054   /* See next_could_contain_tag () for more extensive comments
4055    * on what's going on here.
4056    */
4057
4058   g_return_val_if_fail (line != NULL, NULL);
4059
4060   if (gtk_debug_flags & GTK_DEBUG_TEXT)
4061     gtk_text_btree_check (tree);
4062
4063   if (tag == NULL)
4064     {
4065       /* Right now we can only offer linear-search if the user wants
4066        * to know about any tag toggle at all.
4067        */
4068       return gtk_text_line_previous (line);
4069     }
4070
4071   /* Return same-node line, if any. */
4072   prev = prev_line_under_node (line->parent, line);
4073   if (prev)
4074     return prev;
4075
4076   info = gtk_text_btree_get_existing_tag_info (tree, tag);
4077   if (info == NULL)
4078     return NULL;
4079
4080   if (info->tag_root == NULL)
4081     return NULL;
4082
4083   if (info->tag_root == line->parent)
4084     return NULL; /* we were at the first line under the tag root */
4085
4086   /* Are we below the tag root */
4087   node = line->parent;
4088   below_tag_root = FALSE;
4089   while (node != NULL)
4090     {
4091       if (node == info->tag_root)
4092         {
4093           below_tag_root = TRUE;
4094           break;
4095         }
4096
4097       node = node->parent;
4098     }
4099
4100   if (below_tag_root)
4101     {
4102       /* Look for a previous node under this tag root that has our
4103        * tag.
4104        */
4105
4106       /* this assertion holds because line->parent is not the
4107        * tag root, we are below the tag root, and the tag
4108        * root exists.
4109        */
4110       g_assert (line->parent->parent != NULL);
4111
4112       line_ancestor = line->parent;
4113       line_ancestor_parent = line->parent->parent;
4114
4115       node = line_ancestor_parent->children.node;
4116       while (node != line_ancestor &&
4117              line_ancestor != info->tag_root)
4118         {
4119           GSList *child_nodes = NULL;
4120           GSList *tmp;
4121
4122           /* Create reverse-order list of nodes before
4123            * line_ancestor
4124            */
4125           while (node != line_ancestor
4126                  && node != NULL)
4127             {
4128               child_nodes = g_slist_prepend (child_nodes, node);
4129
4130               node = node->next;
4131             }
4132
4133           /* Try to find a node with our tag on it in the list */
4134           tmp = child_nodes;
4135           while (tmp != NULL)
4136             {
4137               GtkTextBTreeNode *this_node = tmp->data;
4138
4139               g_assert (this_node != line_ancestor);
4140
4141               if (gtk_text_btree_node_has_tag (this_node, tag))
4142                 {
4143                   found_node = this_node;
4144                   g_slist_free (child_nodes);
4145                   goto found;
4146                 }
4147
4148               tmp = g_slist_next (tmp);
4149             }
4150
4151           g_slist_free (child_nodes);
4152
4153           /* Didn't find anything on this level; go up one level. */
4154           line_ancestor = line_ancestor_parent;
4155           line_ancestor_parent = line_ancestor->parent;
4156
4157           node = line_ancestor_parent->children.node;
4158         }
4159
4160       /* No dice. */
4161       return NULL;
4162     }
4163   else
4164     {
4165       gint ordering;
4166
4167       ordering = node_compare (line->parent, info->tag_root);
4168
4169       if (ordering < 0)
4170         {
4171           /* Tag root is ahead of us, so no more lines
4172            * with this tag.
4173            */
4174           return NULL;
4175         }
4176       else
4177         {
4178           /* Tag root is after us, so grab last tagged
4179            * line underneath the tag root.
4180            */
4181           found_node = info->tag_root;
4182           goto found;
4183         }
4184
4185       g_assert_not_reached ();
4186     }
4187
4188  found:
4189
4190   g_assert (found_node != NULL);
4191
4192   /* We have to find the last sub-node of this node that contains
4193    * the target tag.
4194    */
4195   node = found_node;
4196
4197   while (node->level > 0)
4198     {
4199       GSList *child_nodes = NULL;
4200       GSList *iter;
4201       g_assert (node != NULL); /* If this fails, it likely means an
4202                                   incorrect tag summary led us on a
4203                                   wild goose chase down this branch of
4204                                   the tree. */
4205
4206       node = node->children.node;
4207       while (node != NULL)
4208         {
4209           child_nodes = g_slist_prepend (child_nodes, node);
4210           node = node->next;
4211         }
4212
4213       node = NULL; /* detect failure to find a child node. */
4214
4215       iter = child_nodes;
4216       while (iter != NULL)
4217         {
4218           if (gtk_text_btree_node_has_tag (iter->data, tag))
4219             {
4220               /* recurse into this node. */
4221               node = iter->data;
4222               break;
4223             }
4224
4225           iter = g_slist_next (iter);
4226         }
4227
4228       g_slist_free (child_nodes);
4229
4230       g_assert (node != NULL);
4231     }
4232
4233   g_assert (node != NULL);
4234   g_assert (node->level == 0);
4235
4236   /* this assertion is correct, but slow. */
4237   /* g_assert (node_compare (node, line->parent) < 0); */
4238
4239   /* Return last line in this node. */
4240
4241   prev = node->children.line;
4242   while (prev->next)
4243     prev = prev->next;
4244
4245   return prev;
4246 }
4247
4248 /*
4249  * Non-public function implementations
4250  */
4251
4252 static void
4253 summary_list_destroy (Summary *summary)
4254 {
4255   Summary *next;
4256   while (summary != NULL)
4257     {
4258       next = summary->next;
4259       summary_destroy (summary);
4260       summary = next;
4261     }
4262 }
4263
4264 static GtkTextLine*
4265 get_last_line (GtkTextBTree *tree)
4266 {
4267   if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
4268     {
4269       gint n_lines;
4270       GtkTextLine *line;
4271       gint real_line;
4272
4273       n_lines = gtk_text_btree_line_count (tree);
4274
4275       g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4276
4277       line = gtk_text_btree_get_line (tree, n_lines, &real_line);
4278
4279       tree->end_iter_line_stamp = tree->chars_changed_stamp;
4280       tree->end_iter_line = line;
4281     }
4282
4283   return tree->end_iter_line;
4284 }
4285
4286 /*
4287  * Lines
4288  */
4289
4290 static GtkTextLine*
4291 gtk_text_line_new (void)
4292 {
4293   GtkTextLine *line;
4294
4295   line = g_new0(GtkTextLine, 1);
4296
4297   return line;
4298 }
4299
4300 static void
4301 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4302 {
4303   GtkTextLineData *ld;
4304   GtkTextLineData *next;
4305
4306   g_return_if_fail (line != NULL);
4307
4308   ld = line->views;
4309   while (ld != NULL)
4310     {
4311       BTreeView *view;
4312
4313       view = gtk_text_btree_get_view (tree, ld->view_id);
4314
4315       g_assert (view != NULL);
4316
4317       next = ld->next;
4318       gtk_text_layout_free_line_data (view->layout, line, ld);
4319
4320       ld = next;
4321     }
4322
4323   g_free (line);
4324 }
4325
4326 static void
4327 gtk_text_line_set_parent (GtkTextLine *line,
4328                           GtkTextBTreeNode *node)
4329 {
4330   if (line->parent == node)
4331     return;
4332   line->parent = node;
4333   gtk_text_btree_node_invalidate_upward (node, NULL);
4334 }
4335
4336 static void
4337 cleanup_line (GtkTextLine *line)
4338 {
4339   GtkTextLineSegment *seg, **prev_p;
4340   gboolean changed;
4341
4342   /*
4343    * Make a pass over all of the segments in the line, giving each
4344    * a chance to clean itself up.  This could potentially change
4345    * the structure of the line, e.g. by merging two segments
4346    * together or having two segments cancel themselves;  if so,
4347    * then repeat the whole process again, since the first structure
4348    * change might make other structure changes possible.  Repeat
4349    * until eventually there are no changes.
4350    */
4351
4352   changed = TRUE;
4353   while (changed)
4354     {
4355       changed = FALSE;
4356       for (prev_p = &line->segments, seg = *prev_p;
4357            seg != NULL;
4358            prev_p = &(*prev_p)->next, seg = *prev_p)
4359         {
4360           if (seg->type->cleanupFunc != NULL)
4361             {
4362               *prev_p = (*seg->type->cleanupFunc)(seg, line);
4363               if (seg != *prev_p)
4364                 changed = TRUE;
4365             }
4366         }
4367     }
4368 }
4369
4370 /*
4371  * Nodes
4372  */
4373
4374 static NodeData*
4375 node_data_new (gpointer view_id)
4376 {
4377   NodeData *nd;
4378
4379   nd = g_new (NodeData, 1);
4380
4381   nd->view_id = view_id;
4382   nd->next = NULL;
4383   nd->width = 0;
4384   nd->height = 0;
4385   nd->valid = FALSE;
4386
4387   return nd;
4388 }
4389
4390 static void
4391 node_data_destroy (NodeData *nd)
4392 {
4393
4394   g_free (nd);
4395 }
4396
4397 static void
4398 node_data_list_destroy (NodeData *nd)
4399 {
4400   NodeData *iter;
4401   NodeData *next;
4402
4403   iter = nd;
4404   while (iter != NULL)
4405     {
4406       next = iter->next;
4407       node_data_destroy (iter);
4408       iter = next;
4409     }
4410 }
4411
4412 static NodeData*
4413 node_data_find (NodeData *nd, gpointer view_id)
4414 {
4415   while (nd != NULL)
4416     {
4417       if (nd->view_id == view_id)
4418         break;
4419       nd = nd->next;
4420     }
4421   return nd;
4422 }
4423
4424 static void
4425 summary_destroy (Summary *summary)
4426 {
4427   /* Fill with error-triggering garbage */
4428   summary->info = (void*)0x1;
4429   summary->toggle_count = 567;
4430   summary->next = (void*)0x1;
4431   g_free (summary);
4432 }
4433
4434 static GtkTextBTreeNode*
4435 gtk_text_btree_node_new (void)
4436 {
4437   GtkTextBTreeNode *node;
4438
4439   node = g_new (GtkTextBTreeNode, 1);
4440
4441   node->node_data = NULL;
4442
4443   return node;
4444 }
4445
4446 static void
4447 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode  *node,
4448                                          GtkTextTagInfo  *info,
4449                                          gint adjust)
4450 {
4451   Summary *summary;
4452
4453   summary = node->summary;
4454   while (summary != NULL)
4455     {
4456       if (summary->info == info)
4457         {
4458           summary->toggle_count += adjust;
4459           break;
4460         }
4461
4462       summary = summary->next;
4463     }
4464
4465   if (summary == NULL)
4466     {
4467       /* didn't find a summary for our tag. */
4468       g_return_if_fail (adjust > 0);
4469       summary = g_new (Summary, 1);
4470       summary->info = info;
4471       summary->toggle_count = adjust;
4472       summary->next = node->summary;
4473       node->summary = summary;
4474     }
4475 }
4476
4477 /* Note that the tag root and above do not have summaries
4478    for the tag; only nodes below the tag root have
4479    the summaries. */
4480 static gboolean
4481 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4482 {
4483   Summary *summary;
4484
4485   summary = node->summary;
4486   while (summary != NULL)
4487     {
4488       if (tag == NULL ||
4489           summary->info->tag == tag)
4490         return TRUE;
4491
4492       summary = summary->next;
4493     }
4494
4495   return FALSE;
4496 }
4497
4498 /* Add node and all children to the damage region. */
4499 static void
4500 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4501 {
4502   NodeData *nd;
4503
4504   nd = node->node_data;
4505   while (nd != NULL)
4506     {
4507       nd->valid = FALSE;
4508       nd = nd->next;
4509     }
4510
4511   if (node->level == 0)
4512     {
4513       GtkTextLine *line;
4514
4515       line = node->children.line;
4516       while (line != NULL)
4517         {
4518           GtkTextLineData *ld;
4519
4520           ld = line->views;
4521           while (ld != NULL)
4522             {
4523               ld->valid = FALSE;
4524               ld = ld->next;
4525             }
4526
4527           line = line->next;
4528         }
4529     }
4530   else
4531     {
4532       GtkTextBTreeNode *child;
4533
4534       child = node->children.node;
4535
4536       while (child != NULL)
4537         {
4538           gtk_text_btree_node_invalidate_downward (child);
4539
4540           child = child->next;
4541         }
4542     }
4543 }
4544
4545 static void
4546 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4547 {
4548   GtkTextBTreeNode *iter;
4549
4550   iter = node;
4551   while (iter != NULL)
4552     {
4553       NodeData *nd;
4554
4555       if (view_id)
4556         {
4557           nd = node_data_find (iter->node_data, view_id);
4558
4559           if (nd == NULL || !nd->valid)
4560             break; /* Once a node is invalid, we know its parents are as well. */
4561
4562           nd->valid = FALSE;
4563         }
4564       else
4565         {
4566           gboolean should_continue = FALSE;
4567
4568           nd = iter->node_data;
4569           while (nd != NULL)
4570             {
4571               if (nd->valid)
4572                 {
4573                   should_continue = TRUE;
4574                   nd->valid = FALSE;
4575                 }
4576
4577               nd = nd->next;
4578             }
4579
4580           if (!should_continue)
4581             break; /* This node was totally invalidated, so are its
4582                       parents */
4583         }
4584
4585       iter = iter->parent;
4586     }
4587 }
4588
4589
4590 /**
4591  * gtk_text_btree_is_valid:
4592  * @tree: a #GtkTextBTree
4593  * @view_id: ID for the view
4594  *
4595  * Check to see if the entire #GtkTextBTree is valid or not for
4596  * the given view.
4597  *
4598  * Return value: %TRUE if the entire #GtkTextBTree is valid
4599  **/
4600 gboolean
4601 gtk_text_btree_is_valid (GtkTextBTree *tree,
4602                          gpointer      view_id)
4603 {
4604   NodeData *nd;
4605   g_return_val_if_fail (tree != NULL, FALSE);
4606
4607   nd = node_data_find (tree->root_node->node_data, view_id);
4608   return (nd && nd->valid);
4609 }
4610
4611 typedef struct _ValidateState ValidateState;
4612
4613 struct _ValidateState
4614 {
4615   gint remaining_pixels;
4616   gboolean in_validation;
4617   gint y;
4618   gint old_height;
4619   gint new_height;
4620 };
4621
4622 static void
4623 gtk_text_btree_node_validate (BTreeView         *view,
4624                               GtkTextBTreeNode  *node,
4625                               gpointer           view_id,
4626                               ValidateState     *state)
4627 {
4628   gint node_valid = TRUE;
4629   gint node_width = 0;
4630   gint node_height = 0;
4631
4632   NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4633   g_return_if_fail (!nd->valid);
4634
4635   if (node->level == 0)
4636     {
4637       GtkTextLine *line = node->children.line;
4638       GtkTextLineData *ld;
4639
4640       /* Iterate over leading valid lines */
4641       while (line != NULL)
4642         {
4643           ld = gtk_text_line_get_data (line, view_id);
4644
4645           if (!ld || !ld->valid)
4646             break;
4647           else if (state->in_validation)
4648             {
4649               state->in_validation = FALSE;
4650               return;
4651             }
4652           else
4653             {
4654               state->y += ld->height;
4655               node_width = MAX (ld->width, node_width);
4656               node_height += ld->height;
4657             }
4658
4659           line = line->next;
4660         }
4661
4662       state->in_validation = TRUE;
4663
4664       /* Iterate over invalid lines */
4665       while (line != NULL)
4666         {
4667           ld = gtk_text_line_get_data (line, view_id);
4668
4669           if (ld && ld->valid)
4670             break;
4671           else
4672             {
4673               if (ld)
4674                 state->old_height += ld->height;
4675               ld = gtk_text_layout_wrap (view->layout, line, ld);
4676               state->new_height += ld->height;
4677
4678               node_width = MAX (ld->width, node_width);
4679               node_height += ld->height;
4680
4681               state->remaining_pixels -= ld->height;
4682               if (state->remaining_pixels <= 0)
4683                 {
4684                   line = line->next;
4685                   break;
4686                 }
4687             }
4688
4689           line = line->next;
4690         }
4691
4692       /* Iterate over the remaining lines */
4693       while (line != NULL)
4694         {
4695           ld = gtk_text_line_get_data (line, view_id);
4696           state->in_validation = FALSE;
4697
4698           if (!ld || !ld->valid)
4699             node_valid = FALSE;
4700
4701           if (ld)
4702             {
4703               node_width = MAX (ld->width, node_width);
4704               node_height += ld->height;
4705             }
4706
4707           line = line->next;
4708         }
4709     }
4710   else
4711     {
4712       GtkTextBTreeNode *child;
4713       NodeData *child_nd;
4714
4715       child = node->children.node;
4716
4717       /* Iterate over leading valid nodes */
4718       while (child)
4719         {
4720           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4721
4722           if (!child_nd->valid)
4723             break;
4724           else if (state->in_validation)
4725             {
4726               state->in_validation = FALSE;
4727               return;
4728             }
4729           else
4730             {
4731               state->y += child_nd->height;
4732               node_width = MAX (node_width, child_nd->width);
4733               node_height += child_nd->height;
4734             }
4735
4736           child = child->next;
4737         }
4738
4739       /* Iterate over invalid nodes */
4740       while (child)
4741         {
4742           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4743
4744           if (child_nd->valid)
4745             break;
4746           else
4747             {
4748               gtk_text_btree_node_validate (view, child, view_id, state);
4749
4750               if (!child_nd->valid)
4751                 node_valid = FALSE;
4752               node_width = MAX (node_width, child_nd->width);
4753               node_height += child_nd->height;
4754
4755               if (!state->in_validation || state->remaining_pixels <= 0)
4756                 {
4757                   child = child->next;
4758                   break;
4759                 }
4760             }
4761
4762           child = child->next;
4763         }
4764
4765       /* Iterate over the remaining lines */
4766       while (child)
4767         {
4768           child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4769           state->in_validation = FALSE;
4770
4771           if (!child_nd->valid)
4772             node_valid = FALSE;
4773
4774           node_width = MAX (child_nd->width, node_width);
4775           node_height += child_nd->height;
4776
4777           child = child->next;
4778         }
4779     }
4780
4781   nd->width = node_width;
4782   nd->height = node_height;
4783   nd->valid = node_valid;
4784 }
4785
4786 /**
4787  * gtk_text_btree_validate:
4788  * @tree: a #GtkTextBTree
4789  * @view_id: view id
4790  * @max_pixels: the maximum number of pixels to validate. (No more
4791  *              than one paragraph beyond this limit will be validated)
4792  * @y: location to store starting y coordinate of validated region
4793  * @old_height: location to store old height of validated region
4794  * @new_height: location to store new height of validated region
4795  *
4796  * Validate a single contiguous invalid region of a #GtkTextBTree for
4797  * a given view.
4798  *
4799  * Return value: %TRUE if a region has been validated, %FALSE if the
4800  * entire tree was already valid.
4801  **/
4802 gboolean
4803 gtk_text_btree_validate (GtkTextBTree *tree,
4804                          gpointer      view_id,
4805                          gint          max_pixels,
4806                          gint         *y,
4807                          gint         *old_height,
4808                          gint         *new_height)
4809 {
4810   BTreeView *view;
4811
4812   g_return_val_if_fail (tree != NULL, FALSE);
4813
4814   view = gtk_text_btree_get_view (tree, view_id);
4815   g_return_val_if_fail (view != NULL, FALSE);
4816
4817   if (!gtk_text_btree_is_valid (tree, view_id))
4818     {
4819       ValidateState state;
4820
4821       state.remaining_pixels = max_pixels;
4822       state.in_validation = FALSE;
4823       state.y = 0;
4824       state.old_height = 0;
4825       state.new_height = 0;
4826
4827       gtk_text_btree_node_validate (view,
4828                                     tree->root_node,
4829                                     view_id, &state);
4830
4831       if (y)
4832         *y = state.y;
4833       if (old_height)
4834         *old_height = state.old_height;
4835       if (new_height)
4836         *new_height = state.new_height;
4837
4838       if (gtk_debug_flags & GTK_DEBUG_TEXT)
4839         gtk_text_btree_check (tree);
4840
4841       return TRUE;
4842     }
4843   else
4844     return FALSE;
4845 }
4846
4847 static void
4848 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
4849                                              gpointer          view_id,
4850                                              gint             *width_out,
4851                                              gint             *height_out,
4852                                              gboolean         *valid_out)
4853 {
4854   gint width = 0;
4855   gint height = 0;
4856   gboolean valid = TRUE;
4857
4858   if (node->level == 0)
4859     {
4860       GtkTextLine *line = node->children.line;
4861
4862       while (line != NULL)
4863         {
4864           GtkTextLineData *ld = gtk_text_line_get_data (line, view_id);
4865
4866           if (!ld || !ld->valid)
4867             valid = FALSE;
4868
4869           if (ld)
4870             {
4871               width = MAX (ld->width, width);
4872               height += ld->height;
4873             }
4874
4875           line = line->next;
4876         }
4877     }
4878   else
4879     {
4880       GtkTextBTreeNode *child = node->children.node;
4881
4882       while (child)
4883         {
4884           NodeData *child_nd = node_data_find (child->node_data, view_id);
4885
4886           if (!child_nd || !child_nd->valid)
4887             valid = FALSE;
4888
4889           if (child_nd)
4890             {
4891               width = MAX (child_nd->width, width);
4892               height += child_nd->height;
4893             }
4894
4895           child = child->next;
4896         }
4897     }
4898
4899   *width_out = width;
4900   *height_out = height;
4901   *valid_out = valid;
4902 }
4903
4904
4905 /* Recompute the validity and size of the view data for a given
4906  * view at this node from the immediate children of the node
4907  */
4908 static NodeData *
4909 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
4910                                  gpointer          view_id)
4911 {
4912   NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4913   gboolean valid;
4914   gint width;
4915   gint height;
4916
4917   gtk_text_btree_node_compute_view_aggregates (node, view_id,
4918                                                &width, &height, &valid);
4919   nd->width = width;
4920   nd->height = height;
4921   nd->valid = valid;
4922
4923   return nd;
4924 }
4925
4926 static void
4927 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
4928                                         gpointer          view_id)
4929 {
4930   while (node)
4931     {
4932       gtk_text_btree_node_check_valid (node, view_id);
4933       node = node->parent;
4934     }
4935 }
4936
4937 static NodeData *
4938 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
4939                                           gpointer          view_id)
4940 {
4941   if (node->level == 0)
4942     {
4943       return gtk_text_btree_node_check_valid (node, view_id);
4944     }
4945   else
4946     {
4947       GtkTextBTreeNode *child = node->children.node;
4948
4949       NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4950
4951       nd->valid = TRUE;
4952       nd->width = 0;
4953       nd->height = 0;
4954
4955       while (child)
4956         {
4957           NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
4958
4959           if (!child_nd->valid)
4960             nd->valid = FALSE;
4961           nd->width = MAX (child_nd->width, nd->width);
4962           nd->height += child_nd->height;
4963
4964           child = child->next;
4965         }
4966       return nd;
4967     }
4968 }
4969
4970
4971
4972 /**
4973  * gtk_text_btree_validate_line:
4974  * @tree: a #GtkTextBTree
4975  * @line: line to validate
4976  * @view_id: view ID for the view to validate
4977  *
4978  * Revalidate a single line of the btree for the given view, propagate
4979  * results up through the entire tree.
4980  **/
4981 void
4982 gtk_text_btree_validate_line (GtkTextBTree     *tree,
4983                               GtkTextLine      *line,
4984                               gpointer          view_id)
4985 {
4986   GtkTextLineData *ld;
4987   GtkTextLine *last_line;
4988   BTreeView *view;
4989
4990   g_return_if_fail (tree != NULL);
4991   g_return_if_fail (line != NULL);
4992
4993   view = gtk_text_btree_get_view (tree, view_id);
4994   g_return_if_fail (view != NULL);
4995
4996   ld = gtk_text_line_get_data (line, view_id);
4997   if (!ld || !ld->valid)
4998     {
4999       ld = gtk_text_layout_wrap (view->layout, line, ld);
5000       last_line = get_last_line (tree);
5001
5002       gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5003     }
5004 }
5005
5006 static void
5007 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5008 {
5009   if (node->level == 0)
5010     {
5011       GtkTextLine *line;
5012
5013       line = node->children.line;
5014       while (line != NULL)
5015         {
5016           GtkTextLineData *ld;
5017
5018           ld = gtk_text_line_remove_data (line, view_id);
5019
5020           if (ld)
5021             gtk_text_layout_free_line_data (view->layout, line, ld);
5022
5023           line = line->next;
5024         }
5025     }
5026   else
5027     {
5028       GtkTextBTreeNode *child;
5029
5030       child = node->children.node;
5031
5032       while (child != NULL)
5033         {
5034           /* recurse */
5035           gtk_text_btree_node_remove_view (view, child, view_id);
5036
5037           child = child->next;
5038         }
5039     }
5040
5041   gtk_text_btree_node_remove_data (node, view_id);
5042 }
5043
5044 static void
5045 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5046 {
5047   if (node->level == 0)
5048     {
5049       GtkTextLine *line;
5050       GtkTextLineSegment *seg;
5051
5052       while (node->children.line != NULL)
5053         {
5054           line = node->children.line;
5055           node->children.line = line->next;
5056           while (line->segments != NULL)
5057             {
5058               seg = line->segments;
5059               line->segments = seg->next;
5060
5061               if (GTK_IS_TEXT_MARK_SEGMENT (seg))
5062                 {
5063                   /* Set the mark as deleted */
5064                   seg->body.mark.tree = NULL;
5065                   seg->body.mark.line = NULL;
5066                 }
5067
5068               (*seg->type->deleteFunc) (seg, line, 1);
5069             }
5070           gtk_text_line_destroy (tree, line);
5071         }
5072     }
5073   else
5074     {
5075       GtkTextBTreeNode *childPtr;
5076
5077       while (node->children.node != NULL)
5078         {
5079           childPtr = node->children.node;
5080           node->children.node = childPtr->next;
5081           gtk_text_btree_node_destroy (tree, childPtr);
5082         }
5083     }
5084
5085   summary_list_destroy (node->summary);
5086   node_data_list_destroy (node->node_data);
5087   g_free (node);
5088 }
5089
5090 static NodeData*
5091 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5092 {
5093   NodeData *nd;
5094
5095   nd = node->node_data;
5096   while (nd != NULL)
5097     {
5098       if (nd->view_id == view_id)
5099         break;
5100
5101       nd = nd->next;
5102     }
5103
5104   if (nd == NULL)    {
5105     nd = node_data_new (view_id);
5106
5107     if (node->node_data)
5108       nd->next = node->node_data;
5109
5110     node->node_data = nd;
5111   }
5112
5113   return nd;
5114 }
5115
5116 static void
5117 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5118 {
5119   NodeData *nd;
5120   NodeData *prev;
5121
5122   prev = NULL;
5123   nd = node->node_data;
5124   while (nd != NULL)
5125     {
5126       if (nd->view_id == view_id)
5127         break;
5128
5129       prev = nd;
5130       nd = nd->next;
5131     }
5132
5133   if (nd == NULL)
5134     return;
5135
5136   if (prev != NULL)
5137     prev->next = nd->next;
5138
5139   if (node->node_data == nd)
5140     node->node_data = nd->next;
5141
5142   nd->next = NULL;
5143
5144   node_data_destroy (nd);
5145 }
5146
5147 static void
5148 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5149                               gint *width, gint *height)
5150 {
5151   NodeData *nd;
5152
5153   g_return_if_fail (width != NULL);
5154   g_return_if_fail (height != NULL);
5155
5156   nd = gtk_text_btree_node_ensure_data (node, view_id);
5157
5158   if (width)
5159     *width = nd->width;
5160   if (height)
5161     *height = nd->height;
5162 }
5163
5164 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5165  * here isn't quite right, since for a lot of operations we want to
5166  * know which children of the common parent correspond to the two nodes
5167  * (e.g., when computing the order of two iters)
5168  */
5169 static GtkTextBTreeNode *
5170 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5171                                    GtkTextBTreeNode *node2)
5172 {
5173   while (node1->level < node2->level)
5174     node1 = node1->parent;
5175   while (node2->level < node1->level)
5176     node2 = node2->parent;
5177   while (node1 != node2)
5178     {
5179       node1 = node1->parent;
5180       node2 = node2->parent;
5181     }
5182
5183   return node1;
5184 }
5185
5186 /*
5187  * BTree
5188  */
5189
5190 static BTreeView*
5191 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5192 {
5193   BTreeView *view;
5194
5195   view = tree->views;
5196   while (view != NULL)
5197     {
5198       if (view->view_id == view_id)
5199         break;
5200       view = view->next;
5201     }
5202
5203   return view;
5204 }
5205
5206 static void
5207 get_tree_bounds (GtkTextBTree *tree,
5208                  GtkTextIter *start,
5209                  GtkTextIter *end)
5210 {
5211   gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5212   gtk_text_btree_get_last_iter (tree, end);
5213 }
5214
5215 static void
5216 tag_changed_cb (GtkTextTagTable *table,
5217                 GtkTextTag      *tag,
5218                 gboolean         size_changed,
5219                 GtkTextBTree    *tree)
5220 {
5221   if (size_changed)
5222     {
5223       /* We need to queue a relayout on all regions that are tagged with
5224        * this tag.
5225        */
5226
5227       GtkTextIter start;
5228       GtkTextIter end;
5229
5230       if (gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5231         {
5232           /* Must be a last toggle if there was a first one. */
5233           gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5234           gtk_text_btree_invalidate_region (tree,
5235                                             &start, &end);
5236
5237         }
5238     }
5239   else
5240     {
5241       /* We only need to queue a redraw, not a relayout */
5242       BTreeView *view;
5243
5244       view = tree->views;
5245
5246       while (view != NULL)
5247         {
5248           gint width, height;
5249
5250           gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5251           gtk_text_layout_changed (view->layout, 0, height, height);
5252
5253           view = view->next;
5254         }
5255     }
5256 }
5257
5258 static void
5259 tag_removed_cb (GtkTextTagTable *table,
5260                 GtkTextTag *tag,
5261                 GtkTextBTree *tree)
5262 {
5263   /* Remove the tag from the tree */
5264
5265   GtkTextIter start;
5266   GtkTextIter end;
5267
5268   get_tree_bounds (tree, &start, &end);
5269
5270   gtk_text_btree_tag (&start, &end, tag, FALSE);
5271   gtk_text_btree_remove_tag_info (tree, tag);
5272 }
5273
5274
5275 /* Rebalance the out-of-whack node "node" */
5276 static void
5277 gtk_text_btree_rebalance (GtkTextBTree *tree,
5278                           GtkTextBTreeNode *node)
5279 {
5280   /*
5281    * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5282    * up through the tree one GtkTextBTreeNode at a time until the root
5283    * GtkTextBTreeNode has been processed.
5284    */
5285
5286   while (node != NULL)
5287     {
5288       GtkTextBTreeNode *new_node, *child;
5289       GtkTextLine *line;
5290       int i;
5291
5292       /*
5293        * Check to see if the GtkTextBTreeNode has too many children.  If it does,
5294        * then split off all but the first MIN_CHILDREN into a separate
5295        * GtkTextBTreeNode following the original one.  Then repeat until the
5296        * GtkTextBTreeNode has a decent size.
5297        */
5298
5299       if (node->num_children > MAX_CHILDREN)
5300         {
5301           while (1)
5302             {
5303               /*
5304                * If the GtkTextBTreeNode being split is the root
5305                * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5306                * it first.
5307                */
5308
5309               if (node->parent == NULL)
5310                 {
5311                   new_node = gtk_text_btree_node_new ();
5312                   new_node->parent = NULL;
5313                   new_node->next = NULL;
5314                   new_node->summary = NULL;
5315                   new_node->level = node->level + 1;
5316                   new_node->children.node = node;
5317                   recompute_node_counts (tree, new_node);
5318                   tree->root_node = new_node;
5319                 }
5320               new_node = gtk_text_btree_node_new ();
5321               new_node->parent = node->parent;
5322               new_node->next = node->next;
5323               node->next = new_node;
5324               new_node->summary = NULL;
5325               new_node->level = node->level;
5326               new_node->num_children = node->num_children - MIN_CHILDREN;
5327               if (node->level == 0)
5328                 {
5329                   for (i = MIN_CHILDREN-1,
5330                          line = node->children.line;
5331                        i > 0; i--, line = line->next)
5332                     {
5333                       /* Empty loop body. */
5334                     }
5335                   new_node->children.line = line->next;
5336                   line->next = NULL;
5337                 }
5338               else
5339                 {
5340                   for (i = MIN_CHILDREN-1,
5341                          child = node->children.node;
5342                        i > 0; i--, child = child->next)
5343                     {
5344                       /* Empty loop body. */
5345                     }
5346                   new_node->children.node = child->next;
5347                   child->next = NULL;
5348                 }
5349               recompute_node_counts (tree, node);
5350               node->parent->num_children++;
5351               node = new_node;
5352               if (node->num_children <= MAX_CHILDREN)
5353                 {
5354                   recompute_node_counts (tree, node);
5355                   break;
5356                 }
5357             }
5358         }
5359
5360       while (node->num_children < MIN_CHILDREN)
5361         {
5362           GtkTextBTreeNode *other;
5363           GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5364           GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5365           int total_children, first_children, i;
5366
5367           /*
5368            * Too few children for this GtkTextBTreeNode.  If this is the root then,
5369            * it's OK for it to have less than MIN_CHILDREN children
5370            * as long as it's got at least two.  If it has only one
5371            * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5372            * the tree and use its child as the new root.
5373            */
5374
5375           if (node->parent == NULL)
5376             {
5377               if ((node->num_children == 1) && (node->level > 0))
5378                 {
5379                   tree->root_node = node->children.node;
5380                   tree->root_node->parent = NULL;
5381                   summary_list_destroy (node->summary);
5382                   g_free (node);
5383                 }
5384               return;
5385             }
5386
5387           /*
5388            * Not the root.  Make sure that there are siblings to
5389            * balance with.
5390            */
5391
5392           if (node->parent->num_children < 2)
5393             {
5394               gtk_text_btree_rebalance (tree, node->parent);
5395               continue;
5396             }
5397
5398           /*
5399            * Find a sibling neighbor to borrow from, and arrange for
5400            * node to be the earlier of the pair.
5401            */
5402
5403           if (node->next == NULL)
5404             {
5405               for (other = node->parent->children.node;
5406                    other->next != node;
5407                    other = other->next)
5408                 {
5409                   /* Empty loop body. */
5410                 }
5411               node = other;
5412             }
5413           other = node->next;
5414
5415           /*
5416            * We're going to either merge the two siblings together
5417            * into one GtkTextBTreeNode or redivide the children among them to
5418            * balance their loads.  As preparation, join their two
5419            * child lists into a single list and remember the half-way
5420            * point in the list.
5421            */
5422
5423           total_children = node->num_children + other->num_children;
5424           first_children = total_children/2;
5425           if (node->children.node == NULL)
5426             {
5427               node->children = other->children;
5428               other->children.node = NULL;
5429               other->children.line = NULL;
5430             }
5431           if (node->level == 0)
5432             {
5433               GtkTextLine *line;
5434
5435               for (line = node->children.line, i = 1;
5436                    line->next != NULL;
5437                    line = line->next, i++)
5438                 {
5439                   if (i == first_children)
5440                     {
5441                       halfwayline = line;
5442                     }
5443                 }
5444               line->next = other->children.line;
5445               while (i <= first_children)
5446                 {
5447                   halfwayline = line;
5448                   line = line->next;
5449                   i++;
5450                 }
5451             }
5452           else
5453             {
5454               GtkTextBTreeNode *child;
5455
5456               for (child = node->children.node, i = 1;
5457                    child->next != NULL;
5458                    child = child->next, i++)
5459                 {
5460                   if (i <= first_children)
5461                     {
5462                       if (i == first_children)
5463                         {
5464                           halfwaynode = child;
5465                         }
5466                     }
5467                 }
5468               child->next = other->children.node;
5469               while (i <= first_children)
5470                 {
5471                   halfwaynode = child;
5472                   child = child->next;
5473                   i++;
5474                 }
5475             }
5476
5477           /*
5478            * If the two siblings can simply be merged together, do it.
5479            */
5480
5481           if (total_children <= MAX_CHILDREN)
5482             {
5483               recompute_node_counts (tree, node);
5484               node->next = other->next;
5485               node->parent->num_children--;
5486               summary_list_destroy (other->summary);
5487               g_free (other);
5488               continue;
5489             }
5490
5491           /*
5492            * The siblings can't be merged, so just divide their
5493            * children evenly between them.
5494            */
5495
5496           if (node->level == 0)
5497             {
5498               other->children.line = halfwayline->next;
5499               halfwayline->next = NULL;
5500             }
5501           else
5502             {
5503               other->children.node = halfwaynode->next;
5504               halfwaynode->next = NULL;
5505             }
5506
5507           recompute_node_counts (tree, node);
5508           recompute_node_counts (tree, other);
5509         }
5510
5511       node = node->parent;
5512     }
5513 }
5514
5515 static void
5516 post_insert_fixup (GtkTextBTree *tree,
5517                    GtkTextLine *line,
5518                    gint line_count_delta,
5519                    gint char_count_delta)
5520
5521 {
5522   GtkTextBTreeNode *node;
5523
5524   /*
5525    * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5526    * point, then rebalance the tree if necessary.
5527    */
5528
5529   for (node = line->parent ; node != NULL;
5530        node = node->parent)
5531     {
5532       node->num_lines += line_count_delta;
5533       node->num_chars += char_count_delta;
5534     }
5535   node = line->parent;
5536   node->num_children += line_count_delta;
5537
5538   if (node->num_children > MAX_CHILDREN)
5539     {
5540       gtk_text_btree_rebalance (tree, node);
5541     }
5542
5543   if (gtk_debug_flags & GTK_DEBUG_TEXT)
5544     gtk_text_btree_check (tree);
5545 }
5546
5547 static GtkTextTagInfo*
5548 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5549                                       GtkTextTag   *tag)
5550 {
5551   GtkTextTagInfo *info;
5552   GSList *list;
5553
5554
5555   list = tree->tag_infos;
5556   while (list != NULL)
5557     {
5558       info = list->data;
5559       if (info->tag == tag)
5560         return info;
5561
5562       list = g_slist_next (list);
5563     }
5564
5565   return NULL;
5566 }
5567
5568 static GtkTextTagInfo*
5569 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5570                              GtkTextTag   *tag)
5571 {
5572   GtkTextTagInfo *info;
5573
5574   info = gtk_text_btree_get_existing_tag_info (tree, tag);
5575
5576   if (info == NULL)
5577     {
5578       /* didn't find it, create. */
5579
5580       info = g_new (GtkTextTagInfo, 1);
5581
5582       info->tag = tag;
5583       gtk_object_ref (GTK_OBJECT (tag));
5584       info->tag_root = NULL;
5585       info->toggle_count = 0;
5586
5587       tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5588     }
5589
5590   return info;
5591 }
5592
5593 static void
5594 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5595                                 GtkTextTag   *tag)
5596 {
5597   GtkTextTagInfo *info;
5598   GSList *list;
5599   GSList *prev;
5600
5601   prev = NULL;
5602   list = tree->tag_infos;
5603   while (list != NULL)
5604     {
5605       info = list->data;
5606       if (info->tag == tag)
5607         {
5608           if (prev != NULL)
5609             {
5610               prev->next = list->next;
5611             }
5612           else
5613             {
5614               tree->tag_infos = list->next;
5615             }
5616           list->next = NULL;
5617           g_slist_free (list);
5618
5619           gtk_object_unref (GTK_OBJECT (info->tag));
5620
5621           g_free (info);
5622           return;
5623         }
5624
5625       list = g_slist_next (list);
5626     }
5627
5628   g_assert_not_reached ();
5629   return;
5630 }
5631
5632 static void
5633 recompute_level_zero_counts (GtkTextBTreeNode *node)
5634 {
5635   GtkTextLine *line;
5636   GtkTextLineSegment *seg;
5637
5638   g_assert (node->level == 0);
5639
5640   line = node->children.line;
5641   while (line != NULL)
5642     {
5643       node->num_children++;
5644       node->num_lines++;
5645
5646       if (line->parent != node)
5647         gtk_text_line_set_parent (line, node);
5648
5649       seg = line->segments;
5650       while (seg != NULL)
5651         {
5652
5653           node->num_chars += seg->char_count;
5654
5655           if (((seg->type != &gtk_text_toggle_on_type)
5656                && (seg->type != &gtk_text_toggle_off_type))
5657               || !(seg->body.toggle.inNodeCounts))
5658             {
5659               ; /* nothing */
5660             }
5661           else
5662             {
5663               GtkTextTagInfo *info;
5664
5665               info = seg->body.toggle.info;
5666
5667               gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5668             }
5669
5670           seg = seg->next;
5671         }
5672
5673       line = line->next;
5674     }
5675 }
5676
5677 static void
5678 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5679 {
5680   Summary *summary;
5681   GtkTextBTreeNode *child;
5682
5683   g_assert (node->level > 0);
5684
5685   child = node->children.node;
5686   while (child != NULL)
5687     {
5688       node->num_children += 1;
5689       node->num_lines += child->num_lines;
5690       node->num_chars += child->num_chars;
5691
5692       if (child->parent != node)
5693         {
5694           child->parent = node;
5695           gtk_text_btree_node_invalidate_upward (node, NULL);
5696         }
5697
5698       summary = child->summary;
5699       while (summary != NULL)
5700         {
5701           gtk_text_btree_node_adjust_toggle_count (node,
5702                                                    summary->info,
5703                                                    summary->toggle_count);
5704
5705           summary = summary->next;
5706         }
5707
5708       child = child->next;
5709     }
5710 }
5711
5712 /*
5713  *----------------------------------------------------------------------
5714  *
5715  * recompute_node_counts --
5716  *
5717  *      This procedure is called to recompute all the counts in a GtkTextBTreeNode
5718  *      (tags, child information, etc.) by scanning the information in
5719  *      its descendants.  This procedure is called during rebalancing
5720  *      when a GtkTextBTreeNode's child structure has changed.
5721  *
5722  * Results:
5723  *      None.
5724  *
5725  * Side effects:
5726  *      The tag counts for node are modified to reflect its current
5727  *      child structure, as are its num_children, num_lines, num_chars fields.
5728  *      Also, all of the childrens' parent fields are made to point
5729  *      to node.
5730  *
5731  *----------------------------------------------------------------------
5732  */
5733
5734 static void
5735 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5736 {
5737   BTreeView *view;
5738   Summary *summary, *summary2;
5739
5740   /*
5741    * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5742    * the existing Summary records (most of them will probably be reused).
5743    */
5744
5745   summary = node->summary;
5746   while (summary != NULL)
5747     {
5748       summary->toggle_count = 0;
5749       summary = summary->next;
5750     }
5751
5752   node->num_children = 0;
5753   node->num_lines = 0;
5754   node->num_chars = 0;
5755
5756   /*
5757    * Scan through the children, adding the childrens' tag counts into
5758    * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5759    * necessary.
5760    */
5761
5762   if (node->level == 0)
5763     recompute_level_zero_counts (node);
5764   else
5765     recompute_level_nonzero_counts (node);
5766
5767   view = tree->views;
5768   while (view)
5769     {
5770       gtk_text_btree_node_check_valid (node, view->view_id);
5771       view = view->next;
5772     }
5773
5774   /*
5775    * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5776    * records that still have a zero count, or that have all the toggles.
5777    * The GtkTextBTreeNode with the children that account for all the tags toggles
5778    * have no summary information, and they become the tag_root for the tag.
5779    */
5780
5781   summary2 = NULL;
5782   for (summary = node->summary; summary != NULL; )
5783     {
5784       if (summary->toggle_count > 0 &&
5785           summary->toggle_count < summary->info->toggle_count)
5786         {
5787           if (node->level == summary->info->tag_root->level)
5788             {
5789               /*
5790                * The tag's root GtkTextBTreeNode split and some toggles left.
5791                * The tag root must move up a level.
5792                */
5793               summary->info->tag_root = node->parent;
5794             }
5795           summary2 = summary;
5796           summary = summary->next;
5797           continue;
5798         }
5799       if (summary->toggle_count == summary->info->toggle_count)
5800         {
5801           /*
5802            * A GtkTextBTreeNode merge has collected all the toggles under
5803            * one GtkTextBTreeNode.  Push the root down to this level.
5804            */
5805           summary->info->tag_root = node;
5806         }
5807       if (summary2 != NULL)
5808         {
5809           summary2->next = summary->next;
5810           summary_destroy (summary);
5811           summary = summary2->next;
5812         }
5813       else
5814         {
5815           node->summary = summary->next;
5816           summary_destroy (summary);
5817           summary = node->summary;
5818         }
5819     }
5820 }
5821
5822 void
5823 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
5824                                GtkTextTagInfo   *info,
5825                                gint              delta) /* may be negative */
5826 {
5827   Summary *summary, *prevPtr;
5828   GtkTextBTreeNode *node2Ptr;
5829   int rootLevel;                        /* Level of original tag root */
5830
5831   info->toggle_count += delta;
5832
5833   if (info->tag_root == (GtkTextBTreeNode *) NULL)
5834     {
5835       info->tag_root = node;
5836       return;
5837     }
5838
5839   /*
5840    * Note the level of the existing root for the tag so we can detect
5841    * if it needs to be moved because of the toggle count change.
5842    */
5843
5844   rootLevel = info->tag_root->level;
5845
5846   /*
5847    * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
5848    * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
5849    * necessary.
5850    */
5851
5852   for ( ; node != info->tag_root; node = node->parent)
5853     {
5854       /*
5855        * See if there's already an entry for this tag for this GtkTextBTreeNode.  If so,
5856        * perhaps all we have to do is adjust its count.
5857        */
5858
5859       for (prevPtr = NULL, summary = node->summary;
5860            summary != NULL;
5861            prevPtr = summary, summary = summary->next)
5862         {
5863           if (summary->info == info)
5864             {
5865               break;
5866             }
5867         }
5868       if (summary != NULL)
5869         {
5870           summary->toggle_count += delta;
5871           if (summary->toggle_count > 0 &&
5872               summary->toggle_count < info->toggle_count)
5873             {
5874               continue;
5875             }
5876           if (summary->toggle_count != 0)
5877             {
5878               /*
5879                * Should never find a GtkTextBTreeNode with max toggle count at this
5880                * point (there shouldn't have been a summary entry in the
5881                * first place).
5882                */
5883
5884               g_error ("%s: bad toggle count (%d) max (%d)",
5885                        G_STRLOC, summary->toggle_count, info->toggle_count);
5886             }
5887
5888           /*
5889            * Zero toggle count;  must remove this tag from the list.
5890            */
5891
5892           if (prevPtr == NULL)
5893             {
5894               node->summary = summary->next;
5895             }
5896           else
5897             {
5898               prevPtr->next = summary->next;
5899             }
5900           summary_destroy (summary);
5901         }
5902       else
5903         {
5904           /*
5905            * This tag isn't currently in the summary information list.
5906            */
5907
5908           if (rootLevel == node->level)
5909             {
5910
5911               /*
5912                * The old tag root is at the same level in the tree as this
5913                * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode.  Move the tag root up
5914                * a level, in the hopes that it will now cover this GtkTextBTreeNode
5915                * as well as the old root (if not, we'll move it up again
5916                * the next time through the loop).  To push it up one level
5917                * we copy the original toggle count into the summary
5918                * information at the old root and change the root to its
5919                * parent GtkTextBTreeNode.
5920                */
5921
5922               GtkTextBTreeNode *rootnode = info->tag_root;
5923               summary = (Summary *) g_malloc (sizeof (Summary));
5924               summary->info = info;
5925               summary->toggle_count = info->toggle_count - delta;
5926               summary->next = rootnode->summary;
5927               rootnode->summary = summary;
5928               rootnode = rootnode->parent;
5929               rootLevel = rootnode->level;
5930               info->tag_root = rootnode;
5931             }
5932           summary = (Summary *) g_malloc (sizeof (Summary));
5933           summary->info = info;
5934           summary->toggle_count = delta;
5935           summary->next = node->summary;
5936           node->summary = summary;
5937         }
5938     }
5939
5940   /*
5941    * If we've decremented the toggle count, then it may be necessary
5942    * to push the tag root down one or more levels.
5943    */
5944
5945   if (delta >= 0)
5946     {
5947       return;
5948     }
5949   if (info->toggle_count == 0)
5950     {
5951       info->tag_root = (GtkTextBTreeNode *) NULL;
5952       return;
5953     }
5954   node = info->tag_root;
5955   while (node->level > 0)
5956     {
5957       /*
5958        * See if a single child GtkTextBTreeNode accounts for all of the tag's
5959        * toggles.  If so, push the root down one level.
5960        */
5961
5962       for (node2Ptr = node->children.node;
5963            node2Ptr != (GtkTextBTreeNode *)NULL ;
5964            node2Ptr = node2Ptr->next)
5965         {
5966           for (prevPtr = NULL, summary = node2Ptr->summary;
5967                summary != NULL;
5968                prevPtr = summary, summary = summary->next)
5969             {
5970               if (summary->info == info)
5971                 {
5972                   break;
5973                 }
5974             }
5975           if (summary == NULL)
5976             {
5977               continue;
5978             }
5979           if (summary->toggle_count != info->toggle_count)
5980             {
5981               /*
5982                * No GtkTextBTreeNode has all toggles, so the root is still valid.
5983                */
5984
5985               return;
5986             }
5987
5988           /*
5989            * This GtkTextBTreeNode has all the toggles, so push down the root.
5990            */
5991
5992           if (prevPtr == NULL)
5993             {
5994               node2Ptr->summary = summary->next;
5995             }
5996           else
5997             {
5998               prevPtr->next = summary->next;
5999             }
6000           summary_destroy (summary);
6001           info->tag_root = node2Ptr;
6002           break;
6003         }
6004       node = info->tag_root;
6005     }
6006 }
6007
6008 /*
6009  *----------------------------------------------------------------------
6010  *
6011  * inc_count --
6012  *
6013  *      This is a utility procedure used by gtk_text_btree_get_tags.  It
6014  *      increments the count for a particular tag, adding a new
6015  *      entry for that tag if there wasn't one previously.
6016  *
6017  * Results:
6018  *      None.
6019  *
6020  * Side effects:
6021  *      The information at *tagInfoPtr may be modified, and the arrays
6022  *      may be reallocated to make them larger.
6023  *
6024  *----------------------------------------------------------------------
6025  */
6026
6027 static void
6028 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6029 {
6030   GtkTextTag **tag_p;
6031   int count;
6032
6033   for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6034        count > 0; tag_p++, count--)
6035     {
6036       if (*tag_p == tag)
6037         {
6038           tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6039           return;
6040         }
6041     }
6042
6043   /*
6044    * There isn't currently an entry for this tag, so we have to
6045    * make a new one.  If the arrays are full, then enlarge the
6046    * arrays first.
6047    */
6048
6049   if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6050     {
6051       GtkTextTag **newTags;
6052       int *newCounts, newSize;
6053
6054       newSize = 2*tagInfoPtr->arraySize;
6055       newTags = (GtkTextTag **) g_malloc ((unsigned)
6056                                           (newSize*sizeof (GtkTextTag *)));
6057       memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6058               tagInfoPtr->arraySize  *sizeof (GtkTextTag *));
6059       g_free ((char *) tagInfoPtr->tags);
6060       tagInfoPtr->tags = newTags;
6061       newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6062       memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6063               tagInfoPtr->arraySize  *sizeof (int));
6064       g_free ((char *) tagInfoPtr->counts);
6065       tagInfoPtr->counts = newCounts;
6066       tagInfoPtr->arraySize = newSize;
6067     }
6068
6069   tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6070   tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6071   tagInfoPtr->numTags++;
6072 }
6073
6074 static void
6075 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6076                              const GtkTextIter *iter)
6077 {
6078   GtkTextLineSegment *prev;
6079   GtkTextLine *line;
6080   GtkTextBTree *tree;
6081
6082   line = gtk_text_iter_get_text_line (iter);
6083   tree = gtk_text_iter_get_btree (iter);
6084
6085   prev = gtk_text_line_segment_split (iter);
6086   if (prev == NULL)
6087     {
6088       seg->next = line->segments;
6089       line->segments = seg;
6090     }
6091   else
6092     {
6093       seg->next = prev->next;
6094       prev->next = seg;
6095     }
6096   cleanup_line (line);
6097   segments_changed (tree);
6098
6099   if (gtk_debug_flags & GTK_DEBUG_TEXT)
6100     gtk_text_btree_check (tree);
6101 }
6102
6103 static void
6104 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6105                                GtkTextLineSegment *seg,
6106                                GtkTextLine *line)
6107 {
6108   GtkTextLineSegment *prev;
6109
6110   if (line->segments == seg)
6111     {
6112       line->segments = seg->next;
6113     }
6114   else
6115     {
6116       for (prev = line->segments; prev->next != seg;
6117            prev = prev->next)
6118         {
6119           /* Empty loop body. */
6120         }
6121       prev->next = seg->next;
6122     }
6123   cleanup_line (line);
6124   segments_changed (tree);
6125 }
6126
6127 /*
6128  * This is here because it requires BTree internals, it logically
6129  * belongs in gtktextsegment.c
6130  */
6131
6132
6133 /*
6134  *--------------------------------------------------------------
6135  *
6136  * _gtk_toggle_segment_check_func --
6137  *
6138  *      This procedure is invoked to perform consistency checks
6139  *      on toggle segments.
6140  *
6141  * Results:
6142  *      None.
6143  *
6144  * Side effects:
6145  *      If a consistency problem is found the procedure g_errors.
6146  *
6147  *--------------------------------------------------------------
6148  */
6149
6150 void
6151 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6152                                 GtkTextLine *line)
6153 {
6154   Summary *summary;
6155   int needSummary;
6156
6157   if (segPtr->byte_count != 0)
6158     {
6159       g_error ("toggle_segment_check_func: segment had non-zero size");
6160     }
6161   if (!segPtr->body.toggle.inNodeCounts)
6162     {
6163       g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6164     }
6165   needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6166   for (summary = line->parent->summary; ;
6167        summary = summary->next)
6168     {
6169       if (summary == NULL)
6170         {
6171           if (needSummary)
6172             {
6173               g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6174             }
6175           else
6176             {
6177               break;
6178             }
6179         }
6180       if (summary->info == segPtr->body.toggle.info)
6181         {
6182           if (!needSummary)
6183             {
6184               g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6185             }
6186           break;
6187         }
6188     }
6189 }
6190
6191 /*
6192  * Debug
6193  */
6194
6195 static void
6196 gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
6197                                             NodeData         *nd)
6198 {
6199   gint width;
6200   gint height;
6201   gboolean valid;
6202
6203   gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6204                                                &width, &height, &valid);
6205   if (nd->width != width ||
6206       nd->height != height ||
6207       !nd->valid != !valid)
6208     {
6209       g_error ("Node aggregates for view %p are invalid:\n"
6210                "Are (%d,%d,%s), should be (%d,%d,%s)",
6211                nd->view_id,
6212                nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6213                width, height, valid ? "TRUE" : "FALSE");
6214     }
6215 }
6216
6217 static void
6218 gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
6219 {
6220   GtkTextBTreeNode *childnode;
6221   Summary *summary, *summary2;
6222   GtkTextLine *line;
6223   GtkTextLineSegment *segPtr;
6224   int num_children, num_lines, num_chars, toggle_count, min_children;
6225   GtkTextLineData *ld;
6226   NodeData *nd;
6227
6228   if (node->parent != NULL)
6229     {
6230       min_children = MIN_CHILDREN;
6231     }
6232   else if (node->level > 0)
6233     {
6234       min_children = 2;
6235     }
6236   else  {
6237     min_children = 1;
6238   }
6239   if ((node->num_children < min_children)
6240       || (node->num_children > MAX_CHILDREN))
6241     {
6242       g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6243                node->num_children);
6244     }
6245
6246   nd = node->node_data;
6247   while (nd != NULL)
6248     {
6249       gtk_text_btree_node_view_check_consistency (node, nd);
6250       nd = nd->next;
6251     }
6252
6253   num_children = 0;
6254   num_lines = 0;
6255   num_chars = 0;
6256   if (node->level == 0)
6257     {
6258       for (line = node->children.line; line != NULL;
6259            line = line->next)
6260         {
6261           if (line->parent != node)
6262             {
6263               g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6264             }
6265           if (line->segments == NULL)
6266             {
6267               g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6268             }
6269
6270           ld = line->views;
6271           while (ld != NULL)
6272             {
6273               /* Just ensuring we don't segv while doing this loop */
6274
6275               ld = ld->next;
6276             }
6277
6278           for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6279             {
6280               if (segPtr->type->checkFunc != NULL)
6281                 {
6282                   (*segPtr->type->checkFunc)(segPtr, line);
6283                 }
6284               if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6285                   && (segPtr->next != NULL)
6286                   && (segPtr->next->byte_count == 0)
6287                   && (segPtr->next->type->leftGravity))
6288                 {
6289                   g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6290                 }
6291               if ((segPtr->next == NULL)
6292                   && (segPtr->type != &gtk_text_char_type))
6293                 {
6294                   g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6295                 }
6296
6297               num_chars += segPtr->char_count;
6298             }
6299
6300           num_children++;
6301           num_lines++;
6302         }
6303     }
6304   else
6305     {
6306       for (childnode = node->children.node; childnode != NULL;
6307            childnode = childnode->next)
6308         {
6309           if (childnode->parent != node)
6310             {
6311               g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6312             }
6313           if (childnode->level != (node->level-1))
6314             {
6315               g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6316                        node->level, childnode->level);
6317             }
6318           gtk_text_btree_node_check_consistency (childnode);
6319           for (summary = childnode->summary; summary != NULL;
6320                summary = summary->next)
6321             {
6322               for (summary2 = node->summary; ;
6323                    summary2 = summary2->next)
6324                 {
6325                   if (summary2 == NULL)
6326                     {
6327                       if (summary->info->tag_root == node)
6328                         {
6329                           break;
6330                         }
6331                       g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6332                                summary->info->tag->name,
6333                                "present in parent summaries");
6334                     }
6335                   if (summary->info == summary2->info)
6336                     {
6337                       break;
6338                     }
6339                 }
6340             }
6341           num_children++;
6342           num_lines += childnode->num_lines;
6343           num_chars += childnode->num_chars;
6344         }
6345     }
6346   if (num_children != node->num_children)
6347     {
6348       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6349                num_children, node->num_children);
6350     }
6351   if (num_lines != node->num_lines)
6352     {
6353       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6354                num_lines, node->num_lines);
6355     }
6356   if (num_chars != node->num_chars)
6357     {
6358       g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6359                num_chars, node->num_chars);
6360     }
6361
6362   for (summary = node->summary; summary != NULL;
6363        summary = summary->next)
6364     {
6365       if (summary->info->toggle_count == summary->toggle_count)
6366         {
6367           g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6368                    summary->info->tag->name);
6369         }
6370       toggle_count = 0;
6371       if (node->level == 0)
6372         {
6373           for (line = node->children.line; line != NULL;
6374                line = line->next)
6375             {
6376               for (segPtr = line->segments; segPtr != NULL;
6377                    segPtr = segPtr->next)
6378                 {
6379                   if ((segPtr->type != &gtk_text_toggle_on_type)
6380                       && (segPtr->type != &gtk_text_toggle_off_type))
6381                     {
6382                       continue;
6383                     }
6384                   if (segPtr->body.toggle.info == summary->info)
6385                     {
6386                       if (!segPtr->body.toggle.inNodeCounts)
6387                         g_error ("Toggle segment not in the node counts");
6388
6389                       toggle_count ++;
6390                     }
6391                 }
6392             }
6393         }
6394       else
6395         {
6396           for (childnode = node->children.node;
6397                childnode != NULL;
6398                childnode = childnode->next)
6399             {
6400               for (summary2 = childnode->summary;
6401                    summary2 != NULL;
6402                    summary2 = summary2->next)
6403                 {
6404                   if (summary2->info == summary->info)
6405                     {
6406                       toggle_count += summary2->toggle_count;
6407                     }
6408                 }
6409             }
6410         }
6411       if (toggle_count != summary->toggle_count)
6412         {
6413           g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6414                    toggle_count, summary->toggle_count);
6415         }
6416       for (summary2 = summary->next; summary2 != NULL;
6417            summary2 = summary2->next)
6418         {
6419           if (summary2->info == summary->info)
6420             {
6421               g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6422                        summary->info->tag->name);
6423             }
6424         }
6425     }
6426 }
6427
6428 static void
6429 listify_foreach (GtkTextTag *tag, gpointer user_data)
6430 {
6431   GSList** listp = user_data;
6432
6433   *listp = g_slist_prepend (*listp, tag);
6434 }
6435
6436 static GSList*
6437 list_of_tags (GtkTextTagTable *table)
6438 {
6439   GSList *list = NULL;
6440
6441   gtk_text_tag_table_foreach (table, listify_foreach, &list);
6442
6443   return list;
6444 }
6445
6446 void
6447 gtk_text_btree_check (GtkTextBTree *tree)
6448 {
6449   Summary *summary;
6450   GtkTextBTreeNode *node;
6451   GtkTextLine *line;
6452   GtkTextLineSegment *seg;
6453   GtkTextTag *tag;
6454   GSList *taglist = NULL;
6455   int count;
6456   GtkTextTagInfo *info;
6457
6458   /*
6459    * Make sure that the tag toggle counts and the tag root pointers are OK.
6460    */
6461   for (taglist = list_of_tags (tree->table);
6462        taglist != NULL ; taglist = taglist->next)
6463     {
6464       tag = taglist->data;
6465       info = gtk_text_btree_get_existing_tag_info (tree, tag);
6466       if (info != NULL)
6467         {
6468           node = info->tag_root;
6469           if (node == NULL)
6470             {
6471               if (info->toggle_count != 0)
6472                 {
6473                   g_error ("gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6474                            tag->name, info->toggle_count);
6475                 }
6476               continue;         /* no ranges for the tag */
6477             }
6478           else if (info->toggle_count == 0)
6479             {
6480               g_error ("gtk_text_btree_check found root for \"%s\" with no toggles",
6481                        tag->name);
6482             }
6483           else if (info->toggle_count & 1)
6484             {
6485               g_error ("gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6486                        tag->name, info->toggle_count);
6487             }
6488           for (summary = node->summary; summary != NULL;
6489                summary = summary->next)
6490             {
6491               if (summary->info->tag == tag)
6492                 {
6493                   g_error ("gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6494                 }
6495             }
6496           count = 0;
6497           if (node->level > 0)
6498             {
6499               for (node = node->children.node ; node != NULL ;
6500                    node = node->next)
6501                 {
6502                   for (summary = node->summary; summary != NULL;
6503                        summary = summary->next)
6504                     {
6505                       if (summary->info->tag == tag)
6506                         {
6507                           count += summary->toggle_count;
6508                         }
6509                     }
6510                 }
6511             }
6512           else
6513             {
6514               GtkTextLineSegmentClass * last = NULL;
6515
6516               for (line = node->children.line ; line != NULL ;
6517                    line = line->next)
6518                 {
6519                   for (seg = line->segments; seg != NULL;
6520                        seg = seg->next)
6521                     {
6522                       if ((seg->type == &gtk_text_toggle_on_type ||
6523                            seg->type == &gtk_text_toggle_off_type) &&
6524                           seg->body.toggle.info->tag == tag)
6525                         {
6526                           if (last == seg->type)
6527                             g_error ("Two consecutive toggles on or off weren't merged");
6528                           if (!seg->body.toggle.inNodeCounts)
6529                             g_error ("Toggle segment not in the node counts");
6530
6531                           last = seg->type;
6532
6533                           count++;
6534                         }
6535                     }
6536                 }
6537             }
6538           if (count != info->toggle_count)
6539             {
6540               g_error ("gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6541                        info->toggle_count, tag->name, count);
6542             }
6543         }
6544     }
6545
6546   g_slist_free (taglist);
6547   taglist = NULL;
6548
6549   /*
6550    * Call a recursive procedure to do the main body of checks.
6551    */
6552
6553   node = tree->root_node;
6554   gtk_text_btree_node_check_consistency (tree->root_node);
6555
6556   /*
6557    * Make sure that there are at least two lines in the text and
6558    * that the last line has no characters except a newline.
6559    */
6560
6561   if (node->num_lines < 2)
6562     {
6563       g_error ("gtk_text_btree_check: less than 2 lines in tree");
6564     }
6565   if (node->num_chars < 2)
6566     {
6567       g_error ("gtk_text_btree_check: less than 2 chars in tree");
6568     }
6569   while (node->level > 0)
6570     {
6571       node = node->children.node;
6572       while (node->next != NULL)
6573         {
6574           node = node->next;
6575         }
6576     }
6577   line = node->children.line;
6578   while (line->next != NULL)
6579     {
6580       line = line->next;
6581     }
6582   seg = line->segments;
6583   while ((seg->type == &gtk_text_toggle_off_type)
6584          || (seg->type == &gtk_text_right_mark_type)
6585          || (seg->type == &gtk_text_left_mark_type))
6586     {
6587       /*
6588        * It's OK to toggle a tag off in the last line, but
6589        * not to start a new range.  It's also OK to have marks
6590        * in the last line.
6591        */
6592
6593       seg = seg->next;
6594     }
6595   if (seg->type != &gtk_text_char_type)
6596     {
6597       g_error ("gtk_text_btree_check: last line has bogus segment type");
6598     }
6599   if (seg->next != NULL)
6600     {
6601       g_error ("gtk_text_btree_check: last line has too many segments");
6602     }
6603   if (seg->byte_count != 1)
6604     {
6605       g_error ("gtk_text_btree_check: last line has wrong # characters: %d",
6606                seg->byte_count);
6607     }
6608   if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6609     {
6610       g_error ("gtk_text_btree_check: last line had bad value: %s",
6611                seg->body.chars);
6612     }
6613 }
6614
6615 void gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6616 void gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6617 void gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6618 void gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6619
6620 void
6621 gtk_text_btree_spew (GtkTextBTree *tree)
6622 {
6623   GtkTextLine * line;
6624   int real_line;
6625
6626   printf ("%d lines in tree %p\n",
6627           gtk_text_btree_line_count (tree), tree);
6628
6629   line = gtk_text_btree_get_line (tree, 0, &real_line);
6630
6631   while (line != NULL)
6632     {
6633       gtk_text_btree_spew_line (tree, line);
6634       line = gtk_text_line_next (line);
6635     }
6636
6637   printf ("=================== Tag information\n");
6638
6639   {
6640     GSList * list;
6641
6642     list = tree->tag_infos;
6643
6644     while (list != NULL)
6645       {
6646         GtkTextTagInfo *info;
6647
6648         info = list->data;
6649
6650         printf ("  tag `%s': root at %p, toggle count %d\n",
6651                 info->tag->name, info->tag_root, info->toggle_count);
6652
6653         list = g_slist_next (list);
6654       }
6655
6656     if (tree->tag_infos == NULL)
6657       {
6658         printf ("  (no tags in the tree)\n");
6659       }
6660   }
6661
6662   printf ("=================== Tree nodes\n");
6663
6664   {
6665     gtk_text_btree_spew_node (tree->root_node, 0);
6666   }
6667 }
6668
6669 void
6670 gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6671 {
6672   gchar * spaces;
6673   GtkTextLineSegment *seg;
6674
6675   spaces = g_strnfill (indent, ' ');
6676
6677   printf ("%sline %p chars %d bytes %d\n",
6678           spaces, line,
6679           gtk_text_line_char_count (line),
6680           gtk_text_line_byte_count (line));
6681
6682   seg = line->segments;
6683   while (seg != NULL)
6684     {
6685       if (seg->type == &gtk_text_char_type)
6686         {
6687           gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6688           gchar* s;
6689           s = str;
6690           while (*s)
6691             {
6692               if (*s == '\n')
6693                 *s = '\\';
6694               ++s;
6695             }
6696           printf ("%s chars `%s'...\n", spaces, str);
6697           g_free (str);
6698         }
6699       else if (seg->type == &gtk_text_right_mark_type)
6700         {
6701           printf ("%s right mark `%s' visible: %d\n",
6702                   spaces,
6703                   seg->body.mark.name,
6704                   seg->body.mark.visible);
6705         }
6706       else if (seg->type == &gtk_text_left_mark_type)
6707         {
6708           printf ("%s left mark `%s' visible: %d\n",
6709                   spaces,
6710                   seg->body.mark.name,
6711                   seg->body.mark.visible);
6712         }
6713       else if (seg->type == &gtk_text_toggle_on_type ||
6714                seg->type == &gtk_text_toggle_off_type)
6715         {
6716           printf ("%s tag `%s' %s\n",
6717                   spaces, seg->body.toggle.info->tag->name,
6718                   seg->type == &gtk_text_toggle_off_type ? "off" : "on");
6719         }
6720
6721       seg = seg->next;
6722     }
6723
6724   g_free (spaces);
6725 }
6726
6727 void
6728 gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6729 {
6730   gchar * spaces;
6731   GtkTextBTreeNode *iter;
6732   Summary *s;
6733
6734   spaces = g_strnfill (indent, ' ');
6735
6736   printf ("%snode %p level %d children %d lines %d chars %d\n",
6737           spaces, node, node->level,
6738           node->num_children, node->num_lines, node->num_chars);
6739
6740   s = node->summary;
6741   while (s)
6742     {
6743       printf ("%s %d toggles of `%s' below this node\n",
6744               spaces, s->toggle_count, s->info->tag->name);
6745       s = s->next;
6746     }
6747
6748   g_free (spaces);
6749
6750   if (node->level > 0)
6751     {
6752       iter = node->children.node;
6753       while (iter != NULL)
6754         {
6755           gtk_text_btree_spew_node (iter, indent + 2);
6756
6757           iter = iter->next;
6758         }
6759     }
6760   else
6761     {
6762       GtkTextLine *line = node->children.line;
6763       while (line != NULL)
6764         {
6765           gtk_text_btree_spew_line_short (line, indent + 2);
6766
6767           line = line->next;
6768         }
6769     }
6770 }
6771
6772 void
6773 gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
6774 {
6775   GtkTextLineSegment * seg;
6776
6777   printf ("%4d| line: %p parent: %p next: %p\n",
6778           gtk_text_line_get_number (line), line, line->parent, line->next);
6779
6780   seg = line->segments;
6781
6782   while (seg != NULL)
6783     {
6784       gtk_text_btree_spew_segment (tree, seg);
6785       seg = seg->next;
6786     }
6787 }
6788
6789 void
6790 gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
6791 {
6792   printf ("     segment: %p type: %s bytes: %d chars: %d\n",
6793           seg, seg->type->name, seg->byte_count, seg->char_count);
6794
6795   if (seg->type == &gtk_text_char_type)
6796     {
6797       gchar* str = g_strndup (seg->body.chars, seg->byte_count);
6798       printf ("       `%s'\n", str);
6799       g_free (str);
6800     }
6801   else if (seg->type == &gtk_text_right_mark_type)
6802     {
6803       printf ("       right mark `%s' visible: %d not_deleteable: %d\n",
6804               seg->body.mark.name,
6805               seg->body.mark.visible,
6806               seg->body.mark.not_deleteable);
6807     }
6808   else if (seg->type == &gtk_text_left_mark_type)
6809     {
6810       printf ("       left mark `%s' visible: %d not_deleteable: %d\n",
6811               seg->body.mark.name,
6812               seg->body.mark.visible,
6813               seg->body.mark.not_deleteable);
6814     }
6815   else if (seg->type == &gtk_text_toggle_on_type ||
6816            seg->type == &gtk_text_toggle_off_type)
6817     {
6818       printf ("       tag `%s' priority %d\n",
6819               seg->body.toggle.info->tag->name,
6820               seg->body.toggle.info->tag->priority);
6821     }
6822 }
6823
6824