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.
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>
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.
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.
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.
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.
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.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
60 #include "gtktexttag.h"
61 #include "gtktexttagtable.h"
62 #include "gtktextlayout.h"
63 #include "gtktextiterprivate.h"
65 #include "gtktextmarkprivate.h"
73 * The structure below is used to pass information between
74 * _gtk_text_btree_get_tags and inc_count:
77 typedef struct TagInfo {
78 int numTags; /* Number of tags for which there
79 * is currently information in
81 int arraySize; /* Number of entries allocated for
83 GtkTextTag **tags; /* Array of tags seen so far.
85 int *counts; /* Toggle count (so far) for each
86 * entry in tags. Malloc-ed. */
91 * This is used to store per-view width/height info at the tree nodes.
94 typedef struct _NodeData NodeData;
100 /* Height and width of this node */
102 signed int width : 24;
104 /* boolean indicating whether the lines below this node are in need of validation.
105 * However, width/height should always represent the current total width and
106 * max height for lines below this node; the valid flag indicates whether the
107 * width/height on the lines needs recomputing, not whether the totals
110 guint valid : 8; /* Actually a boolean */
115 * The data structure below keeps summary information about one tag as part
116 * of the tag information in a node.
119 typedef struct Summary {
120 GtkTextTagInfo *info; /* Handle for tag. */
121 int toggle_count; /* Number of transitions into or
122 * out of this tag that occur in
123 * the subtree rooted at this node. */
124 struct Summary *next; /* Next in list of all tags for same
125 * node, or NULL if at end of list. */
129 * The data structure below defines a node in the B-tree.
132 struct _GtkTextBTreeNode {
133 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
134 * this is the root. */
135 GtkTextBTreeNode *next; /* Next in list of siblings with the
136 * same parent node, or NULL for end
138 Summary *summary; /* First in malloc-ed list of info
139 * about tags in this subtree (NULL if
140 * no tag info in the subtree). */
141 int level; /* Level of this node in the B-tree.
142 * 0 refers to the bottom of the tree
143 * (children are lines, not nodes). */
144 union { /* First in linked list of children. */
145 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
146 GtkTextLine *line; /* Used if level == 0. */
148 int num_children; /* Number of children of this node. */
149 int num_lines; /* Total number of lines (leaves) in
150 * the subtree rooted here. */
151 int num_chars; /* Number of chars below here */
158 * Used to store the list of views in our btree
161 typedef struct _BTreeView BTreeView;
165 GtkTextLayout *layout;
171 * And the tree itself
174 struct _GtkTextBTree {
175 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
176 GtkTextTagTable *table;
177 GHashTable *mark_table;
179 GtkTextMark *insert_mark;
180 GtkTextMark *selection_bound_mark;
181 GtkTextBuffer *buffer;
184 gulong tag_changed_handler;
186 /* Incremented when a segment with a byte size > 0
187 * is added to or removed from the tree (i.e. the
188 * length of a line may have changed, and lines may
189 * have been added or removed). This invalidates
190 * all outstanding iterators.
192 guint chars_changed_stamp;
193 /* Incremented when any segments are added or deleted;
194 * this makes outstanding iterators recalculate their
195 * pointed-to segment and segment offset.
197 guint segments_changed_stamp;
199 /* Cache the last line in the buffer */
200 GtkTextLine *last_line;
201 guint last_line_stamp;
203 /* Cache the next-to-last line in the buffer,
204 * containing the end iterator
206 GtkTextLine *end_iter_line;
207 GtkTextLineSegment *end_iter_segment;
208 int end_iter_segment_byte_index;
209 int end_iter_segment_char_offset;
210 guint end_iter_line_stamp;
211 guint end_iter_segment_stamp;
213 GHashTable *child_anchor_table;
218 * Upper and lower bounds on how many children a node may have:
219 * rebalance when either of these limits is exceeded. MAX_CHILDREN
220 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
223 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
224 shallow. It appears to be faster to locate a particular line number
225 if the tree is narrow and deep, since it is more finely sorted. I
226 guess this may increase memory use though, and make it slower to
227 walk the tree in order, or locate a particular byte index (which
228 is done by walking the tree in order).
230 There's basically a tradeoff here. However I'm thinking we want to
231 add pixels, byte counts, and char counts to the tree nodes,
232 at that point narrow and deep should speed up all operations,
233 not just the line number searches.
237 #define MAX_CHILDREN 12
238 #define MIN_CHILDREN 6
240 #define MAX_CHILDREN 6
241 #define MIN_CHILDREN 3
248 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
250 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
251 GtkTextBTreeNode *node);
252 static GtkTextLine * get_last_line (GtkTextBTree *tree);
253 static void post_insert_fixup (GtkTextBTree *tree,
254 GtkTextLine *insert_line,
255 gint char_count_delta,
256 gint line_count_delta);
257 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
258 GtkTextTagInfo *info,
260 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
263 static void segments_changed (GtkTextBTree *tree);
264 static void chars_changed (GtkTextBTree *tree);
265 static void summary_list_destroy (Summary *summary);
266 static GtkTextLine *gtk_text_line_new (void);
267 static void gtk_text_line_destroy (GtkTextBTree *tree,
269 static void gtk_text_line_set_parent (GtkTextLine *line,
270 GtkTextBTreeNode *node);
271 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
275 static NodeData *node_data_new (gpointer view_id);
276 static void node_data_destroy (NodeData *nd);
277 static void node_data_list_destroy (NodeData *nd);
278 static NodeData *node_data_find (NodeData *nd,
281 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
282 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
283 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
285 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
289 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
292 static void gtk_text_btree_node_remove_view (BTreeView *view,
293 GtkTextBTreeNode *node,
295 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
296 GtkTextBTreeNode *node);
297 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
301 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
307 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
308 GtkTextBTreeNode *node2);
309 static void get_tree_bounds (GtkTextBTree *tree,
312 static void tag_changed_cb (GtkTextTagTable *table,
314 gboolean size_changed,
316 static void cleanup_line (GtkTextLine *line);
317 static void recompute_node_counts (GtkTextBTree *tree,
318 GtkTextBTreeNode *node);
319 static void inc_count (GtkTextTag *tag,
321 TagInfo *tagInfoPtr);
323 static void summary_destroy (Summary *summary);
325 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
326 const GtkTextIter *iter);
327 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
328 GtkTextLineSegment *seg,
332 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
334 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
336 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
339 static void redisplay_region (GtkTextBTree *tree,
340 const GtkTextIter *start,
341 const GtkTextIter *end);
343 /* Inline thingies */
346 segments_changed (GtkTextBTree *tree)
348 tree->segments_changed_stamp += 1;
352 chars_changed (GtkTextBTree *tree)
354 tree->chars_changed_stamp += 1;
362 _gtk_text_btree_new (GtkTextTagTable *table,
363 GtkTextBuffer *buffer)
366 GtkTextBTreeNode *root_node;
367 GtkTextLine *line, *line2;
369 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
370 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
373 * The tree will initially have two empty lines. The second line
374 * isn't actually part of the tree's contents, but its presence
375 * makes several operations easier. The tree will have one GtkTextBTreeNode,
376 * which is also the root of the tree.
379 /* Create the root node. */
381 root_node = gtk_text_btree_node_new ();
383 line = gtk_text_line_new ();
384 line2 = gtk_text_line_new ();
386 root_node->parent = NULL;
387 root_node->next = NULL;
388 root_node->summary = NULL;
389 root_node->level = 0;
390 root_node->children.line = line;
391 root_node->num_children = 2;
392 root_node->num_lines = 2;
393 root_node->num_chars = 2;
395 line->parent = root_node;
398 line->segments = _gtk_char_segment_new ("\n", 1);
400 line2->parent = root_node;
402 line2->segments = _gtk_char_segment_new ("\n", 1);
404 /* Create the tree itself */
406 tree = g_new0(GtkTextBTree, 1);
407 tree->root_node = root_node;
411 /* Set these to values that are unlikely to be found
412 * in random memory garbage, and also avoid
413 * duplicates between tree instances.
415 tree->chars_changed_stamp = g_random_int ();
416 tree->segments_changed_stamp = g_random_int ();
418 tree->last_line_stamp = tree->chars_changed_stamp - 1;
419 tree->last_line = NULL;
421 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
422 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
423 tree->end_iter_line = NULL;
424 tree->end_iter_segment_byte_index = 0;
425 tree->end_iter_segment_char_offset = 0;
427 g_object_ref (tree->table);
429 tree->tag_changed_handler = g_signal_connect (tree->table,
431 G_CALLBACK (tag_changed_cb),
434 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
435 tree->child_anchor_table = NULL;
437 /* We don't ref the buffer, since the buffer owns us;
438 * we'd have some circularity issues. The buffer always
439 * lasts longer than the BTree
441 tree->buffer = buffer;
445 GtkTextLineSegment *seg;
447 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
450 tree->insert_mark = _gtk_text_btree_set_mark (tree,
457 seg = tree->insert_mark->segment;
459 seg->body.mark.not_deleteable = TRUE;
460 seg->body.mark.visible = TRUE;
462 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
469 seg = tree->selection_bound_mark->segment;
471 seg->body.mark.not_deleteable = TRUE;
473 g_object_ref (tree->insert_mark);
474 g_object_ref (tree->selection_bound_mark);
483 _gtk_text_btree_ref (GtkTextBTree *tree)
485 g_return_if_fail (tree != NULL);
486 g_return_if_fail (tree->refcount > 0);
492 _gtk_text_btree_unref (GtkTextBTree *tree)
494 g_return_if_fail (tree != NULL);
495 g_return_if_fail (tree->refcount > 0);
499 if (tree->refcount == 0)
501 g_signal_handler_disconnect (tree->table,
502 tree->tag_changed_handler);
504 g_object_unref (tree->table);
507 gtk_text_btree_node_destroy (tree, tree->root_node);
508 tree->root_node = NULL;
510 g_assert (g_hash_table_size (tree->mark_table) == 0);
511 g_hash_table_destroy (tree->mark_table);
512 tree->mark_table = NULL;
513 if (tree->child_anchor_table != NULL)
515 g_hash_table_destroy (tree->child_anchor_table);
516 tree->child_anchor_table = NULL;
519 g_object_unref (tree->insert_mark);
520 tree->insert_mark = NULL;
521 g_object_unref (tree->selection_bound_mark);
522 tree->selection_bound_mark = NULL;
529 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
535 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
537 return tree->chars_changed_stamp;
541 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
543 return tree->segments_changed_stamp;
547 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
549 g_return_if_fail (tree != NULL);
550 segments_changed (tree);
554 * Indexable segment mutation
558 * The following function is responsible for resolving the bidi direction
559 * for the lines between start and end. But it also calculates any
560 * dependent bidi direction for surrounding lines that change as a result
561 * of the bidi direction decisions within the range. The function is
562 * trying to do as little propagation as is needed.
565 gtk_text_btree_resolve_bidi (GtkTextIter *start,
568 GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
569 GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
570 PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
572 /* Resolve the strong bidi direction for all lines between
575 start_line = _gtk_text_iter_get_text_line (start);
576 start_line_prev = _gtk_text_line_previous (start_line);
577 end_line = _gtk_text_iter_get_text_line (end);
578 end_line_next = _gtk_text_line_next (end_line);
581 while (line && line != end_line_next)
583 /* Loop through the segments and search for a strong character
585 GtkTextLineSegment *seg = line->segments;
586 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
590 if (seg->byte_count > 0)
592 PangoDirection pango_dir;
594 pango_dir = pango_find_base_dir (seg->body.chars,
597 if (pango_dir != PANGO_DIRECTION_NEUTRAL)
599 line->dir_strong = pango_dir;
606 line = _gtk_text_line_next (line);
611 /* The variable dir_above_propagated contains the forward propagated
612 * direction before start. It is neutral if start is in the beginning
615 dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
617 dir_above_propagated = start_line_prev->dir_propagated_forward;
619 /* Loop forward and propagate the direction of each paragraph
620 * to all neutral lines.
623 last_strong = dir_above_propagated;
624 while (line != end_line_next)
626 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
627 last_strong = line->dir_strong;
629 line->dir_propagated_forward = last_strong;
631 line = _gtk_text_line_next (line);
634 /* Continue propagating as long as the previous resolved forward
635 * is different from last_strong.
638 GtkTextIter end_propagate;
641 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
642 line->dir_propagated_forward != last_strong)
644 GtkTextLine *prev = line;
645 line->dir_propagated_forward = last_strong;
647 line = _gtk_text_line_next(line);
655 /* The last line to invalidate is the last line before the
656 * line with the strong character. Or in case of the end of the
657 * buffer, the last line of the buffer. (There seems to be an
658 * extra "virtual" last line in the buffer that must not be used
659 * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
660 * _gtk_text_line_previous is ok in that case as well.)
662 line = _gtk_text_line_previous (line);
663 _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
664 _gtk_text_btree_invalidate_region (tree, end, &end_propagate);
669 /* The variable dir_below_propagated contains the backward propagated
670 * direction after end. It is neutral if end is at the end of
673 dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
675 dir_below_propagated = end_line_next->dir_propagated_back;
677 /* Loop backward and propagate the direction of each paragraph
678 * to all neutral lines.
681 last_strong = dir_below_propagated;
682 while (line != start_line_prev)
684 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
685 last_strong = line->dir_strong;
687 line->dir_propagated_back = last_strong;
689 line = _gtk_text_line_previous (line);
692 /* Continue propagating as long as the resolved backward dir
693 * is different from last_strong.
696 GtkTextIter start_propagate;
699 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
700 line->dir_propagated_back != last_strong)
702 GtkTextLine *prev = line;
703 line->dir_propagated_back = last_strong;
705 line = _gtk_text_line_previous (line);
713 /* We only need to invalidate for backwards propagation if the
714 * line we ended up on didn't get a direction from forwards
717 if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
719 _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
720 _gtk_text_btree_invalidate_region(tree, &start_propagate, start);
726 _gtk_text_btree_delete (GtkTextIter *start,
729 GtkTextLineSegment *prev_seg; /* The segment just before the start
730 * of the deletion range. */
731 GtkTextLineSegment *last_seg; /* The segment just after the end
732 * of the deletion range. */
733 GtkTextLineSegment *seg, *next;
734 GtkTextLine *curline;
735 GtkTextBTreeNode *curnode, *node;
737 GtkTextLine *start_line;
738 GtkTextLine *end_line;
739 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
740 gint start_byte_offset;
742 g_return_if_fail (start != NULL);
743 g_return_if_fail (end != NULL);
744 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
745 _gtk_text_iter_get_btree (end));
747 gtk_text_iter_order (start, end);
749 tree = _gtk_text_iter_get_btree (start);
751 if (gtk_debug_flags & GTK_DEBUG_TEXT)
752 _gtk_text_btree_check (tree);
754 /* Broadcast the need for redisplay before we break the iterators */
755 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
756 _gtk_text_btree_invalidate_region (tree, start, end);
758 /* Save the byte offset so we can reset the iterators */
759 start_byte_offset = gtk_text_iter_get_line_index (start);
761 start_line = _gtk_text_iter_get_text_line (start);
762 end_line = _gtk_text_iter_get_text_line (end);
765 * Split the start and end segments, so we have a place
766 * to insert our new text.
768 * Tricky point: split at end first; otherwise the split
769 * at end may invalidate seg and/or prev_seg. This allows
770 * us to avoid invalidating segments for start.
773 last_seg = gtk_text_line_segment_split (end);
774 if (last_seg != NULL)
775 last_seg = last_seg->next;
777 last_seg = end_line->segments;
779 prev_seg = gtk_text_line_segment_split (start);
780 if (prev_seg != NULL)
782 seg = prev_seg->next;
783 prev_seg->next = last_seg;
787 seg = start_line->segments;
788 start_line->segments = last_seg;
791 /* notify iterators that their segments need recomputation,
792 just for robustness. */
793 segments_changed (tree);
796 * Delete all of the segments between prev_seg and last_seg.
799 curline = start_line;
800 curnode = curline->parent;
801 while (seg != last_seg)
807 GtkTextLine *nextline;
810 * We just ran off the end of a line. First find the
811 * next line, then go back to the old line and delete it
812 * (unless it's the starting line for the range).
815 nextline = _gtk_text_line_next (curline);
816 if (curline != start_line)
818 if (curnode == start_line->parent)
819 start_line->next = curline->next;
821 curnode->children.line = curline->next;
823 for (node = curnode; node != NULL;
826 /* Don't update node->num_chars, because
827 * that was done when we deleted the segments.
829 node->num_lines -= 1;
832 curnode->num_children -= 1;
833 curline->next = deleted_lines;
834 deleted_lines = curline;
838 seg = curline->segments;
841 * If the GtkTextBTreeNode is empty then delete it and its parents,
842 * recursively upwards until a non-empty GtkTextBTreeNode is found.
845 while (curnode->num_children == 0)
847 GtkTextBTreeNode *parent;
849 parent = curnode->parent;
850 if (parent->children.node == curnode)
852 parent->children.node = curnode->next;
856 GtkTextBTreeNode *prevnode = parent->children.node;
857 while (prevnode->next != curnode)
859 prevnode = prevnode->next;
861 prevnode->next = curnode->next;
863 parent->num_children--;
864 gtk_text_btree_node_free_empty (tree, curnode);
867 curnode = curline->parent;
872 char_count = seg->char_count;
874 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
877 * This segment refuses to die. Move it to prev_seg and
878 * advance prev_seg if the segment has left gravity.
881 if (prev_seg == NULL)
883 seg->next = start_line->segments;
884 start_line->segments = seg;
888 seg->next = prev_seg->next;
889 prev_seg->next = seg;
891 if (seg->type->leftGravity)
898 /* Segment is gone. Decrement the char count of the node and
900 for (node = curnode; node != NULL;
903 node->num_chars -= char_count;
911 * If the beginning and end of the deletion range are in different
912 * lines, join the two lines together and discard the ending line.
915 if (start_line != end_line)
918 GtkTextBTreeNode *ancestor_node;
919 GtkTextLine *prevline;
922 /* last_seg was appended to start_line up at the top of this function */
924 for (seg = last_seg; seg != NULL;
927 chars_moved += seg->char_count;
928 if (seg->type->lineChangeFunc != NULL)
930 (*seg->type->lineChangeFunc)(seg, end_line);
934 for (node = start_line->parent; node != NULL;
937 node->num_chars += chars_moved;
940 curnode = end_line->parent;
941 for (node = curnode; node != NULL;
944 node->num_chars -= chars_moved;
947 curnode->num_children--;
948 prevline = curnode->children.line;
949 if (prevline == end_line)
951 curnode->children.line = end_line->next;
955 while (prevline->next != end_line)
957 prevline = prevline->next;
959 prevline->next = end_line->next;
961 end_line->next = deleted_lines;
962 deleted_lines = end_line;
964 /* We now fix up the per-view aggregates. We add all the height and
965 * width for the deleted lines to the start line, so that when revalidation
966 * occurs, the correct change in size is seen.
968 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
975 gint deleted_width = 0;
976 gint deleted_height = 0;
978 line = deleted_lines;
981 GtkTextLine *next_line = line->next;
982 ld = _gtk_text_line_get_data (line, view->view_id);
986 deleted_width = MAX (deleted_width, ld->width);
987 deleted_height += ld->height;
991 gtk_text_line_destroy (tree, line);
996 if (deleted_width > 0 || deleted_height > 0)
998 ld = _gtk_text_line_get_data (start_line, view->view_id);
1002 /* This means that start_line has never been validated.
1003 * We don't really want to do the validation here but
1004 * we do need to store our temporary sizes. So we
1005 * create the line data and assume a line w/h of 0.
1007 ld = _gtk_text_line_data_new (view->layout, start_line);
1008 _gtk_text_line_add_data (start_line, ld);
1014 ld->width = MAX (deleted_width, ld->width);
1015 ld->height += deleted_height;
1019 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1020 if (ancestor_node->parent)
1021 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1026 /* avoid dangling pointer */
1027 deleted_lines = NULL;
1029 gtk_text_btree_rebalance (tree, curnode);
1033 * Cleanup the segments in the new line.
1036 cleanup_line (start_line);
1039 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1042 gtk_text_btree_rebalance (tree, start_line->parent);
1044 /* Notify outstanding iterators that they
1046 chars_changed (tree);
1047 segments_changed (tree);
1049 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1050 _gtk_text_btree_check (tree);
1052 /* Re-initialize our iterators */
1053 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1056 _gtk_text_btree_resolve_bidi (start, end);
1060 _gtk_text_btree_insert (GtkTextIter *iter,
1064 GtkTextLineSegment *prev_seg; /* The segment just before the first
1065 * new segment (NULL means new segment
1066 * is at beginning of line). */
1067 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1068 * are inserted just after this one.
1069 * NULL means insert at beginning of
1071 GtkTextLine *line; /* Current line (new segments are
1072 * added to this line). */
1073 GtkTextLineSegment *seg;
1074 GtkTextLine *newline;
1075 int chunk_len; /* # characters in current chunk. */
1076 gint sol; /* start of line */
1077 gint eol; /* Pointer to character just after last
1078 * one in current chunk.
1080 gint delim; /* index of paragraph delimiter */
1081 int line_count_delta; /* Counts change to total number of
1085 int char_count_delta; /* change to number of chars */
1087 gint start_byte_index;
1088 GtkTextLine *start_line;
1090 g_return_if_fail (text != NULL);
1091 g_return_if_fail (iter != NULL);
1094 len = strlen (text);
1096 /* extract iterator info */
1097 tree = _gtk_text_iter_get_btree (iter);
1098 line = _gtk_text_iter_get_text_line (iter);
1101 start_byte_index = gtk_text_iter_get_line_index (iter);
1103 /* Get our insertion segment split. Note this assumes line allows
1104 * char insertions, which isn't true of the "last" line. But iter
1105 * should not be on that line, as we assert here.
1107 g_assert (!_gtk_text_line_is_last (line, tree));
1108 prev_seg = gtk_text_line_segment_split (iter);
1111 /* Invalidate all iterators */
1112 chars_changed (tree);
1113 segments_changed (tree);
1116 * Chop the text up into lines and create a new segment for
1117 * each line, plus a new line for the leftovers from the
1123 line_count_delta = 0;
1124 char_count_delta = 0;
1129 pango_find_paragraph_boundary (text + sol,
1134 /* make these relative to the start of the text */
1138 g_assert (eol >= sol);
1139 g_assert (delim >= sol);
1140 g_assert (eol >= delim);
1141 g_assert (sol >= 0);
1142 g_assert (eol <= len);
1144 chunk_len = eol - sol;
1146 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1147 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1149 char_count_delta += seg->char_count;
1151 if (cur_seg == NULL)
1153 seg->next = line->segments;
1154 line->segments = seg;
1158 seg->next = cur_seg->next;
1159 cur_seg->next = seg;
1164 /* chunk didn't end with a paragraph separator */
1165 g_assert (eol == len);
1170 * The chunk ended with a newline, so create a new GtkTextLine
1171 * and move the remainder of the old line to it.
1174 newline = gtk_text_line_new ();
1175 gtk_text_line_set_parent (newline, line->parent);
1176 newline->next = line->next;
1177 line->next = newline;
1178 newline->segments = seg->next;
1186 * Cleanup the starting line for the insertion, plus the ending
1187 * line if it's different.
1190 cleanup_line (start_line);
1191 if (line != start_line)
1193 cleanup_line (line);
1196 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1198 /* Invalidate our region, and reset the iterator the user
1199 passed in to point to the end of the inserted text. */
1205 _gtk_text_btree_get_iter_at_line (tree,
1211 /* We could almost certainly be more efficient here
1212 by saving the information from the insertion loop
1214 gtk_text_iter_forward_chars (&end, char_count_delta);
1216 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1217 _gtk_text_btree_invalidate_region (tree,
1221 /* Convenience for the user */
1224 _gtk_text_btree_resolve_bidi (&start, &end);
1229 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1230 GtkTextLineSegment *seg)
1234 GtkTextLineSegment *prevPtr;
1237 gint start_byte_offset;
1239 line = _gtk_text_iter_get_text_line (iter);
1240 tree = _gtk_text_iter_get_btree (iter);
1241 start_byte_offset = gtk_text_iter_get_line_index (iter);
1243 prevPtr = gtk_text_line_segment_split (iter);
1244 if (prevPtr == NULL)
1246 seg->next = line->segments;
1247 line->segments = seg;
1251 seg->next = prevPtr->next;
1252 prevPtr->next = seg;
1255 post_insert_fixup (tree, line, 0, seg->char_count);
1257 chars_changed (tree);
1258 segments_changed (tree);
1260 /* reset *iter for the user, and invalidate tree nodes */
1262 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1265 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1267 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1268 _gtk_text_btree_invalidate_region (tree, &start, iter);
1272 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1275 GtkTextLineSegment *seg;
1277 seg = _gtk_pixbuf_segment_new (pixbuf);
1279 insert_pixbuf_or_widget_segment (iter, seg);
1283 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1284 GtkTextChildAnchor *anchor)
1286 GtkTextLineSegment *seg;
1289 if (anchor->segment != NULL)
1291 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1295 seg = _gtk_widget_segment_new (anchor);
1297 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1298 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1300 insert_pixbuf_or_widget_segment (iter, seg);
1302 if (tree->child_anchor_table == NULL)
1303 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1305 g_hash_table_insert (tree->child_anchor_table,
1306 seg->body.child.obj,
1307 seg->body.child.obj);
1311 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1313 GtkTextLineSegment *seg;
1315 seg = anchor->segment;
1317 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1326 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1327 GtkTextBTreeNode *node, gint y, gint *line_top,
1328 GtkTextLine *last_line)
1332 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1333 _gtk_text_btree_check (tree);
1335 if (node->level == 0)
1339 line = node->children.line;
1341 while (line != NULL && line != last_line)
1343 GtkTextLineData *ld;
1345 ld = _gtk_text_line_get_data (line, view->view_id);
1349 if (y < (current_y + (ld ? ld->height : 0)))
1352 current_y += ld->height;
1353 *line_top += ld->height;
1362 GtkTextBTreeNode *child;
1364 child = node->children.node;
1366 while (child != NULL)
1371 gtk_text_btree_node_get_size (child, view->view_id,
1374 if (y < (current_y + height))
1375 return find_line_by_y (tree, view, child,
1376 y - current_y, line_top,
1379 current_y += height;
1380 *line_top += height;
1382 child = child->next;
1390 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1397 GtkTextLine *last_line;
1400 view = gtk_text_btree_get_view (tree, view_id);
1401 g_return_val_if_fail (view != NULL, NULL);
1403 last_line = get_last_line (tree);
1405 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1409 *line_top_out = line_top;
1415 find_line_top_in_line_list (GtkTextBTree *tree,
1418 GtkTextLine *target_line,
1421 while (line != NULL)
1423 GtkTextLineData *ld;
1425 if (line == target_line)
1428 ld = _gtk_text_line_get_data (line, view->view_id);
1435 g_assert_not_reached (); /* If we get here, our
1436 target line didn't exist
1437 under its parent node */
1442 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1443 GtkTextLine *target_line,
1450 GtkTextBTreeNode *node;
1452 view = gtk_text_btree_get_view (tree, view_id);
1454 g_return_val_if_fail (view != NULL, 0);
1457 node = target_line->parent;
1458 while (node != NULL)
1460 nodes = g_slist_prepend (nodes, node);
1461 node = node->parent;
1465 while (iter != NULL)
1469 if (node->level == 0)
1471 g_slist_free (nodes);
1472 return find_line_top_in_line_list (tree, view,
1473 node->children.line,
1478 GtkTextBTreeNode *child;
1479 GtkTextBTreeNode *target_node;
1481 g_assert (iter->next != NULL); /* not at level 0 */
1482 target_node = iter->next->data;
1484 child = node->children.node;
1486 while (child != NULL)
1491 if (child == target_node)
1495 gtk_text_btree_node_get_size (child, view->view_id,
1499 child = child->next;
1501 g_assert (child != NULL); /* should have broken out before we
1505 iter = g_slist_next (iter);
1508 g_assert_not_reached (); /* we return when we find the target line */
1513 _gtk_text_btree_add_view (GtkTextBTree *tree,
1514 GtkTextLayout *layout)
1517 GtkTextLine *last_line;
1518 GtkTextLineData *line_data;
1520 g_return_if_fail (tree != NULL);
1522 view = g_new (BTreeView, 1);
1524 view->view_id = layout;
1525 view->layout = layout;
1527 view->next = tree->views;
1532 g_assert (tree->views->prev == NULL);
1533 tree->views->prev = view;
1538 /* The last line in the buffer has identity values for the per-view
1539 * data so that we can avoid special case checks for it in a large
1542 last_line = get_last_line (tree);
1544 line_data = g_new (GtkTextLineData, 1);
1545 line_data->view_id = layout;
1546 line_data->next = NULL;
1547 line_data->width = 0;
1548 line_data->height = 0;
1549 line_data->valid = TRUE;
1551 _gtk_text_line_add_data (last_line, line_data);
1555 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1559 GtkTextLine *last_line;
1560 GtkTextLineData *line_data;
1562 g_return_if_fail (tree != NULL);
1566 while (view != NULL)
1568 if (view->view_id == view_id)
1574 g_return_if_fail (view != NULL);
1577 view->next->prev = view->prev;
1580 view->prev->next = view->next;
1582 if (view == tree->views)
1583 tree->views = view->next;
1585 /* Remove the line data for the last line which we added ourselves.
1586 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1588 last_line = get_last_line (tree);
1589 line_data = _gtk_text_line_remove_data (last_line, view_id);
1592 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1594 view->layout = (gpointer) 0xdeadbeef;
1595 view->view_id = (gpointer) 0xdeadbeef;
1601 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1602 const GtkTextIter *start,
1603 const GtkTextIter *end)
1609 while (view != NULL)
1611 gtk_text_layout_invalidate (view->layout, start, end);
1618 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1623 g_return_if_fail (tree != NULL);
1624 g_return_if_fail (view_id != NULL);
1626 gtk_text_btree_node_get_size (tree->root_node, view_id,
1641 iter_stack_new (void)
1644 stack = g_new (IterStack, 1);
1645 stack->iters = NULL;
1652 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1655 if (stack->count > stack->alloced)
1657 stack->alloced = stack->count*2;
1658 stack->iters = g_realloc (stack->iters,
1659 stack->alloced*sizeof (GtkTextIter));
1661 stack->iters[stack->count-1] = *iter;
1665 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1667 if (stack->count == 0)
1672 *iter = stack->iters[stack->count];
1678 iter_stack_free (IterStack *stack)
1680 g_free (stack->iters);
1685 iter_stack_invert (IterStack *stack)
1687 if (stack->count > 0)
1690 guint j = stack->count - 1;
1695 tmp = stack->iters[i];
1696 stack->iters[i] = stack->iters[j];
1697 stack->iters[j] = tmp;
1706 queue_tag_redisplay (GtkTextBTree *tree,
1708 const GtkTextIter *start,
1709 const GtkTextIter *end)
1711 if (_gtk_text_tag_affects_size (tag))
1713 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1714 _gtk_text_btree_invalidate_region (tree, start, end);
1716 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1718 /* We only need to queue a redraw, not a relayout */
1719 redisplay_region (tree, start, end);
1722 /* We don't need to do anything if the tag doesn't affect display */
1726 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1727 const GtkTextIter *end_orig,
1731 GtkTextLineSegment *seg, *prev;
1732 GtkTextLine *cleanupline;
1733 gboolean toggled_on;
1734 GtkTextLine *start_line;
1735 GtkTextLine *end_line;
1737 GtkTextIter start, end;
1740 GtkTextTagInfo *info;
1742 g_return_if_fail (start_orig != NULL);
1743 g_return_if_fail (end_orig != NULL);
1744 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1745 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1746 _gtk_text_iter_get_btree (end_orig));
1747 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1750 printf ("%s tag %s from %d to %d\n",
1751 add ? "Adding" : "Removing",
1753 gtk_text_buffer_get_offset (start_orig),
1754 gtk_text_buffer_get_offset (end_orig));
1757 if (gtk_text_iter_equal (start_orig, end_orig))
1760 start = *start_orig;
1763 gtk_text_iter_order (&start, &end);
1765 tree = _gtk_text_iter_get_btree (&start);
1767 queue_tag_redisplay (tree, tag, &start, &end);
1769 info = gtk_text_btree_get_tag_info (tree, tag);
1771 start_line = _gtk_text_iter_get_text_line (&start);
1772 end_line = _gtk_text_iter_get_text_line (&end);
1774 /* Find all tag toggles in the region; we are going to delete them.
1775 We need to find them in advance, because
1776 forward_find_tag_toggle () won't work once we start playing around
1778 stack = iter_stack_new ();
1781 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1782 * which is deliberate - we don't want to delete a toggle at the
1785 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1787 if (gtk_text_iter_compare (&iter, &end) >= 0)
1790 iter_stack_push (stack, &iter);
1793 /* We need to traverse the toggles in order. */
1794 iter_stack_invert (stack);
1797 * See whether the tag is present at the start of the range. If
1798 * the state doesn't already match what we want then add a toggle
1802 toggled_on = gtk_text_iter_has_tag (&start, tag);
1803 if ( (add && !toggled_on) ||
1804 (!add && toggled_on) )
1806 /* This could create a second toggle at the start position;
1807 cleanup_line () will remove it if so. */
1808 seg = _gtk_toggle_segment_new (info, add);
1810 prev = gtk_text_line_segment_split (&start);
1813 seg->next = start_line->segments;
1814 start_line->segments = seg;
1818 seg->next = prev->next;
1822 /* cleanup_line adds the new toggle to the node counts. */
1824 printf ("added toggle at start\n");
1826 /* we should probably call segments_changed, but in theory
1827 any still-cached segments in the iters we are about to
1828 use are still valid, since they're in front
1834 * Scan the range of characters and delete any internal tag
1835 * transitions. Keep track of what the old state was at the end
1836 * of the range, and add a toggle there if it's needed.
1840 cleanupline = start_line;
1841 while (iter_stack_pop (stack, &iter))
1843 GtkTextLineSegment *indexable_seg;
1846 line = _gtk_text_iter_get_text_line (&iter);
1847 seg = _gtk_text_iter_get_any_segment (&iter);
1848 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1850 g_assert (seg != NULL);
1851 g_assert (indexable_seg != NULL);
1852 g_assert (seg != indexable_seg);
1854 prev = line->segments;
1856 /* Find the segment that actually toggles this tag. */
1857 while (seg != indexable_seg)
1859 g_assert (seg != NULL);
1860 g_assert (indexable_seg != NULL);
1861 g_assert (seg != indexable_seg);
1863 if ( (seg->type == >k_text_toggle_on_type ||
1864 seg->type == >k_text_toggle_off_type) &&
1865 (seg->body.toggle.info == info) )
1871 g_assert (seg != NULL);
1872 g_assert (indexable_seg != NULL);
1874 g_assert (seg != indexable_seg); /* If this happens, then
1875 forward_to_tag_toggle was
1877 g_assert (seg->body.toggle.info->tag == tag);
1879 /* If this happens, when previously tagging we didn't merge
1880 overlapping tags. */
1881 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1882 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1884 toggled_on = !toggled_on;
1887 printf ("deleting %s toggle\n",
1888 seg->type == >k_text_toggle_on_type ? "on" : "off");
1890 /* Remove toggle segment from the list. */
1893 line->segments = seg->next;
1897 while (prev->next != seg)
1901 prev->next = seg->next;
1904 /* Inform iterators we've hosed them. This actually reflects a
1905 bit of inefficiency; if you have the same tag toggled on and
1906 off a lot in a single line, we keep having the rescan from
1907 the front of the line. Of course we have to do that to get
1908 "prev" anyway, but here we are doing it an additional
1910 segments_changed (tree);
1912 /* Update node counts */
1913 if (seg->body.toggle.inNodeCounts)
1915 _gtk_change_node_toggle_count (line->parent,
1917 seg->body.toggle.inNodeCounts = FALSE;
1922 /* We only clean up lines when we're done with them, saves some
1923 gratuitous line-segment-traversals */
1925 if (cleanupline != line)
1927 cleanup_line (cleanupline);
1932 iter_stack_free (stack);
1934 /* toggled_on now reflects the toggle state _just before_ the
1935 end iterator. The end iterator could already have a toggle
1936 on or a toggle off. */
1937 if ( (add && !toggled_on) ||
1938 (!add && toggled_on) )
1940 /* This could create a second toggle at the start position;
1941 cleanup_line () will remove it if so. */
1943 seg = _gtk_toggle_segment_new (info, !add);
1945 prev = gtk_text_line_segment_split (&end);
1948 seg->next = end_line->segments;
1949 end_line->segments = seg;
1953 seg->next = prev->next;
1956 /* cleanup_line adds the new toggle to the node counts. */
1957 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1959 printf ("added toggle at end\n");
1964 * Cleanup cleanupline and the last line of the range, if
1965 * these are different.
1968 cleanup_line (cleanupline);
1969 if (cleanupline != end_line)
1971 cleanup_line (end_line);
1974 segments_changed (tree);
1976 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1977 _gtk_text_btree_check (tree);
1986 get_line_internal (GtkTextBTree *tree,
1988 gint *real_line_number,
1989 gboolean include_last)
1991 GtkTextBTreeNode *node;
1996 line_count = _gtk_text_btree_line_count (tree);
2000 if (line_number < 0)
2002 line_number = line_count;
2004 else if (line_number > line_count)
2006 line_number = line_count;
2009 if (real_line_number)
2010 *real_line_number = line_number;
2012 node = tree->root_node;
2013 lines_left = line_number;
2016 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2020 while (node->level != 0)
2022 for (node = node->children.node;
2023 node->num_lines <= lines_left;
2029 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2032 lines_left -= node->num_lines;
2037 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2040 for (line = node->children.line; lines_left > 0;
2046 g_error ("gtk_text_btree_find_line ran out of lines");
2055 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2058 _gtk_text_btree_get_line (tree,
2059 _gtk_text_btree_line_count (tree) - 1,
2064 _gtk_text_btree_get_line (GtkTextBTree *tree,
2066 gint *real_line_number)
2068 return get_line_internal (tree, line_number, real_line_number, TRUE);
2072 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2074 gint *real_line_number)
2076 return get_line_internal (tree, line_number, real_line_number, FALSE);
2080 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2082 gint *line_start_index,
2083 gint *real_char_index)
2085 GtkTextBTreeNode *node;
2087 GtkTextLineSegment *seg;
2092 node = tree->root_node;
2094 /* Clamp to valid indexes (-1 is magic for "highest index"),
2095 * node->num_chars includes the two newlines that aren't really
2098 if (char_index < 0 || char_index >= (node->num_chars - 1))
2100 char_index = node->num_chars - 2;
2103 *real_char_index = char_index;
2106 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2110 chars_left = char_index;
2111 while (node->level != 0)
2113 for (node = node->children.node;
2114 chars_left >= node->num_chars;
2117 chars_left -= node->num_chars;
2119 g_assert (chars_left >= 0);
2123 if (chars_left == 0)
2125 /* Start of a line */
2127 *line_start_index = char_index;
2128 return node->children.line;
2132 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2138 for (line = node->children.line; line != NULL; line = line->next)
2140 seg = line->segments;
2143 if (chars_in_line + seg->char_count > chars_left)
2144 goto found; /* found our line/segment */
2146 chars_in_line += seg->char_count;
2151 chars_left -= chars_in_line;
2159 g_assert (line != NULL); /* hosage, ran out of lines */
2160 g_assert (seg != NULL);
2162 *line_start_index = char_index - chars_left;
2167 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2170 GtkTextBTreeNode *node;
2171 GtkTextLine *siblingline;
2172 GtkTextLineSegment *seg;
2173 int src, dst, index;
2179 #define NUM_TAG_INFOS 10
2181 line = _gtk_text_iter_get_text_line (iter);
2182 tree = _gtk_text_iter_get_btree (iter);
2183 byte_index = gtk_text_iter_get_line_index (iter);
2185 tagInfo.numTags = 0;
2186 tagInfo.arraySize = NUM_TAG_INFOS;
2187 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2188 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2191 * Record tag toggles within the line of indexPtr but preceding
2192 * indexPtr. Note that if this loop segfaults, your
2193 * byte_index probably points past the sum of all
2194 * seg->byte_count */
2196 for (index = 0, seg = line->segments;
2197 (index + seg->byte_count) <= byte_index;
2198 index += seg->byte_count, seg = seg->next)
2200 if ((seg->type == >k_text_toggle_on_type)
2201 || (seg->type == >k_text_toggle_off_type))
2203 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2208 * Record toggles for tags in lines that are predecessors of
2209 * line but under the same level-0 GtkTextBTreeNode.
2212 for (siblingline = line->parent->children.line;
2213 siblingline != line;
2214 siblingline = siblingline->next)
2216 for (seg = siblingline->segments; seg != NULL;
2219 if ((seg->type == >k_text_toggle_on_type)
2220 || (seg->type == >k_text_toggle_off_type))
2222 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2228 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2229 * toggles for all siblings that precede that GtkTextBTreeNode.
2232 for (node = line->parent; node->parent != NULL;
2233 node = node->parent)
2235 GtkTextBTreeNode *siblingPtr;
2238 for (siblingPtr = node->parent->children.node;
2239 siblingPtr != node; siblingPtr = siblingPtr->next)
2241 for (summary = siblingPtr->summary; summary != NULL;
2242 summary = summary->next)
2244 if (summary->toggle_count & 1)
2246 inc_count (summary->info->tag, summary->toggle_count,
2254 * Go through the tag information and squash out all of the tags
2255 * that have even toggle counts (these tags exist before the point
2256 * of interest, but not at the desired character itself).
2259 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2261 if (tagInfo.counts[src] & 1)
2263 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2264 tagInfo.tags[dst] = tagInfo.tags[src];
2270 g_free (tagInfo.counts);
2273 g_free (tagInfo.tags);
2276 return tagInfo.tags;
2280 copy_segment (GString *string,
2281 gboolean include_hidden,
2282 gboolean include_nonchars,
2283 const GtkTextIter *start,
2284 const GtkTextIter *end)
2286 GtkTextLineSegment *end_seg;
2287 GtkTextLineSegment *seg;
2289 if (gtk_text_iter_equal (start, end))
2292 seg = _gtk_text_iter_get_indexable_segment (start);
2293 end_seg = _gtk_text_iter_get_indexable_segment (end);
2295 if (seg->type == >k_text_char_type)
2297 gboolean copy = TRUE;
2298 gint copy_bytes = 0;
2299 gint copy_start = 0;
2301 /* Don't copy if we're invisible; segments are invisible/not
2302 as a whole, no need to check each char */
2303 if (!include_hidden &&
2304 _gtk_text_btree_char_is_invisible (start))
2307 /* printf (" <invisible>\n"); */
2310 copy_start = _gtk_text_iter_get_segment_byte (start);
2314 /* End is in the same segment; need to copy fewer bytes. */
2315 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2317 copy_bytes = end_byte - copy_start;
2320 copy_bytes = seg->byte_count - copy_start;
2322 g_assert (copy_bytes != 0); /* Due to iter equality check at
2323 front of this function. */
2327 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2329 g_string_append_len (string,
2330 seg->body.chars + copy_start,
2334 /* printf (" :%s\n", string->str); */
2336 else if (seg->type == >k_text_pixbuf_type ||
2337 seg->type == >k_text_child_type)
2339 gboolean copy = TRUE;
2341 if (!include_nonchars)
2345 else if (!include_hidden &&
2346 _gtk_text_btree_char_is_invisible (start))
2353 g_string_append_len (string,
2354 gtk_text_unknown_char_utf8,
2362 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2363 const GtkTextIter *end_orig,
2364 gboolean include_hidden,
2365 gboolean include_nonchars)
2367 GtkTextLineSegment *seg;
2368 GtkTextLineSegment *end_seg;
2376 g_return_val_if_fail (start_orig != NULL, NULL);
2377 g_return_val_if_fail (end_orig != NULL, NULL);
2378 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2379 _gtk_text_iter_get_btree (end_orig), NULL);
2381 start = *start_orig;
2384 gtk_text_iter_order (&start, &end);
2386 retval = g_string_new (NULL);
2388 tree = _gtk_text_iter_get_btree (&start);
2390 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2392 seg = _gtk_text_iter_get_indexable_segment (&iter);
2393 while (seg != end_seg)
2395 copy_segment (retval, include_hidden, include_nonchars,
2398 _gtk_text_iter_forward_indexable_segment (&iter);
2400 seg = _gtk_text_iter_get_indexable_segment (&iter);
2403 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2406 g_string_free (retval, FALSE);
2411 _gtk_text_btree_line_count (GtkTextBTree *tree)
2413 /* Subtract bogus line at the end; we return a count
2415 return tree->root_node->num_lines - 1;
2419 _gtk_text_btree_char_count (GtkTextBTree *tree)
2421 /* Exclude newline in bogus last line and the
2422 * one in the last line that is after the end iterator
2424 return tree->root_node->num_chars - 2;
2427 #define LOTSA_TAGS 1000
2429 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2431 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2433 int deftagCnts[LOTSA_TAGS];
2434 int *tagCnts = deftagCnts;
2435 GtkTextTag *deftags[LOTSA_TAGS];
2436 GtkTextTag **tags = deftags;
2438 GtkTextBTreeNode *node;
2439 GtkTextLine *siblingline;
2440 GtkTextLineSegment *seg;
2447 line = _gtk_text_iter_get_text_line (iter);
2448 tree = _gtk_text_iter_get_btree (iter);
2449 byte_index = gtk_text_iter_get_line_index (iter);
2451 numTags = gtk_text_tag_table_get_size (tree->table);
2453 /* almost always avoid malloc, so stay out of system calls */
2454 if (LOTSA_TAGS < numTags)
2456 tagCnts = g_new (int, numTags);
2457 tags = g_new (GtkTextTag*, numTags);
2460 for (i=0; i<numTags; i++)
2466 * Record tag toggles within the line of indexPtr but preceding
2470 for (index = 0, seg = line->segments;
2471 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2472 index += seg->byte_count, seg = seg->next)
2474 if ((seg->type == >k_text_toggle_on_type)
2475 || (seg->type == >k_text_toggle_off_type))
2477 tag = seg->body.toggle.info->tag;
2478 if (tag->invisible_set && tag->values->invisible)
2480 tags[tag->priority] = tag;
2481 tagCnts[tag->priority]++;
2487 * Record toggles for tags in lines that are predecessors of
2488 * line but under the same level-0 GtkTextBTreeNode.
2491 for (siblingline = line->parent->children.line;
2492 siblingline != line;
2493 siblingline = siblingline->next)
2495 for (seg = siblingline->segments; seg != NULL;
2498 if ((seg->type == >k_text_toggle_on_type)
2499 || (seg->type == >k_text_toggle_off_type))
2501 tag = seg->body.toggle.info->tag;
2502 if (tag->invisible_set && tag->values->invisible)
2504 tags[tag->priority] = tag;
2505 tagCnts[tag->priority]++;
2512 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2513 * for all siblings that precede that GtkTextBTreeNode.
2516 for (node = line->parent; node->parent != NULL;
2517 node = node->parent)
2519 GtkTextBTreeNode *siblingPtr;
2522 for (siblingPtr = node->parent->children.node;
2523 siblingPtr != node; siblingPtr = siblingPtr->next)
2525 for (summary = siblingPtr->summary; summary != NULL;
2526 summary = summary->next)
2528 if (summary->toggle_count & 1)
2530 tag = summary->info->tag;
2531 if (tag->invisible_set && tag->values->invisible)
2533 tags[tag->priority] = tag;
2534 tagCnts[tag->priority] += summary->toggle_count;
2542 * Now traverse from highest priority to lowest,
2543 * take invisible value from first odd count (= on)
2546 for (i = numTags-1; i >=0; i--)
2550 /* FIXME not sure this should be if 0 */
2552 #ifndef ALWAYS_SHOW_SELECTION
2553 /* who would make the selection invisible? */
2554 if ((tag == tkxt->seltag)
2555 && !(tkxt->flags & GOT_FOCUS))
2561 invisible = tags[i]->values->invisible;
2566 if (LOTSA_TAGS < numTags)
2581 redisplay_region (GtkTextBTree *tree,
2582 const GtkTextIter *start,
2583 const GtkTextIter *end)
2586 GtkTextLine *start_line, *end_line;
2588 if (gtk_text_iter_compare (start, end) > 0)
2590 const GtkTextIter *tmp = start;
2595 start_line = _gtk_text_iter_get_text_line (start);
2596 end_line = _gtk_text_iter_get_text_line (end);
2599 while (view != NULL)
2601 gint start_y, end_y;
2602 GtkTextLineData *ld;
2604 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2606 if (end_line == start_line)
2609 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2611 ld = _gtk_text_line_get_data (end_line, view->view_id);
2613 end_y += ld->height;
2615 gtk_text_layout_changed (view->layout, start_y,
2624 redisplay_mark (GtkTextLineSegment *mark)
2629 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2631 mark->body.mark.obj);
2634 gtk_text_iter_forward_char (&end);
2636 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2637 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2642 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2644 if (!mark->body.mark.visible)
2647 redisplay_mark (mark);
2651 ensure_not_off_end (GtkTextBTree *tree,
2652 GtkTextLineSegment *mark,
2655 if (gtk_text_iter_get_line (iter) ==
2656 _gtk_text_btree_line_count (tree))
2657 gtk_text_iter_backward_char (iter);
2660 static GtkTextLineSegment*
2661 real_set_mark (GtkTextBTree *tree,
2662 GtkTextMark *existing_mark,
2664 gboolean left_gravity,
2665 const GtkTextIter *where,
2666 gboolean should_exist,
2667 gboolean redraw_selections)
2669 GtkTextLineSegment *mark;
2672 g_return_val_if_fail (tree != NULL, NULL);
2673 g_return_val_if_fail (where != NULL, NULL);
2674 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2677 mark = existing_mark->segment;
2678 else if (name != NULL)
2679 mark = g_hash_table_lookup (tree->mark_table,
2684 if (should_exist && mark == NULL)
2686 g_warning ("No mark `%s' exists!", name);
2690 /* OK if !should_exist and it does already exist, in that case
2696 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2697 _gtk_text_iter_check (&iter);
2701 if (redraw_selections &&
2702 (mark == tree->insert_mark->segment ||
2703 mark == tree->selection_bound_mark->segment))
2705 GtkTextIter old_pos;
2707 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2708 mark->body.mark.obj);
2709 redisplay_region (tree, &old_pos, where);
2713 * don't let visible marks be after the final newline of the
2717 if (mark->body.mark.visible)
2719 ensure_not_off_end (tree, mark, &iter);
2722 /* Redraw the mark's old location. */
2723 redisplay_mark_if_visible (mark);
2725 /* Unlink mark from its current location.
2726 This could hose our iterator... */
2727 gtk_text_btree_unlink_segment (tree, mark,
2728 mark->body.mark.line);
2729 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2730 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2732 segments_changed (tree); /* make sure the iterator recomputes its
2737 mark = _gtk_mark_segment_new (tree,
2741 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2743 if (mark->body.mark.name)
2744 g_hash_table_insert (tree->mark_table,
2745 mark->body.mark.name,
2749 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2750 _gtk_text_iter_check (&iter);
2752 /* Link mark into new location */
2753 gtk_text_btree_link_segment (mark, &iter);
2755 /* Invalidate some iterators. */
2756 segments_changed (tree);
2759 * update the screen at the mark's new location.
2762 redisplay_mark_if_visible (mark);
2764 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2765 _gtk_text_iter_check (&iter);
2767 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2768 _gtk_text_btree_check (tree);
2775 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2776 GtkTextMark *existing_mark,
2778 gboolean left_gravity,
2779 const GtkTextIter *iter,
2780 gboolean should_exist)
2782 GtkTextLineSegment *seg;
2784 seg = real_set_mark (tree, existing_mark,
2785 name, left_gravity, iter, should_exist,
2788 return seg ? seg->body.mark.obj : NULL;
2792 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2796 GtkTextIter tmp_start, tmp_end;
2798 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2800 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2801 tree->selection_bound_mark);
2803 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2815 gtk_text_iter_order (&tmp_start, &tmp_end);
2828 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2829 const GtkTextIter *iter)
2831 _gtk_text_btree_select_range (tree, iter, iter);
2835 _gtk_text_btree_select_range (GtkTextBTree *tree,
2836 const GtkTextIter *ins,
2837 const GtkTextIter *bound)
2839 GtkTextIter start, end;
2841 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2842 redisplay_region (tree, &start, &end);
2844 /* Move insert AND selection_bound before we redisplay */
2845 real_set_mark (tree, tree->insert_mark,
2846 "insert", FALSE, ins, TRUE, FALSE);
2847 real_set_mark (tree, tree->selection_bound_mark,
2848 "selection_bound", FALSE, bound, TRUE, FALSE);
2853 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2858 g_return_if_fail (tree != NULL);
2859 g_return_if_fail (name != NULL);
2861 mark = g_hash_table_lookup (tree->mark_table,
2864 _gtk_text_btree_remove_mark (tree, mark);
2868 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2869 GtkTextLineSegment *segment)
2872 if (segment->body.mark.name)
2873 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2875 segment->body.mark.tree = NULL;
2876 segment->body.mark.line = NULL;
2878 /* Remove the ref on the mark, which frees segment as a side effect
2879 * if this is the last reference.
2881 g_object_unref (segment->body.mark.obj);
2885 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2888 GtkTextLineSegment *segment;
2890 g_return_if_fail (mark != NULL);
2891 g_return_if_fail (tree != NULL);
2893 segment = mark->segment;
2895 if (segment->body.mark.not_deleteable)
2897 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2901 /* This calls cleanup_line and segments_changed */
2902 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2904 _gtk_text_btree_release_mark_segment (tree, segment);
2908 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2909 GtkTextMark *segment)
2911 return segment == tree->insert_mark;
2915 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2916 GtkTextMark *segment)
2918 return segment == tree->selection_bound_mark;
2922 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2925 GtkTextLineSegment *seg;
2927 g_return_val_if_fail (tree != NULL, NULL);
2928 g_return_val_if_fail (name != NULL, NULL);
2930 seg = g_hash_table_lookup (tree->mark_table, name);
2932 return seg ? seg->body.mark.obj : NULL;
2936 * gtk_text_mark_set_visible:
2937 * @mark: a #GtkTextMark
2938 * @setting: visibility of mark
2940 * Sets the visibility of @mark; the insertion point is normally
2941 * visible, i.e. you can see it as a vertical bar. Also, the text
2942 * widget uses a visible mark to indicate where a drop will occur when
2943 * dragging-and-dropping text. Most other marks are not visible.
2944 * Marks are not visible by default.
2948 gtk_text_mark_set_visible (GtkTextMark *mark,
2951 GtkTextLineSegment *seg;
2953 g_return_if_fail (mark != NULL);
2955 seg = mark->segment;
2957 if (seg->body.mark.visible == setting)
2961 seg->body.mark.visible = setting;
2963 redisplay_mark (seg);
2968 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2971 GtkTextBTreeNode *node;
2972 GtkTextTagInfo *info;
2974 g_return_val_if_fail (tree != NULL, NULL);
2978 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2983 if (info->tag_root == NULL)
2986 node = info->tag_root;
2988 /* We know the tag root has instances of the given
2991 continue_outer_loop:
2992 g_assert (node != NULL);
2993 while (node->level > 0)
2995 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2996 node = node->children.node;
2997 while (node != NULL)
2999 if (gtk_text_btree_node_has_tag (node, tag))
3000 goto continue_outer_loop;
3004 g_assert (node != NULL);
3007 g_assert (node != NULL); /* The tag summaries said some node had
3010 g_assert (node->level == 0);
3012 return node->children.line;
3016 /* Looking for any tag at all (tag == NULL).
3017 Unfortunately this can't be done in a simple and efficient way
3018 right now; so I'm just going to return the
3019 first line in the btree. FIXME */
3020 return _gtk_text_btree_get_line (tree, 0, NULL);
3025 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3028 GtkTextBTreeNode *node;
3029 GtkTextBTreeNode *last_node;
3031 GtkTextTagInfo *info;
3033 g_return_val_if_fail (tree != NULL, NULL);
3037 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3039 if (info->tag_root == NULL)
3042 node = info->tag_root;
3043 /* We know the tag root has instances of the given
3046 while (node->level > 0)
3048 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3050 node = node->children.node;
3051 while (node != NULL)
3053 if (gtk_text_btree_node_has_tag (node, tag))
3061 g_assert (node != NULL); /* The tag summaries said some node had
3064 g_assert (node->level == 0);
3066 /* Find the last line in this node */
3067 line = node->children.line;
3068 while (line->next != NULL)
3075 /* This search can't be done efficiently at the moment,
3076 at least not without complexity.
3077 So, we just return the last line.
3079 return _gtk_text_btree_get_end_iter_line (tree);
3089 _gtk_text_line_get_number (GtkTextLine *line)
3092 GtkTextBTreeNode *node, *parent, *node2;
3096 * First count how many lines precede this one in its level-0
3100 node = line->parent;
3102 for (line2 = node->children.line; line2 != line;
3103 line2 = line2->next)
3107 g_error ("gtk_text_btree_line_number couldn't find line");
3113 * Now work up through the levels of the tree one at a time,
3114 * counting how many lines are in GtkTextBTreeNodes preceding the current
3118 for (parent = node->parent ; parent != NULL;
3119 node = parent, parent = parent->parent)
3121 for (node2 = parent->children.node; node2 != node;
3122 node2 = node2->next)
3126 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3128 index += node2->num_lines;
3134 static GtkTextLineSegment*
3135 find_toggle_segment_before_char (GtkTextLine *line,
3139 GtkTextLineSegment *seg;
3140 GtkTextLineSegment *toggle_seg;
3145 seg = line->segments;
3146 while ( (index + seg->char_count) <= char_in_line )
3148 if (((seg->type == >k_text_toggle_on_type)
3149 || (seg->type == >k_text_toggle_off_type))
3150 && (seg->body.toggle.info->tag == tag))
3153 index += seg->char_count;
3160 static GtkTextLineSegment*
3161 find_toggle_segment_before_byte (GtkTextLine *line,
3165 GtkTextLineSegment *seg;
3166 GtkTextLineSegment *toggle_seg;
3171 seg = line->segments;
3172 while ( (index + seg->byte_count) <= byte_in_line )
3174 if (((seg->type == >k_text_toggle_on_type)
3175 || (seg->type == >k_text_toggle_off_type))
3176 && (seg->body.toggle.info->tag == tag))
3179 index += seg->byte_count;
3187 find_toggle_outside_current_line (GtkTextLine *line,
3191 GtkTextBTreeNode *node;
3192 GtkTextLine *sibling_line;
3193 GtkTextLineSegment *seg;
3194 GtkTextLineSegment *toggle_seg;
3196 GtkTextTagInfo *info = NULL;
3199 * No toggle in this line. Look for toggles for the tag in lines
3200 * that are predecessors of line but under the same
3201 * level-0 GtkTextBTreeNode.
3204 sibling_line = line->parent->children.line;
3205 while (sibling_line != line)
3207 seg = sibling_line->segments;
3210 if (((seg->type == >k_text_toggle_on_type)
3211 || (seg->type == >k_text_toggle_off_type))
3212 && (seg->body.toggle.info->tag == tag))
3218 sibling_line = sibling_line->next;
3221 if (toggle_seg != NULL)
3222 return (toggle_seg->type == >k_text_toggle_on_type);
3225 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3226 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3227 * siblings that precede that GtkTextBTreeNode.
3230 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3236 node = line->parent;
3237 while (node->parent != NULL)
3239 GtkTextBTreeNode *sibling_node;
3241 sibling_node = node->parent->children.node;
3242 while (sibling_node != node)
3246 summary = sibling_node->summary;
3247 while (summary != NULL)
3249 if (summary->info == info)
3250 toggles += summary->toggle_count;
3252 summary = summary->next;
3255 sibling_node = sibling_node->next;
3258 if (node == info->tag_root)
3261 node = node->parent;
3265 * An odd number of toggles means that the tag is present at the
3269 return (toggles & 1) != 0;
3272 /* FIXME this function is far too slow, for no good reason. */
3274 _gtk_text_line_char_has_tag (GtkTextLine *line,
3279 GtkTextLineSegment *toggle_seg;
3281 g_return_val_if_fail (line != NULL, FALSE);
3284 * Check for toggles for the tag in the line but before
3285 * the char. If there is one, its type indicates whether or
3286 * not the character is tagged.
3289 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3291 if (toggle_seg != NULL)
3292 return (toggle_seg->type == >k_text_toggle_on_type);
3294 return find_toggle_outside_current_line (line, tree, tag);
3298 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3303 GtkTextLineSegment *toggle_seg;
3305 g_return_val_if_fail (line != NULL, FALSE);
3308 * Check for toggles for the tag in the line but before
3309 * the char. If there is one, its type indicates whether or
3310 * not the character is tagged.
3313 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3315 if (toggle_seg != NULL)
3316 return (toggle_seg->type == >k_text_toggle_on_type);
3318 return find_toggle_outside_current_line (line, tree, tag);
3322 _gtk_text_line_is_last (GtkTextLine *line,
3325 return line == get_last_line (tree);
3329 ensure_end_iter_line (GtkTextBTree *tree)
3331 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3336 /* n_lines is without the magic line at the end */
3337 n_lines = _gtk_text_btree_line_count (tree);
3339 g_assert (n_lines >= 1);
3341 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3343 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3348 ensure_end_iter_segment (GtkTextBTree *tree)
3350 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3352 GtkTextLineSegment *seg;
3353 GtkTextLineSegment *last_with_chars;
3355 ensure_end_iter_line (tree);
3357 last_with_chars = NULL;
3359 seg = tree->end_iter_line->segments;
3362 if (seg->char_count > 0)
3363 last_with_chars = seg;
3367 tree->end_iter_segment = last_with_chars;
3369 /* We know the last char in the last line is '\n' */
3370 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3371 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3373 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3375 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3376 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3381 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3384 ensure_end_iter_line (tree);
3386 return line == tree->end_iter_line;
3390 _gtk_text_btree_is_end (GtkTextBTree *tree,
3392 GtkTextLineSegment *seg,
3396 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3398 /* Do this first to avoid walking segments in most cases */
3399 if (!_gtk_text_line_contains_end_iter (line, tree))
3402 ensure_end_iter_segment (tree);
3404 if (seg != tree->end_iter_segment)
3407 if (byte_index >= 0)
3408 return byte_index == tree->end_iter_segment_byte_index;
3410 return char_offset == tree->end_iter_segment_char_offset;
3414 _gtk_text_line_next (GtkTextLine *line)
3416 GtkTextBTreeNode *node;
3418 if (line->next != NULL)
3423 * This was the last line associated with the particular parent
3424 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3425 * then search down from that GtkTextBTreeNode to find the first
3429 node = line->parent;
3430 while (node != NULL && node->next == NULL)
3431 node = node->parent;
3437 while (node->level > 0)
3439 node = node->children.node;
3442 g_assert (node->children.line != line);
3444 return node->children.line;
3449 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3453 next = _gtk_text_line_next (line);
3455 /* If we were on the end iter line, we can't go to
3458 if (next && next->next == NULL && /* these checks are optimization only */
3459 _gtk_text_line_next (next) == NULL)
3466 _gtk_text_line_previous (GtkTextLine *line)
3468 GtkTextBTreeNode *node;
3469 GtkTextBTreeNode *node2;
3473 * Find the line under this GtkTextBTreeNode just before the starting line.
3475 prev = line->parent->children.line; /* First line at leaf */
3476 while (prev != line)
3478 if (prev->next == line)
3484 g_error ("gtk_text_btree_previous_line ran out of lines");
3488 * This was the first line associated with the particular parent
3489 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3490 * then search down from that GtkTextBTreeNode to find its last line.
3492 for (node = line->parent; ; node = node->parent)
3494 if (node == NULL || node->parent == NULL)
3496 else if (node != node->parent->children.node)
3500 for (node2 = node->parent->children.node; ;
3501 node2 = node2->children.node)
3503 while (node2->next != node)
3504 node2 = node2->next;
3506 if (node2->level == 0)
3512 for (prev = node2->children.line ; ; prev = prev->next)
3514 if (prev->next == NULL)
3518 g_assert_not_reached ();
3524 _gtk_text_line_data_new (GtkTextLayout *layout,
3527 GtkTextLineData *line_data;
3529 line_data = g_new (GtkTextLineData, 1);
3531 line_data->view_id = layout;
3532 line_data->next = NULL;
3533 line_data->width = 0;
3534 line_data->height = 0;
3535 line_data->valid = FALSE;
3541 _gtk_text_line_add_data (GtkTextLine *line,
3542 GtkTextLineData *data)
3544 g_return_if_fail (line != NULL);
3545 g_return_if_fail (data != NULL);
3546 g_return_if_fail (data->view_id != NULL);
3550 data->next = line->views;
3560 _gtk_text_line_remove_data (GtkTextLine *line,
3563 GtkTextLineData *prev;
3564 GtkTextLineData *iter;
3566 g_return_val_if_fail (line != NULL, NULL);
3567 g_return_val_if_fail (view_id != NULL, NULL);
3571 while (iter != NULL)
3573 if (iter->view_id == view_id)
3582 prev->next = iter->next;
3584 line->views = iter->next;
3593 _gtk_text_line_get_data (GtkTextLine *line,
3596 GtkTextLineData *iter;
3598 g_return_val_if_fail (line != NULL, NULL);
3599 g_return_val_if_fail (view_id != NULL, NULL);
3602 while (iter != NULL)
3604 if (iter->view_id == view_id)
3613 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3614 GtkTextLineData *ld)
3616 /* For now this is totally unoptimized. FIXME?
3618 We could probably optimize the case where the width removed
3619 is less than the max width for the parent node,
3620 and the case where the height is unchanged when we re-wrap.
3623 g_return_if_fail (ld != NULL);
3626 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3630 _gtk_text_line_char_count (GtkTextLine *line)
3632 GtkTextLineSegment *seg;
3636 seg = line->segments;
3639 size += seg->char_count;
3646 _gtk_text_line_byte_count (GtkTextLine *line)
3648 GtkTextLineSegment *seg;
3652 seg = line->segments;
3655 size += seg->byte_count;
3663 _gtk_text_line_char_index (GtkTextLine *target_line)
3665 GSList *node_stack = NULL;
3666 GtkTextBTreeNode *iter;
3670 /* Push all our parent nodes onto a stack */
3671 iter = target_line->parent;
3673 g_assert (iter != NULL);
3675 while (iter != NULL)
3677 node_stack = g_slist_prepend (node_stack, iter);
3679 iter = iter->parent;
3682 /* Check that we have the root node on top of the stack. */
3683 g_assert (node_stack != NULL &&
3684 node_stack->data != NULL &&
3685 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3687 /* Add up chars in all nodes before the nodes in our stack.
3691 iter = node_stack->data;
3692 while (iter != NULL)
3694 GtkTextBTreeNode *child_iter;
3695 GtkTextBTreeNode *next_node;
3697 next_node = node_stack->next ?
3698 node_stack->next->data : NULL;
3699 node_stack = g_slist_remove (node_stack, node_stack->data);
3701 if (iter->level == 0)
3703 /* stack should be empty when we're on the last node */
3704 g_assert (node_stack == NULL);
3705 break; /* Our children are now lines */
3708 g_assert (next_node != NULL);
3709 g_assert (iter != NULL);
3710 g_assert (next_node->parent == iter);
3712 /* Add up chars before us in the tree */
3713 child_iter = iter->children.node;
3714 while (child_iter != next_node)
3716 g_assert (child_iter != NULL);
3718 num_chars += child_iter->num_chars;
3720 child_iter = child_iter->next;
3726 g_assert (iter != NULL);
3727 g_assert (iter == target_line->parent);
3729 /* Since we don't store char counts in lines, only in segments, we
3730 have to iterate over the lines adding up segment char counts
3731 until we find our line. */
3732 line = iter->children.line;
3733 while (line != target_line)
3735 g_assert (line != NULL);
3737 num_chars += _gtk_text_line_char_count (line);
3742 g_assert (line == target_line);
3748 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3752 GtkTextLineSegment *seg;
3755 g_return_val_if_fail (line != NULL, NULL);
3757 offset = byte_offset;
3758 seg = line->segments;
3760 while (offset >= seg->byte_count)
3762 g_assert (seg != NULL); /* means an invalid byte index */
3763 offset -= seg->byte_count;
3768 *seg_offset = offset;
3774 _gtk_text_line_char_to_segment (GtkTextLine *line,
3778 GtkTextLineSegment *seg;
3781 g_return_val_if_fail (line != NULL, NULL);
3783 offset = char_offset;
3784 seg = line->segments;
3786 while (offset >= seg->char_count)
3788 g_assert (seg != NULL); /* means an invalid char index */
3789 offset -= seg->char_count;
3794 *seg_offset = offset;
3800 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3804 GtkTextLineSegment *seg;
3807 g_return_val_if_fail (line != NULL, NULL);
3809 offset = byte_offset;
3810 seg = line->segments;
3812 while (offset > 0 && offset >= seg->byte_count)
3814 g_assert (seg != NULL); /* means an invalid byte index */
3815 offset -= seg->byte_count;
3820 *seg_offset = offset;
3826 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3830 GtkTextLineSegment *seg;
3833 g_return_val_if_fail (line != NULL, NULL);
3835 offset = char_offset;
3836 seg = line->segments;
3838 while (offset > 0 && offset >= seg->char_count)
3840 g_assert (seg != NULL); /* means an invalid byte index */
3841 offset -= seg->char_count;
3846 *seg_offset = offset;
3852 _gtk_text_line_byte_to_char (GtkTextLine *line,
3856 GtkTextLineSegment *seg;
3858 g_return_val_if_fail (line != NULL, 0);
3859 g_return_val_if_fail (byte_offset >= 0, 0);
3862 seg = line->segments;
3863 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3864 the next segment) */
3866 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3868 byte_offset -= seg->byte_count;
3869 char_offset += seg->char_count;
3874 g_assert (seg != NULL);
3876 /* Now byte_offset is the offset into the current segment,
3877 and char_offset is the start of the current segment.
3878 Optimize the case where no chars use > 1 byte */
3879 if (seg->byte_count == seg->char_count)
3880 return char_offset + byte_offset;
3883 if (seg->type == >k_text_char_type)
3884 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3887 g_assert (seg->char_count == 1);
3888 g_assert (byte_offset == 0);
3896 _gtk_text_line_char_to_byte (GtkTextLine *line,
3899 g_warning ("FIXME not implemented");
3904 /* FIXME sync with char_locate (or figure out a clean
3905 way to merge the two functions) */
3907 _gtk_text_line_byte_locate (GtkTextLine *line,
3909 GtkTextLineSegment **segment,
3910 GtkTextLineSegment **any_segment,
3911 gint *seg_byte_offset,
3912 gint *line_byte_offset)
3914 GtkTextLineSegment *seg;
3915 GtkTextLineSegment *after_prev_indexable;
3916 GtkTextLineSegment *after_last_indexable;
3917 GtkTextLineSegment *last_indexable;
3921 g_return_val_if_fail (line != NULL, FALSE);
3922 g_return_val_if_fail (byte_offset >= 0, FALSE);
3925 *any_segment = NULL;
3928 offset = byte_offset;
3930 last_indexable = NULL;
3931 after_last_indexable = line->segments;
3932 after_prev_indexable = line->segments;
3933 seg = line->segments;
3935 /* The loop ends when we're inside a segment;
3936 last_indexable refers to the last segment
3937 we passed entirely. */
3938 while (seg && offset >= seg->byte_count)
3940 if (seg->char_count > 0)
3942 offset -= seg->byte_count;
3943 bytes_in_line += seg->byte_count;
3944 last_indexable = seg;
3945 after_prev_indexable = after_last_indexable;
3946 after_last_indexable = last_indexable->next;
3954 /* We went off the end of the line */
3956 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3963 if (after_last_indexable != NULL)
3964 *any_segment = after_last_indexable;
3966 *any_segment = *segment;
3969 /* Override any_segment if we're in the middle of a segment. */
3971 *any_segment = *segment;
3973 *seg_byte_offset = offset;
3975 g_assert (*segment != NULL);
3976 g_assert (*any_segment != NULL);
3977 g_assert (*seg_byte_offset < (*segment)->byte_count);
3979 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3984 /* FIXME sync with byte_locate (or figure out a clean
3985 way to merge the two functions) */
3987 _gtk_text_line_char_locate (GtkTextLine *line,
3989 GtkTextLineSegment **segment,
3990 GtkTextLineSegment **any_segment,
3991 gint *seg_char_offset,
3992 gint *line_char_offset)
3994 GtkTextLineSegment *seg;
3995 GtkTextLineSegment *after_prev_indexable;
3996 GtkTextLineSegment *after_last_indexable;
3997 GtkTextLineSegment *last_indexable;
4001 g_return_val_if_fail (line != NULL, FALSE);
4002 g_return_val_if_fail (char_offset >= 0, FALSE);
4005 *any_segment = NULL;
4008 offset = char_offset;
4010 last_indexable = NULL;
4011 after_last_indexable = line->segments;
4012 after_prev_indexable = line->segments;
4013 seg = line->segments;
4015 /* The loop ends when we're inside a segment;
4016 last_indexable refers to the last segment
4017 we passed entirely. */
4018 while (seg && offset >= seg->char_count)
4020 if (seg->char_count > 0)
4022 offset -= seg->char_count;
4023 chars_in_line += seg->char_count;
4024 last_indexable = seg;
4025 after_prev_indexable = after_last_indexable;
4026 after_last_indexable = last_indexable->next;
4034 /* end of the line */
4036 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4043 if (after_last_indexable != NULL)
4044 *any_segment = after_last_indexable;
4046 *any_segment = *segment;
4049 /* Override any_segment if we're in the middle of a segment. */
4051 *any_segment = *segment;
4053 *seg_char_offset = offset;
4055 g_assert (*segment != NULL);
4056 g_assert (*any_segment != NULL);
4057 g_assert (*seg_char_offset < (*segment)->char_count);
4059 *line_char_offset = chars_in_line + *seg_char_offset;
4065 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4067 gint *line_char_offset,
4068 gint *seg_char_offset)
4070 GtkTextLineSegment *seg;
4073 g_return_if_fail (line != NULL);
4074 g_return_if_fail (byte_offset >= 0);
4076 *line_char_offset = 0;
4078 offset = byte_offset;
4079 seg = line->segments;
4081 while (offset >= seg->byte_count)
4083 offset -= seg->byte_count;
4084 *line_char_offset += seg->char_count;
4086 g_assert (seg != NULL); /* means an invalid char offset */
4089 g_assert (seg->char_count > 0); /* indexable. */
4091 /* offset is now the number of bytes into the current segment we
4092 * want to go. Count chars into the current segment.
4095 if (seg->type == >k_text_char_type)
4097 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4099 g_assert (*seg_char_offset < seg->char_count);
4101 *line_char_offset += *seg_char_offset;
4105 g_assert (offset == 0);
4106 *seg_char_offset = 0;
4111 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4113 gint *line_byte_offset,
4114 gint *seg_byte_offset)
4116 GtkTextLineSegment *seg;
4119 g_return_if_fail (line != NULL);
4120 g_return_if_fail (char_offset >= 0);
4122 *line_byte_offset = 0;
4124 offset = char_offset;
4125 seg = line->segments;
4127 while (offset >= seg->char_count)
4129 offset -= seg->char_count;
4130 *line_byte_offset += seg->byte_count;
4132 g_assert (seg != NULL); /* means an invalid char offset */
4135 g_assert (seg->char_count > 0); /* indexable. */
4137 /* offset is now the number of chars into the current segment we
4138 want to go. Count bytes into the current segment. */
4140 if (seg->type == >k_text_char_type)
4142 *seg_byte_offset = 0;
4146 const char * start = seg->body.chars + *seg_byte_offset;
4148 bytes = g_utf8_next_char (start) - start;
4149 *seg_byte_offset += bytes;
4153 g_assert (*seg_byte_offset < seg->byte_count);
4155 *line_byte_offset += *seg_byte_offset;
4159 g_assert (offset == 0);
4160 *seg_byte_offset = 0;
4165 node_compare (GtkTextBTreeNode *lhs,
4166 GtkTextBTreeNode *rhs)
4168 GtkTextBTreeNode *iter;
4169 GtkTextBTreeNode *node;
4170 GtkTextBTreeNode *common_parent;
4171 GtkTextBTreeNode *parent_of_lower;
4172 GtkTextBTreeNode *parent_of_higher;
4173 gboolean lhs_is_lower;
4174 GtkTextBTreeNode *lower;
4175 GtkTextBTreeNode *higher;
4177 /* This function assumes that lhs and rhs are not underneath each
4184 if (lhs->level < rhs->level)
4186 lhs_is_lower = TRUE;
4192 lhs_is_lower = FALSE;
4197 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4198 * of the common parent we used to reach the common parent; the
4199 * ordering of these child nodes in the child list is the ordering
4203 /* Get on the same level (may be on same level already) */
4205 while (node->level < higher->level)
4206 node = node->parent;
4208 g_assert (node->level == higher->level);
4210 g_assert (node != higher); /* Happens if lower is underneath higher */
4212 /* Go up until we have two children with a common parent.
4214 parent_of_lower = node;
4215 parent_of_higher = higher;
4217 while (parent_of_lower->parent != parent_of_higher->parent)
4219 parent_of_lower = parent_of_lower->parent;
4220 parent_of_higher = parent_of_higher->parent;
4223 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4225 common_parent = parent_of_lower->parent;
4227 g_assert (common_parent != NULL);
4229 /* See which is first in the list of common_parent's children */
4230 iter = common_parent->children.node;
4231 while (iter != NULL)
4233 if (iter == parent_of_higher)
4235 /* higher is less than lower */
4238 return 1; /* lhs > rhs */
4242 else if (iter == parent_of_lower)
4244 /* lower is less than higher */
4247 return -1; /* lhs < rhs */
4255 g_assert_not_reached ();
4259 /* remember that tag == NULL means "any tag" */
4261 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4265 GtkTextBTreeNode *node;
4266 GtkTextTagInfo *info;
4267 gboolean below_tag_root;
4269 g_return_val_if_fail (line != NULL, NULL);
4271 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4272 _gtk_text_btree_check (tree);
4276 /* Right now we can only offer linear-search if the user wants
4277 * to know about any tag toggle at all.
4279 return _gtk_text_line_next_excluding_last (line);
4282 /* Our tag summaries only have node precision, not line
4283 * precision. This means that if any line under a node could contain a
4284 * tag, then any of the others could also contain a tag.
4286 * In the future we could have some mechanism to keep track of how
4287 * many toggles we've found under a node so far, since we have a
4288 * count of toggles under the node. But for now I'm going with KISS.
4291 /* return same-node line, if any. */
4295 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4299 if (info->tag_root == NULL)
4302 if (info->tag_root == line->parent)
4303 return NULL; /* we were at the last line under the tag root */
4305 /* We need to go up out of this node, and on to the next one with
4306 toggles for the target tag. If we're below the tag root, we need to
4307 find the next node below the tag root that has tag summaries. If
4308 we're not below the tag root, we need to see if the tag root is
4309 after us in the tree, and if so, return the first line underneath
4312 node = line->parent;
4313 below_tag_root = FALSE;
4314 while (node != NULL)
4316 if (node == info->tag_root)
4318 below_tag_root = TRUE;
4322 node = node->parent;
4327 node = line->parent;
4328 while (node != info->tag_root)
4330 if (node->next == NULL)
4331 node = node->parent;
4336 if (gtk_text_btree_node_has_tag (node, tag))
4346 ordering = node_compare (line->parent, info->tag_root);
4350 /* Tag root is ahead of us, so search there. */
4351 node = info->tag_root;
4356 /* Tag root is after us, so no more lines that
4357 * could contain the tag.
4362 g_assert_not_reached ();
4367 g_assert (node != NULL);
4369 /* We have to find the first sub-node of this node that contains
4373 while (node->level > 0)
4375 g_assert (node != NULL); /* If this fails, it likely means an
4376 incorrect tag summary led us on a
4377 wild goose chase down this branch of
4379 node = node->children.node;
4380 while (node != NULL)
4382 if (gtk_text_btree_node_has_tag (node, tag))
4388 g_assert (node != NULL);
4389 g_assert (node->level == 0);
4391 return node->children.line;
4395 prev_line_under_node (GtkTextBTreeNode *node,
4400 prev = node->children.line;
4406 while (prev->next != line)
4416 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4420 GtkTextBTreeNode *node;
4421 GtkTextBTreeNode *found_node = NULL;
4422 GtkTextTagInfo *info;
4423 gboolean below_tag_root;
4425 GtkTextBTreeNode *line_ancestor;
4426 GtkTextBTreeNode *line_ancestor_parent;
4428 /* See next_could_contain_tag () for more extensive comments
4429 * on what's going on here.
4432 g_return_val_if_fail (line != NULL, NULL);
4434 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4435 _gtk_text_btree_check (tree);
4439 /* Right now we can only offer linear-search if the user wants
4440 * to know about any tag toggle at all.
4442 return _gtk_text_line_previous (line);
4445 /* Return same-node line, if any. */
4446 prev = prev_line_under_node (line->parent, line);
4450 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4454 if (info->tag_root == NULL)
4457 if (info->tag_root == line->parent)
4458 return NULL; /* we were at the first line under the tag root */
4460 /* Are we below the tag root */
4461 node = line->parent;
4462 below_tag_root = FALSE;
4463 while (node != NULL)
4465 if (node == info->tag_root)
4467 below_tag_root = TRUE;
4471 node = node->parent;
4476 /* Look for a previous node under this tag root that has our
4480 /* this assertion holds because line->parent is not the
4481 * tag root, we are below the tag root, and the tag
4484 g_assert (line->parent->parent != NULL);
4486 line_ancestor = line->parent;
4487 line_ancestor_parent = line->parent->parent;
4489 node = line_ancestor_parent->children.node;
4490 while (node != line_ancestor &&
4491 line_ancestor != info->tag_root)
4493 GSList *child_nodes = NULL;
4496 /* Create reverse-order list of nodes before
4499 while (node != line_ancestor
4502 child_nodes = g_slist_prepend (child_nodes, node);
4507 /* Try to find a node with our tag on it in the list */
4511 GtkTextBTreeNode *this_node = tmp->data;
4513 g_assert (this_node != line_ancestor);
4515 if (gtk_text_btree_node_has_tag (this_node, tag))
4517 found_node = this_node;
4518 g_slist_free (child_nodes);
4522 tmp = g_slist_next (tmp);
4525 g_slist_free (child_nodes);
4527 /* Didn't find anything on this level; go up one level. */
4528 line_ancestor = line_ancestor_parent;
4529 line_ancestor_parent = line_ancestor->parent;
4531 if (line_ancestor_parent != NULL)
4533 node = line_ancestor_parent->children.node;
4544 ordering = node_compare (line->parent, info->tag_root);
4548 /* Tag root is ahead of us, so no more lines
4555 /* Tag root is after us, so grab last tagged
4556 * line underneath the tag root.
4558 found_node = info->tag_root;
4562 g_assert_not_reached ();
4567 g_assert (found_node != NULL);
4569 /* We have to find the last sub-node of this node that contains
4574 while (node->level > 0)
4576 GSList *child_nodes = NULL;
4578 g_assert (node != NULL); /* If this fails, it likely means an
4579 incorrect tag summary led us on a
4580 wild goose chase down this branch of
4583 node = node->children.node;
4584 while (node != NULL)
4586 child_nodes = g_slist_prepend (child_nodes, node);
4590 node = NULL; /* detect failure to find a child node. */
4593 while (iter != NULL)
4595 if (gtk_text_btree_node_has_tag (iter->data, tag))
4597 /* recurse into this node. */
4602 iter = g_slist_next (iter);
4605 g_slist_free (child_nodes);
4607 g_assert (node != NULL);
4610 g_assert (node != NULL);
4611 g_assert (node->level == 0);
4613 /* this assertion is correct, but slow. */
4614 /* g_assert (node_compare (node, line->parent) < 0); */
4616 /* Return last line in this node. */
4618 prev = node->children.line;
4626 * Non-public function implementations
4630 summary_list_destroy (Summary *summary)
4633 while (summary != NULL)
4635 next = summary->next;
4636 summary_destroy (summary);
4642 get_last_line (GtkTextBTree *tree)
4644 if (tree->last_line_stamp != tree->chars_changed_stamp)
4650 n_lines = _gtk_text_btree_line_count (tree);
4652 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4654 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4656 tree->last_line_stamp = tree->chars_changed_stamp;
4657 tree->last_line = line;
4660 return tree->last_line;
4668 gtk_text_line_new (void)
4672 line = g_new0(GtkTextLine, 1);
4673 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4674 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4675 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4681 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4683 GtkTextLineData *ld;
4684 GtkTextLineData *next;
4686 g_return_if_fail (line != NULL);
4693 view = gtk_text_btree_get_view (tree, ld->view_id);
4695 g_assert (view != NULL);
4698 gtk_text_layout_free_line_data (view->layout, line, ld);
4707 gtk_text_line_set_parent (GtkTextLine *line,
4708 GtkTextBTreeNode *node)
4710 if (line->parent == node)
4712 line->parent = node;
4713 gtk_text_btree_node_invalidate_upward (node, NULL);
4717 cleanup_line (GtkTextLine *line)
4719 GtkTextLineSegment *seg, **prev_p;
4723 * Make a pass over all of the segments in the line, giving each
4724 * a chance to clean itself up. This could potentially change
4725 * the structure of the line, e.g. by merging two segments
4726 * together or having two segments cancel themselves; if so,
4727 * then repeat the whole process again, since the first structure
4728 * change might make other structure changes possible. Repeat
4729 * until eventually there are no changes.
4736 for (prev_p = &line->segments, seg = *prev_p;
4738 prev_p = &(*prev_p)->next, seg = *prev_p)
4740 if (seg->type->cleanupFunc != NULL)
4742 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4755 node_data_new (gpointer view_id)
4759 nd = g_new (NodeData, 1);
4761 nd->view_id = view_id;
4771 node_data_destroy (NodeData *nd)
4777 node_data_list_destroy (NodeData *nd)
4783 while (iter != NULL)
4786 node_data_destroy (iter);
4792 node_data_find (NodeData *nd, gpointer view_id)
4796 if (nd->view_id == view_id)
4804 summary_destroy (Summary *summary)
4806 /* Fill with error-triggering garbage */
4807 summary->info = (void*)0x1;
4808 summary->toggle_count = 567;
4809 summary->next = (void*)0x1;
4813 static GtkTextBTreeNode*
4814 gtk_text_btree_node_new (void)
4816 GtkTextBTreeNode *node;
4818 node = g_new (GtkTextBTreeNode, 1);
4820 node->node_data = NULL;
4826 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4827 GtkTextTagInfo *info,
4832 summary = node->summary;
4833 while (summary != NULL)
4835 if (summary->info == info)
4837 summary->toggle_count += adjust;
4841 summary = summary->next;
4844 if (summary == NULL)
4846 /* didn't find a summary for our tag. */
4847 g_return_if_fail (adjust > 0);
4848 summary = g_new (Summary, 1);
4849 summary->info = info;
4850 summary->toggle_count = adjust;
4851 summary->next = node->summary;
4852 node->summary = summary;
4856 /* Note that the tag root and above do not have summaries
4857 for the tag; only nodes below the tag root have
4860 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4864 summary = node->summary;
4865 while (summary != NULL)
4868 summary->info->tag == tag)
4871 summary = summary->next;
4877 /* Add node and all children to the damage region. */
4879 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4883 nd = node->node_data;
4890 if (node->level == 0)
4894 line = node->children.line;
4895 while (line != NULL)
4897 GtkTextLineData *ld;
4911 GtkTextBTreeNode *child;
4913 child = node->children.node;
4915 while (child != NULL)
4917 gtk_text_btree_node_invalidate_downward (child);
4919 child = child->next;
4925 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4927 GtkTextBTreeNode *iter;
4930 while (iter != NULL)
4936 nd = node_data_find (iter->node_data, view_id);
4938 if (nd == NULL || !nd->valid)
4939 break; /* Once a node is invalid, we know its parents are as well. */
4945 gboolean should_continue = FALSE;
4947 nd = iter->node_data;
4952 should_continue = TRUE;
4959 if (!should_continue)
4960 break; /* This node was totally invalidated, so are its
4964 iter = iter->parent;
4970 * _gtk_text_btree_is_valid:
4971 * @tree: a #GtkTextBTree
4972 * @view_id: ID for the view
4974 * Check to see if the entire #GtkTextBTree is valid or not for
4977 * Return value: %TRUE if the entire #GtkTextBTree is valid
4980 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4984 g_return_val_if_fail (tree != NULL, FALSE);
4986 nd = node_data_find (tree->root_node->node_data, view_id);
4987 return (nd && nd->valid);
4990 typedef struct _ValidateState ValidateState;
4992 struct _ValidateState
4994 gint remaining_pixels;
4995 gboolean in_validation;
5002 gtk_text_btree_node_validate (BTreeView *view,
5003 GtkTextBTreeNode *node,
5005 ValidateState *state)
5007 gint node_valid = TRUE;
5008 gint node_width = 0;
5009 gint node_height = 0;
5011 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5012 g_return_if_fail (!nd->valid);
5014 if (node->level == 0)
5016 GtkTextLine *line = node->children.line;
5017 GtkTextLineData *ld;
5019 /* Iterate over leading valid lines */
5020 while (line != NULL)
5022 ld = _gtk_text_line_get_data (line, view_id);
5024 if (!ld || !ld->valid)
5026 else if (state->in_validation)
5028 state->in_validation = FALSE;
5033 state->y += ld->height;
5034 node_width = MAX (ld->width, node_width);
5035 node_height += ld->height;
5041 state->in_validation = TRUE;
5043 /* Iterate over invalid lines */
5044 while (line != NULL)
5046 ld = _gtk_text_line_get_data (line, view_id);
5048 if (ld && ld->valid)
5053 state->old_height += ld->height;
5054 ld = gtk_text_layout_wrap (view->layout, line, ld);
5055 state->new_height += ld->height;
5057 node_width = MAX (ld->width, node_width);
5058 node_height += ld->height;
5060 state->remaining_pixels -= ld->height;
5061 if (state->remaining_pixels <= 0)
5071 /* Iterate over the remaining lines */
5072 while (line != NULL)
5074 ld = _gtk_text_line_get_data (line, view_id);
5075 state->in_validation = FALSE;
5077 if (!ld || !ld->valid)
5082 node_width = MAX (ld->width, node_width);
5083 node_height += ld->height;
5091 GtkTextBTreeNode *child;
5094 child = node->children.node;
5096 /* Iterate over leading valid nodes */
5099 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5101 if (!child_nd->valid)
5103 else if (state->in_validation)
5105 state->in_validation = FALSE;
5110 state->y += child_nd->height;
5111 node_width = MAX (node_width, child_nd->width);
5112 node_height += child_nd->height;
5115 child = child->next;
5118 /* Iterate over invalid nodes */
5121 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5123 if (child_nd->valid)
5127 gtk_text_btree_node_validate (view, child, view_id, state);
5129 if (!child_nd->valid)
5131 node_width = MAX (node_width, child_nd->width);
5132 node_height += child_nd->height;
5134 if (!state->in_validation || state->remaining_pixels <= 0)
5136 child = child->next;
5141 child = child->next;
5144 /* Iterate over the remaining lines */
5147 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5148 state->in_validation = FALSE;
5150 if (!child_nd->valid)
5153 node_width = MAX (child_nd->width, node_width);
5154 node_height += child_nd->height;
5156 child = child->next;
5160 nd->width = node_width;
5161 nd->height = node_height;
5162 nd->valid = node_valid;
5166 * _gtk_text_btree_validate:
5167 * @tree: a #GtkTextBTree
5169 * @max_pixels: the maximum number of pixels to validate. (No more
5170 * than one paragraph beyond this limit will be validated)
5171 * @y: location to store starting y coordinate of validated region
5172 * @old_height: location to store old height of validated region
5173 * @new_height: location to store new height of validated region
5175 * Validate a single contiguous invalid region of a #GtkTextBTree for
5178 * Return value: %TRUE if a region has been validated, %FALSE if the
5179 * entire tree was already valid.
5182 _gtk_text_btree_validate (GtkTextBTree *tree,
5191 g_return_val_if_fail (tree != NULL, FALSE);
5193 view = gtk_text_btree_get_view (tree, view_id);
5194 g_return_val_if_fail (view != NULL, FALSE);
5196 if (!_gtk_text_btree_is_valid (tree, view_id))
5198 ValidateState state;
5200 state.remaining_pixels = max_pixels;
5201 state.in_validation = FALSE;
5203 state.old_height = 0;
5204 state.new_height = 0;
5206 gtk_text_btree_node_validate (view,
5213 *old_height = state.old_height;
5215 *new_height = state.new_height;
5217 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5218 _gtk_text_btree_check (tree);
5227 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5231 gboolean *valid_out)
5235 gboolean valid = TRUE;
5237 if (node->level == 0)
5239 GtkTextLine *line = node->children.line;
5241 while (line != NULL)
5243 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5245 if (!ld || !ld->valid)
5250 width = MAX (ld->width, width);
5251 height += ld->height;
5259 GtkTextBTreeNode *child = node->children.node;
5263 NodeData *child_nd = node_data_find (child->node_data, view_id);
5265 if (!child_nd || !child_nd->valid)
5270 width = MAX (child_nd->width, width);
5271 height += child_nd->height;
5274 child = child->next;
5279 *height_out = height;
5284 /* Recompute the validity and size of the view data for a given
5285 * view at this node from the immediate children of the node
5288 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5291 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5296 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5297 &width, &height, &valid);
5299 nd->height = height;
5306 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5311 gtk_text_btree_node_check_valid (node, view_id);
5312 node = node->parent;
5317 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5320 if (node->level == 0)
5322 return gtk_text_btree_node_check_valid (node, view_id);
5326 GtkTextBTreeNode *child = node->children.node;
5328 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5336 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5338 if (!child_nd->valid)
5340 nd->width = MAX (child_nd->width, nd->width);
5341 nd->height += child_nd->height;
5343 child = child->next;
5352 * _gtk_text_btree_validate_line:
5353 * @tree: a #GtkTextBTree
5354 * @line: line to validate
5355 * @view_id: view ID for the view to validate
5357 * Revalidate a single line of the btree for the given view, propagate
5358 * results up through the entire tree.
5361 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5365 GtkTextLineData *ld;
5368 g_return_if_fail (tree != NULL);
5369 g_return_if_fail (line != NULL);
5371 view = gtk_text_btree_get_view (tree, view_id);
5372 g_return_if_fail (view != NULL);
5374 ld = _gtk_text_line_get_data (line, view_id);
5375 if (!ld || !ld->valid)
5377 ld = gtk_text_layout_wrap (view->layout, line, ld);
5379 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5384 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5386 if (node->level == 0)
5390 line = node->children.line;
5391 while (line != NULL)
5393 GtkTextLineData *ld;
5395 ld = _gtk_text_line_remove_data (line, view_id);
5398 gtk_text_layout_free_line_data (view->layout, line, ld);
5405 GtkTextBTreeNode *child;
5407 child = node->children.node;
5409 while (child != NULL)
5412 gtk_text_btree_node_remove_view (view, child, view_id);
5414 child = child->next;
5418 gtk_text_btree_node_remove_data (node, view_id);
5422 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5424 if (node->level == 0)
5427 GtkTextLineSegment *seg;
5429 while (node->children.line != NULL)
5431 line = node->children.line;
5432 node->children.line = line->next;
5433 while (line->segments != NULL)
5435 seg = line->segments;
5436 line->segments = seg->next;
5438 (*seg->type->deleteFunc) (seg, line, TRUE);
5440 gtk_text_line_destroy (tree, line);
5445 GtkTextBTreeNode *childPtr;
5447 while (node->children.node != NULL)
5449 childPtr = node->children.node;
5450 node->children.node = childPtr->next;
5451 gtk_text_btree_node_destroy (tree, childPtr);
5455 gtk_text_btree_node_free_empty (tree, node);
5459 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5460 GtkTextBTreeNode *node)
5462 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5463 (node->level == 0 && node->children.line == NULL));
5465 summary_list_destroy (node->summary);
5466 node_data_list_destroy (node->node_data);
5471 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5475 nd = node->node_data;
5478 if (nd->view_id == view_id)
5486 nd = node_data_new (view_id);
5488 if (node->node_data)
5489 nd->next = node->node_data;
5491 node->node_data = nd;
5498 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5504 nd = node->node_data;
5507 if (nd->view_id == view_id)
5518 prev->next = nd->next;
5520 if (node->node_data == nd)
5521 node->node_data = nd->next;
5525 node_data_destroy (nd);
5529 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5530 gint *width, gint *height)
5534 g_return_if_fail (width != NULL);
5535 g_return_if_fail (height != NULL);
5537 nd = gtk_text_btree_node_ensure_data (node, view_id);
5542 *height = nd->height;
5545 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5546 * here isn't quite right, since for a lot of operations we want to
5547 * know which children of the common parent correspond to the two nodes
5548 * (e.g., when computing the order of two iters)
5550 static GtkTextBTreeNode *
5551 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5552 GtkTextBTreeNode *node2)
5554 while (node1->level < node2->level)
5555 node1 = node1->parent;
5556 while (node2->level < node1->level)
5557 node2 = node2->parent;
5558 while (node1 != node2)
5560 node1 = node1->parent;
5561 node2 = node2->parent;
5572 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5577 while (view != NULL)
5579 if (view->view_id == view_id)
5588 get_tree_bounds (GtkTextBTree *tree,
5592 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5593 _gtk_text_btree_get_end_iter (tree, end);
5597 tag_changed_cb (GtkTextTagTable *table,
5599 gboolean size_changed,
5604 /* We need to queue a relayout on all regions that are tagged with
5611 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5613 /* Must be a last toggle if there was a first one. */
5614 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5615 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5616 _gtk_text_btree_invalidate_region (tree,
5623 /* We only need to queue a redraw, not a relayout */
5628 while (view != NULL)
5632 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5633 gtk_text_layout_changed (view->layout, 0, height, height);
5641 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5644 /* Remove the tag from the tree */
5649 get_tree_bounds (tree, &start, &end);
5651 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5652 gtk_text_btree_remove_tag_info (tree, tag);
5656 /* Rebalance the out-of-whack node "node" */
5658 gtk_text_btree_rebalance (GtkTextBTree *tree,
5659 GtkTextBTreeNode *node)
5662 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5663 * up through the tree one GtkTextBTreeNode at a time until the root
5664 * GtkTextBTreeNode has been processed.
5667 while (node != NULL)
5669 GtkTextBTreeNode *new_node, *child;
5674 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5675 * then split off all but the first MIN_CHILDREN into a separate
5676 * GtkTextBTreeNode following the original one. Then repeat until the
5677 * GtkTextBTreeNode has a decent size.
5680 if (node->num_children > MAX_CHILDREN)
5685 * If the GtkTextBTreeNode being split is the root
5686 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5690 if (node->parent == NULL)
5692 new_node = gtk_text_btree_node_new ();
5693 new_node->parent = NULL;
5694 new_node->next = NULL;
5695 new_node->summary = NULL;
5696 new_node->level = node->level + 1;
5697 new_node->children.node = node;
5698 recompute_node_counts (tree, new_node);
5699 tree->root_node = new_node;
5701 new_node = gtk_text_btree_node_new ();
5702 new_node->parent = node->parent;
5703 new_node->next = node->next;
5704 node->next = new_node;
5705 new_node->summary = NULL;
5706 new_node->level = node->level;
5707 new_node->num_children = node->num_children - MIN_CHILDREN;
5708 if (node->level == 0)
5710 for (i = MIN_CHILDREN-1,
5711 line = node->children.line;
5712 i > 0; i--, line = line->next)
5714 /* Empty loop body. */
5716 new_node->children.line = line->next;
5721 for (i = MIN_CHILDREN-1,
5722 child = node->children.node;
5723 i > 0; i--, child = child->next)
5725 /* Empty loop body. */
5727 new_node->children.node = child->next;
5730 recompute_node_counts (tree, node);
5731 node->parent->num_children++;
5733 if (node->num_children <= MAX_CHILDREN)
5735 recompute_node_counts (tree, node);
5741 while (node->num_children < MIN_CHILDREN)
5743 GtkTextBTreeNode *other;
5744 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5745 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5746 int total_children, first_children, i;
5749 * Too few children for this GtkTextBTreeNode. If this is the root then,
5750 * it's OK for it to have less than MIN_CHILDREN children
5751 * as long as it's got at least two. If it has only one
5752 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5753 * the tree and use its child as the new root.
5756 if (node->parent == NULL)
5758 if ((node->num_children == 1) && (node->level > 0))
5760 tree->root_node = node->children.node;
5761 tree->root_node->parent = NULL;
5763 node->children.node = NULL;
5764 gtk_text_btree_node_free_empty (tree, node);
5770 * Not the root. Make sure that there are siblings to
5774 if (node->parent->num_children < 2)
5776 gtk_text_btree_rebalance (tree, node->parent);
5781 * Find a sibling neighbor to borrow from, and arrange for
5782 * node to be the earlier of the pair.
5785 if (node->next == NULL)
5787 for (other = node->parent->children.node;
5788 other->next != node;
5789 other = other->next)
5791 /* Empty loop body. */
5798 * We're going to either merge the two siblings together
5799 * into one GtkTextBTreeNode or redivide the children among them to
5800 * balance their loads. As preparation, join their two
5801 * child lists into a single list and remember the half-way
5802 * point in the list.
5805 total_children = node->num_children + other->num_children;
5806 first_children = total_children/2;
5807 if (node->children.node == NULL)
5809 node->children = other->children;
5810 other->children.node = NULL;
5811 other->children.line = NULL;
5813 if (node->level == 0)
5817 for (line = node->children.line, i = 1;
5819 line = line->next, i++)
5821 if (i == first_children)
5826 line->next = other->children.line;
5827 while (i <= first_children)
5836 GtkTextBTreeNode *child;
5838 for (child = node->children.node, i = 1;
5839 child->next != NULL;
5840 child = child->next, i++)
5842 if (i <= first_children)
5844 if (i == first_children)
5846 halfwaynode = child;
5850 child->next = other->children.node;
5851 while (i <= first_children)
5853 halfwaynode = child;
5854 child = child->next;
5860 * If the two siblings can simply be merged together, do it.
5863 if (total_children <= MAX_CHILDREN)
5865 recompute_node_counts (tree, node);
5866 node->next = other->next;
5867 node->parent->num_children--;
5869 other->children.node = NULL;
5870 other->children.line = NULL;
5871 gtk_text_btree_node_free_empty (tree, other);
5876 * The siblings can't be merged, so just divide their
5877 * children evenly between them.
5880 if (node->level == 0)
5882 other->children.line = halfwayline->next;
5883 halfwayline->next = NULL;
5887 other->children.node = halfwaynode->next;
5888 halfwaynode->next = NULL;
5891 recompute_node_counts (tree, node);
5892 recompute_node_counts (tree, other);
5895 node = node->parent;
5900 post_insert_fixup (GtkTextBTree *tree,
5902 gint line_count_delta,
5903 gint char_count_delta)
5906 GtkTextBTreeNode *node;
5909 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5910 * point, then rebalance the tree if necessary.
5913 for (node = line->parent ; node != NULL;
5914 node = node->parent)
5916 node->num_lines += line_count_delta;
5917 node->num_chars += char_count_delta;
5919 node = line->parent;
5920 node->num_children += line_count_delta;
5922 if (node->num_children > MAX_CHILDREN)
5924 gtk_text_btree_rebalance (tree, node);
5927 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5928 _gtk_text_btree_check (tree);
5931 static GtkTextTagInfo*
5932 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5935 GtkTextTagInfo *info;
5939 list = tree->tag_infos;
5940 while (list != NULL)
5943 if (info->tag == tag)
5946 list = g_slist_next (list);
5952 static GtkTextTagInfo*
5953 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5956 GtkTextTagInfo *info;
5958 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5962 /* didn't find it, create. */
5964 info = g_new (GtkTextTagInfo, 1);
5968 info->tag_root = NULL;
5969 info->toggle_count = 0;
5971 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5974 g_print ("Created tag info %p for tag %s(%p)\n",
5975 info, info->tag->name ? info->tag->name : "anon",
5984 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5987 GtkTextTagInfo *info;
5992 list = tree->tag_infos;
5993 while (list != NULL)
5996 if (info->tag == tag)
5999 g_print ("Removing tag info %p for tag %s(%p)\n",
6000 info, info->tag->name ? info->tag->name : "anon",
6006 prev->next = list->next;
6010 tree->tag_infos = list->next;
6013 g_slist_free (list);
6015 g_object_unref (info->tag);
6022 list = g_slist_next (list);
6027 recompute_level_zero_counts (GtkTextBTreeNode *node)
6030 GtkTextLineSegment *seg;
6032 g_assert (node->level == 0);
6034 line = node->children.line;
6035 while (line != NULL)
6037 node->num_children++;
6040 if (line->parent != node)
6041 gtk_text_line_set_parent (line, node);
6043 seg = line->segments;
6047 node->num_chars += seg->char_count;
6049 if (((seg->type != >k_text_toggle_on_type)
6050 && (seg->type != >k_text_toggle_off_type))
6051 || !(seg->body.toggle.inNodeCounts))
6057 GtkTextTagInfo *info;
6059 info = seg->body.toggle.info;
6061 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6072 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6075 GtkTextBTreeNode *child;
6077 g_assert (node->level > 0);
6079 child = node->children.node;
6080 while (child != NULL)
6082 node->num_children += 1;
6083 node->num_lines += child->num_lines;
6084 node->num_chars += child->num_chars;
6086 if (child->parent != node)
6088 child->parent = node;
6089 gtk_text_btree_node_invalidate_upward (node, NULL);
6092 summary = child->summary;
6093 while (summary != NULL)
6095 gtk_text_btree_node_adjust_toggle_count (node,
6097 summary->toggle_count);
6099 summary = summary->next;
6102 child = child->next;
6107 *----------------------------------------------------------------------
6109 * recompute_node_counts --
6111 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6112 * (tags, child information, etc.) by scanning the information in
6113 * its descendants. This procedure is called during rebalancing
6114 * when a GtkTextBTreeNode's child structure has changed.
6120 * The tag counts for node are modified to reflect its current
6121 * child structure, as are its num_children, num_lines, num_chars fields.
6122 * Also, all of the childrens' parent fields are made to point
6125 *----------------------------------------------------------------------
6129 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6132 Summary *summary, *summary2;
6135 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6136 * the existing Summary records (most of them will probably be reused).
6139 summary = node->summary;
6140 while (summary != NULL)
6142 summary->toggle_count = 0;
6143 summary = summary->next;
6146 node->num_children = 0;
6147 node->num_lines = 0;
6148 node->num_chars = 0;
6151 * Scan through the children, adding the childrens' tag counts into
6152 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6156 if (node->level == 0)
6157 recompute_level_zero_counts (node);
6159 recompute_level_nonzero_counts (node);
6164 gtk_text_btree_node_check_valid (node, view->view_id);
6169 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6170 * records that still have a zero count, or that have all the toggles.
6171 * The GtkTextBTreeNode with the children that account for all the tags toggles
6172 * have no summary information, and they become the tag_root for the tag.
6176 for (summary = node->summary; summary != NULL; )
6178 if (summary->toggle_count > 0 &&
6179 summary->toggle_count < summary->info->toggle_count)
6181 if (node->level == summary->info->tag_root->level)
6184 * The tag's root GtkTextBTreeNode split and some toggles left.
6185 * The tag root must move up a level.
6187 summary->info->tag_root = node->parent;
6190 summary = summary->next;
6193 if (summary->toggle_count == summary->info->toggle_count)
6196 * A GtkTextBTreeNode merge has collected all the toggles under
6197 * one GtkTextBTreeNode. Push the root down to this level.
6199 summary->info->tag_root = node;
6201 if (summary2 != NULL)
6203 summary2->next = summary->next;
6204 summary_destroy (summary);
6205 summary = summary2->next;
6209 node->summary = summary->next;
6210 summary_destroy (summary);
6211 summary = node->summary;
6217 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6218 GtkTextTagInfo *info,
6219 gint delta) /* may be negative */
6221 Summary *summary, *prevPtr;
6222 GtkTextBTreeNode *node2Ptr;
6223 int rootLevel; /* Level of original tag root */
6225 info->toggle_count += delta;
6227 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6229 info->tag_root = node;
6234 * Note the level of the existing root for the tag so we can detect
6235 * if it needs to be moved because of the toggle count change.
6238 rootLevel = info->tag_root->level;
6241 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6242 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6246 for ( ; node != info->tag_root; node = node->parent)
6249 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6250 * perhaps all we have to do is adjust its count.
6253 for (prevPtr = NULL, summary = node->summary;
6255 prevPtr = summary, summary = summary->next)
6257 if (summary->info == info)
6262 if (summary != NULL)
6264 summary->toggle_count += delta;
6265 if (summary->toggle_count > 0 &&
6266 summary->toggle_count < info->toggle_count)
6270 if (summary->toggle_count != 0)
6273 * Should never find a GtkTextBTreeNode with max toggle count at this
6274 * point (there shouldn't have been a summary entry in the
6278 g_error ("%s: bad toggle count (%d) max (%d)",
6279 G_STRLOC, summary->toggle_count, info->toggle_count);
6283 * Zero toggle count; must remove this tag from the list.
6286 if (prevPtr == NULL)
6288 node->summary = summary->next;
6292 prevPtr->next = summary->next;
6294 summary_destroy (summary);
6299 * This tag isn't currently in the summary information list.
6302 if (rootLevel == node->level)
6306 * The old tag root is at the same level in the tree as this
6307 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6308 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6309 * as well as the old root (if not, we'll move it up again
6310 * the next time through the loop). To push it up one level
6311 * we copy the original toggle count into the summary
6312 * information at the old root and change the root to its
6313 * parent GtkTextBTreeNode.
6316 GtkTextBTreeNode *rootnode = info->tag_root;
6317 summary = (Summary *) g_malloc (sizeof (Summary));
6318 summary->info = info;
6319 summary->toggle_count = info->toggle_count - delta;
6320 summary->next = rootnode->summary;
6321 rootnode->summary = summary;
6322 rootnode = rootnode->parent;
6323 rootLevel = rootnode->level;
6324 info->tag_root = rootnode;
6326 summary = (Summary *) g_malloc (sizeof (Summary));
6327 summary->info = info;
6328 summary->toggle_count = delta;
6329 summary->next = node->summary;
6330 node->summary = summary;
6335 * If we've decremented the toggle count, then it may be necessary
6336 * to push the tag root down one or more levels.
6343 if (info->toggle_count == 0)
6345 info->tag_root = (GtkTextBTreeNode *) NULL;
6348 node = info->tag_root;
6349 while (node->level > 0)
6352 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6353 * toggles. If so, push the root down one level.
6356 for (node2Ptr = node->children.node;
6357 node2Ptr != (GtkTextBTreeNode *)NULL ;
6358 node2Ptr = node2Ptr->next)
6360 for (prevPtr = NULL, summary = node2Ptr->summary;
6362 prevPtr = summary, summary = summary->next)
6364 if (summary->info == info)
6369 if (summary == NULL)
6373 if (summary->toggle_count != info->toggle_count)
6376 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6383 * This GtkTextBTreeNode has all the toggles, so push down the root.
6386 if (prevPtr == NULL)
6388 node2Ptr->summary = summary->next;
6392 prevPtr->next = summary->next;
6394 summary_destroy (summary);
6395 info->tag_root = node2Ptr;
6398 node = info->tag_root;
6403 *----------------------------------------------------------------------
6407 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6408 * increments the count for a particular tag, adding a new
6409 * entry for that tag if there wasn't one previously.
6415 * The information at *tagInfoPtr may be modified, and the arrays
6416 * may be reallocated to make them larger.
6418 *----------------------------------------------------------------------
6422 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6427 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6428 count > 0; tag_p++, count--)
6432 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6438 * There isn't currently an entry for this tag, so we have to
6439 * make a new one. If the arrays are full, then enlarge the
6443 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6445 GtkTextTag **newTags;
6446 int *newCounts, newSize;
6448 newSize = 2*tagInfoPtr->arraySize;
6449 newTags = (GtkTextTag **) g_malloc ((unsigned)
6450 (newSize*sizeof (GtkTextTag *)));
6451 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6452 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6453 g_free ((char *) tagInfoPtr->tags);
6454 tagInfoPtr->tags = newTags;
6455 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6456 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6457 tagInfoPtr->arraySize *sizeof (int));
6458 g_free ((char *) tagInfoPtr->counts);
6459 tagInfoPtr->counts = newCounts;
6460 tagInfoPtr->arraySize = newSize;
6463 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6464 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6465 tagInfoPtr->numTags++;
6469 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6470 const GtkTextIter *iter)
6472 GtkTextLineSegment *prev;
6476 line = _gtk_text_iter_get_text_line (iter);
6477 tree = _gtk_text_iter_get_btree (iter);
6479 prev = gtk_text_line_segment_split (iter);
6482 seg->next = line->segments;
6483 line->segments = seg;
6487 seg->next = prev->next;
6490 cleanup_line (line);
6491 segments_changed (tree);
6493 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6494 _gtk_text_btree_check (tree);
6498 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6499 GtkTextLineSegment *seg,
6502 GtkTextLineSegment *prev;
6504 if (line->segments == seg)
6506 line->segments = seg->next;
6510 for (prev = line->segments; prev->next != seg;
6513 /* Empty loop body. */
6515 prev->next = seg->next;
6517 cleanup_line (line);
6518 segments_changed (tree);
6522 * This is here because it requires BTree internals, it logically
6523 * belongs in gtktextsegment.c
6528 *--------------------------------------------------------------
6530 * _gtk_toggle_segment_check_func --
6532 * This procedure is invoked to perform consistency checks
6533 * on toggle segments.
6539 * If a consistency problem is found the procedure g_errors.
6541 *--------------------------------------------------------------
6545 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6551 if (segPtr->byte_count != 0)
6553 g_error ("toggle_segment_check_func: segment had non-zero size");
6555 if (!segPtr->body.toggle.inNodeCounts)
6557 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6559 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6560 for (summary = line->parent->summary; ;
6561 summary = summary->next)
6563 if (summary == NULL)
6567 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6574 if (summary->info == segPtr->body.toggle.info)
6578 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6590 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6591 GtkTextBTreeNode *node,
6601 while (view != NULL)
6603 if (view->view_id == nd->view_id)
6610 g_error ("Node has data for a view %p no longer attached to the tree",
6613 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6614 &width, &height, &valid);
6616 /* valid aggregate not checked the same as width/height, because on
6617 * btree rebalance we can have invalid nodes where all lines below
6618 * them are actually valid, due to moving lines around between
6621 * The guarantee is that if there are invalid lines the node is
6622 * invalid - we don't guarantee that if the node is invalid there
6623 * are invalid lines.
6626 if (nd->width != width ||
6627 nd->height != height ||
6628 (nd->valid && !valid))
6630 g_error ("Node aggregates for view %p are invalid:\n"
6631 "Are (%d,%d,%s), should be (%d,%d,%s)",
6633 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6634 width, height, valid ? "TRUE" : "FALSE");
6639 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6640 GtkTextBTreeNode *node)
6642 GtkTextBTreeNode *childnode;
6643 Summary *summary, *summary2;
6645 GtkTextLineSegment *segPtr;
6646 int num_children, num_lines, num_chars, toggle_count, min_children;
6647 GtkTextLineData *ld;
6650 if (node->parent != NULL)
6652 min_children = MIN_CHILDREN;
6654 else if (node->level > 0)
6661 if ((node->num_children < min_children)
6662 || (node->num_children > MAX_CHILDREN))
6664 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6665 node->num_children);
6668 nd = node->node_data;
6671 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6678 if (node->level == 0)
6680 for (line = node->children.line; line != NULL;
6683 if (line->parent != node)
6685 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6687 if (line->segments == NULL)
6689 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6695 /* Just ensuring we don't segv while doing this loop */
6700 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6702 if (segPtr->type->checkFunc != NULL)
6704 (*segPtr->type->checkFunc)(segPtr, line);
6706 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6707 && (segPtr->next != NULL)
6708 && (segPtr->next->byte_count == 0)
6709 && (segPtr->next->type->leftGravity))
6711 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6713 if ((segPtr->next == NULL)
6714 && (segPtr->type != >k_text_char_type))
6716 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6719 num_chars += segPtr->char_count;
6728 for (childnode = node->children.node; childnode != NULL;
6729 childnode = childnode->next)
6731 if (childnode->parent != node)
6733 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6735 if (childnode->level != (node->level-1))
6737 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6738 node->level, childnode->level);
6740 gtk_text_btree_node_check_consistency (tree, childnode);
6741 for (summary = childnode->summary; summary != NULL;
6742 summary = summary->next)
6744 for (summary2 = node->summary; ;
6745 summary2 = summary2->next)
6747 if (summary2 == NULL)
6749 if (summary->info->tag_root == node)
6753 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6754 summary->info->tag->name,
6755 "present in parent summaries");
6757 if (summary->info == summary2->info)
6764 num_lines += childnode->num_lines;
6765 num_chars += childnode->num_chars;
6768 if (num_children != node->num_children)
6770 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6771 num_children, node->num_children);
6773 if (num_lines != node->num_lines)
6775 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6776 num_lines, node->num_lines);
6778 if (num_chars != node->num_chars)
6780 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6781 num_chars, node->num_chars);
6784 for (summary = node->summary; summary != NULL;
6785 summary = summary->next)
6787 if (summary->info->toggle_count == summary->toggle_count)
6789 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6790 summary->info->tag->name);
6793 if (node->level == 0)
6795 for (line = node->children.line; line != NULL;
6798 for (segPtr = line->segments; segPtr != NULL;
6799 segPtr = segPtr->next)
6801 if ((segPtr->type != >k_text_toggle_on_type)
6802 && (segPtr->type != >k_text_toggle_off_type))
6806 if (segPtr->body.toggle.info == summary->info)
6808 if (!segPtr->body.toggle.inNodeCounts)
6809 g_error ("Toggle segment not in the node counts");
6818 for (childnode = node->children.node;
6820 childnode = childnode->next)
6822 for (summary2 = childnode->summary;
6824 summary2 = summary2->next)
6826 if (summary2->info == summary->info)
6828 toggle_count += summary2->toggle_count;
6833 if (toggle_count != summary->toggle_count)
6835 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6836 toggle_count, summary->toggle_count);
6838 for (summary2 = summary->next; summary2 != NULL;
6839 summary2 = summary2->next)
6841 if (summary2->info == summary->info)
6843 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6844 summary->info->tag->name);
6851 listify_foreach (GtkTextTag *tag, gpointer user_data)
6853 GSList** listp = user_data;
6855 *listp = g_slist_prepend (*listp, tag);
6859 list_of_tags (GtkTextTagTable *table)
6861 GSList *list = NULL;
6863 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6869 _gtk_text_btree_check (GtkTextBTree *tree)
6872 GtkTextBTreeNode *node;
6874 GtkTextLineSegment *seg;
6876 GSList *all_tags, *taglist = NULL;
6878 GtkTextTagInfo *info;
6881 * Make sure that the tag toggle counts and the tag root pointers are OK.
6883 all_tags = list_of_tags (tree->table);
6884 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6886 tag = taglist->data;
6887 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6890 node = info->tag_root;
6893 if (info->toggle_count != 0)
6895 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6896 tag->name, info->toggle_count);
6898 continue; /* no ranges for the tag */
6900 else if (info->toggle_count == 0)
6902 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6905 else if (info->toggle_count & 1)
6907 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6908 tag->name, info->toggle_count);
6910 for (summary = node->summary; summary != NULL;
6911 summary = summary->next)
6913 if (summary->info->tag == tag)
6915 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6919 if (node->level > 0)
6921 for (node = node->children.node ; node != NULL ;
6924 for (summary = node->summary; summary != NULL;
6925 summary = summary->next)
6927 if (summary->info->tag == tag)
6929 count += summary->toggle_count;
6936 GtkTextLineSegmentClass * last = NULL;
6938 for (line = node->children.line ; line != NULL ;
6941 for (seg = line->segments; seg != NULL;
6944 if ((seg->type == >k_text_toggle_on_type ||
6945 seg->type == >k_text_toggle_off_type) &&
6946 seg->body.toggle.info->tag == tag)
6948 if (last == seg->type)
6949 g_error ("Two consecutive toggles on or off weren't merged");
6950 if (!seg->body.toggle.inNodeCounts)
6951 g_error ("Toggle segment not in the node counts");
6960 if (count != info->toggle_count)
6962 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6963 info->toggle_count, tag->name, count);
6968 g_slist_free (all_tags);
6971 * Call a recursive procedure to do the main body of checks.
6974 node = tree->root_node;
6975 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6978 * Make sure that there are at least two lines in the text and
6979 * that the last line has no characters except a newline.
6982 if (node->num_lines < 2)
6984 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6986 if (node->num_chars < 2)
6988 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6990 while (node->level > 0)
6992 node = node->children.node;
6993 while (node->next != NULL)
6998 line = node->children.line;
6999 while (line->next != NULL)
7003 seg = line->segments;
7004 while ((seg->type == >k_text_toggle_off_type)
7005 || (seg->type == >k_text_right_mark_type)
7006 || (seg->type == >k_text_left_mark_type))
7009 * It's OK to toggle a tag off in the last line, but
7010 * not to start a new range. It's also OK to have marks
7016 if (seg->type != >k_text_char_type)
7018 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7020 if (seg->next != NULL)
7022 g_error ("_gtk_text_btree_check: last line has too many segments");
7024 if (seg->byte_count != 1)
7026 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7029 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7031 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7036 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7037 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7038 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7039 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7042 _gtk_text_btree_spew (GtkTextBTree *tree)
7047 printf ("%d lines in tree %p\n",
7048 _gtk_text_btree_line_count (tree), tree);
7050 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7052 while (line != NULL)
7054 _gtk_text_btree_spew_line (tree, line);
7055 line = _gtk_text_line_next (line);
7058 printf ("=================== Tag information\n");
7063 list = tree->tag_infos;
7065 while (list != NULL)
7067 GtkTextTagInfo *info;
7071 printf (" tag `%s': root at %p, toggle count %d\n",
7072 info->tag->name, info->tag_root, info->toggle_count);
7074 list = g_slist_next (list);
7077 if (tree->tag_infos == NULL)
7079 printf (" (no tags in the tree)\n");
7083 printf ("=================== Tree nodes\n");
7086 _gtk_text_btree_spew_node (tree->root_node, 0);
7091 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7094 GtkTextLineSegment *seg;
7096 spaces = g_strnfill (indent, ' ');
7098 printf ("%sline %p chars %d bytes %d\n",
7100 _gtk_text_line_char_count (line),
7101 _gtk_text_line_byte_count (line));
7103 seg = line->segments;
7106 if (seg->type == >k_text_char_type)
7108 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7113 if (*s == '\n' || *s == '\r')
7117 printf ("%s chars `%s'...\n", spaces, str);
7120 else if (seg->type == >k_text_right_mark_type)
7122 printf ("%s right mark `%s' visible: %d\n",
7124 seg->body.mark.name,
7125 seg->body.mark.visible);
7127 else if (seg->type == >k_text_left_mark_type)
7129 printf ("%s left mark `%s' visible: %d\n",
7131 seg->body.mark.name,
7132 seg->body.mark.visible);
7134 else if (seg->type == >k_text_toggle_on_type ||
7135 seg->type == >k_text_toggle_off_type)
7137 printf ("%s tag `%s' %s\n",
7138 spaces, seg->body.toggle.info->tag->name,
7139 seg->type == >k_text_toggle_off_type ? "off" : "on");
7149 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7152 GtkTextBTreeNode *iter;
7155 spaces = g_strnfill (indent, ' ');
7157 printf ("%snode %p level %d children %d lines %d chars %d\n",
7158 spaces, node, node->level,
7159 node->num_children, node->num_lines, node->num_chars);
7164 printf ("%s %d toggles of `%s' below this node\n",
7165 spaces, s->toggle_count, s->info->tag->name);
7171 if (node->level > 0)
7173 iter = node->children.node;
7174 while (iter != NULL)
7176 _gtk_text_btree_spew_node (iter, indent + 2);
7183 GtkTextLine *line = node->children.line;
7184 while (line != NULL)
7186 _gtk_text_btree_spew_line_short (line, indent + 2);
7194 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7196 GtkTextLineSegment * seg;
7198 printf ("%4d| line: %p parent: %p next: %p\n",
7199 _gtk_text_line_get_number (line), line, line->parent, line->next);
7201 seg = line->segments;
7205 _gtk_text_btree_spew_segment (tree, seg);
7211 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7213 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7214 seg, seg->type->name, seg->byte_count, seg->char_count);
7216 if (seg->type == >k_text_char_type)
7218 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7219 printf (" `%s'\n", str);
7222 else if (seg->type == >k_text_right_mark_type)
7224 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7225 seg->body.mark.name,
7226 seg->body.mark.visible,
7227 seg->body.mark.not_deleteable);
7229 else if (seg->type == >k_text_left_mark_type)
7231 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7232 seg->body.mark.name,
7233 seg->body.mark.visible,
7234 seg->body.mark.not_deleteable);
7236 else if (seg->type == >k_text_toggle_on_type ||
7237 seg->type == >k_text_toggle_off_type)
7239 printf (" tag `%s' priority %d\n",
7240 seg->body.toggle.info->tag->name,
7241 seg->body.toggle.info->tag->priority);