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