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