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