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
57 #include "gtktextbtree.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
75 * The structure below is used to pass information between
76 * _gtk_text_btree_get_tags and inc_count:
79 typedef struct TagInfo {
80 int numTags; /* Number of tags for which there
81 * is currently information in
83 int arraySize; /* Number of entries allocated for
85 GtkTextTag **tags; /* Array of tags seen so far.
87 int *counts; /* Toggle count (so far) for each
88 * entry in tags. Malloc-ed. */
93 * This is used to store per-view width/height info at the tree nodes.
96 typedef struct _NodeData NodeData;
102 /* Height and width of this node */
104 signed int width : 24;
106 /* boolean indicating whether the lines below this node are in need of validation.
107 * However, width/height should always represent the current total width and
108 * max height for lines below this node; the valid flag indicates whether the
109 * width/height on the lines needs recomputing, not whether the totals
112 guint valid : 8; /* Actually a boolean */
117 * The data structure below keeps summary information about one tag as part
118 * of the tag information in a node.
121 typedef struct Summary {
122 GtkTextTagInfo *info; /* Handle for tag. */
123 int toggle_count; /* Number of transitions into or
124 * out of this tag that occur in
125 * the subtree rooted at this node. */
126 struct Summary *next; /* Next in list of all tags for same
127 * node, or NULL if at end of list. */
131 * The data structure below defines a node in the B-tree.
134 struct _GtkTextBTreeNode {
135 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
136 * this is the root. */
137 GtkTextBTreeNode *next; /* Next in list of siblings with the
138 * same parent node, or NULL for end
140 Summary *summary; /* First in malloc-ed list of info
141 * about tags in this subtree (NULL if
142 * no tag info in the subtree). */
143 int level; /* Level of this node in the B-tree.
144 * 0 refers to the bottom of the tree
145 * (children are lines, not nodes). */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
150 int num_children; /* Number of children of this node. */
151 int num_lines; /* Total number of lines (leaves) in
152 * the subtree rooted here. */
153 int num_chars; /* Number of chars below here */
160 * Used to store the list of views in our btree
163 typedef struct _BTreeView BTreeView;
167 GtkTextLayout *layout;
173 * And the tree itself
176 struct _GtkTextBTree {
177 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
178 GtkTextTagTable *table;
179 GHashTable *mark_table;
181 GtkTextMark *insert_mark;
182 GtkTextMark *selection_bound_mark;
183 GtkTextBuffer *buffer;
186 gulong tag_changed_handler;
188 /* Incremented when a segment with a byte size > 0
189 * is added to or removed from the tree (i.e. the
190 * length of a line may have changed, and lines may
191 * have been added or removed). This invalidates
192 * all outstanding iterators.
194 guint chars_changed_stamp;
195 /* Incremented when any segments are added or deleted;
196 * this makes outstanding iterators recalculate their
197 * pointed-to segment and segment offset.
199 guint segments_changed_stamp;
201 /* Cache the last line in the buffer */
202 GtkTextLine *last_line;
203 guint last_line_stamp;
205 /* Cache the next-to-last line in the buffer,
206 * containing the end iterator
208 GtkTextLine *end_iter_line;
209 GtkTextLineSegment *end_iter_segment;
210 int end_iter_segment_byte_index;
211 int end_iter_segment_char_offset;
212 guint end_iter_line_stamp;
213 guint end_iter_segment_stamp;
215 GHashTable *child_anchor_table;
220 * Upper and lower bounds on how many children a node may have:
221 * rebalance when either of these limits is exceeded. MAX_CHILDREN
222 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
225 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
226 shallow. It appears to be faster to locate a particular line number
227 if the tree is narrow and deep, since it is more finely sorted. I
228 guess this may increase memory use though, and make it slower to
229 walk the tree in order, or locate a particular byte index (which
230 is done by walking the tree in order).
232 There's basically a tradeoff here. However I'm thinking we want to
233 add pixels, byte counts, and char counts to the tree nodes,
234 at that point narrow and deep should speed up all operations,
235 not just the line number searches.
239 #define MAX_CHILDREN 12
240 #define MIN_CHILDREN 6
242 #define MAX_CHILDREN 6
243 #define MIN_CHILDREN 3
250 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
252 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
253 GtkTextBTreeNode *node);
254 static GtkTextLine * get_last_line (GtkTextBTree *tree);
255 static void post_insert_fixup (GtkTextBTree *tree,
256 GtkTextLine *insert_line,
257 gint char_count_delta,
258 gint line_count_delta);
259 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
260 GtkTextTagInfo *info,
262 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
265 static void segments_changed (GtkTextBTree *tree);
266 static void chars_changed (GtkTextBTree *tree);
267 static void summary_list_destroy (Summary *summary);
268 static GtkTextLine *gtk_text_line_new (void);
269 static void gtk_text_line_destroy (GtkTextBTree *tree,
271 static void gtk_text_line_set_parent (GtkTextLine *line,
272 GtkTextBTreeNode *node);
273 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277 static NodeData *node_data_new (gpointer view_id);
278 static void node_data_destroy (NodeData *nd);
279 static void node_data_list_destroy (NodeData *nd);
280 static NodeData *node_data_find (NodeData *nd,
283 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
284 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
285 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
289 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
291 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
294 static void gtk_text_btree_node_remove_view (BTreeView *view,
295 GtkTextBTreeNode *node,
297 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
300 GtkTextBTreeNode *node);
301 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
305 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
309 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
310 GtkTextBTreeNode *node2);
311 static void get_tree_bounds (GtkTextBTree *tree,
314 static void tag_changed_cb (GtkTextTagTable *table,
316 gboolean size_changed,
318 static void cleanup_line (GtkTextLine *line);
319 static void recompute_node_counts (GtkTextBTree *tree,
320 GtkTextBTreeNode *node);
321 static void inc_count (GtkTextTag *tag,
323 TagInfo *tagInfoPtr);
325 static void summary_destroy (Summary *summary);
327 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
328 const GtkTextIter *iter);
329 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
330 GtkTextLineSegment *seg,
334 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
336 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
338 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
341 static void redisplay_region (GtkTextBTree *tree,
342 const GtkTextIter *start,
343 const GtkTextIter *end);
345 /* Inline thingies */
348 segments_changed (GtkTextBTree *tree)
350 tree->segments_changed_stamp += 1;
354 chars_changed (GtkTextBTree *tree)
356 tree->chars_changed_stamp += 1;
364 _gtk_text_btree_new (GtkTextTagTable *table,
365 GtkTextBuffer *buffer)
368 GtkTextBTreeNode *root_node;
369 GtkTextLine *line, *line2;
371 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
372 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
375 * The tree will initially have two empty lines. The second line
376 * isn't actually part of the tree's contents, but its presence
377 * makes several operations easier. The tree will have one GtkTextBTreeNode,
378 * which is also the root of the tree.
381 /* Create the root node. */
383 root_node = gtk_text_btree_node_new ();
385 line = gtk_text_line_new ();
386 line2 = gtk_text_line_new ();
388 root_node->parent = NULL;
389 root_node->next = NULL;
390 root_node->summary = NULL;
391 root_node->level = 0;
392 root_node->children.line = line;
393 root_node->num_children = 2;
394 root_node->num_lines = 2;
395 root_node->num_chars = 2;
397 line->parent = root_node;
400 line->segments = _gtk_char_segment_new ("\n", 1);
402 line2->parent = root_node;
404 line2->segments = _gtk_char_segment_new ("\n", 1);
406 /* Create the tree itself */
408 tree = g_new0(GtkTextBTree, 1);
409 tree->root_node = root_node;
413 /* Set these to values that are unlikely to be found
414 * in random memory garbage, and also avoid
415 * duplicates between tree instances.
417 tree->chars_changed_stamp = g_random_int ();
418 tree->segments_changed_stamp = g_random_int ();
420 tree->last_line_stamp = tree->chars_changed_stamp - 1;
421 tree->last_line = NULL;
423 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
424 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
425 tree->end_iter_line = NULL;
426 tree->end_iter_segment_byte_index = 0;
427 tree->end_iter_segment_char_offset = 0;
429 g_object_ref (tree->table);
431 tree->tag_changed_handler = g_signal_connect (tree->table,
433 G_CALLBACK (tag_changed_cb),
436 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
437 tree->child_anchor_table = NULL;
439 /* We don't ref the buffer, since the buffer owns us;
440 * we'd have some circularity issues. The buffer always
441 * lasts longer than the BTree
443 tree->buffer = buffer;
447 GtkTextLineSegment *seg;
449 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
452 tree->insert_mark = _gtk_text_btree_set_mark (tree,
459 seg = tree->insert_mark->segment;
461 seg->body.mark.not_deleteable = TRUE;
462 seg->body.mark.visible = TRUE;
464 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
471 seg = tree->selection_bound_mark->segment;
473 seg->body.mark.not_deleteable = TRUE;
475 g_object_ref (tree->insert_mark);
476 g_object_ref (tree->selection_bound_mark);
485 _gtk_text_btree_ref (GtkTextBTree *tree)
487 g_return_if_fail (tree != NULL);
488 g_return_if_fail (tree->refcount > 0);
494 _gtk_text_btree_unref (GtkTextBTree *tree)
496 g_return_if_fail (tree != NULL);
497 g_return_if_fail (tree->refcount > 0);
501 if (tree->refcount == 0)
503 g_signal_handler_disconnect (tree->table,
504 tree->tag_changed_handler);
506 g_object_unref (tree->table);
509 gtk_text_btree_node_destroy (tree, tree->root_node);
510 tree->root_node = NULL;
512 g_assert (g_hash_table_size (tree->mark_table) == 0);
513 g_hash_table_destroy (tree->mark_table);
514 tree->mark_table = NULL;
515 if (tree->child_anchor_table != NULL)
517 g_hash_table_destroy (tree->child_anchor_table);
518 tree->child_anchor_table = NULL;
521 g_object_unref (tree->insert_mark);
522 tree->insert_mark = NULL;
523 g_object_unref (tree->selection_bound_mark);
524 tree->selection_bound_mark = NULL;
531 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
537 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
539 return tree->chars_changed_stamp;
543 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
545 return tree->segments_changed_stamp;
549 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
551 g_return_if_fail (tree != NULL);
552 segments_changed (tree);
556 * Indexable segment mutation
560 * The following function is responsible for resolving the bidi direction
561 * for the lines between start and end. But it also calculates any
562 * dependent bidi direction for surrounding lines that change as a result
563 * of the bidi direction decisions within the range. The function is
564 * trying to do as little propagation as is needed.
567 gtk_text_btree_resolve_bidi (GtkTextIter *start,
570 GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
571 GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
572 PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
574 /* Resolve the strong bidi direction for all lines between
577 start_line = _gtk_text_iter_get_text_line (start);
578 start_line_prev = _gtk_text_line_previous (start_line);
579 end_line = _gtk_text_iter_get_text_line (end);
580 end_line_next = _gtk_text_line_next (end_line);
583 while (line && line != end_line_next)
585 /* Loop through the segments and search for a strong character
587 GtkTextLineSegment *seg = line->segments;
588 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
592 if (seg->type == >k_text_char_type && seg->byte_count > 0)
594 PangoDirection pango_dir;
596 pango_dir = pango_find_base_dir (seg->body.chars,
599 if (pango_dir != PANGO_DIRECTION_NEUTRAL)
601 line->dir_strong = pango_dir;
608 line = _gtk_text_line_next (line);
613 /* The variable dir_above_propagated contains the forward propagated
614 * direction before start. It is neutral if start is in the beginning
617 dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
619 dir_above_propagated = start_line_prev->dir_propagated_forward;
621 /* Loop forward and propagate the direction of each paragraph
622 * to all neutral lines.
625 last_strong = dir_above_propagated;
626 while (line != end_line_next)
628 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
629 last_strong = line->dir_strong;
631 line->dir_propagated_forward = last_strong;
633 line = _gtk_text_line_next (line);
636 /* Continue propagating as long as the previous resolved forward
637 * is different from last_strong.
640 GtkTextIter end_propagate;
643 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
644 line->dir_propagated_forward != last_strong)
646 GtkTextLine *prev = line;
647 line->dir_propagated_forward = last_strong;
649 line = _gtk_text_line_next(line);
657 /* The last line to invalidate is the last line before the
658 * line with the strong character. Or in case of the end of the
659 * buffer, the last line of the buffer. (There seems to be an
660 * extra "virtual" last line in the buffer that must not be used
661 * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
662 * _gtk_text_line_previous is ok in that case as well.)
664 line = _gtk_text_line_previous (line);
665 _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
666 _gtk_text_btree_invalidate_region (tree, end, &end_propagate);
671 /* The variable dir_below_propagated contains the backward propagated
672 * direction after end. It is neutral if end is at the end of
675 dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
677 dir_below_propagated = end_line_next->dir_propagated_back;
679 /* Loop backward and propagate the direction of each paragraph
680 * to all neutral lines.
683 last_strong = dir_below_propagated;
684 while (line != start_line_prev)
686 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
687 last_strong = line->dir_strong;
689 line->dir_propagated_back = last_strong;
691 line = _gtk_text_line_previous (line);
694 /* Continue propagating as long as the resolved backward dir
695 * is different from last_strong.
698 GtkTextIter start_propagate;
701 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
702 line->dir_propagated_back != last_strong)
704 GtkTextLine *prev = line;
705 line->dir_propagated_back = last_strong;
707 line = _gtk_text_line_previous (line);
715 /* We only need to invalidate for backwards propagation if the
716 * line we ended up on didn't get a direction from forwards
719 if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
721 _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
722 _gtk_text_btree_invalidate_region(tree, &start_propagate, start);
728 _gtk_text_btree_delete (GtkTextIter *start,
731 GtkTextLineSegment *prev_seg; /* The segment just before the start
732 * of the deletion range. */
733 GtkTextLineSegment *last_seg; /* The segment just after the end
734 * of the deletion range. */
735 GtkTextLineSegment *seg, *next, *next2;
736 GtkTextLine *curline;
737 GtkTextBTreeNode *curnode, *node;
739 GtkTextLine *start_line;
740 GtkTextLine *end_line;
742 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
743 gint start_byte_offset;
745 g_return_if_fail (start != NULL);
746 g_return_if_fail (end != NULL);
747 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
748 _gtk_text_iter_get_btree (end));
750 gtk_text_iter_order (start, end);
752 tree = _gtk_text_iter_get_btree (start);
754 if (gtk_debug_flags & GTK_DEBUG_TEXT)
755 _gtk_text_btree_check (tree);
757 /* Broadcast the need for redisplay before we break the iterators */
758 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
759 _gtk_text_btree_invalidate_region (tree, start, end);
761 /* Save the byte offset so we can reset the iterators */
762 start_byte_offset = gtk_text_iter_get_line_index (start);
764 start_line = _gtk_text_iter_get_text_line (start);
765 end_line = _gtk_text_iter_get_text_line (end);
768 * Split the start and end segments, so we have a place
769 * to insert our new text.
771 * Tricky point: split at end first; otherwise the split
772 * at end may invalidate seg and/or prev_seg. This allows
773 * us to avoid invalidating segments for start.
776 last_seg = gtk_text_line_segment_split (end);
777 if (last_seg != NULL)
778 last_seg = last_seg->next;
780 last_seg = end_line->segments;
782 prev_seg = gtk_text_line_segment_split (start);
783 if (prev_seg != NULL)
785 seg = prev_seg->next;
786 prev_seg->next = last_seg;
790 seg = start_line->segments;
791 start_line->segments = last_seg;
794 /* notify iterators that their segments need recomputation,
795 just for robustness. */
796 segments_changed (tree);
799 * Delete all of the segments between prev_seg and last_seg.
802 curline = start_line;
803 curnode = curline->parent;
804 while (seg != last_seg)
810 GtkTextLine *nextline;
813 * We just ran off the end of a line. First find the
814 * next line, then go back to the old line and delete it
815 * (unless it's the starting line for the range).
818 nextline = _gtk_text_line_next (curline);
819 if (curline != start_line)
821 if (curnode == start_line->parent)
822 start_line->next = curline->next;
824 curnode->children.line = curline->next;
826 for (node = curnode; node != NULL;
829 /* Don't update node->num_chars, because
830 * that was done when we deleted the segments.
832 node->num_lines -= 1;
835 curnode->num_children -= 1;
836 curline->next = deleted_lines;
837 deleted_lines = curline;
841 seg = curline->segments;
844 * If the GtkTextBTreeNode is empty then delete it and its parents,
845 * recursively upwards until a non-empty GtkTextBTreeNode is found.
848 while (curnode->num_children == 0)
850 GtkTextBTreeNode *parent;
852 parent = curnode->parent;
853 if (parent->children.node == curnode)
855 parent->children.node = curnode->next;
859 GtkTextBTreeNode *prevnode = parent->children.node;
860 while (prevnode->next != curnode)
862 prevnode = prevnode->next;
864 prevnode->next = curnode->next;
866 parent->num_children--;
867 gtk_text_btree_node_free_empty (tree, curnode);
870 curnode = curline->parent;
875 char_count = seg->char_count;
877 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
880 * This segment refuses to die. Move it to prev_seg and
881 * advance prev_seg if the segment has left gravity.
884 if (prev_seg == NULL)
886 seg->next = start_line->segments;
887 start_line->segments = seg;
889 else if (prev_seg->next &&
890 prev_seg->next != last_seg &&
891 seg->type == >k_text_toggle_off_type &&
892 prev_seg->next->type == >k_text_toggle_on_type &&
893 seg->body.toggle.info == prev_seg->next->body.toggle.info)
895 /* Try to match an off toggle with the matching on toggle
896 * if it immediately follows. This is a common case, and
897 * handling it here prevents quadratic blowup in
898 * cleanup_line() below. See bug 317125.
900 next2 = prev_seg->next->next;
901 g_free ((char *)prev_seg->next);
902 prev_seg->next = next2;
903 g_free ((char *)seg);
908 seg->next = prev_seg->next;
909 prev_seg->next = seg;
912 if (seg && seg->type->leftGravity)
919 /* Segment is gone. Decrement the char count of the node and
921 for (node = curnode; node != NULL;
924 node->num_chars -= char_count;
932 * If the beginning and end of the deletion range are in different
933 * lines, join the two lines together and discard the ending line.
936 if (start_line != end_line)
939 GtkTextBTreeNode *ancestor_node;
940 GtkTextLine *prevline;
943 /* last_seg was appended to start_line up at the top of this function */
945 for (seg = last_seg; seg != NULL;
948 chars_moved += seg->char_count;
949 if (seg->type->lineChangeFunc != NULL)
951 (*seg->type->lineChangeFunc)(seg, end_line);
955 for (node = start_line->parent; node != NULL;
958 node->num_chars += chars_moved;
961 curnode = end_line->parent;
962 for (node = curnode; node != NULL;
965 node->num_chars -= chars_moved;
968 curnode->num_children--;
969 prevline = curnode->children.line;
970 if (prevline == end_line)
972 curnode->children.line = end_line->next;
976 while (prevline->next != end_line)
978 prevline = prevline->next;
980 prevline->next = end_line->next;
982 end_line->next = deleted_lines;
983 deleted_lines = end_line;
985 /* We now fix up the per-view aggregates. We add all the height and
986 * width for the deleted lines to the start line, so that when revalidation
987 * occurs, the correct change in size is seen.
989 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
995 gint deleted_width = 0;
996 gint deleted_height = 0;
998 line = deleted_lines;
1001 GtkTextLine *next_line = line->next;
1002 ld = _gtk_text_line_get_data (line, view->view_id);
1006 deleted_width = MAX (deleted_width, ld->width);
1007 deleted_height += ld->height;
1013 if (deleted_width > 0 || deleted_height > 0)
1015 ld = _gtk_text_line_get_data (start_line, view->view_id);
1019 /* This means that start_line has never been validated.
1020 * We don't really want to do the validation here but
1021 * we do need to store our temporary sizes. So we
1022 * create the line data and assume a line w/h of 0.
1024 ld = _gtk_text_line_data_new (view->layout, start_line);
1025 _gtk_text_line_add_data (start_line, ld);
1031 ld->width = MAX (deleted_width, ld->width);
1032 ld->height += deleted_height;
1036 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1037 if (ancestor_node->parent)
1038 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1043 line = deleted_lines;
1046 GtkTextLine *next_line = line->next;
1048 gtk_text_line_destroy (tree, line);
1053 /* avoid dangling pointer */
1054 deleted_lines = NULL;
1056 gtk_text_btree_rebalance (tree, curnode);
1060 * Cleanup the segments in the new line.
1063 cleanup_line (start_line);
1066 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1069 gtk_text_btree_rebalance (tree, start_line->parent);
1071 /* Notify outstanding iterators that they
1073 chars_changed (tree);
1074 segments_changed (tree);
1076 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1077 _gtk_text_btree_check (tree);
1079 /* Re-initialize our iterators */
1080 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1083 gtk_text_btree_resolve_bidi (start, end);
1087 _gtk_text_btree_insert (GtkTextIter *iter,
1091 GtkTextLineSegment *prev_seg; /* The segment just before the first
1092 * new segment (NULL means new segment
1093 * is at beginning of line). */
1094 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1095 * are inserted just after this one.
1096 * NULL means insert at beginning of
1098 GtkTextLine *line; /* Current line (new segments are
1099 * added to this line). */
1100 GtkTextLineSegment *seg;
1101 GtkTextLine *newline;
1102 int chunk_len; /* # characters in current chunk. */
1103 gint sol; /* start of line */
1104 gint eol; /* Pointer to character just after last
1105 * one in current chunk.
1107 gint delim; /* index of paragraph delimiter */
1108 int line_count_delta; /* Counts change to total number of
1112 int char_count_delta; /* change to number of chars */
1114 gint start_byte_index;
1115 GtkTextLine *start_line;
1117 g_return_if_fail (text != NULL);
1118 g_return_if_fail (iter != NULL);
1121 len = strlen (text);
1123 /* extract iterator info */
1124 tree = _gtk_text_iter_get_btree (iter);
1125 line = _gtk_text_iter_get_text_line (iter);
1128 start_byte_index = gtk_text_iter_get_line_index (iter);
1130 /* Get our insertion segment split. Note this assumes line allows
1131 * char insertions, which isn't true of the "last" line. But iter
1132 * should not be on that line, as we assert here.
1134 g_assert (!_gtk_text_line_is_last (line, tree));
1135 prev_seg = gtk_text_line_segment_split (iter);
1138 /* Invalidate all iterators */
1139 chars_changed (tree);
1140 segments_changed (tree);
1143 * Chop the text up into lines and create a new segment for
1144 * each line, plus a new line for the leftovers from the
1150 line_count_delta = 0;
1151 char_count_delta = 0;
1156 pango_find_paragraph_boundary (text + sol,
1161 /* make these relative to the start of the text */
1165 g_assert (eol >= sol);
1166 g_assert (delim >= sol);
1167 g_assert (eol >= delim);
1168 g_assert (sol >= 0);
1169 g_assert (eol <= len);
1171 chunk_len = eol - sol;
1173 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1174 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1176 char_count_delta += seg->char_count;
1178 if (cur_seg == NULL)
1180 seg->next = line->segments;
1181 line->segments = seg;
1185 seg->next = cur_seg->next;
1186 cur_seg->next = seg;
1191 /* chunk didn't end with a paragraph separator */
1192 g_assert (eol == len);
1197 * The chunk ended with a newline, so create a new GtkTextLine
1198 * and move the remainder of the old line to it.
1201 newline = gtk_text_line_new ();
1202 gtk_text_line_set_parent (newline, line->parent);
1203 newline->next = line->next;
1204 line->next = newline;
1205 newline->segments = seg->next;
1213 * Cleanup the starting line for the insertion, plus the ending
1214 * line if it's different.
1217 cleanup_line (start_line);
1218 if (line != start_line)
1220 cleanup_line (line);
1223 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1225 /* Invalidate our region, and reset the iterator the user
1226 passed in to point to the end of the inserted text. */
1232 _gtk_text_btree_get_iter_at_line (tree,
1238 /* We could almost certainly be more efficient here
1239 by saving the information from the insertion loop
1241 gtk_text_iter_forward_chars (&end, char_count_delta);
1243 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1244 _gtk_text_btree_invalidate_region (tree,
1248 /* Convenience for the user */
1251 gtk_text_btree_resolve_bidi (&start, &end);
1256 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1257 GtkTextLineSegment *seg)
1261 GtkTextLineSegment *prevPtr;
1264 gint start_byte_offset;
1266 line = _gtk_text_iter_get_text_line (iter);
1267 tree = _gtk_text_iter_get_btree (iter);
1268 start_byte_offset = gtk_text_iter_get_line_index (iter);
1270 prevPtr = gtk_text_line_segment_split (iter);
1271 if (prevPtr == NULL)
1273 seg->next = line->segments;
1274 line->segments = seg;
1278 seg->next = prevPtr->next;
1279 prevPtr->next = seg;
1282 post_insert_fixup (tree, line, 0, seg->char_count);
1284 chars_changed (tree);
1285 segments_changed (tree);
1287 /* reset *iter for the user, and invalidate tree nodes */
1289 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1292 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1294 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1295 _gtk_text_btree_invalidate_region (tree, &start, iter);
1299 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1302 GtkTextLineSegment *seg;
1304 seg = _gtk_pixbuf_segment_new (pixbuf);
1306 insert_pixbuf_or_widget_segment (iter, seg);
1310 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1311 GtkTextChildAnchor *anchor)
1313 GtkTextLineSegment *seg;
1316 if (anchor->segment != NULL)
1318 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1322 seg = _gtk_widget_segment_new (anchor);
1324 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1325 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1327 insert_pixbuf_or_widget_segment (iter, seg);
1329 if (tree->child_anchor_table == NULL)
1330 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1332 g_hash_table_insert (tree->child_anchor_table,
1333 seg->body.child.obj,
1334 seg->body.child.obj);
1338 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1340 GtkTextLineSegment *seg;
1342 seg = anchor->segment;
1344 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1353 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1354 GtkTextBTreeNode *node, gint y, gint *line_top,
1355 GtkTextLine *last_line)
1359 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1360 _gtk_text_btree_check (tree);
1362 if (node->level == 0)
1366 line = node->children.line;
1368 while (line != NULL && line != last_line)
1370 GtkTextLineData *ld;
1372 ld = _gtk_text_line_get_data (line, view->view_id);
1376 if (y < (current_y + (ld ? ld->height : 0)))
1379 current_y += ld->height;
1380 *line_top += ld->height;
1389 GtkTextBTreeNode *child;
1391 child = node->children.node;
1393 while (child != NULL)
1398 gtk_text_btree_node_get_size (child, view->view_id,
1401 if (y < (current_y + height))
1402 return find_line_by_y (tree, view, child,
1403 y - current_y, line_top,
1406 current_y += height;
1407 *line_top += height;
1409 child = child->next;
1417 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1424 GtkTextLine *last_line;
1427 view = gtk_text_btree_get_view (tree, view_id);
1428 g_return_val_if_fail (view != NULL, NULL);
1430 last_line = get_last_line (tree);
1432 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1436 *line_top_out = line_top;
1442 find_line_top_in_line_list (GtkTextBTree *tree,
1445 GtkTextLine *target_line,
1448 while (line != NULL)
1450 GtkTextLineData *ld;
1452 if (line == target_line)
1455 ld = _gtk_text_line_get_data (line, view->view_id);
1462 g_assert_not_reached (); /* If we get here, our
1463 target line didn't exist
1464 under its parent node */
1469 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1470 GtkTextLine *target_line,
1477 GtkTextBTreeNode *node;
1479 view = gtk_text_btree_get_view (tree, view_id);
1481 g_return_val_if_fail (view != NULL, 0);
1484 node = target_line->parent;
1485 while (node != NULL)
1487 nodes = g_slist_prepend (nodes, node);
1488 node = node->parent;
1492 while (iter != NULL)
1496 if (node->level == 0)
1498 g_slist_free (nodes);
1499 return find_line_top_in_line_list (tree, view,
1500 node->children.line,
1505 GtkTextBTreeNode *child;
1506 GtkTextBTreeNode *target_node;
1508 g_assert (iter->next != NULL); /* not at level 0 */
1509 target_node = iter->next->data;
1511 child = node->children.node;
1513 while (child != NULL)
1518 if (child == target_node)
1522 gtk_text_btree_node_get_size (child, view->view_id,
1526 child = child->next;
1528 g_assert (child != NULL); /* should have broken out before we
1532 iter = g_slist_next (iter);
1535 g_assert_not_reached (); /* we return when we find the target line */
1540 _gtk_text_btree_add_view (GtkTextBTree *tree,
1541 GtkTextLayout *layout)
1544 GtkTextLine *last_line;
1545 GtkTextLineData *line_data;
1547 g_return_if_fail (tree != NULL);
1549 view = g_new (BTreeView, 1);
1551 view->view_id = layout;
1552 view->layout = layout;
1554 view->next = tree->views;
1559 g_assert (tree->views->prev == NULL);
1560 tree->views->prev = view;
1565 /* The last line in the buffer has identity values for the per-view
1566 * data so that we can avoid special case checks for it in a large
1569 last_line = get_last_line (tree);
1571 line_data = g_new (GtkTextLineData, 1);
1572 line_data->view_id = layout;
1573 line_data->next = NULL;
1574 line_data->width = 0;
1575 line_data->height = 0;
1576 line_data->valid = TRUE;
1578 _gtk_text_line_add_data (last_line, line_data);
1582 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1586 GtkTextLine *last_line;
1587 GtkTextLineData *line_data;
1589 g_return_if_fail (tree != NULL);
1593 while (view != NULL)
1595 if (view->view_id == view_id)
1601 g_return_if_fail (view != NULL);
1604 view->next->prev = view->prev;
1607 view->prev->next = view->next;
1609 if (view == tree->views)
1610 tree->views = view->next;
1612 /* Remove the line data for the last line which we added ourselves.
1613 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1615 last_line = get_last_line (tree);
1616 line_data = _gtk_text_line_remove_data (last_line, view_id);
1619 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1621 view->layout = (gpointer) 0xdeadbeef;
1622 view->view_id = (gpointer) 0xdeadbeef;
1628 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1629 const GtkTextIter *start,
1630 const GtkTextIter *end)
1636 while (view != NULL)
1638 gtk_text_layout_invalidate (view->layout, start, end);
1645 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1650 g_return_if_fail (tree != NULL);
1651 g_return_if_fail (view_id != NULL);
1653 gtk_text_btree_node_get_size (tree->root_node, view_id,
1668 iter_stack_new (void)
1671 stack = g_slice_new (IterStack);
1672 stack->iters = NULL;
1679 iter_stack_push (IterStack *stack,
1680 const GtkTextIter *iter)
1683 if (stack->count > stack->alloced)
1685 stack->alloced = stack->count*2;
1686 stack->iters = g_realloc (stack->iters,
1687 stack->alloced * sizeof (GtkTextIter));
1689 stack->iters[stack->count-1] = *iter;
1693 iter_stack_pop (IterStack *stack,
1696 if (stack->count == 0)
1701 *iter = stack->iters[stack->count];
1707 iter_stack_free (IterStack *stack)
1709 g_free (stack->iters);
1710 g_slice_free (IterStack, stack);
1714 iter_stack_invert (IterStack *stack)
1716 if (stack->count > 0)
1719 guint j = stack->count - 1;
1724 tmp = stack->iters[i];
1725 stack->iters[i] = stack->iters[j];
1726 stack->iters[j] = tmp;
1735 queue_tag_redisplay (GtkTextBTree *tree,
1737 const GtkTextIter *start,
1738 const GtkTextIter *end)
1740 if (_gtk_text_tag_affects_size (tag))
1742 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1743 _gtk_text_btree_invalidate_region (tree, start, end);
1745 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1747 /* We only need to queue a redraw, not a relayout */
1748 redisplay_region (tree, start, end);
1751 /* We don't need to do anything if the tag doesn't affect display */
1755 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1756 const GtkTextIter *end_orig,
1760 GtkTextLineSegment *seg, *prev;
1761 GtkTextLine *cleanupline;
1762 gboolean toggled_on;
1763 GtkTextLine *start_line;
1764 GtkTextLine *end_line;
1766 GtkTextIter start, end;
1769 GtkTextTagInfo *info;
1771 g_return_if_fail (start_orig != NULL);
1772 g_return_if_fail (end_orig != NULL);
1773 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1774 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1775 _gtk_text_iter_get_btree (end_orig));
1776 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1779 printf ("%s tag %s from %d to %d\n",
1780 add ? "Adding" : "Removing",
1782 gtk_text_buffer_get_offset (start_orig),
1783 gtk_text_buffer_get_offset (end_orig));
1786 if (gtk_text_iter_equal (start_orig, end_orig))
1789 start = *start_orig;
1792 gtk_text_iter_order (&start, &end);
1794 tree = _gtk_text_iter_get_btree (&start);
1796 queue_tag_redisplay (tree, tag, &start, &end);
1798 info = gtk_text_btree_get_tag_info (tree, tag);
1800 start_line = _gtk_text_iter_get_text_line (&start);
1801 end_line = _gtk_text_iter_get_text_line (&end);
1803 /* Find all tag toggles in the region; we are going to delete them.
1804 We need to find them in advance, because
1805 forward_find_tag_toggle () won't work once we start playing around
1807 stack = iter_stack_new ();
1810 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1811 * which is deliberate - we don't want to delete a toggle at the
1814 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1816 if (gtk_text_iter_compare (&iter, &end) >= 0)
1819 iter_stack_push (stack, &iter);
1822 /* We need to traverse the toggles in order. */
1823 iter_stack_invert (stack);
1826 * See whether the tag is present at the start of the range. If
1827 * the state doesn't already match what we want then add a toggle
1831 toggled_on = gtk_text_iter_has_tag (&start, tag);
1832 if ( (add && !toggled_on) ||
1833 (!add && toggled_on) )
1835 /* This could create a second toggle at the start position;
1836 cleanup_line () will remove it if so. */
1837 seg = _gtk_toggle_segment_new (info, add);
1839 prev = gtk_text_line_segment_split (&start);
1842 seg->next = start_line->segments;
1843 start_line->segments = seg;
1847 seg->next = prev->next;
1851 /* cleanup_line adds the new toggle to the node counts. */
1853 printf ("added toggle at start\n");
1855 /* we should probably call segments_changed, but in theory
1856 any still-cached segments in the iters we are about to
1857 use are still valid, since they're in front
1863 * Scan the range of characters and delete any internal tag
1864 * transitions. Keep track of what the old state was at the end
1865 * of the range, and add a toggle there if it's needed.
1869 cleanupline = start_line;
1870 while (iter_stack_pop (stack, &iter))
1872 GtkTextLineSegment *indexable_seg;
1875 line = _gtk_text_iter_get_text_line (&iter);
1876 seg = _gtk_text_iter_get_any_segment (&iter);
1877 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1879 g_assert (seg != NULL);
1880 g_assert (indexable_seg != NULL);
1881 g_assert (seg != indexable_seg);
1883 prev = line->segments;
1885 /* Find the segment that actually toggles this tag. */
1886 while (seg != indexable_seg)
1888 g_assert (seg != NULL);
1889 g_assert (indexable_seg != NULL);
1890 g_assert (seg != indexable_seg);
1892 if ( (seg->type == >k_text_toggle_on_type ||
1893 seg->type == >k_text_toggle_off_type) &&
1894 (seg->body.toggle.info == info) )
1900 g_assert (seg != NULL);
1901 g_assert (indexable_seg != NULL);
1903 g_assert (seg != indexable_seg); /* If this happens, then
1904 forward_to_tag_toggle was
1906 g_assert (seg->body.toggle.info->tag == tag);
1908 /* If this happens, when previously tagging we didn't merge
1909 overlapping tags. */
1910 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1911 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1913 toggled_on = !toggled_on;
1916 printf ("deleting %s toggle\n",
1917 seg->type == >k_text_toggle_on_type ? "on" : "off");
1919 /* Remove toggle segment from the list. */
1922 line->segments = seg->next;
1926 while (prev->next != seg)
1930 prev->next = seg->next;
1933 /* Inform iterators we've hosed them. This actually reflects a
1934 bit of inefficiency; if you have the same tag toggled on and
1935 off a lot in a single line, we keep having the rescan from
1936 the front of the line. Of course we have to do that to get
1937 "prev" anyway, but here we are doing it an additional
1939 segments_changed (tree);
1941 /* Update node counts */
1942 if (seg->body.toggle.inNodeCounts)
1944 _gtk_change_node_toggle_count (line->parent,
1946 seg->body.toggle.inNodeCounts = FALSE;
1951 /* We only clean up lines when we're done with them, saves some
1952 gratuitous line-segment-traversals */
1954 if (cleanupline != line)
1956 cleanup_line (cleanupline);
1961 iter_stack_free (stack);
1963 /* toggled_on now reflects the toggle state _just before_ the
1964 end iterator. The end iterator could already have a toggle
1965 on or a toggle off. */
1966 if ( (add && !toggled_on) ||
1967 (!add && toggled_on) )
1969 /* This could create a second toggle at the start position;
1970 cleanup_line () will remove it if so. */
1972 seg = _gtk_toggle_segment_new (info, !add);
1974 prev = gtk_text_line_segment_split (&end);
1977 seg->next = end_line->segments;
1978 end_line->segments = seg;
1982 seg->next = prev->next;
1985 /* cleanup_line adds the new toggle to the node counts. */
1986 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1988 printf ("added toggle at end\n");
1993 * Cleanup cleanupline and the last line of the range, if
1994 * these are different.
1997 cleanup_line (cleanupline);
1998 if (cleanupline != end_line)
2000 cleanup_line (end_line);
2003 segments_changed (tree);
2005 queue_tag_redisplay (tree, tag, &start, &end);
2007 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2008 _gtk_text_btree_check (tree);
2017 get_line_internal (GtkTextBTree *tree,
2019 gint *real_line_number,
2020 gboolean include_last)
2022 GtkTextBTreeNode *node;
2027 line_count = _gtk_text_btree_line_count (tree);
2031 if (line_number < 0)
2033 line_number = line_count;
2035 else if (line_number > line_count)
2037 line_number = line_count;
2040 if (real_line_number)
2041 *real_line_number = line_number;
2043 node = tree->root_node;
2044 lines_left = line_number;
2047 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2051 while (node->level != 0)
2053 for (node = node->children.node;
2054 node->num_lines <= lines_left;
2060 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2063 lines_left -= node->num_lines;
2068 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2071 for (line = node->children.line; lines_left > 0;
2077 g_error ("gtk_text_btree_find_line ran out of lines");
2086 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2089 _gtk_text_btree_get_line (tree,
2090 _gtk_text_btree_line_count (tree) - 1,
2095 _gtk_text_btree_get_line (GtkTextBTree *tree,
2097 gint *real_line_number)
2099 return get_line_internal (tree, line_number, real_line_number, TRUE);
2103 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2105 gint *real_line_number)
2107 return get_line_internal (tree, line_number, real_line_number, FALSE);
2111 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2113 gint *line_start_index,
2114 gint *real_char_index)
2116 GtkTextBTreeNode *node;
2118 GtkTextLineSegment *seg;
2122 node = tree->root_node;
2124 /* Clamp to valid indexes (-1 is magic for "highest index"),
2125 * node->num_chars includes the two newlines that aren't really
2128 if (char_index < 0 || char_index >= (node->num_chars - 1))
2130 char_index = node->num_chars - 2;
2133 *real_char_index = char_index;
2136 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2140 chars_left = char_index;
2141 while (node->level != 0)
2143 for (node = node->children.node;
2144 chars_left >= node->num_chars;
2147 chars_left -= node->num_chars;
2149 g_assert (chars_left >= 0);
2153 if (chars_left == 0)
2155 /* Start of a line */
2157 *line_start_index = char_index;
2158 return node->children.line;
2162 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2167 for (line = node->children.line; line != NULL; line = line->next)
2169 seg = line->segments;
2172 if (chars_in_line + seg->char_count > chars_left)
2173 goto found; /* found our line/segment */
2175 chars_in_line += seg->char_count;
2180 chars_left -= chars_in_line;
2188 g_assert (line != NULL); /* hosage, ran out of lines */
2189 g_assert (seg != NULL);
2191 *line_start_index = char_index - chars_left;
2196 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2199 GtkTextBTreeNode *node;
2200 GtkTextLine *siblingline;
2201 GtkTextLineSegment *seg;
2202 int src, dst, index;
2207 #define NUM_TAG_INFOS 10
2209 line = _gtk_text_iter_get_text_line (iter);
2210 byte_index = gtk_text_iter_get_line_index (iter);
2212 tagInfo.numTags = 0;
2213 tagInfo.arraySize = NUM_TAG_INFOS;
2214 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2215 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2218 * Record tag toggles within the line of indexPtr but preceding
2219 * indexPtr. Note that if this loop segfaults, your
2220 * byte_index probably points past the sum of all
2221 * seg->byte_count */
2223 for (index = 0, seg = line->segments;
2224 (index + seg->byte_count) <= byte_index;
2225 index += seg->byte_count, seg = seg->next)
2227 if ((seg->type == >k_text_toggle_on_type)
2228 || (seg->type == >k_text_toggle_off_type))
2230 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2235 * Record toggles for tags in lines that are predecessors of
2236 * line but under the same level-0 GtkTextBTreeNode.
2239 for (siblingline = line->parent->children.line;
2240 siblingline != line;
2241 siblingline = siblingline->next)
2243 for (seg = siblingline->segments; seg != NULL;
2246 if ((seg->type == >k_text_toggle_on_type)
2247 || (seg->type == >k_text_toggle_off_type))
2249 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2255 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2256 * toggles for all siblings that precede that GtkTextBTreeNode.
2259 for (node = line->parent; node->parent != NULL;
2260 node = node->parent)
2262 GtkTextBTreeNode *siblingPtr;
2265 for (siblingPtr = node->parent->children.node;
2266 siblingPtr != node; siblingPtr = siblingPtr->next)
2268 for (summary = siblingPtr->summary; summary != NULL;
2269 summary = summary->next)
2271 if (summary->toggle_count & 1)
2273 inc_count (summary->info->tag, summary->toggle_count,
2281 * Go through the tag information and squash out all of the tags
2282 * that have even toggle counts (these tags exist before the point
2283 * of interest, but not at the desired character itself).
2286 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2288 if (tagInfo.counts[src] & 1)
2290 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2291 tagInfo.tags[dst] = tagInfo.tags[src];
2297 g_free (tagInfo.counts);
2300 g_free (tagInfo.tags);
2303 return tagInfo.tags;
2307 copy_segment (GString *string,
2308 gboolean include_hidden,
2309 gboolean include_nonchars,
2310 const GtkTextIter *start,
2311 const GtkTextIter *end)
2313 GtkTextLineSegment *end_seg;
2314 GtkTextLineSegment *seg;
2316 if (gtk_text_iter_equal (start, end))
2319 seg = _gtk_text_iter_get_indexable_segment (start);
2320 end_seg = _gtk_text_iter_get_indexable_segment (end);
2322 if (seg->type == >k_text_char_type)
2324 gboolean copy = TRUE;
2325 gint copy_bytes = 0;
2326 gint copy_start = 0;
2328 /* Don't copy if we're invisible; segments are invisible/not
2329 as a whole, no need to check each char */
2330 if (!include_hidden &&
2331 _gtk_text_btree_char_is_invisible (start))
2334 /* printf (" <invisible>\n"); */
2337 copy_start = _gtk_text_iter_get_segment_byte (start);
2341 /* End is in the same segment; need to copy fewer bytes. */
2342 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2344 copy_bytes = end_byte - copy_start;
2347 copy_bytes = seg->byte_count - copy_start;
2349 g_assert (copy_bytes != 0); /* Due to iter equality check at
2350 front of this function. */
2354 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2356 g_string_append_len (string,
2357 seg->body.chars + copy_start,
2361 /* printf (" :%s\n", string->str); */
2363 else if (seg->type == >k_text_pixbuf_type ||
2364 seg->type == >k_text_child_type)
2366 gboolean copy = TRUE;
2368 if (!include_nonchars)
2372 else if (!include_hidden &&
2373 _gtk_text_btree_char_is_invisible (start))
2380 g_string_append_len (string,
2381 gtk_text_unknown_char_utf8,
2389 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2390 const GtkTextIter *end_orig,
2391 gboolean include_hidden,
2392 gboolean include_nonchars)
2394 GtkTextLineSegment *seg;
2395 GtkTextLineSegment *end_seg;
2402 g_return_val_if_fail (start_orig != NULL, NULL);
2403 g_return_val_if_fail (end_orig != NULL, NULL);
2404 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2405 _gtk_text_iter_get_btree (end_orig), NULL);
2407 start = *start_orig;
2410 gtk_text_iter_order (&start, &end);
2412 retval = g_string_new (NULL);
2414 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2416 seg = _gtk_text_iter_get_indexable_segment (&iter);
2417 while (seg != end_seg)
2419 copy_segment (retval, include_hidden, include_nonchars,
2422 _gtk_text_iter_forward_indexable_segment (&iter);
2424 seg = _gtk_text_iter_get_indexable_segment (&iter);
2427 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2430 g_string_free (retval, FALSE);
2435 _gtk_text_btree_line_count (GtkTextBTree *tree)
2437 /* Subtract bogus line at the end; we return a count
2439 return tree->root_node->num_lines - 1;
2443 _gtk_text_btree_char_count (GtkTextBTree *tree)
2445 /* Exclude newline in bogus last line and the
2446 * one in the last line that is after the end iterator
2448 return tree->root_node->num_chars - 2;
2451 #define LOTSA_TAGS 1000
2453 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2455 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2457 int deftagCnts[LOTSA_TAGS] = { 0, };
2458 int *tagCnts = deftagCnts;
2459 GtkTextTag *deftags[LOTSA_TAGS];
2460 GtkTextTag **tags = deftags;
2462 GtkTextBTreeNode *node;
2463 GtkTextLine *siblingline;
2464 GtkTextLineSegment *seg;
2471 line = _gtk_text_iter_get_text_line (iter);
2472 tree = _gtk_text_iter_get_btree (iter);
2473 byte_index = gtk_text_iter_get_line_index (iter);
2475 numTags = gtk_text_tag_table_get_size (tree->table);
2477 /* almost always avoid malloc, so stay out of system calls */
2478 if (LOTSA_TAGS < numTags)
2480 tagCnts = g_new0 (int, numTags);
2481 tags = g_new (GtkTextTag*, numTags);
2485 * Record tag toggles within the line of indexPtr but preceding
2489 for (index = 0, seg = line->segments;
2490 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2491 index += seg->byte_count, seg = seg->next)
2493 if ((seg->type == >k_text_toggle_on_type)
2494 || (seg->type == >k_text_toggle_off_type))
2496 tag = seg->body.toggle.info->tag;
2497 if (tag->invisible_set && tag->values->invisible)
2499 tags[tag->priority] = tag;
2500 tagCnts[tag->priority]++;
2506 * Record toggles for tags in lines that are predecessors of
2507 * line but under the same level-0 GtkTextBTreeNode.
2510 for (siblingline = line->parent->children.line;
2511 siblingline != line;
2512 siblingline = siblingline->next)
2514 for (seg = siblingline->segments; seg != NULL;
2517 if ((seg->type == >k_text_toggle_on_type)
2518 || (seg->type == >k_text_toggle_off_type))
2520 tag = seg->body.toggle.info->tag;
2521 if (tag->invisible_set && tag->values->invisible)
2523 tags[tag->priority] = tag;
2524 tagCnts[tag->priority]++;
2531 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2532 * for all siblings that precede that GtkTextBTreeNode.
2535 for (node = line->parent; node->parent != NULL;
2536 node = node->parent)
2538 GtkTextBTreeNode *siblingPtr;
2541 for (siblingPtr = node->parent->children.node;
2542 siblingPtr != node; siblingPtr = siblingPtr->next)
2544 for (summary = siblingPtr->summary; summary != NULL;
2545 summary = summary->next)
2547 if (summary->toggle_count & 1)
2549 tag = summary->info->tag;
2550 if (tag->invisible_set && tag->values->invisible)
2552 tags[tag->priority] = tag;
2553 tagCnts[tag->priority] += summary->toggle_count;
2561 * Now traverse from highest priority to lowest,
2562 * take invisible value from first odd count (= on)
2565 for (i = numTags-1; i >=0; i--)
2569 /* FIXME not sure this should be if 0 */
2571 #ifndef ALWAYS_SHOW_SELECTION
2572 /* who would make the selection invisible? */
2573 if ((tag == tkxt->seltag)
2574 && !(tkxt->flags & GOT_FOCUS))
2580 invisible = tags[i]->values->invisible;
2585 if (LOTSA_TAGS < numTags)
2600 redisplay_region (GtkTextBTree *tree,
2601 const GtkTextIter *start,
2602 const GtkTextIter *end)
2605 GtkTextLine *start_line, *end_line;
2607 if (gtk_text_iter_compare (start, end) > 0)
2609 const GtkTextIter *tmp = start;
2614 start_line = _gtk_text_iter_get_text_line (start);
2615 end_line = _gtk_text_iter_get_text_line (end);
2618 while (view != NULL)
2620 gint start_y, end_y;
2621 GtkTextLineData *ld;
2623 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2625 if (end_line == start_line)
2628 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2630 ld = _gtk_text_line_get_data (end_line, view->view_id);
2632 end_y += ld->height;
2634 gtk_text_layout_changed (view->layout, start_y,
2643 redisplay_mark (GtkTextLineSegment *mark)
2648 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2650 mark->body.mark.obj);
2653 gtk_text_iter_forward_char (&end);
2655 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2656 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2661 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2663 if (!mark->body.mark.visible)
2666 redisplay_mark (mark);
2670 ensure_not_off_end (GtkTextBTree *tree,
2671 GtkTextLineSegment *mark,
2674 if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
2675 gtk_text_iter_backward_char (iter);
2678 static GtkTextLineSegment*
2679 real_set_mark (GtkTextBTree *tree,
2680 GtkTextMark *existing_mark,
2682 gboolean left_gravity,
2683 const GtkTextIter *where,
2684 gboolean should_exist,
2685 gboolean redraw_selections)
2687 GtkTextLineSegment *mark;
2690 g_return_val_if_fail (tree != NULL, NULL);
2691 g_return_val_if_fail (where != NULL, NULL);
2692 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2695 mark = existing_mark->segment;
2696 else if (name != NULL)
2697 mark = g_hash_table_lookup (tree->mark_table,
2702 if (should_exist && mark == NULL)
2704 g_warning ("No mark `%s' exists!", name);
2708 /* OK if !should_exist and it does already exist, in that case
2714 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2715 _gtk_text_iter_check (&iter);
2719 if (redraw_selections &&
2720 (mark == tree->insert_mark->segment ||
2721 mark == tree->selection_bound_mark->segment))
2723 GtkTextIter old_pos;
2725 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2726 mark->body.mark.obj);
2727 redisplay_region (tree, &old_pos, where);
2731 * don't let visible marks be after the final newline of the
2735 if (mark->body.mark.visible)
2737 ensure_not_off_end (tree, mark, &iter);
2740 /* Redraw the mark's old location. */
2741 redisplay_mark_if_visible (mark);
2743 /* Unlink mark from its current location.
2744 This could hose our iterator... */
2745 gtk_text_btree_unlink_segment (tree, mark,
2746 mark->body.mark.line);
2747 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2748 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2750 segments_changed (tree); /* make sure the iterator recomputes its
2755 mark = _gtk_mark_segment_new (tree,
2759 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2761 if (mark->body.mark.name)
2762 g_hash_table_insert (tree->mark_table,
2763 mark->body.mark.name,
2767 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2768 _gtk_text_iter_check (&iter);
2770 /* Link mark into new location */
2771 gtk_text_btree_link_segment (mark, &iter);
2773 /* Invalidate some iterators. */
2774 segments_changed (tree);
2777 * update the screen at the mark's new location.
2780 redisplay_mark_if_visible (mark);
2782 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2783 _gtk_text_iter_check (&iter);
2785 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2786 _gtk_text_btree_check (tree);
2793 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2794 GtkTextMark *existing_mark,
2796 gboolean left_gravity,
2797 const GtkTextIter *iter,
2798 gboolean should_exist)
2800 GtkTextLineSegment *seg;
2802 seg = real_set_mark (tree, existing_mark,
2803 name, left_gravity, iter, should_exist,
2806 return seg ? seg->body.mark.obj : NULL;
2810 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2814 GtkTextIter tmp_start, tmp_end;
2816 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2818 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2819 tree->selection_bound_mark);
2821 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2833 gtk_text_iter_order (&tmp_start, &tmp_end);
2846 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2847 const GtkTextIter *iter)
2849 _gtk_text_btree_select_range (tree, iter, iter);
2853 _gtk_text_btree_select_range (GtkTextBTree *tree,
2854 const GtkTextIter *ins,
2855 const GtkTextIter *bound)
2857 GtkTextIter start, end;
2859 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2860 redisplay_region (tree, &start, &end);
2862 /* Move insert AND selection_bound before we redisplay */
2863 real_set_mark (tree, tree->insert_mark,
2864 "insert", FALSE, ins, TRUE, FALSE);
2865 real_set_mark (tree, tree->selection_bound_mark,
2866 "selection_bound", FALSE, bound, TRUE, FALSE);
2868 redisplay_region (tree, ins, bound);
2873 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2878 g_return_if_fail (tree != NULL);
2879 g_return_if_fail (name != NULL);
2881 mark = g_hash_table_lookup (tree->mark_table,
2884 _gtk_text_btree_remove_mark (tree, mark);
2888 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2889 GtkTextLineSegment *segment)
2892 if (segment->body.mark.name)
2893 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2895 segment->body.mark.tree = NULL;
2896 segment->body.mark.line = NULL;
2898 /* Remove the ref on the mark, which frees segment as a side effect
2899 * if this is the last reference.
2901 g_object_unref (segment->body.mark.obj);
2905 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2908 GtkTextLineSegment *segment;
2910 g_return_if_fail (mark != NULL);
2911 g_return_if_fail (tree != NULL);
2913 segment = mark->segment;
2915 if (segment->body.mark.not_deleteable)
2917 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2921 /* This calls cleanup_line and segments_changed */
2922 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2924 _gtk_text_btree_release_mark_segment (tree, segment);
2928 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2929 GtkTextMark *segment)
2931 return segment == tree->insert_mark;
2935 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2936 GtkTextMark *segment)
2938 return segment == tree->selection_bound_mark;
2942 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2945 GtkTextLineSegment *seg;
2947 g_return_val_if_fail (tree != NULL, NULL);
2948 g_return_val_if_fail (name != NULL, NULL);
2950 seg = g_hash_table_lookup (tree->mark_table, name);
2952 return seg ? seg->body.mark.obj : NULL;
2956 * gtk_text_mark_set_visible:
2957 * @mark: a #GtkTextMark
2958 * @setting: visibility of mark
2960 * Sets the visibility of @mark; the insertion point is normally
2961 * visible, i.e. you can see it as a vertical bar. Also, the text
2962 * widget uses a visible mark to indicate where a drop will occur when
2963 * dragging-and-dropping text. Most other marks are not visible.
2964 * Marks are not visible by default.
2968 gtk_text_mark_set_visible (GtkTextMark *mark,
2971 GtkTextLineSegment *seg;
2973 g_return_if_fail (mark != NULL);
2975 seg = mark->segment;
2977 if (seg->body.mark.visible == setting)
2981 seg->body.mark.visible = setting;
2983 redisplay_mark (seg);
2988 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2991 GtkTextBTreeNode *node;
2992 GtkTextTagInfo *info;
2994 g_return_val_if_fail (tree != NULL, NULL);
2998 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3003 if (info->tag_root == NULL)
3006 node = info->tag_root;
3008 /* We know the tag root has instances of the given
3011 continue_outer_loop:
3012 g_assert (node != NULL);
3013 while (node->level > 0)
3015 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3016 node = node->children.node;
3017 while (node != NULL)
3019 if (gtk_text_btree_node_has_tag (node, tag))
3020 goto continue_outer_loop;
3024 g_assert (node != NULL);
3027 g_assert (node != NULL); /* The tag summaries said some node had
3030 g_assert (node->level == 0);
3032 return node->children.line;
3036 /* Looking for any tag at all (tag == NULL).
3037 Unfortunately this can't be done in a simple and efficient way
3038 right now; so I'm just going to return the
3039 first line in the btree. FIXME */
3040 return _gtk_text_btree_get_line (tree, 0, NULL);
3045 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3048 GtkTextBTreeNode *node;
3049 GtkTextBTreeNode *last_node;
3051 GtkTextTagInfo *info;
3053 g_return_val_if_fail (tree != NULL, NULL);
3057 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3059 if (info->tag_root == NULL)
3062 node = info->tag_root;
3063 /* We know the tag root has instances of the given
3066 while (node->level > 0)
3068 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3070 node = node->children.node;
3071 while (node != NULL)
3073 if (gtk_text_btree_node_has_tag (node, tag))
3081 g_assert (node != NULL); /* The tag summaries said some node had
3084 g_assert (node->level == 0);
3086 /* Find the last line in this node */
3087 line = node->children.line;
3088 while (line->next != NULL)
3095 /* This search can't be done efficiently at the moment,
3096 at least not without complexity.
3097 So, we just return the last line.
3099 return _gtk_text_btree_get_end_iter_line (tree);
3109 _gtk_text_line_get_number (GtkTextLine *line)
3112 GtkTextBTreeNode *node, *parent, *node2;
3116 * First count how many lines precede this one in its level-0
3120 node = line->parent;
3122 for (line2 = node->children.line; line2 != line;
3123 line2 = line2->next)
3127 g_error ("gtk_text_btree_line_number couldn't find line");
3133 * Now work up through the levels of the tree one at a time,
3134 * counting how many lines are in GtkTextBTreeNodes preceding the current
3138 for (parent = node->parent ; parent != NULL;
3139 node = parent, parent = parent->parent)
3141 for (node2 = parent->children.node; node2 != node;
3142 node2 = node2->next)
3146 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3148 index += node2->num_lines;
3154 static GtkTextLineSegment*
3155 find_toggle_segment_before_char (GtkTextLine *line,
3159 GtkTextLineSegment *seg;
3160 GtkTextLineSegment *toggle_seg;
3165 seg = line->segments;
3166 while ( (index + seg->char_count) <= char_in_line )
3168 if (((seg->type == >k_text_toggle_on_type)
3169 || (seg->type == >k_text_toggle_off_type))
3170 && (seg->body.toggle.info->tag == tag))
3173 index += seg->char_count;
3180 static GtkTextLineSegment*
3181 find_toggle_segment_before_byte (GtkTextLine *line,
3185 GtkTextLineSegment *seg;
3186 GtkTextLineSegment *toggle_seg;
3191 seg = line->segments;
3192 while ( (index + seg->byte_count) <= byte_in_line )
3194 if (((seg->type == >k_text_toggle_on_type)
3195 || (seg->type == >k_text_toggle_off_type))
3196 && (seg->body.toggle.info->tag == tag))
3199 index += seg->byte_count;
3207 find_toggle_outside_current_line (GtkTextLine *line,
3211 GtkTextBTreeNode *node;
3212 GtkTextLine *sibling_line;
3213 GtkTextLineSegment *seg;
3214 GtkTextLineSegment *toggle_seg;
3216 GtkTextTagInfo *info = NULL;
3219 * No toggle in this line. Look for toggles for the tag in lines
3220 * that are predecessors of line but under the same
3221 * level-0 GtkTextBTreeNode.
3224 sibling_line = line->parent->children.line;
3225 while (sibling_line != line)
3227 seg = sibling_line->segments;
3230 if (((seg->type == >k_text_toggle_on_type)
3231 || (seg->type == >k_text_toggle_off_type))
3232 && (seg->body.toggle.info->tag == tag))
3238 sibling_line = sibling_line->next;
3241 if (toggle_seg != NULL)
3242 return (toggle_seg->type == >k_text_toggle_on_type);
3245 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3246 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3247 * siblings that precede that GtkTextBTreeNode.
3250 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3256 node = line->parent;
3257 while (node->parent != NULL)
3259 GtkTextBTreeNode *sibling_node;
3261 sibling_node = node->parent->children.node;
3262 while (sibling_node != node)
3266 summary = sibling_node->summary;
3267 while (summary != NULL)
3269 if (summary->info == info)
3270 toggles += summary->toggle_count;
3272 summary = summary->next;
3275 sibling_node = sibling_node->next;
3278 if (node == info->tag_root)
3281 node = node->parent;
3285 * An odd number of toggles means that the tag is present at the
3289 return (toggles & 1) != 0;
3292 /* FIXME this function is far too slow, for no good reason. */
3294 _gtk_text_line_char_has_tag (GtkTextLine *line,
3299 GtkTextLineSegment *toggle_seg;
3301 g_return_val_if_fail (line != NULL, FALSE);
3304 * Check for toggles for the tag in the line but before
3305 * the char. If there is one, its type indicates whether or
3306 * not the character is tagged.
3309 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3311 if (toggle_seg != NULL)
3312 return (toggle_seg->type == >k_text_toggle_on_type);
3314 return find_toggle_outside_current_line (line, tree, tag);
3318 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3323 GtkTextLineSegment *toggle_seg;
3325 g_return_val_if_fail (line != NULL, FALSE);
3328 * Check for toggles for the tag in the line but before
3329 * the char. If there is one, its type indicates whether or
3330 * not the character is tagged.
3333 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3335 if (toggle_seg != NULL)
3336 return (toggle_seg->type == >k_text_toggle_on_type);
3338 return find_toggle_outside_current_line (line, tree, tag);
3342 _gtk_text_line_is_last (GtkTextLine *line,
3345 return line == get_last_line (tree);
3349 ensure_end_iter_line (GtkTextBTree *tree)
3351 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3355 /* n_lines is without the magic line at the end */
3356 g_assert (_gtk_text_btree_line_count (tree) >= 1);
3358 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3360 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3365 ensure_end_iter_segment (GtkTextBTree *tree)
3367 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3369 GtkTextLineSegment *seg;
3370 GtkTextLineSegment *last_with_chars;
3372 ensure_end_iter_line (tree);
3374 last_with_chars = NULL;
3376 seg = tree->end_iter_line->segments;
3379 if (seg->char_count > 0)
3380 last_with_chars = seg;
3384 tree->end_iter_segment = last_with_chars;
3386 /* We know the last char in the last line is '\n' */
3387 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3388 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3390 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3392 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3393 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3398 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3401 ensure_end_iter_line (tree);
3403 return line == tree->end_iter_line;
3407 _gtk_text_btree_is_end (GtkTextBTree *tree,
3409 GtkTextLineSegment *seg,
3413 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3415 /* Do this first to avoid walking segments in most cases */
3416 if (!_gtk_text_line_contains_end_iter (line, tree))
3419 ensure_end_iter_segment (tree);
3421 if (seg != tree->end_iter_segment)
3424 if (byte_index >= 0)
3425 return byte_index == tree->end_iter_segment_byte_index;
3427 return char_offset == tree->end_iter_segment_char_offset;
3431 _gtk_text_line_next (GtkTextLine *line)
3433 GtkTextBTreeNode *node;
3435 if (line->next != NULL)
3440 * This was the last line associated with the particular parent
3441 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3442 * then search down from that GtkTextBTreeNode to find the first
3446 node = line->parent;
3447 while (node != NULL && node->next == NULL)
3448 node = node->parent;
3454 while (node->level > 0)
3456 node = node->children.node;
3459 g_assert (node->children.line != line);
3461 return node->children.line;
3466 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3470 next = _gtk_text_line_next (line);
3472 /* If we were on the end iter line, we can't go to
3475 if (next && next->next == NULL && /* these checks are optimization only */
3476 _gtk_text_line_next (next) == NULL)
3483 _gtk_text_line_previous (GtkTextLine *line)
3485 GtkTextBTreeNode *node;
3486 GtkTextBTreeNode *node2;
3490 * Find the line under this GtkTextBTreeNode just before the starting line.
3492 prev = line->parent->children.line; /* First line at leaf */
3493 while (prev != line)
3495 if (prev->next == line)
3501 g_error ("gtk_text_btree_previous_line ran out of lines");
3505 * This was the first line associated with the particular parent
3506 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3507 * then search down from that GtkTextBTreeNode to find its last line.
3509 for (node = line->parent; ; node = node->parent)
3511 if (node == NULL || node->parent == NULL)
3513 else if (node != node->parent->children.node)
3517 for (node2 = node->parent->children.node; ;
3518 node2 = node2->children.node)
3520 while (node2->next != node)
3521 node2 = node2->next;
3523 if (node2->level == 0)
3529 for (prev = node2->children.line ; ; prev = prev->next)
3531 if (prev->next == NULL)
3535 g_assert_not_reached ();
3541 _gtk_text_line_data_new (GtkTextLayout *layout,
3544 GtkTextLineData *line_data;
3546 line_data = g_new (GtkTextLineData, 1);
3548 line_data->view_id = layout;
3549 line_data->next = NULL;
3550 line_data->width = 0;
3551 line_data->height = 0;
3552 line_data->valid = FALSE;
3558 _gtk_text_line_add_data (GtkTextLine *line,
3559 GtkTextLineData *data)
3561 g_return_if_fail (line != NULL);
3562 g_return_if_fail (data != NULL);
3563 g_return_if_fail (data->view_id != NULL);
3567 data->next = line->views;
3577 _gtk_text_line_remove_data (GtkTextLine *line,
3580 GtkTextLineData *prev;
3581 GtkTextLineData *iter;
3583 g_return_val_if_fail (line != NULL, NULL);
3584 g_return_val_if_fail (view_id != NULL, NULL);
3588 while (iter != NULL)
3590 if (iter->view_id == view_id)
3599 prev->next = iter->next;
3601 line->views = iter->next;
3610 _gtk_text_line_get_data (GtkTextLine *line,
3613 GtkTextLineData *iter;
3615 g_return_val_if_fail (line != NULL, NULL);
3616 g_return_val_if_fail (view_id != NULL, NULL);
3619 while (iter != NULL)
3621 if (iter->view_id == view_id)
3630 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3631 GtkTextLineData *ld)
3633 /* For now this is totally unoptimized. FIXME?
3635 We could probably optimize the case where the width removed
3636 is less than the max width for the parent node,
3637 and the case where the height is unchanged when we re-wrap.
3640 g_return_if_fail (ld != NULL);
3643 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3647 _gtk_text_line_char_count (GtkTextLine *line)
3649 GtkTextLineSegment *seg;
3653 seg = line->segments;
3656 size += seg->char_count;
3663 _gtk_text_line_byte_count (GtkTextLine *line)
3665 GtkTextLineSegment *seg;
3669 seg = line->segments;
3672 size += seg->byte_count;
3680 _gtk_text_line_char_index (GtkTextLine *target_line)
3682 GSList *node_stack = NULL;
3683 GtkTextBTreeNode *iter;
3687 /* Push all our parent nodes onto a stack */
3688 iter = target_line->parent;
3690 g_assert (iter != NULL);
3692 while (iter != NULL)
3694 node_stack = g_slist_prepend (node_stack, iter);
3696 iter = iter->parent;
3699 /* Check that we have the root node on top of the stack. */
3700 g_assert (node_stack != NULL &&
3701 node_stack->data != NULL &&
3702 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3704 /* Add up chars in all nodes before the nodes in our stack.
3708 iter = node_stack->data;
3709 while (iter != NULL)
3711 GtkTextBTreeNode *child_iter;
3712 GtkTextBTreeNode *next_node;
3714 next_node = node_stack->next ?
3715 node_stack->next->data : NULL;
3716 node_stack = g_slist_remove (node_stack, node_stack->data);
3718 if (iter->level == 0)
3720 /* stack should be empty when we're on the last node */
3721 g_assert (node_stack == NULL);
3722 break; /* Our children are now lines */
3725 g_assert (next_node != NULL);
3726 g_assert (iter != NULL);
3727 g_assert (next_node->parent == iter);
3729 /* Add up chars before us in the tree */
3730 child_iter = iter->children.node;
3731 while (child_iter != next_node)
3733 g_assert (child_iter != NULL);
3735 num_chars += child_iter->num_chars;
3737 child_iter = child_iter->next;
3743 g_assert (iter != NULL);
3744 g_assert (iter == target_line->parent);
3746 /* Since we don't store char counts in lines, only in segments, we
3747 have to iterate over the lines adding up segment char counts
3748 until we find our line. */
3749 line = iter->children.line;
3750 while (line != target_line)
3752 g_assert (line != NULL);
3754 num_chars += _gtk_text_line_char_count (line);
3759 g_assert (line == target_line);
3765 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3769 GtkTextLineSegment *seg;
3772 g_return_val_if_fail (line != NULL, NULL);
3774 offset = byte_offset;
3775 seg = line->segments;
3777 while (offset >= seg->byte_count)
3779 offset -= seg->byte_count;
3781 g_assert (seg != NULL); /* means an invalid byte index */
3785 *seg_offset = offset;
3791 _gtk_text_line_char_to_segment (GtkTextLine *line,
3795 GtkTextLineSegment *seg;
3798 g_return_val_if_fail (line != NULL, NULL);
3800 offset = char_offset;
3801 seg = line->segments;
3803 while (offset >= seg->char_count)
3805 offset -= seg->char_count;
3807 g_assert (seg != NULL); /* means an invalid char index */
3811 *seg_offset = offset;
3817 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3821 GtkTextLineSegment *seg;
3824 g_return_val_if_fail (line != NULL, NULL);
3826 offset = byte_offset;
3827 seg = line->segments;
3829 while (offset > 0 && offset >= seg->byte_count)
3831 offset -= seg->byte_count;
3833 g_assert (seg != NULL); /* means an invalid byte index */
3837 *seg_offset = offset;
3843 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3847 GtkTextLineSegment *seg;
3850 g_return_val_if_fail (line != NULL, NULL);
3852 offset = char_offset;
3853 seg = line->segments;
3855 while (offset > 0 && offset >= seg->char_count)
3857 offset -= seg->char_count;
3859 g_assert (seg != NULL); /* means an invalid byte index */
3863 *seg_offset = offset;
3869 _gtk_text_line_byte_to_char (GtkTextLine *line,
3873 GtkTextLineSegment *seg;
3875 g_return_val_if_fail (line != NULL, 0);
3876 g_return_val_if_fail (byte_offset >= 0, 0);
3879 seg = line->segments;
3880 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3881 the next segment) */
3883 byte_offset -= seg->byte_count;
3884 char_offset += seg->char_count;
3886 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3889 g_assert (seg != NULL);
3891 /* Now byte_offset is the offset into the current segment,
3892 and char_offset is the start of the current segment.
3893 Optimize the case where no chars use > 1 byte */
3894 if (seg->byte_count == seg->char_count)
3895 return char_offset + byte_offset;
3898 if (seg->type == >k_text_char_type)
3899 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3902 g_assert (seg->char_count == 1);
3903 g_assert (byte_offset == 0);
3911 _gtk_text_line_char_to_byte (GtkTextLine *line,
3914 g_warning ("FIXME not implemented");
3919 /* FIXME sync with char_locate (or figure out a clean
3920 way to merge the two functions) */
3922 _gtk_text_line_byte_locate (GtkTextLine *line,
3924 GtkTextLineSegment **segment,
3925 GtkTextLineSegment **any_segment,
3926 gint *seg_byte_offset,
3927 gint *line_byte_offset)
3929 GtkTextLineSegment *seg;
3930 GtkTextLineSegment *after_last_indexable;
3931 GtkTextLineSegment *last_indexable;
3935 g_return_val_if_fail (line != NULL, FALSE);
3936 g_return_val_if_fail (byte_offset >= 0, FALSE);
3939 *any_segment = NULL;
3942 offset = byte_offset;
3944 last_indexable = NULL;
3945 after_last_indexable = line->segments;
3946 seg = line->segments;
3948 /* The loop ends when we're inside a segment;
3949 last_indexable refers to the last segment
3950 we passed entirely. */
3951 while (seg && offset >= seg->byte_count)
3953 if (seg->char_count > 0)
3955 offset -= seg->byte_count;
3956 bytes_in_line += seg->byte_count;
3957 last_indexable = seg;
3958 after_last_indexable = last_indexable->next;
3966 /* We went off the end of the line */
3968 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3975 if (after_last_indexable != NULL)
3976 *any_segment = after_last_indexable;
3978 *any_segment = *segment;
3981 /* Override any_segment if we're in the middle of a segment. */
3983 *any_segment = *segment;
3985 *seg_byte_offset = offset;
3987 g_assert (*segment != NULL);
3988 g_assert (*any_segment != NULL);
3989 g_assert (*seg_byte_offset < (*segment)->byte_count);
3991 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3996 /* FIXME sync with byte_locate (or figure out a clean
3997 way to merge the two functions) */
3999 _gtk_text_line_char_locate (GtkTextLine *line,
4001 GtkTextLineSegment **segment,
4002 GtkTextLineSegment **any_segment,
4003 gint *seg_char_offset,
4004 gint *line_char_offset)
4006 GtkTextLineSegment *seg;
4007 GtkTextLineSegment *after_last_indexable;
4008 GtkTextLineSegment *last_indexable;
4012 g_return_val_if_fail (line != NULL, FALSE);
4013 g_return_val_if_fail (char_offset >= 0, FALSE);
4016 *any_segment = NULL;
4019 offset = char_offset;
4021 last_indexable = NULL;
4022 after_last_indexable = line->segments;
4023 seg = line->segments;
4025 /* The loop ends when we're inside a segment;
4026 last_indexable refers to the last segment
4027 we passed entirely. */
4028 while (seg && offset >= seg->char_count)
4030 if (seg->char_count > 0)
4032 offset -= seg->char_count;
4033 chars_in_line += seg->char_count;
4034 last_indexable = seg;
4035 after_last_indexable = last_indexable->next;
4043 /* end of the line */
4045 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4052 if (after_last_indexable != NULL)
4053 *any_segment = after_last_indexable;
4055 *any_segment = *segment;
4058 /* Override any_segment if we're in the middle of a segment. */
4060 *any_segment = *segment;
4062 *seg_char_offset = offset;
4064 g_assert (*segment != NULL);
4065 g_assert (*any_segment != NULL);
4066 g_assert (*seg_char_offset < (*segment)->char_count);
4068 *line_char_offset = chars_in_line + *seg_char_offset;
4074 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4076 gint *line_char_offset,
4077 gint *seg_char_offset)
4079 GtkTextLineSegment *seg;
4082 g_return_if_fail (line != NULL);
4083 g_return_if_fail (byte_offset >= 0);
4085 *line_char_offset = 0;
4087 offset = byte_offset;
4088 seg = line->segments;
4090 while (offset >= seg->byte_count)
4092 offset -= seg->byte_count;
4093 *line_char_offset += seg->char_count;
4095 g_assert (seg != NULL); /* means an invalid char offset */
4098 g_assert (seg->char_count > 0); /* indexable. */
4100 /* offset is now the number of bytes into the current segment we
4101 * want to go. Count chars into the current segment.
4104 if (seg->type == >k_text_char_type)
4106 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4108 g_assert (*seg_char_offset < seg->char_count);
4110 *line_char_offset += *seg_char_offset;
4114 g_assert (offset == 0);
4115 *seg_char_offset = 0;
4120 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4122 gint *line_byte_offset,
4123 gint *seg_byte_offset)
4125 GtkTextLineSegment *seg;
4128 g_return_if_fail (line != NULL);
4129 g_return_if_fail (char_offset >= 0);
4131 *line_byte_offset = 0;
4133 offset = char_offset;
4134 seg = line->segments;
4136 while (offset >= seg->char_count)
4138 offset -= seg->char_count;
4139 *line_byte_offset += seg->byte_count;
4141 g_assert (seg != NULL); /* means an invalid char offset */
4144 g_assert (seg->char_count > 0); /* indexable. */
4146 /* offset is now the number of chars into the current segment we
4147 want to go. Count bytes into the current segment. */
4149 if (seg->type == >k_text_char_type)
4153 /* if in the last fourth of the segment walk backwards */
4154 if (seg->char_count - offset < seg->char_count / 4)
4155 p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4156 offset - seg->char_count);
4158 p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4160 *seg_byte_offset = p - seg->body.chars;
4162 g_assert (*seg_byte_offset < seg->byte_count);
4164 *line_byte_offset += *seg_byte_offset;
4168 g_assert (offset == 0);
4169 *seg_byte_offset = 0;
4174 node_compare (GtkTextBTreeNode *lhs,
4175 GtkTextBTreeNode *rhs)
4177 GtkTextBTreeNode *iter;
4178 GtkTextBTreeNode *node;
4179 GtkTextBTreeNode *common_parent;
4180 GtkTextBTreeNode *parent_of_lower;
4181 GtkTextBTreeNode *parent_of_higher;
4182 gboolean lhs_is_lower;
4183 GtkTextBTreeNode *lower;
4184 GtkTextBTreeNode *higher;
4186 /* This function assumes that lhs and rhs are not underneath each
4193 if (lhs->level < rhs->level)
4195 lhs_is_lower = TRUE;
4201 lhs_is_lower = FALSE;
4206 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4207 * of the common parent we used to reach the common parent; the
4208 * ordering of these child nodes in the child list is the ordering
4212 /* Get on the same level (may be on same level already) */
4214 while (node->level < higher->level)
4215 node = node->parent;
4217 g_assert (node->level == higher->level);
4219 g_assert (node != higher); /* Happens if lower is underneath higher */
4221 /* Go up until we have two children with a common parent.
4223 parent_of_lower = node;
4224 parent_of_higher = higher;
4226 while (parent_of_lower->parent != parent_of_higher->parent)
4228 parent_of_lower = parent_of_lower->parent;
4229 parent_of_higher = parent_of_higher->parent;
4232 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4234 common_parent = parent_of_lower->parent;
4236 g_assert (common_parent != NULL);
4238 /* See which is first in the list of common_parent's children */
4239 iter = common_parent->children.node;
4240 while (iter != NULL)
4242 if (iter == parent_of_higher)
4244 /* higher is less than lower */
4247 return 1; /* lhs > rhs */
4251 else if (iter == parent_of_lower)
4253 /* lower is less than higher */
4256 return -1; /* lhs < rhs */
4264 g_assert_not_reached ();
4268 /* remember that tag == NULL means "any tag" */
4270 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4274 GtkTextBTreeNode *node;
4275 GtkTextTagInfo *info;
4276 gboolean below_tag_root;
4278 g_return_val_if_fail (line != NULL, NULL);
4280 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4281 _gtk_text_btree_check (tree);
4285 /* Right now we can only offer linear-search if the user wants
4286 * to know about any tag toggle at all.
4288 return _gtk_text_line_next_excluding_last (line);
4291 /* Our tag summaries only have node precision, not line
4292 * precision. This means that if any line under a node could contain a
4293 * tag, then any of the others could also contain a tag.
4295 * In the future we could have some mechanism to keep track of how
4296 * many toggles we've found under a node so far, since we have a
4297 * count of toggles under the node. But for now I'm going with KISS.
4300 /* return same-node line, if any. */
4304 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4308 if (info->tag_root == NULL)
4311 if (info->tag_root == line->parent)
4312 return NULL; /* we were at the last line under the tag root */
4314 /* We need to go up out of this node, and on to the next one with
4315 toggles for the target tag. If we're below the tag root, we need to
4316 find the next node below the tag root that has tag summaries. If
4317 we're not below the tag root, we need to see if the tag root is
4318 after us in the tree, and if so, return the first line underneath
4321 node = line->parent;
4322 below_tag_root = FALSE;
4323 while (node != NULL)
4325 if (node == info->tag_root)
4327 below_tag_root = TRUE;
4331 node = node->parent;
4336 node = line->parent;
4337 while (node != info->tag_root)
4339 if (node->next == NULL)
4340 node = node->parent;
4345 if (gtk_text_btree_node_has_tag (node, tag))
4355 ordering = node_compare (line->parent, info->tag_root);
4359 /* Tag root is ahead of us, so search there. */
4360 node = info->tag_root;
4365 /* Tag root is after us, so no more lines that
4366 * could contain the tag.
4371 g_assert_not_reached ();
4376 g_assert (node != NULL);
4378 /* We have to find the first sub-node of this node that contains
4382 while (node->level > 0)
4384 g_assert (node != NULL); /* If this fails, it likely means an
4385 incorrect tag summary led us on a
4386 wild goose chase down this branch of
4388 node = node->children.node;
4389 while (node != NULL)
4391 if (gtk_text_btree_node_has_tag (node, tag))
4397 g_assert (node != NULL);
4398 g_assert (node->level == 0);
4400 return node->children.line;
4404 prev_line_under_node (GtkTextBTreeNode *node,
4409 prev = node->children.line;
4415 while (prev->next != line)
4425 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4429 GtkTextBTreeNode *node;
4430 GtkTextBTreeNode *found_node = NULL;
4431 GtkTextTagInfo *info;
4432 gboolean below_tag_root;
4434 GtkTextBTreeNode *line_ancestor;
4435 GtkTextBTreeNode *line_ancestor_parent;
4437 /* See next_could_contain_tag () for more extensive comments
4438 * on what's going on here.
4441 g_return_val_if_fail (line != NULL, NULL);
4443 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4444 _gtk_text_btree_check (tree);
4448 /* Right now we can only offer linear-search if the user wants
4449 * to know about any tag toggle at all.
4451 return _gtk_text_line_previous (line);
4454 /* Return same-node line, if any. */
4455 prev = prev_line_under_node (line->parent, line);
4459 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4463 if (info->tag_root == NULL)
4466 if (info->tag_root == line->parent)
4467 return NULL; /* we were at the first line under the tag root */
4469 /* Are we below the tag root */
4470 node = line->parent;
4471 below_tag_root = FALSE;
4472 while (node != NULL)
4474 if (node == info->tag_root)
4476 below_tag_root = TRUE;
4480 node = node->parent;
4485 /* Look for a previous node under this tag root that has our
4489 /* this assertion holds because line->parent is not the
4490 * tag root, we are below the tag root, and the tag
4493 g_assert (line->parent->parent != NULL);
4495 line_ancestor = line->parent;
4496 line_ancestor_parent = line->parent->parent;
4498 while (line_ancestor != info->tag_root)
4500 GSList *child_nodes = NULL;
4503 /* Create reverse-order list of nodes before
4506 if (line_ancestor_parent != NULL)
4507 node = line_ancestor_parent->children.node;
4509 node = line_ancestor;
4511 while (node != line_ancestor && node != NULL)
4513 child_nodes = g_slist_prepend (child_nodes, node);
4518 /* Try to find a node with our tag on it in the list */
4522 GtkTextBTreeNode *this_node = tmp->data;
4524 g_assert (this_node != line_ancestor);
4526 if (gtk_text_btree_node_has_tag (this_node, tag))
4528 found_node = this_node;
4529 g_slist_free (child_nodes);
4533 tmp = g_slist_next (tmp);
4536 g_slist_free (child_nodes);
4538 /* Didn't find anything on this level; go up one level. */
4539 line_ancestor = line_ancestor_parent;
4540 line_ancestor_parent = line_ancestor->parent;
4550 ordering = node_compare (line->parent, info->tag_root);
4554 /* Tag root is ahead of us, so no more lines
4561 /* Tag root is after us, so grab last tagged
4562 * line underneath the tag root.
4564 found_node = info->tag_root;
4568 g_assert_not_reached ();
4573 g_assert (found_node != NULL);
4575 /* We have to find the last sub-node of this node that contains
4580 while (node->level > 0)
4582 GSList *child_nodes = NULL;
4584 g_assert (node != NULL); /* If this fails, it likely means an
4585 incorrect tag summary led us on a
4586 wild goose chase down this branch of
4589 node = node->children.node;
4590 while (node != NULL)
4592 child_nodes = g_slist_prepend (child_nodes, node);
4596 node = NULL; /* detect failure to find a child node. */
4599 while (iter != NULL)
4601 if (gtk_text_btree_node_has_tag (iter->data, tag))
4603 /* recurse into this node. */
4608 iter = g_slist_next (iter);
4611 g_slist_free (child_nodes);
4613 g_assert (node != NULL);
4616 g_assert (node != NULL);
4617 g_assert (node->level == 0);
4619 /* this assertion is correct, but slow. */
4620 /* g_assert (node_compare (node, line->parent) < 0); */
4622 /* Return last line in this node. */
4624 prev = node->children.line;
4632 * Non-public function implementations
4636 summary_list_destroy (Summary *summary)
4638 g_slice_free_chain (Summary, summary, next);
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 prev_p = &line->segments;
4737 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4739 if (seg->type->cleanupFunc != NULL)
4741 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4749 prev_p = &(*prev_p)->next;
4759 node_data_new (gpointer view_id)
4763 nd = g_slice_new (NodeData);
4765 nd->view_id = view_id;
4775 node_data_destroy (NodeData *nd)
4777 g_slice_free (NodeData, nd);
4781 node_data_list_destroy (NodeData *nd)
4783 g_slice_free_chain (NodeData, nd, next);
4787 node_data_find (NodeData *nd,
4792 if (nd->view_id == view_id)
4800 summary_destroy (Summary *summary)
4802 /* Fill with error-triggering garbage */
4803 summary->info = (void*)0x1;
4804 summary->toggle_count = 567;
4805 summary->next = (void*)0x1;
4806 g_slice_free (Summary, summary);
4809 static GtkTextBTreeNode*
4810 gtk_text_btree_node_new (void)
4812 GtkTextBTreeNode *node;
4814 node = g_new (GtkTextBTreeNode, 1);
4816 node->node_data = NULL;
4822 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4823 GtkTextTagInfo *info,
4828 summary = node->summary;
4829 while (summary != NULL)
4831 if (summary->info == info)
4833 summary->toggle_count += adjust;
4837 summary = summary->next;
4840 if (summary == NULL)
4842 /* didn't find a summary for our tag. */
4843 g_return_if_fail (adjust > 0);
4844 summary = g_slice_new (Summary);
4845 summary->info = info;
4846 summary->toggle_count = adjust;
4847 summary->next = node->summary;
4848 node->summary = summary;
4852 /* Note that the tag root and above do not have summaries
4853 for the tag; only nodes below the tag root have
4856 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4860 summary = node->summary;
4861 while (summary != NULL)
4864 summary->info->tag == tag)
4867 summary = summary->next;
4873 /* Add node and all children to the damage region. */
4875 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4879 nd = node->node_data;
4886 if (node->level == 0)
4890 line = node->children.line;
4891 while (line != NULL)
4893 GtkTextLineData *ld;
4907 GtkTextBTreeNode *child;
4909 child = node->children.node;
4911 while (child != NULL)
4913 gtk_text_btree_node_invalidate_downward (child);
4915 child = child->next;
4921 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4923 GtkTextBTreeNode *iter;
4926 while (iter != NULL)
4932 nd = node_data_find (iter->node_data, view_id);
4934 if (nd == NULL || !nd->valid)
4935 break; /* Once a node is invalid, we know its parents are as well. */
4941 gboolean should_continue = FALSE;
4943 nd = iter->node_data;
4948 should_continue = TRUE;
4955 if (!should_continue)
4956 break; /* This node was totally invalidated, so are its
4960 iter = iter->parent;
4966 * _gtk_text_btree_is_valid:
4967 * @tree: a #GtkTextBTree
4968 * @view_id: ID for the view
4970 * Check to see if the entire #GtkTextBTree is valid or not for
4973 * Return value: %TRUE if the entire #GtkTextBTree is valid
4976 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4980 g_return_val_if_fail (tree != NULL, FALSE);
4982 nd = node_data_find (tree->root_node->node_data, view_id);
4983 return (nd && nd->valid);
4986 typedef struct _ValidateState ValidateState;
4988 struct _ValidateState
4990 gint remaining_pixels;
4991 gboolean in_validation;
4998 gtk_text_btree_node_validate (BTreeView *view,
4999 GtkTextBTreeNode *node,
5001 ValidateState *state)
5003 gint node_valid = TRUE;
5004 gint node_width = 0;
5005 gint node_height = 0;
5007 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5008 g_return_if_fail (!nd->valid);
5010 if (node->level == 0)
5012 GtkTextLine *line = node->children.line;
5013 GtkTextLineData *ld;
5015 /* Iterate over leading valid lines */
5016 while (line != NULL)
5018 ld = _gtk_text_line_get_data (line, view_id);
5020 if (!ld || !ld->valid)
5022 else if (state->in_validation)
5024 state->in_validation = FALSE;
5029 state->y += ld->height;
5030 node_width = MAX (ld->width, node_width);
5031 node_height += ld->height;
5037 state->in_validation = TRUE;
5039 /* Iterate over invalid lines */
5040 while (line != NULL)
5042 ld = _gtk_text_line_get_data (line, view_id);
5044 if (ld && ld->valid)
5049 state->old_height += ld->height;
5050 ld = gtk_text_layout_wrap (view->layout, line, ld);
5051 state->new_height += ld->height;
5053 node_width = MAX (ld->width, node_width);
5054 node_height += ld->height;
5056 state->remaining_pixels -= ld->height;
5057 if (state->remaining_pixels <= 0)
5067 /* Iterate over the remaining lines */
5068 while (line != NULL)
5070 ld = _gtk_text_line_get_data (line, view_id);
5071 state->in_validation = FALSE;
5073 if (!ld || !ld->valid)
5078 node_width = MAX (ld->width, node_width);
5079 node_height += ld->height;
5087 GtkTextBTreeNode *child;
5090 child = node->children.node;
5092 /* Iterate over leading valid nodes */
5095 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5097 if (!child_nd->valid)
5099 else if (state->in_validation)
5101 state->in_validation = FALSE;
5106 state->y += child_nd->height;
5107 node_width = MAX (node_width, child_nd->width);
5108 node_height += child_nd->height;
5111 child = child->next;
5114 /* Iterate over invalid nodes */
5117 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5119 if (child_nd->valid)
5123 gtk_text_btree_node_validate (view, child, view_id, state);
5125 if (!child_nd->valid)
5127 node_width = MAX (node_width, child_nd->width);
5128 node_height += child_nd->height;
5130 if (!state->in_validation || state->remaining_pixels <= 0)
5132 child = child->next;
5137 child = child->next;
5140 /* Iterate over the remaining lines */
5143 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5144 state->in_validation = FALSE;
5146 if (!child_nd->valid)
5149 node_width = MAX (child_nd->width, node_width);
5150 node_height += child_nd->height;
5152 child = child->next;
5156 nd->width = node_width;
5157 nd->height = node_height;
5158 nd->valid = node_valid;
5162 * _gtk_text_btree_validate:
5163 * @tree: a #GtkTextBTree
5165 * @max_pixels: the maximum number of pixels to validate. (No more
5166 * than one paragraph beyond this limit will be validated)
5167 * @y: location to store starting y coordinate of validated region
5168 * @old_height: location to store old height of validated region
5169 * @new_height: location to store new height of validated region
5171 * Validate a single contiguous invalid region of a #GtkTextBTree for
5174 * Return value: %TRUE if a region has been validated, %FALSE if the
5175 * entire tree was already valid.
5178 _gtk_text_btree_validate (GtkTextBTree *tree,
5187 g_return_val_if_fail (tree != NULL, FALSE);
5189 view = gtk_text_btree_get_view (tree, view_id);
5190 g_return_val_if_fail (view != NULL, FALSE);
5192 if (!_gtk_text_btree_is_valid (tree, view_id))
5194 ValidateState state;
5196 state.remaining_pixels = max_pixels;
5197 state.in_validation = FALSE;
5199 state.old_height = 0;
5200 state.new_height = 0;
5202 gtk_text_btree_node_validate (view,
5209 *old_height = state.old_height;
5211 *new_height = state.new_height;
5213 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5214 _gtk_text_btree_check (tree);
5223 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5227 gboolean *valid_out)
5231 gboolean valid = TRUE;
5233 if (node->level == 0)
5235 GtkTextLine *line = node->children.line;
5237 while (line != NULL)
5239 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5241 if (!ld || !ld->valid)
5246 width = MAX (ld->width, width);
5247 height += ld->height;
5255 GtkTextBTreeNode *child = node->children.node;
5259 NodeData *child_nd = node_data_find (child->node_data, view_id);
5261 if (!child_nd || !child_nd->valid)
5266 width = MAX (child_nd->width, width);
5267 height += child_nd->height;
5270 child = child->next;
5275 *height_out = height;
5280 /* Recompute the validity and size of the view data for a given
5281 * view at this node from the immediate children of the node
5284 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5287 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5292 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5293 &width, &height, &valid);
5295 nd->height = height;
5302 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5307 gtk_text_btree_node_check_valid (node, view_id);
5308 node = node->parent;
5313 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5316 if (node->level == 0)
5318 return gtk_text_btree_node_check_valid (node, view_id);
5322 GtkTextBTreeNode *child = node->children.node;
5324 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5332 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5334 if (!child_nd->valid)
5336 nd->width = MAX (child_nd->width, nd->width);
5337 nd->height += child_nd->height;
5339 child = child->next;
5348 * _gtk_text_btree_validate_line:
5349 * @tree: a #GtkTextBTree
5350 * @line: line to validate
5351 * @view_id: view ID for the view to validate
5353 * Revalidate a single line of the btree for the given view, propagate
5354 * results up through the entire tree.
5357 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5361 GtkTextLineData *ld;
5364 g_return_if_fail (tree != NULL);
5365 g_return_if_fail (line != NULL);
5367 view = gtk_text_btree_get_view (tree, view_id);
5368 g_return_if_fail (view != NULL);
5370 ld = _gtk_text_line_get_data (line, view_id);
5371 if (!ld || !ld->valid)
5373 ld = gtk_text_layout_wrap (view->layout, line, ld);
5375 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5380 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5382 if (node->level == 0)
5386 line = node->children.line;
5387 while (line != NULL)
5389 GtkTextLineData *ld;
5391 ld = _gtk_text_line_remove_data (line, view_id);
5394 gtk_text_layout_free_line_data (view->layout, line, ld);
5401 GtkTextBTreeNode *child;
5403 child = node->children.node;
5405 while (child != NULL)
5408 gtk_text_btree_node_remove_view (view, child, view_id);
5410 child = child->next;
5414 gtk_text_btree_node_remove_data (node, view_id);
5418 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5420 if (node->level == 0)
5423 GtkTextLineSegment *seg;
5425 while (node->children.line != NULL)
5427 line = node->children.line;
5428 node->children.line = line->next;
5429 while (line->segments != NULL)
5431 seg = line->segments;
5432 line->segments = seg->next;
5434 (*seg->type->deleteFunc) (seg, line, TRUE);
5436 gtk_text_line_destroy (tree, line);
5441 GtkTextBTreeNode *childPtr;
5443 while (node->children.node != NULL)
5445 childPtr = node->children.node;
5446 node->children.node = childPtr->next;
5447 gtk_text_btree_node_destroy (tree, childPtr);
5451 gtk_text_btree_node_free_empty (tree, node);
5455 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5456 GtkTextBTreeNode *node)
5458 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5459 (node->level == 0 && node->children.line == NULL));
5461 summary_list_destroy (node->summary);
5462 node_data_list_destroy (node->node_data);
5467 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5471 nd = node->node_data;
5474 if (nd->view_id == view_id)
5482 nd = node_data_new (view_id);
5484 if (node->node_data)
5485 nd->next = node->node_data;
5487 node->node_data = nd;
5494 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5500 nd = node->node_data;
5503 if (nd->view_id == view_id)
5514 prev->next = nd->next;
5516 if (node->node_data == nd)
5517 node->node_data = nd->next;
5521 node_data_destroy (nd);
5525 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5526 gint *width, gint *height)
5530 g_return_if_fail (width != NULL);
5531 g_return_if_fail (height != NULL);
5533 nd = gtk_text_btree_node_ensure_data (node, view_id);
5538 *height = nd->height;
5541 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5542 * here isn't quite right, since for a lot of operations we want to
5543 * know which children of the common parent correspond to the two nodes
5544 * (e.g., when computing the order of two iters)
5546 static GtkTextBTreeNode *
5547 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5548 GtkTextBTreeNode *node2)
5550 while (node1->level < node2->level)
5551 node1 = node1->parent;
5552 while (node2->level < node1->level)
5553 node2 = node2->parent;
5554 while (node1 != node2)
5556 node1 = node1->parent;
5557 node2 = node2->parent;
5568 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5573 while (view != NULL)
5575 if (view->view_id == view_id)
5584 get_tree_bounds (GtkTextBTree *tree,
5588 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5589 _gtk_text_btree_get_end_iter (tree, end);
5593 tag_changed_cb (GtkTextTagTable *table,
5595 gboolean size_changed,
5600 /* We need to queue a relayout on all regions that are tagged with
5607 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5609 /* Must be a last toggle if there was a first one. */
5610 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5611 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5612 _gtk_text_btree_invalidate_region (tree,
5619 /* We only need to queue a redraw, not a relayout */
5624 while (view != NULL)
5628 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5629 gtk_text_layout_changed (view->layout, 0, height, height);
5637 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5640 /* Remove the tag from the tree */
5645 get_tree_bounds (tree, &start, &end);
5647 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5648 gtk_text_btree_remove_tag_info (tree, tag);
5652 /* Rebalance the out-of-whack node "node" */
5654 gtk_text_btree_rebalance (GtkTextBTree *tree,
5655 GtkTextBTreeNode *node)
5658 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5659 * up through the tree one GtkTextBTreeNode at a time until the root
5660 * GtkTextBTreeNode has been processed.
5663 while (node != NULL)
5665 GtkTextBTreeNode *new_node, *child;
5670 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5671 * then split off all but the first MIN_CHILDREN into a separate
5672 * GtkTextBTreeNode following the original one. Then repeat until the
5673 * GtkTextBTreeNode has a decent size.
5676 if (node->num_children > MAX_CHILDREN)
5681 * If the GtkTextBTreeNode being split is the root
5682 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5686 if (node->parent == NULL)
5688 new_node = gtk_text_btree_node_new ();
5689 new_node->parent = NULL;
5690 new_node->next = NULL;
5691 new_node->summary = NULL;
5692 new_node->level = node->level + 1;
5693 new_node->children.node = node;
5694 recompute_node_counts (tree, new_node);
5695 tree->root_node = new_node;
5697 new_node = gtk_text_btree_node_new ();
5698 new_node->parent = node->parent;
5699 new_node->next = node->next;
5700 node->next = new_node;
5701 new_node->summary = NULL;
5702 new_node->level = node->level;
5703 new_node->num_children = node->num_children - MIN_CHILDREN;
5704 if (node->level == 0)
5706 for (i = MIN_CHILDREN-1,
5707 line = node->children.line;
5708 i > 0; i--, line = line->next)
5710 /* Empty loop body. */
5712 new_node->children.line = line->next;
5717 for (i = MIN_CHILDREN-1,
5718 child = node->children.node;
5719 i > 0; i--, child = child->next)
5721 /* Empty loop body. */
5723 new_node->children.node = child->next;
5726 recompute_node_counts (tree, node);
5727 node->parent->num_children++;
5729 if (node->num_children <= MAX_CHILDREN)
5731 recompute_node_counts (tree, node);
5737 while (node->num_children < MIN_CHILDREN)
5739 GtkTextBTreeNode *other;
5740 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5741 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5742 int total_children, first_children, i;
5745 * Too few children for this GtkTextBTreeNode. If this is the root then,
5746 * it's OK for it to have less than MIN_CHILDREN children
5747 * as long as it's got at least two. If it has only one
5748 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5749 * the tree and use its child as the new root.
5752 if (node->parent == NULL)
5754 if ((node->num_children == 1) && (node->level > 0))
5756 tree->root_node = node->children.node;
5757 tree->root_node->parent = NULL;
5759 node->children.node = NULL;
5760 gtk_text_btree_node_free_empty (tree, node);
5766 * Not the root. Make sure that there are siblings to
5770 if (node->parent->num_children < 2)
5772 gtk_text_btree_rebalance (tree, node->parent);
5777 * Find a sibling neighbor to borrow from, and arrange for
5778 * node to be the earlier of the pair.
5781 if (node->next == NULL)
5783 for (other = node->parent->children.node;
5784 other->next != node;
5785 other = other->next)
5787 /* Empty loop body. */
5794 * We're going to either merge the two siblings together
5795 * into one GtkTextBTreeNode or redivide the children among them to
5796 * balance their loads. As preparation, join their two
5797 * child lists into a single list and remember the half-way
5798 * point in the list.
5801 total_children = node->num_children + other->num_children;
5802 first_children = total_children/2;
5803 if (node->children.node == NULL)
5805 node->children = other->children;
5806 other->children.node = NULL;
5807 other->children.line = NULL;
5809 if (node->level == 0)
5813 for (line = node->children.line, i = 1;
5815 line = line->next, i++)
5817 if (i == first_children)
5822 line->next = other->children.line;
5823 while (i <= first_children)
5832 GtkTextBTreeNode *child;
5834 for (child = node->children.node, i = 1;
5835 child->next != NULL;
5836 child = child->next, i++)
5838 if (i <= first_children)
5840 if (i == first_children)
5842 halfwaynode = child;
5846 child->next = other->children.node;
5847 while (i <= first_children)
5849 halfwaynode = child;
5850 child = child->next;
5856 * If the two siblings can simply be merged together, do it.
5859 if (total_children <= MAX_CHILDREN)
5861 recompute_node_counts (tree, node);
5862 node->next = other->next;
5863 node->parent->num_children--;
5865 other->children.node = NULL;
5866 other->children.line = NULL;
5867 gtk_text_btree_node_free_empty (tree, other);
5872 * The siblings can't be merged, so just divide their
5873 * children evenly between them.
5876 if (node->level == 0)
5878 other->children.line = halfwayline->next;
5879 halfwayline->next = NULL;
5883 other->children.node = halfwaynode->next;
5884 halfwaynode->next = NULL;
5887 recompute_node_counts (tree, node);
5888 recompute_node_counts (tree, other);
5891 node = node->parent;
5896 post_insert_fixup (GtkTextBTree *tree,
5898 gint line_count_delta,
5899 gint char_count_delta)
5902 GtkTextBTreeNode *node;
5905 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5906 * point, then rebalance the tree if necessary.
5909 for (node = line->parent ; node != NULL;
5910 node = node->parent)
5912 node->num_lines += line_count_delta;
5913 node->num_chars += char_count_delta;
5915 node = line->parent;
5916 node->num_children += line_count_delta;
5918 if (node->num_children > MAX_CHILDREN)
5920 gtk_text_btree_rebalance (tree, node);
5923 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5924 _gtk_text_btree_check (tree);
5927 static GtkTextTagInfo*
5928 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5931 GtkTextTagInfo *info;
5935 list = tree->tag_infos;
5936 while (list != NULL)
5939 if (info->tag == tag)
5942 list = g_slist_next (list);
5948 static GtkTextTagInfo*
5949 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5952 GtkTextTagInfo *info;
5954 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5958 /* didn't find it, create. */
5960 info = g_slice_new (GtkTextTagInfo);
5964 info->tag_root = NULL;
5965 info->toggle_count = 0;
5967 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5970 g_print ("Created tag info %p for tag %s(%p)\n",
5971 info, info->tag->name ? info->tag->name : "anon",
5980 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5983 GtkTextTagInfo *info;
5988 list = tree->tag_infos;
5989 while (list != NULL)
5992 if (info->tag == tag)
5995 g_print ("Removing tag info %p for tag %s(%p)\n",
5996 info, info->tag->name ? info->tag->name : "anon",
6002 prev->next = list->next;
6006 tree->tag_infos = list->next;
6009 g_slist_free (list);
6011 g_object_unref (info->tag);
6013 g_slice_free (GtkTextTagInfo, info);
6018 list = g_slist_next (list);
6023 recompute_level_zero_counts (GtkTextBTreeNode *node)
6026 GtkTextLineSegment *seg;
6028 g_assert (node->level == 0);
6030 line = node->children.line;
6031 while (line != NULL)
6033 node->num_children++;
6036 if (line->parent != node)
6037 gtk_text_line_set_parent (line, node);
6039 seg = line->segments;
6043 node->num_chars += seg->char_count;
6045 if (((seg->type != >k_text_toggle_on_type)
6046 && (seg->type != >k_text_toggle_off_type))
6047 || !(seg->body.toggle.inNodeCounts))
6053 GtkTextTagInfo *info;
6055 info = seg->body.toggle.info;
6057 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6068 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6071 GtkTextBTreeNode *child;
6073 g_assert (node->level > 0);
6075 child = node->children.node;
6076 while (child != NULL)
6078 node->num_children += 1;
6079 node->num_lines += child->num_lines;
6080 node->num_chars += child->num_chars;
6082 if (child->parent != node)
6084 child->parent = node;
6085 gtk_text_btree_node_invalidate_upward (node, NULL);
6088 summary = child->summary;
6089 while (summary != NULL)
6091 gtk_text_btree_node_adjust_toggle_count (node,
6093 summary->toggle_count);
6095 summary = summary->next;
6098 child = child->next;
6103 *----------------------------------------------------------------------
6105 * recompute_node_counts --
6107 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6108 * (tags, child information, etc.) by scanning the information in
6109 * its descendants. This procedure is called during rebalancing
6110 * when a GtkTextBTreeNode's child structure has changed.
6116 * The tag counts for node are modified to reflect its current
6117 * child structure, as are its num_children, num_lines, num_chars fields.
6118 * Also, all of the childrens' parent fields are made to point
6121 *----------------------------------------------------------------------
6125 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6128 Summary *summary, *summary2;
6131 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6132 * the existing Summary records (most of them will probably be reused).
6135 summary = node->summary;
6136 while (summary != NULL)
6138 summary->toggle_count = 0;
6139 summary = summary->next;
6142 node->num_children = 0;
6143 node->num_lines = 0;
6144 node->num_chars = 0;
6147 * Scan through the children, adding the childrens' tag counts into
6148 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6152 if (node->level == 0)
6153 recompute_level_zero_counts (node);
6155 recompute_level_nonzero_counts (node);
6160 gtk_text_btree_node_check_valid (node, view->view_id);
6165 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6166 * records that still have a zero count, or that have all the toggles.
6167 * The GtkTextBTreeNode with the children that account for all the tags toggles
6168 * have no summary information, and they become the tag_root for the tag.
6172 for (summary = node->summary; summary != NULL; )
6174 if (summary->toggle_count > 0 &&
6175 summary->toggle_count < summary->info->toggle_count)
6177 if (node->level == summary->info->tag_root->level)
6180 * The tag's root GtkTextBTreeNode split and some toggles left.
6181 * The tag root must move up a level.
6183 summary->info->tag_root = node->parent;
6186 summary = summary->next;
6189 if (summary->toggle_count == summary->info->toggle_count)
6192 * A GtkTextBTreeNode merge has collected all the toggles under
6193 * one GtkTextBTreeNode. Push the root down to this level.
6195 summary->info->tag_root = node;
6197 if (summary2 != NULL)
6199 summary2->next = summary->next;
6200 summary_destroy (summary);
6201 summary = summary2->next;
6205 node->summary = summary->next;
6206 summary_destroy (summary);
6207 summary = node->summary;
6213 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6214 GtkTextTagInfo *info,
6215 gint delta) /* may be negative */
6217 Summary *summary, *prevPtr;
6218 GtkTextBTreeNode *node2Ptr;
6219 int rootLevel; /* Level of original tag root */
6221 info->toggle_count += delta;
6223 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6225 info->tag_root = node;
6230 * Note the level of the existing root for the tag so we can detect
6231 * if it needs to be moved because of the toggle count change.
6234 rootLevel = info->tag_root->level;
6237 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6238 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6242 for ( ; node != info->tag_root; node = node->parent)
6245 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6246 * perhaps all we have to do is adjust its count.
6249 for (prevPtr = NULL, summary = node->summary;
6251 prevPtr = summary, summary = summary->next)
6253 if (summary->info == info)
6258 if (summary != NULL)
6260 summary->toggle_count += delta;
6261 if (summary->toggle_count > 0 &&
6262 summary->toggle_count < info->toggle_count)
6266 if (summary->toggle_count != 0)
6269 * Should never find a GtkTextBTreeNode with max toggle count at this
6270 * point (there shouldn't have been a summary entry in the
6274 g_error ("%s: bad toggle count (%d) max (%d)",
6275 G_STRLOC, summary->toggle_count, info->toggle_count);
6279 * Zero toggle count; must remove this tag from the list.
6282 if (prevPtr == NULL)
6284 node->summary = summary->next;
6288 prevPtr->next = summary->next;
6290 summary_destroy (summary);
6295 * This tag isn't currently in the summary information list.
6298 if (rootLevel == node->level)
6302 * The old tag root is at the same level in the tree as this
6303 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6304 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6305 * as well as the old root (if not, we'll move it up again
6306 * the next time through the loop). To push it up one level
6307 * we copy the original toggle count into the summary
6308 * information at the old root and change the root to its
6309 * parent GtkTextBTreeNode.
6312 GtkTextBTreeNode *rootnode = info->tag_root;
6313 summary = g_slice_new (Summary);
6314 summary->info = info;
6315 summary->toggle_count = info->toggle_count - delta;
6316 summary->next = rootnode->summary;
6317 rootnode->summary = summary;
6318 rootnode = rootnode->parent;
6319 rootLevel = rootnode->level;
6320 info->tag_root = rootnode;
6322 summary = g_slice_new (Summary);
6323 summary->info = info;
6324 summary->toggle_count = delta;
6325 summary->next = node->summary;
6326 node->summary = summary;
6331 * If we've decremented the toggle count, then it may be necessary
6332 * to push the tag root down one or more levels.
6339 if (info->toggle_count == 0)
6341 info->tag_root = (GtkTextBTreeNode *) NULL;
6344 node = info->tag_root;
6345 while (node->level > 0)
6348 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6349 * toggles. If so, push the root down one level.
6352 for (node2Ptr = node->children.node;
6353 node2Ptr != (GtkTextBTreeNode *)NULL ;
6354 node2Ptr = node2Ptr->next)
6356 for (prevPtr = NULL, summary = node2Ptr->summary;
6358 prevPtr = summary, summary = summary->next)
6360 if (summary->info == info)
6365 if (summary == NULL)
6369 if (summary->toggle_count != info->toggle_count)
6372 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6379 * This GtkTextBTreeNode has all the toggles, so push down the root.
6382 if (prevPtr == NULL)
6384 node2Ptr->summary = summary->next;
6388 prevPtr->next = summary->next;
6390 summary_destroy (summary);
6391 info->tag_root = node2Ptr;
6394 node = info->tag_root;
6399 *----------------------------------------------------------------------
6403 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6404 * increments the count for a particular tag, adding a new
6405 * entry for that tag if there wasn't one previously.
6411 * The information at *tagInfoPtr may be modified, and the arrays
6412 * may be reallocated to make them larger.
6414 *----------------------------------------------------------------------
6418 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6423 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6424 count > 0; tag_p++, count--)
6428 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6434 * There isn't currently an entry for this tag, so we have to
6435 * make a new one. If the arrays are full, then enlarge the
6439 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6441 GtkTextTag **newTags;
6442 int *newCounts, newSize;
6444 newSize = 2*tagInfoPtr->arraySize;
6445 newTags = (GtkTextTag **) g_malloc ((unsigned)
6446 (newSize*sizeof (GtkTextTag *)));
6447 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6448 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6449 g_free ((char *) tagInfoPtr->tags);
6450 tagInfoPtr->tags = newTags;
6451 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6452 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6453 tagInfoPtr->arraySize *sizeof (int));
6454 g_free ((char *) tagInfoPtr->counts);
6455 tagInfoPtr->counts = newCounts;
6456 tagInfoPtr->arraySize = newSize;
6459 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6460 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6461 tagInfoPtr->numTags++;
6465 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6466 const GtkTextIter *iter)
6468 GtkTextLineSegment *prev;
6472 line = _gtk_text_iter_get_text_line (iter);
6473 tree = _gtk_text_iter_get_btree (iter);
6475 prev = gtk_text_line_segment_split (iter);
6478 seg->next = line->segments;
6479 line->segments = seg;
6483 seg->next = prev->next;
6486 cleanup_line (line);
6487 segments_changed (tree);
6489 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6490 _gtk_text_btree_check (tree);
6494 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6495 GtkTextLineSegment *seg,
6498 GtkTextLineSegment *prev;
6500 if (line->segments == seg)
6502 line->segments = seg->next;
6506 for (prev = line->segments; prev->next != seg;
6509 /* Empty loop body. */
6511 prev->next = seg->next;
6513 cleanup_line (line);
6514 segments_changed (tree);
6518 * This is here because it requires BTree internals, it logically
6519 * belongs in gtktextsegment.c
6524 *--------------------------------------------------------------
6526 * _gtk_toggle_segment_check_func --
6528 * This procedure is invoked to perform consistency checks
6529 * on toggle segments.
6535 * If a consistency problem is found the procedure g_errors.
6537 *--------------------------------------------------------------
6541 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6547 if (segPtr->byte_count != 0)
6549 g_error ("toggle_segment_check_func: segment had non-zero size");
6551 if (!segPtr->body.toggle.inNodeCounts)
6553 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6555 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6556 for (summary = line->parent->summary; ;
6557 summary = summary->next)
6559 if (summary == NULL)
6563 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6570 if (summary->info == segPtr->body.toggle.info)
6574 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6586 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6587 GtkTextBTreeNode *node,
6597 while (view != NULL)
6599 if (view->view_id == nd->view_id)
6606 g_error ("Node has data for a view %p no longer attached to the tree",
6609 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6610 &width, &height, &valid);
6612 /* valid aggregate not checked the same as width/height, because on
6613 * btree rebalance we can have invalid nodes where all lines below
6614 * them are actually valid, due to moving lines around between
6617 * The guarantee is that if there are invalid lines the node is
6618 * invalid - we don't guarantee that if the node is invalid there
6619 * are invalid lines.
6622 if (nd->width != width ||
6623 nd->height != height ||
6624 (nd->valid && !valid))
6626 g_error ("Node aggregates for view %p are invalid:\n"
6627 "Are (%d,%d,%s), should be (%d,%d,%s)",
6629 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6630 width, height, valid ? "TRUE" : "FALSE");
6635 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6636 GtkTextBTreeNode *node)
6638 GtkTextBTreeNode *childnode;
6639 Summary *summary, *summary2;
6641 GtkTextLineSegment *segPtr;
6642 int num_children, num_lines, num_chars, toggle_count, min_children;
6643 GtkTextLineData *ld;
6646 if (node->parent != NULL)
6648 min_children = MIN_CHILDREN;
6650 else if (node->level > 0)
6657 if ((node->num_children < min_children)
6658 || (node->num_children > MAX_CHILDREN))
6660 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6661 node->num_children);
6664 nd = node->node_data;
6667 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6674 if (node->level == 0)
6676 for (line = node->children.line; line != NULL;
6679 if (line->parent != node)
6681 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6683 if (line->segments == NULL)
6685 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6691 /* Just ensuring we don't segv while doing this loop */
6696 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6698 if (segPtr->type->checkFunc != NULL)
6700 (*segPtr->type->checkFunc)(segPtr, line);
6702 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6703 && (segPtr->next != NULL)
6704 && (segPtr->next->byte_count == 0)
6705 && (segPtr->next->type->leftGravity))
6707 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6709 if ((segPtr->next == NULL)
6710 && (segPtr->type != >k_text_char_type))
6712 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6715 num_chars += segPtr->char_count;
6724 for (childnode = node->children.node; childnode != NULL;
6725 childnode = childnode->next)
6727 if (childnode->parent != node)
6729 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6731 if (childnode->level != (node->level-1))
6733 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6734 node->level, childnode->level);
6736 gtk_text_btree_node_check_consistency (tree, childnode);
6737 for (summary = childnode->summary; summary != NULL;
6738 summary = summary->next)
6740 for (summary2 = node->summary; ;
6741 summary2 = summary2->next)
6743 if (summary2 == NULL)
6745 if (summary->info->tag_root == node)
6749 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6750 summary->info->tag->name,
6751 "present in parent summaries");
6753 if (summary->info == summary2->info)
6760 num_lines += childnode->num_lines;
6761 num_chars += childnode->num_chars;
6764 if (num_children != node->num_children)
6766 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6767 num_children, node->num_children);
6769 if (num_lines != node->num_lines)
6771 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6772 num_lines, node->num_lines);
6774 if (num_chars != node->num_chars)
6776 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6777 num_chars, node->num_chars);
6780 for (summary = node->summary; summary != NULL;
6781 summary = summary->next)
6783 if (summary->info->toggle_count == summary->toggle_count)
6785 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6786 summary->info->tag->name);
6789 if (node->level == 0)
6791 for (line = node->children.line; line != NULL;
6794 for (segPtr = line->segments; segPtr != NULL;
6795 segPtr = segPtr->next)
6797 if ((segPtr->type != >k_text_toggle_on_type)
6798 && (segPtr->type != >k_text_toggle_off_type))
6802 if (segPtr->body.toggle.info == summary->info)
6804 if (!segPtr->body.toggle.inNodeCounts)
6805 g_error ("Toggle segment not in the node counts");
6814 for (childnode = node->children.node;
6816 childnode = childnode->next)
6818 for (summary2 = childnode->summary;
6820 summary2 = summary2->next)
6822 if (summary2->info == summary->info)
6824 toggle_count += summary2->toggle_count;
6829 if (toggle_count != summary->toggle_count)
6831 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6832 toggle_count, summary->toggle_count);
6834 for (summary2 = summary->next; summary2 != NULL;
6835 summary2 = summary2->next)
6837 if (summary2->info == summary->info)
6839 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6840 summary->info->tag->name);
6847 listify_foreach (GtkTextTag *tag, gpointer user_data)
6849 GSList** listp = user_data;
6851 *listp = g_slist_prepend (*listp, tag);
6855 list_of_tags (GtkTextTagTable *table)
6857 GSList *list = NULL;
6859 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6865 _gtk_text_btree_check (GtkTextBTree *tree)
6868 GtkTextBTreeNode *node;
6870 GtkTextLineSegment *seg;
6872 GSList *all_tags, *taglist = NULL;
6874 GtkTextTagInfo *info;
6877 * Make sure that the tag toggle counts and the tag root pointers are OK.
6879 all_tags = list_of_tags (tree->table);
6880 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6882 tag = taglist->data;
6883 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6886 node = info->tag_root;
6889 if (info->toggle_count != 0)
6891 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6892 tag->name, info->toggle_count);
6894 continue; /* no ranges for the tag */
6896 else if (info->toggle_count == 0)
6898 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6901 else if (info->toggle_count & 1)
6903 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6904 tag->name, info->toggle_count);
6906 for (summary = node->summary; summary != NULL;
6907 summary = summary->next)
6909 if (summary->info->tag == tag)
6911 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6915 if (node->level > 0)
6917 for (node = node->children.node ; node != NULL ;
6920 for (summary = node->summary; summary != NULL;
6921 summary = summary->next)
6923 if (summary->info->tag == tag)
6925 count += summary->toggle_count;
6932 const GtkTextLineSegmentClass *last = NULL;
6934 for (line = node->children.line ; line != NULL ;
6937 for (seg = line->segments; seg != NULL;
6940 if ((seg->type == >k_text_toggle_on_type ||
6941 seg->type == >k_text_toggle_off_type) &&
6942 seg->body.toggle.info->tag == tag)
6944 if (last == seg->type)
6945 g_error ("Two consecutive toggles on or off weren't merged");
6946 if (!seg->body.toggle.inNodeCounts)
6947 g_error ("Toggle segment not in the node counts");
6956 if (count != info->toggle_count)
6958 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6959 info->toggle_count, tag->name, count);
6964 g_slist_free (all_tags);
6967 * Call a recursive procedure to do the main body of checks.
6970 node = tree->root_node;
6971 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6974 * Make sure that there are at least two lines in the text and
6975 * that the last line has no characters except a newline.
6978 if (node->num_lines < 2)
6980 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6982 if (node->num_chars < 2)
6984 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6986 while (node->level > 0)
6988 node = node->children.node;
6989 while (node->next != NULL)
6994 line = node->children.line;
6995 while (line->next != NULL)
6999 seg = line->segments;
7000 while ((seg->type == >k_text_toggle_off_type)
7001 || (seg->type == >k_text_right_mark_type)
7002 || (seg->type == >k_text_left_mark_type))
7005 * It's OK to toggle a tag off in the last line, but
7006 * not to start a new range. It's also OK to have marks
7012 if (seg->type != >k_text_char_type)
7014 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7016 if (seg->next != NULL)
7018 g_error ("_gtk_text_btree_check: last line has too many segments");
7020 if (seg->byte_count != 1)
7022 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7025 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7027 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7032 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7033 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7034 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7035 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7038 _gtk_text_btree_spew (GtkTextBTree *tree)
7043 printf ("%d lines in tree %p\n",
7044 _gtk_text_btree_line_count (tree), tree);
7046 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7048 while (line != NULL)
7050 _gtk_text_btree_spew_line (tree, line);
7051 line = _gtk_text_line_next (line);
7054 printf ("=================== Tag information\n");
7059 list = tree->tag_infos;
7061 while (list != NULL)
7063 GtkTextTagInfo *info;
7067 printf (" tag `%s': root at %p, toggle count %d\n",
7068 info->tag->name, info->tag_root, info->toggle_count);
7070 list = g_slist_next (list);
7073 if (tree->tag_infos == NULL)
7075 printf (" (no tags in the tree)\n");
7079 printf ("=================== Tree nodes\n");
7082 _gtk_text_btree_spew_node (tree->root_node, 0);
7087 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7090 GtkTextLineSegment *seg;
7092 spaces = g_strnfill (indent, ' ');
7094 printf ("%sline %p chars %d bytes %d\n",
7096 _gtk_text_line_char_count (line),
7097 _gtk_text_line_byte_count (line));
7099 seg = line->segments;
7102 if (seg->type == >k_text_char_type)
7104 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7109 if (*s == '\n' || *s == '\r')
7113 printf ("%s chars `%s'...\n", spaces, str);
7116 else if (seg->type == >k_text_right_mark_type)
7118 printf ("%s right mark `%s' visible: %d\n",
7120 seg->body.mark.name,
7121 seg->body.mark.visible);
7123 else if (seg->type == >k_text_left_mark_type)
7125 printf ("%s left mark `%s' visible: %d\n",
7127 seg->body.mark.name,
7128 seg->body.mark.visible);
7130 else if (seg->type == >k_text_toggle_on_type ||
7131 seg->type == >k_text_toggle_off_type)
7133 printf ("%s tag `%s' %s\n",
7134 spaces, seg->body.toggle.info->tag->name,
7135 seg->type == >k_text_toggle_off_type ? "off" : "on");
7145 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7148 GtkTextBTreeNode *iter;
7151 spaces = g_strnfill (indent, ' ');
7153 printf ("%snode %p level %d children %d lines %d chars %d\n",
7154 spaces, node, node->level,
7155 node->num_children, node->num_lines, node->num_chars);
7160 printf ("%s %d toggles of `%s' below this node\n",
7161 spaces, s->toggle_count, s->info->tag->name);
7167 if (node->level > 0)
7169 iter = node->children.node;
7170 while (iter != NULL)
7172 _gtk_text_btree_spew_node (iter, indent + 2);
7179 GtkTextLine *line = node->children.line;
7180 while (line != NULL)
7182 _gtk_text_btree_spew_line_short (line, indent + 2);
7190 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7192 GtkTextLineSegment * seg;
7194 printf ("%4d| line: %p parent: %p next: %p\n",
7195 _gtk_text_line_get_number (line), line, line->parent, line->next);
7197 seg = line->segments;
7201 _gtk_text_btree_spew_segment (tree, seg);
7207 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7209 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7210 seg, seg->type->name, seg->byte_count, seg->char_count);
7212 if (seg->type == >k_text_char_type)
7214 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7215 printf (" `%s'\n", str);
7218 else if (seg->type == >k_text_right_mark_type)
7220 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7221 seg->body.mark.name,
7222 seg->body.mark.visible,
7223 seg->body.mark.not_deleteable);
7225 else if (seg->type == >k_text_left_mark_type)
7227 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7228 seg->body.mark.name,
7229 seg->body.mark.visible,
7230 seg->body.mark.not_deleteable);
7232 else if (seg->type == >k_text_toggle_on_type ||
7233 seg->type == >k_text_toggle_off_type)
7235 printf (" tag `%s' priority %d\n",
7236 seg->body.toggle.info->tag->name,
7237 seg->body.toggle.info->tag->priority);
7241 #define __GTK_TEXT_BTREE_C__
7242 #include "gtkaliasdef.c"