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