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