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