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