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;
741 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
742 gint start_byte_offset;
744 g_return_if_fail (start != NULL);
745 g_return_if_fail (end != NULL);
746 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
747 _gtk_text_iter_get_btree (end));
749 gtk_text_iter_order (start, end);
751 tree = _gtk_text_iter_get_btree (start);
753 if (gtk_debug_flags & GTK_DEBUG_TEXT)
754 _gtk_text_btree_check (tree);
756 /* Broadcast the need for redisplay before we break the iterators */
757 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
758 _gtk_text_btree_invalidate_region (tree, start, end);
760 /* Save the byte offset so we can reset the iterators */
761 start_byte_offset = gtk_text_iter_get_line_index (start);
763 start_line = _gtk_text_iter_get_text_line (start);
764 end_line = _gtk_text_iter_get_text_line (end);
767 * Split the start and end segments, so we have a place
768 * to insert our new text.
770 * Tricky point: split at end first; otherwise the split
771 * at end may invalidate seg and/or prev_seg. This allows
772 * us to avoid invalidating segments for start.
775 last_seg = gtk_text_line_segment_split (end);
776 if (last_seg != NULL)
777 last_seg = last_seg->next;
779 last_seg = end_line->segments;
781 prev_seg = gtk_text_line_segment_split (start);
782 if (prev_seg != NULL)
784 seg = prev_seg->next;
785 prev_seg->next = last_seg;
789 seg = start_line->segments;
790 start_line->segments = last_seg;
793 /* notify iterators that their segments need recomputation,
794 just for robustness. */
795 segments_changed (tree);
798 * Delete all of the segments between prev_seg and last_seg.
801 curline = start_line;
802 curnode = curline->parent;
803 while (seg != last_seg)
809 GtkTextLine *nextline;
812 * We just ran off the end of a line. First find the
813 * next line, then go back to the old line and delete it
814 * (unless it's the starting line for the range).
817 nextline = _gtk_text_line_next (curline);
818 if (curline != start_line)
820 if (curnode == start_line->parent)
821 start_line->next = curline->next;
823 curnode->children.line = curline->next;
825 for (node = curnode; node != NULL;
828 /* Don't update node->num_chars, because
829 * that was done when we deleted the segments.
831 node->num_lines -= 1;
834 curnode->num_children -= 1;
835 curline->next = deleted_lines;
836 deleted_lines = curline;
840 seg = curline->segments;
843 * If the GtkTextBTreeNode is empty then delete it and its parents,
844 * recursively upwards until a non-empty GtkTextBTreeNode is found.
847 while (curnode->num_children == 0)
849 GtkTextBTreeNode *parent;
851 parent = curnode->parent;
852 if (parent->children.node == curnode)
854 parent->children.node = curnode->next;
858 GtkTextBTreeNode *prevnode = parent->children.node;
859 while (prevnode->next != curnode)
861 prevnode = prevnode->next;
863 prevnode->next = curnode->next;
865 parent->num_children--;
866 gtk_text_btree_node_free_empty (tree, curnode);
869 curnode = curline->parent;
874 char_count = seg->char_count;
876 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
879 * This segment refuses to die. Move it to prev_seg and
880 * advance prev_seg if the segment has left gravity.
883 if (prev_seg == NULL)
885 seg->next = start_line->segments;
886 start_line->segments = seg;
888 else if (prev_seg->next &&
889 prev_seg->next != last_seg &&
890 seg->type == >k_text_toggle_off_type &&
891 prev_seg->next->type == >k_text_toggle_on_type &&
892 seg->body.toggle.info == prev_seg->next->body.toggle.info)
894 /* Try to match an off toggle with the matching on toggle
895 * if it immediately follows. This is a common case, and
896 * handling it here prevents quadratic blowup in
897 * cleanup_line() below. See bug 317125.
899 next2 = prev_seg->next->next;
900 g_free ((char *)prev_seg->next);
901 prev_seg->next = next2;
902 g_free ((char *)seg);
907 seg->next = prev_seg->next;
908 prev_seg->next = seg;
911 if (seg && seg->type->leftGravity)
918 /* Segment is gone. Decrement the char count of the node and
920 for (node = curnode; node != NULL;
923 node->num_chars -= char_count;
931 * If the beginning and end of the deletion range are in different
932 * lines, join the two lines together and discard the ending line.
935 if (start_line != end_line)
938 GtkTextBTreeNode *ancestor_node;
939 GtkTextLine *prevline;
942 /* last_seg was appended to start_line up at the top of this function */
944 for (seg = last_seg; seg != NULL;
947 chars_moved += seg->char_count;
948 if (seg->type->lineChangeFunc != NULL)
950 (*seg->type->lineChangeFunc)(seg, end_line);
954 for (node = start_line->parent; node != NULL;
957 node->num_chars += chars_moved;
960 curnode = end_line->parent;
961 for (node = curnode; node != NULL;
964 node->num_chars -= chars_moved;
967 curnode->num_children--;
968 prevline = curnode->children.line;
969 if (prevline == end_line)
971 curnode->children.line = end_line->next;
975 while (prevline->next != end_line)
977 prevline = prevline->next;
979 prevline->next = end_line->next;
981 end_line->next = deleted_lines;
982 deleted_lines = end_line;
984 /* We now fix up the per-view aggregates. We add all the height and
985 * width for the deleted lines to the start line, so that when revalidation
986 * occurs, the correct change in size is seen.
988 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;
1011 gtk_text_line_destroy (tree, line);
1016 if (deleted_width > 0 || deleted_height > 0)
1018 ld = _gtk_text_line_get_data (start_line, view->view_id);
1022 /* This means that start_line has never been validated.
1023 * We don't really want to do the validation here but
1024 * we do need to store our temporary sizes. So we
1025 * create the line data and assume a line w/h of 0.
1027 ld = _gtk_text_line_data_new (view->layout, start_line);
1028 _gtk_text_line_add_data (start_line, ld);
1034 ld->width = MAX (deleted_width, ld->width);
1035 ld->height += deleted_height;
1039 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1040 if (ancestor_node->parent)
1041 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1046 /* avoid dangling pointer */
1047 deleted_lines = NULL;
1049 gtk_text_btree_rebalance (tree, curnode);
1053 * Cleanup the segments in the new line.
1056 cleanup_line (start_line);
1059 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1062 gtk_text_btree_rebalance (tree, start_line->parent);
1064 /* Notify outstanding iterators that they
1066 chars_changed (tree);
1067 segments_changed (tree);
1069 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1070 _gtk_text_btree_check (tree);
1072 /* Re-initialize our iterators */
1073 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1076 gtk_text_btree_resolve_bidi (start, end);
1080 _gtk_text_btree_insert (GtkTextIter *iter,
1084 GtkTextLineSegment *prev_seg; /* The segment just before the first
1085 * new segment (NULL means new segment
1086 * is at beginning of line). */
1087 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1088 * are inserted just after this one.
1089 * NULL means insert at beginning of
1091 GtkTextLine *line; /* Current line (new segments are
1092 * added to this line). */
1093 GtkTextLineSegment *seg;
1094 GtkTextLine *newline;
1095 int chunk_len; /* # characters in current chunk. */
1096 gint sol; /* start of line */
1097 gint eol; /* Pointer to character just after last
1098 * one in current chunk.
1100 gint delim; /* index of paragraph delimiter */
1101 int line_count_delta; /* Counts change to total number of
1105 int char_count_delta; /* change to number of chars */
1107 gint start_byte_index;
1108 GtkTextLine *start_line;
1110 g_return_if_fail (text != NULL);
1111 g_return_if_fail (iter != NULL);
1114 len = strlen (text);
1116 /* extract iterator info */
1117 tree = _gtk_text_iter_get_btree (iter);
1118 line = _gtk_text_iter_get_text_line (iter);
1121 start_byte_index = gtk_text_iter_get_line_index (iter);
1123 /* Get our insertion segment split. Note this assumes line allows
1124 * char insertions, which isn't true of the "last" line. But iter
1125 * should not be on that line, as we assert here.
1127 g_assert (!_gtk_text_line_is_last (line, tree));
1128 prev_seg = gtk_text_line_segment_split (iter);
1131 /* Invalidate all iterators */
1132 chars_changed (tree);
1133 segments_changed (tree);
1136 * Chop the text up into lines and create a new segment for
1137 * each line, plus a new line for the leftovers from the
1143 line_count_delta = 0;
1144 char_count_delta = 0;
1149 pango_find_paragraph_boundary (text + sol,
1154 /* make these relative to the start of the text */
1158 g_assert (eol >= sol);
1159 g_assert (delim >= sol);
1160 g_assert (eol >= delim);
1161 g_assert (sol >= 0);
1162 g_assert (eol <= len);
1164 chunk_len = eol - sol;
1166 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1167 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1169 char_count_delta += seg->char_count;
1171 if (cur_seg == NULL)
1173 seg->next = line->segments;
1174 line->segments = seg;
1178 seg->next = cur_seg->next;
1179 cur_seg->next = seg;
1184 /* chunk didn't end with a paragraph separator */
1185 g_assert (eol == len);
1190 * The chunk ended with a newline, so create a new GtkTextLine
1191 * and move the remainder of the old line to it.
1194 newline = gtk_text_line_new ();
1195 gtk_text_line_set_parent (newline, line->parent);
1196 newline->next = line->next;
1197 line->next = newline;
1198 newline->segments = seg->next;
1206 * Cleanup the starting line for the insertion, plus the ending
1207 * line if it's different.
1210 cleanup_line (start_line);
1211 if (line != start_line)
1213 cleanup_line (line);
1216 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1218 /* Invalidate our region, and reset the iterator the user
1219 passed in to point to the end of the inserted text. */
1225 _gtk_text_btree_get_iter_at_line (tree,
1231 /* We could almost certainly be more efficient here
1232 by saving the information from the insertion loop
1234 gtk_text_iter_forward_chars (&end, char_count_delta);
1236 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1237 _gtk_text_btree_invalidate_region (tree,
1241 /* Convenience for the user */
1244 gtk_text_btree_resolve_bidi (&start, &end);
1249 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1250 GtkTextLineSegment *seg)
1254 GtkTextLineSegment *prevPtr;
1257 gint start_byte_offset;
1259 line = _gtk_text_iter_get_text_line (iter);
1260 tree = _gtk_text_iter_get_btree (iter);
1261 start_byte_offset = gtk_text_iter_get_line_index (iter);
1263 prevPtr = gtk_text_line_segment_split (iter);
1264 if (prevPtr == NULL)
1266 seg->next = line->segments;
1267 line->segments = seg;
1271 seg->next = prevPtr->next;
1272 prevPtr->next = seg;
1275 post_insert_fixup (tree, line, 0, seg->char_count);
1277 chars_changed (tree);
1278 segments_changed (tree);
1280 /* reset *iter for the user, and invalidate tree nodes */
1282 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1285 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1287 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1288 _gtk_text_btree_invalidate_region (tree, &start, iter);
1292 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1295 GtkTextLineSegment *seg;
1297 seg = _gtk_pixbuf_segment_new (pixbuf);
1299 insert_pixbuf_or_widget_segment (iter, seg);
1303 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1304 GtkTextChildAnchor *anchor)
1306 GtkTextLineSegment *seg;
1309 if (anchor->segment != NULL)
1311 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1315 seg = _gtk_widget_segment_new (anchor);
1317 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1318 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1320 insert_pixbuf_or_widget_segment (iter, seg);
1322 if (tree->child_anchor_table == NULL)
1323 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1325 g_hash_table_insert (tree->child_anchor_table,
1326 seg->body.child.obj,
1327 seg->body.child.obj);
1331 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1333 GtkTextLineSegment *seg;
1335 seg = anchor->segment;
1337 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1346 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1347 GtkTextBTreeNode *node, gint y, gint *line_top,
1348 GtkTextLine *last_line)
1352 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1353 _gtk_text_btree_check (tree);
1355 if (node->level == 0)
1359 line = node->children.line;
1361 while (line != NULL && line != last_line)
1363 GtkTextLineData *ld;
1365 ld = _gtk_text_line_get_data (line, view->view_id);
1369 if (y < (current_y + (ld ? ld->height : 0)))
1372 current_y += ld->height;
1373 *line_top += ld->height;
1382 GtkTextBTreeNode *child;
1384 child = node->children.node;
1386 while (child != NULL)
1391 gtk_text_btree_node_get_size (child, view->view_id,
1394 if (y < (current_y + height))
1395 return find_line_by_y (tree, view, child,
1396 y - current_y, line_top,
1399 current_y += height;
1400 *line_top += height;
1402 child = child->next;
1410 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1417 GtkTextLine *last_line;
1420 view = gtk_text_btree_get_view (tree, view_id);
1421 g_return_val_if_fail (view != NULL, NULL);
1423 last_line = get_last_line (tree);
1425 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1429 *line_top_out = line_top;
1435 find_line_top_in_line_list (GtkTextBTree *tree,
1438 GtkTextLine *target_line,
1441 while (line != NULL)
1443 GtkTextLineData *ld;
1445 if (line == target_line)
1448 ld = _gtk_text_line_get_data (line, view->view_id);
1455 g_assert_not_reached (); /* If we get here, our
1456 target line didn't exist
1457 under its parent node */
1462 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1463 GtkTextLine *target_line,
1470 GtkTextBTreeNode *node;
1472 view = gtk_text_btree_get_view (tree, view_id);
1474 g_return_val_if_fail (view != NULL, 0);
1477 node = target_line->parent;
1478 while (node != NULL)
1480 nodes = g_slist_prepend (nodes, node);
1481 node = node->parent;
1485 while (iter != NULL)
1489 if (node->level == 0)
1491 g_slist_free (nodes);
1492 return find_line_top_in_line_list (tree, view,
1493 node->children.line,
1498 GtkTextBTreeNode *child;
1499 GtkTextBTreeNode *target_node;
1501 g_assert (iter->next != NULL); /* not at level 0 */
1502 target_node = iter->next->data;
1504 child = node->children.node;
1506 while (child != NULL)
1511 if (child == target_node)
1515 gtk_text_btree_node_get_size (child, view->view_id,
1519 child = child->next;
1521 g_assert (child != NULL); /* should have broken out before we
1525 iter = g_slist_next (iter);
1528 g_assert_not_reached (); /* we return when we find the target line */
1533 _gtk_text_btree_add_view (GtkTextBTree *tree,
1534 GtkTextLayout *layout)
1537 GtkTextLine *last_line;
1538 GtkTextLineData *line_data;
1540 g_return_if_fail (tree != NULL);
1542 view = g_new (BTreeView, 1);
1544 view->view_id = layout;
1545 view->layout = layout;
1547 view->next = tree->views;
1552 g_assert (tree->views->prev == NULL);
1553 tree->views->prev = view;
1558 /* The last line in the buffer has identity values for the per-view
1559 * data so that we can avoid special case checks for it in a large
1562 last_line = get_last_line (tree);
1564 line_data = g_new (GtkTextLineData, 1);
1565 line_data->view_id = layout;
1566 line_data->next = NULL;
1567 line_data->width = 0;
1568 line_data->height = 0;
1569 line_data->valid = TRUE;
1571 _gtk_text_line_add_data (last_line, line_data);
1575 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1579 GtkTextLine *last_line;
1580 GtkTextLineData *line_data;
1582 g_return_if_fail (tree != NULL);
1586 while (view != NULL)
1588 if (view->view_id == view_id)
1594 g_return_if_fail (view != NULL);
1597 view->next->prev = view->prev;
1600 view->prev->next = view->next;
1602 if (view == tree->views)
1603 tree->views = view->next;
1605 /* Remove the line data for the last line which we added ourselves.
1606 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1608 last_line = get_last_line (tree);
1609 line_data = _gtk_text_line_remove_data (last_line, view_id);
1612 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1614 view->layout = (gpointer) 0xdeadbeef;
1615 view->view_id = (gpointer) 0xdeadbeef;
1621 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1622 const GtkTextIter *start,
1623 const GtkTextIter *end)
1629 while (view != NULL)
1631 gtk_text_layout_invalidate (view->layout, start, end);
1638 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1643 g_return_if_fail (tree != NULL);
1644 g_return_if_fail (view_id != NULL);
1646 gtk_text_btree_node_get_size (tree->root_node, view_id,
1661 iter_stack_new (void)
1664 stack = g_slice_new (IterStack);
1665 stack->iters = NULL;
1672 iter_stack_push (IterStack *stack,
1673 const GtkTextIter *iter)
1676 if (stack->count > stack->alloced)
1678 stack->alloced = stack->count*2;
1679 stack->iters = g_realloc (stack->iters,
1680 stack->alloced * sizeof (GtkTextIter));
1682 stack->iters[stack->count-1] = *iter;
1686 iter_stack_pop (IterStack *stack,
1689 if (stack->count == 0)
1694 *iter = stack->iters[stack->count];
1700 iter_stack_free (IterStack *stack)
1702 g_free (stack->iters);
1703 g_slice_free (IterStack, stack);
1707 iter_stack_invert (IterStack *stack)
1709 if (stack->count > 0)
1712 guint j = stack->count - 1;
1717 tmp = stack->iters[i];
1718 stack->iters[i] = stack->iters[j];
1719 stack->iters[j] = tmp;
1728 queue_tag_redisplay (GtkTextBTree *tree,
1730 const GtkTextIter *start,
1731 const GtkTextIter *end)
1733 if (_gtk_text_tag_affects_size (tag))
1735 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1736 _gtk_text_btree_invalidate_region (tree, start, end);
1738 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1740 /* We only need to queue a redraw, not a relayout */
1741 redisplay_region (tree, start, end);
1744 /* We don't need to do anything if the tag doesn't affect display */
1748 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1749 const GtkTextIter *end_orig,
1753 GtkTextLineSegment *seg, *prev;
1754 GtkTextLine *cleanupline;
1755 gboolean toggled_on;
1756 GtkTextLine *start_line;
1757 GtkTextLine *end_line;
1759 GtkTextIter start, end;
1762 GtkTextTagInfo *info;
1764 g_return_if_fail (start_orig != NULL);
1765 g_return_if_fail (end_orig != NULL);
1766 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1767 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1768 _gtk_text_iter_get_btree (end_orig));
1769 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1772 printf ("%s tag %s from %d to %d\n",
1773 add ? "Adding" : "Removing",
1775 gtk_text_buffer_get_offset (start_orig),
1776 gtk_text_buffer_get_offset (end_orig));
1779 if (gtk_text_iter_equal (start_orig, end_orig))
1782 start = *start_orig;
1785 gtk_text_iter_order (&start, &end);
1787 tree = _gtk_text_iter_get_btree (&start);
1789 queue_tag_redisplay (tree, tag, &start, &end);
1791 info = gtk_text_btree_get_tag_info (tree, tag);
1793 start_line = _gtk_text_iter_get_text_line (&start);
1794 end_line = _gtk_text_iter_get_text_line (&end);
1796 /* Find all tag toggles in the region; we are going to delete them.
1797 We need to find them in advance, because
1798 forward_find_tag_toggle () won't work once we start playing around
1800 stack = iter_stack_new ();
1803 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1804 * which is deliberate - we don't want to delete a toggle at the
1807 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1809 if (gtk_text_iter_compare (&iter, &end) >= 0)
1812 iter_stack_push (stack, &iter);
1815 /* We need to traverse the toggles in order. */
1816 iter_stack_invert (stack);
1819 * See whether the tag is present at the start of the range. If
1820 * the state doesn't already match what we want then add a toggle
1824 toggled_on = gtk_text_iter_has_tag (&start, tag);
1825 if ( (add && !toggled_on) ||
1826 (!add && toggled_on) )
1828 /* This could create a second toggle at the start position;
1829 cleanup_line () will remove it if so. */
1830 seg = _gtk_toggle_segment_new (info, add);
1832 prev = gtk_text_line_segment_split (&start);
1835 seg->next = start_line->segments;
1836 start_line->segments = seg;
1840 seg->next = prev->next;
1844 /* cleanup_line adds the new toggle to the node counts. */
1846 printf ("added toggle at start\n");
1848 /* we should probably call segments_changed, but in theory
1849 any still-cached segments in the iters we are about to
1850 use are still valid, since they're in front
1856 * Scan the range of characters and delete any internal tag
1857 * transitions. Keep track of what the old state was at the end
1858 * of the range, and add a toggle there if it's needed.
1862 cleanupline = start_line;
1863 while (iter_stack_pop (stack, &iter))
1865 GtkTextLineSegment *indexable_seg;
1868 line = _gtk_text_iter_get_text_line (&iter);
1869 seg = _gtk_text_iter_get_any_segment (&iter);
1870 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1872 g_assert (seg != NULL);
1873 g_assert (indexable_seg != NULL);
1874 g_assert (seg != indexable_seg);
1876 prev = line->segments;
1878 /* Find the segment that actually toggles this tag. */
1879 while (seg != indexable_seg)
1881 g_assert (seg != NULL);
1882 g_assert (indexable_seg != NULL);
1883 g_assert (seg != indexable_seg);
1885 if ( (seg->type == >k_text_toggle_on_type ||
1886 seg->type == >k_text_toggle_off_type) &&
1887 (seg->body.toggle.info == info) )
1893 g_assert (seg != NULL);
1894 g_assert (indexable_seg != NULL);
1896 g_assert (seg != indexable_seg); /* If this happens, then
1897 forward_to_tag_toggle was
1899 g_assert (seg->body.toggle.info->tag == tag);
1901 /* If this happens, when previously tagging we didn't merge
1902 overlapping tags. */
1903 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1904 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1906 toggled_on = !toggled_on;
1909 printf ("deleting %s toggle\n",
1910 seg->type == >k_text_toggle_on_type ? "on" : "off");
1912 /* Remove toggle segment from the list. */
1915 line->segments = seg->next;
1919 while (prev->next != seg)
1923 prev->next = seg->next;
1926 /* Inform iterators we've hosed them. This actually reflects a
1927 bit of inefficiency; if you have the same tag toggled on and
1928 off a lot in a single line, we keep having the rescan from
1929 the front of the line. Of course we have to do that to get
1930 "prev" anyway, but here we are doing it an additional
1932 segments_changed (tree);
1934 /* Update node counts */
1935 if (seg->body.toggle.inNodeCounts)
1937 _gtk_change_node_toggle_count (line->parent,
1939 seg->body.toggle.inNodeCounts = FALSE;
1944 /* We only clean up lines when we're done with them, saves some
1945 gratuitous line-segment-traversals */
1947 if (cleanupline != line)
1949 cleanup_line (cleanupline);
1954 iter_stack_free (stack);
1956 /* toggled_on now reflects the toggle state _just before_ the
1957 end iterator. The end iterator could already have a toggle
1958 on or a toggle off. */
1959 if ( (add && !toggled_on) ||
1960 (!add && toggled_on) )
1962 /* This could create a second toggle at the start position;
1963 cleanup_line () will remove it if so. */
1965 seg = _gtk_toggle_segment_new (info, !add);
1967 prev = gtk_text_line_segment_split (&end);
1970 seg->next = end_line->segments;
1971 end_line->segments = seg;
1975 seg->next = prev->next;
1978 /* cleanup_line adds the new toggle to the node counts. */
1979 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1981 printf ("added toggle at end\n");
1986 * Cleanup cleanupline and the last line of the range, if
1987 * these are different.
1990 cleanup_line (cleanupline);
1991 if (cleanupline != end_line)
1993 cleanup_line (end_line);
1996 segments_changed (tree);
1998 queue_tag_redisplay (tree, tag, &start, &end);
2000 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2001 _gtk_text_btree_check (tree);
2010 get_line_internal (GtkTextBTree *tree,
2012 gint *real_line_number,
2013 gboolean include_last)
2015 GtkTextBTreeNode *node;
2020 line_count = _gtk_text_btree_line_count (tree);
2024 if (line_number < 0)
2026 line_number = line_count;
2028 else if (line_number > line_count)
2030 line_number = line_count;
2033 if (real_line_number)
2034 *real_line_number = line_number;
2036 node = tree->root_node;
2037 lines_left = line_number;
2040 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2044 while (node->level != 0)
2046 for (node = node->children.node;
2047 node->num_lines <= lines_left;
2053 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2056 lines_left -= node->num_lines;
2061 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2064 for (line = node->children.line; lines_left > 0;
2070 g_error ("gtk_text_btree_find_line ran out of lines");
2079 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2082 _gtk_text_btree_get_line (tree,
2083 _gtk_text_btree_line_count (tree) - 1,
2088 _gtk_text_btree_get_line (GtkTextBTree *tree,
2090 gint *real_line_number)
2092 return get_line_internal (tree, line_number, real_line_number, TRUE);
2096 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2098 gint *real_line_number)
2100 return get_line_internal (tree, line_number, real_line_number, FALSE);
2104 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2106 gint *line_start_index,
2107 gint *real_char_index)
2109 GtkTextBTreeNode *node;
2111 GtkTextLineSegment *seg;
2115 node = tree->root_node;
2117 /* Clamp to valid indexes (-1 is magic for "highest index"),
2118 * node->num_chars includes the two newlines that aren't really
2121 if (char_index < 0 || char_index >= (node->num_chars - 1))
2123 char_index = node->num_chars - 2;
2126 *real_char_index = char_index;
2129 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2133 chars_left = char_index;
2134 while (node->level != 0)
2136 for (node = node->children.node;
2137 chars_left >= node->num_chars;
2140 chars_left -= node->num_chars;
2142 g_assert (chars_left >= 0);
2146 if (chars_left == 0)
2148 /* Start of a line */
2150 *line_start_index = char_index;
2151 return node->children.line;
2155 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2160 for (line = node->children.line; line != NULL; line = line->next)
2162 seg = line->segments;
2165 if (chars_in_line + seg->char_count > chars_left)
2166 goto found; /* found our line/segment */
2168 chars_in_line += seg->char_count;
2173 chars_left -= chars_in_line;
2181 g_assert (line != NULL); /* hosage, ran out of lines */
2182 g_assert (seg != NULL);
2184 *line_start_index = char_index - chars_left;
2189 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2192 GtkTextBTreeNode *node;
2193 GtkTextLine *siblingline;
2194 GtkTextLineSegment *seg;
2195 int src, dst, index;
2200 #define NUM_TAG_INFOS 10
2202 line = _gtk_text_iter_get_text_line (iter);
2203 byte_index = gtk_text_iter_get_line_index (iter);
2205 tagInfo.numTags = 0;
2206 tagInfo.arraySize = NUM_TAG_INFOS;
2207 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2208 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2211 * Record tag toggles within the line of indexPtr but preceding
2212 * indexPtr. Note that if this loop segfaults, your
2213 * byte_index probably points past the sum of all
2214 * seg->byte_count */
2216 for (index = 0, seg = line->segments;
2217 (index + seg->byte_count) <= byte_index;
2218 index += seg->byte_count, seg = seg->next)
2220 if ((seg->type == >k_text_toggle_on_type)
2221 || (seg->type == >k_text_toggle_off_type))
2223 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2228 * Record toggles for tags in lines that are predecessors of
2229 * line but under the same level-0 GtkTextBTreeNode.
2232 for (siblingline = line->parent->children.line;
2233 siblingline != line;
2234 siblingline = siblingline->next)
2236 for (seg = siblingline->segments; seg != NULL;
2239 if ((seg->type == >k_text_toggle_on_type)
2240 || (seg->type == >k_text_toggle_off_type))
2242 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2248 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2249 * toggles for all siblings that precede that GtkTextBTreeNode.
2252 for (node = line->parent; node->parent != NULL;
2253 node = node->parent)
2255 GtkTextBTreeNode *siblingPtr;
2258 for (siblingPtr = node->parent->children.node;
2259 siblingPtr != node; siblingPtr = siblingPtr->next)
2261 for (summary = siblingPtr->summary; summary != NULL;
2262 summary = summary->next)
2264 if (summary->toggle_count & 1)
2266 inc_count (summary->info->tag, summary->toggle_count,
2274 * Go through the tag information and squash out all of the tags
2275 * that have even toggle counts (these tags exist before the point
2276 * of interest, but not at the desired character itself).
2279 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2281 if (tagInfo.counts[src] & 1)
2283 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2284 tagInfo.tags[dst] = tagInfo.tags[src];
2290 g_free (tagInfo.counts);
2293 g_free (tagInfo.tags);
2296 return tagInfo.tags;
2300 copy_segment (GString *string,
2301 gboolean include_hidden,
2302 gboolean include_nonchars,
2303 const GtkTextIter *start,
2304 const GtkTextIter *end)
2306 GtkTextLineSegment *end_seg;
2307 GtkTextLineSegment *seg;
2309 if (gtk_text_iter_equal (start, end))
2312 seg = _gtk_text_iter_get_indexable_segment (start);
2313 end_seg = _gtk_text_iter_get_indexable_segment (end);
2315 if (seg->type == >k_text_char_type)
2317 gboolean copy = TRUE;
2318 gint copy_bytes = 0;
2319 gint copy_start = 0;
2321 /* Don't copy if we're invisible; segments are invisible/not
2322 as a whole, no need to check each char */
2323 if (!include_hidden &&
2324 _gtk_text_btree_char_is_invisible (start))
2327 /* printf (" <invisible>\n"); */
2330 copy_start = _gtk_text_iter_get_segment_byte (start);
2334 /* End is in the same segment; need to copy fewer bytes. */
2335 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2337 copy_bytes = end_byte - copy_start;
2340 copy_bytes = seg->byte_count - copy_start;
2342 g_assert (copy_bytes != 0); /* Due to iter equality check at
2343 front of this function. */
2347 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2349 g_string_append_len (string,
2350 seg->body.chars + copy_start,
2354 /* printf (" :%s\n", string->str); */
2356 else if (seg->type == >k_text_pixbuf_type ||
2357 seg->type == >k_text_child_type)
2359 gboolean copy = TRUE;
2361 if (!include_nonchars)
2365 else if (!include_hidden &&
2366 _gtk_text_btree_char_is_invisible (start))
2373 g_string_append_len (string,
2374 gtk_text_unknown_char_utf8,
2382 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2383 const GtkTextIter *end_orig,
2384 gboolean include_hidden,
2385 gboolean include_nonchars)
2387 GtkTextLineSegment *seg;
2388 GtkTextLineSegment *end_seg;
2395 g_return_val_if_fail (start_orig != NULL, NULL);
2396 g_return_val_if_fail (end_orig != NULL, NULL);
2397 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2398 _gtk_text_iter_get_btree (end_orig), NULL);
2400 start = *start_orig;
2403 gtk_text_iter_order (&start, &end);
2405 retval = g_string_new (NULL);
2407 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2409 seg = _gtk_text_iter_get_indexable_segment (&iter);
2410 while (seg != end_seg)
2412 copy_segment (retval, include_hidden, include_nonchars,
2415 _gtk_text_iter_forward_indexable_segment (&iter);
2417 seg = _gtk_text_iter_get_indexable_segment (&iter);
2420 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2423 g_string_free (retval, FALSE);
2428 _gtk_text_btree_line_count (GtkTextBTree *tree)
2430 /* Subtract bogus line at the end; we return a count
2432 return tree->root_node->num_lines - 1;
2436 _gtk_text_btree_char_count (GtkTextBTree *tree)
2438 /* Exclude newline in bogus last line and the
2439 * one in the last line that is after the end iterator
2441 return tree->root_node->num_chars - 2;
2444 #define LOTSA_TAGS 1000
2446 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2448 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2450 int deftagCnts[LOTSA_TAGS] = { 0, };
2451 int *tagCnts = deftagCnts;
2452 GtkTextTag *deftags[LOTSA_TAGS];
2453 GtkTextTag **tags = deftags;
2455 GtkTextBTreeNode *node;
2456 GtkTextLine *siblingline;
2457 GtkTextLineSegment *seg;
2464 line = _gtk_text_iter_get_text_line (iter);
2465 tree = _gtk_text_iter_get_btree (iter);
2466 byte_index = gtk_text_iter_get_line_index (iter);
2468 numTags = gtk_text_tag_table_get_size (tree->table);
2470 /* almost always avoid malloc, so stay out of system calls */
2471 if (LOTSA_TAGS < numTags)
2473 tagCnts = g_new0 (int, numTags);
2474 tags = g_new (GtkTextTag*, numTags);
2478 * Record tag toggles within the line of indexPtr but preceding
2482 for (index = 0, seg = line->segments;
2483 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2484 index += seg->byte_count, seg = seg->next)
2486 if ((seg->type == >k_text_toggle_on_type)
2487 || (seg->type == >k_text_toggle_off_type))
2489 tag = seg->body.toggle.info->tag;
2490 if (tag->invisible_set && tag->values->invisible)
2492 tags[tag->priority] = tag;
2493 tagCnts[tag->priority]++;
2499 * Record toggles for tags in lines that are predecessors of
2500 * line but under the same level-0 GtkTextBTreeNode.
2503 for (siblingline = line->parent->children.line;
2504 siblingline != line;
2505 siblingline = siblingline->next)
2507 for (seg = siblingline->segments; seg != NULL;
2510 if ((seg->type == >k_text_toggle_on_type)
2511 || (seg->type == >k_text_toggle_off_type))
2513 tag = seg->body.toggle.info->tag;
2514 if (tag->invisible_set && tag->values->invisible)
2516 tags[tag->priority] = tag;
2517 tagCnts[tag->priority]++;
2524 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2525 * for all siblings that precede that GtkTextBTreeNode.
2528 for (node = line->parent; node->parent != NULL;
2529 node = node->parent)
2531 GtkTextBTreeNode *siblingPtr;
2534 for (siblingPtr = node->parent->children.node;
2535 siblingPtr != node; siblingPtr = siblingPtr->next)
2537 for (summary = siblingPtr->summary; summary != NULL;
2538 summary = summary->next)
2540 if (summary->toggle_count & 1)
2542 tag = summary->info->tag;
2543 if (tag->invisible_set && tag->values->invisible)
2545 tags[tag->priority] = tag;
2546 tagCnts[tag->priority] += summary->toggle_count;
2554 * Now traverse from highest priority to lowest,
2555 * take invisible value from first odd count (= on)
2558 for (i = numTags-1; i >=0; i--)
2562 /* FIXME not sure this should be if 0 */
2564 #ifndef ALWAYS_SHOW_SELECTION
2565 /* who would make the selection invisible? */
2566 if ((tag == tkxt->seltag)
2567 && !(tkxt->flags & GOT_FOCUS))
2573 invisible = tags[i]->values->invisible;
2578 if (LOTSA_TAGS < numTags)
2593 redisplay_region (GtkTextBTree *tree,
2594 const GtkTextIter *start,
2595 const GtkTextIter *end)
2598 GtkTextLine *start_line, *end_line;
2600 if (gtk_text_iter_compare (start, end) > 0)
2602 const GtkTextIter *tmp = start;
2607 start_line = _gtk_text_iter_get_text_line (start);
2608 end_line = _gtk_text_iter_get_text_line (end);
2611 while (view != NULL)
2613 gint start_y, end_y;
2614 GtkTextLineData *ld;
2616 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2618 if (end_line == start_line)
2621 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2623 ld = _gtk_text_line_get_data (end_line, view->view_id);
2625 end_y += ld->height;
2627 gtk_text_layout_changed (view->layout, start_y,
2636 redisplay_mark (GtkTextLineSegment *mark)
2641 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2643 mark->body.mark.obj);
2646 gtk_text_iter_forward_char (&end);
2648 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2649 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2654 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2656 if (!mark->body.mark.visible)
2659 redisplay_mark (mark);
2663 ensure_not_off_end (GtkTextBTree *tree,
2664 GtkTextLineSegment *mark,
2667 if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
2668 gtk_text_iter_backward_char (iter);
2671 static GtkTextLineSegment*
2672 real_set_mark (GtkTextBTree *tree,
2673 GtkTextMark *existing_mark,
2675 gboolean left_gravity,
2676 const GtkTextIter *where,
2677 gboolean should_exist,
2678 gboolean redraw_selections)
2680 GtkTextLineSegment *mark;
2683 g_return_val_if_fail (tree != NULL, NULL);
2684 g_return_val_if_fail (where != NULL, NULL);
2685 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2688 mark = existing_mark->segment;
2689 else if (name != NULL)
2690 mark = g_hash_table_lookup (tree->mark_table,
2695 if (should_exist && mark == NULL)
2697 g_warning ("No mark `%s' exists!", name);
2701 /* OK if !should_exist and it does already exist, in that case
2707 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2708 _gtk_text_iter_check (&iter);
2712 if (redraw_selections &&
2713 (mark == tree->insert_mark->segment ||
2714 mark == tree->selection_bound_mark->segment))
2716 GtkTextIter old_pos;
2718 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2719 mark->body.mark.obj);
2720 redisplay_region (tree, &old_pos, where);
2724 * don't let visible marks be after the final newline of the
2728 if (mark->body.mark.visible)
2730 ensure_not_off_end (tree, mark, &iter);
2733 /* Redraw the mark's old location. */
2734 redisplay_mark_if_visible (mark);
2736 /* Unlink mark from its current location.
2737 This could hose our iterator... */
2738 gtk_text_btree_unlink_segment (tree, mark,
2739 mark->body.mark.line);
2740 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2741 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2743 segments_changed (tree); /* make sure the iterator recomputes its
2748 mark = _gtk_mark_segment_new (tree,
2752 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2754 if (mark->body.mark.name)
2755 g_hash_table_insert (tree->mark_table,
2756 mark->body.mark.name,
2760 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2761 _gtk_text_iter_check (&iter);
2763 /* Link mark into new location */
2764 gtk_text_btree_link_segment (mark, &iter);
2766 /* Invalidate some iterators. */
2767 segments_changed (tree);
2770 * update the screen at the mark's new location.
2773 redisplay_mark_if_visible (mark);
2775 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2776 _gtk_text_iter_check (&iter);
2778 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2779 _gtk_text_btree_check (tree);
2786 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2787 GtkTextMark *existing_mark,
2789 gboolean left_gravity,
2790 const GtkTextIter *iter,
2791 gboolean should_exist)
2793 GtkTextLineSegment *seg;
2795 seg = real_set_mark (tree, existing_mark,
2796 name, left_gravity, iter, should_exist,
2799 return seg ? seg->body.mark.obj : NULL;
2803 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2807 GtkTextIter tmp_start, tmp_end;
2809 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2811 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2812 tree->selection_bound_mark);
2814 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2826 gtk_text_iter_order (&tmp_start, &tmp_end);
2839 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2840 const GtkTextIter *iter)
2842 _gtk_text_btree_select_range (tree, iter, iter);
2846 _gtk_text_btree_select_range (GtkTextBTree *tree,
2847 const GtkTextIter *ins,
2848 const GtkTextIter *bound)
2850 GtkTextIter start, end;
2852 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2853 redisplay_region (tree, &start, &end);
2855 /* Move insert AND selection_bound before we redisplay */
2856 real_set_mark (tree, tree->insert_mark,
2857 "insert", FALSE, ins, TRUE, FALSE);
2858 real_set_mark (tree, tree->selection_bound_mark,
2859 "selection_bound", FALSE, bound, TRUE, FALSE);
2861 redisplay_region (tree, ins, bound);
2866 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2871 g_return_if_fail (tree != NULL);
2872 g_return_if_fail (name != NULL);
2874 mark = g_hash_table_lookup (tree->mark_table,
2877 _gtk_text_btree_remove_mark (tree, mark);
2881 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2882 GtkTextLineSegment *segment)
2885 if (segment->body.mark.name)
2886 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2888 segment->body.mark.tree = NULL;
2889 segment->body.mark.line = NULL;
2891 /* Remove the ref on the mark, which frees segment as a side effect
2892 * if this is the last reference.
2894 g_object_unref (segment->body.mark.obj);
2898 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2901 GtkTextLineSegment *segment;
2903 g_return_if_fail (mark != NULL);
2904 g_return_if_fail (tree != NULL);
2906 segment = mark->segment;
2908 if (segment->body.mark.not_deleteable)
2910 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2914 /* This calls cleanup_line and segments_changed */
2915 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2917 _gtk_text_btree_release_mark_segment (tree, segment);
2921 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2922 GtkTextMark *segment)
2924 return segment == tree->insert_mark;
2928 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2929 GtkTextMark *segment)
2931 return segment == tree->selection_bound_mark;
2935 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2938 GtkTextLineSegment *seg;
2940 g_return_val_if_fail (tree != NULL, NULL);
2941 g_return_val_if_fail (name != NULL, NULL);
2943 seg = g_hash_table_lookup (tree->mark_table, name);
2945 return seg ? seg->body.mark.obj : NULL;
2949 * gtk_text_mark_set_visible:
2950 * @mark: a #GtkTextMark
2951 * @setting: visibility of mark
2953 * Sets the visibility of @mark; the insertion point is normally
2954 * visible, i.e. you can see it as a vertical bar. Also, the text
2955 * widget uses a visible mark to indicate where a drop will occur when
2956 * dragging-and-dropping text. Most other marks are not visible.
2957 * Marks are not visible by default.
2961 gtk_text_mark_set_visible (GtkTextMark *mark,
2964 GtkTextLineSegment *seg;
2966 g_return_if_fail (mark != NULL);
2968 seg = mark->segment;
2970 if (seg->body.mark.visible == setting)
2974 seg->body.mark.visible = setting;
2976 redisplay_mark (seg);
2981 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2984 GtkTextBTreeNode *node;
2985 GtkTextTagInfo *info;
2987 g_return_val_if_fail (tree != NULL, NULL);
2991 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2996 if (info->tag_root == NULL)
2999 node = info->tag_root;
3001 /* We know the tag root has instances of the given
3004 continue_outer_loop:
3005 g_assert (node != NULL);
3006 while (node->level > 0)
3008 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3009 node = node->children.node;
3010 while (node != NULL)
3012 if (gtk_text_btree_node_has_tag (node, tag))
3013 goto continue_outer_loop;
3017 g_assert (node != NULL);
3020 g_assert (node != NULL); /* The tag summaries said some node had
3023 g_assert (node->level == 0);
3025 return node->children.line;
3029 /* Looking for any tag at all (tag == NULL).
3030 Unfortunately this can't be done in a simple and efficient way
3031 right now; so I'm just going to return the
3032 first line in the btree. FIXME */
3033 return _gtk_text_btree_get_line (tree, 0, NULL);
3038 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3041 GtkTextBTreeNode *node;
3042 GtkTextBTreeNode *last_node;
3044 GtkTextTagInfo *info;
3046 g_return_val_if_fail (tree != NULL, NULL);
3050 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3052 if (info->tag_root == NULL)
3055 node = info->tag_root;
3056 /* We know the tag root has instances of the given
3059 while (node->level > 0)
3061 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3063 node = node->children.node;
3064 while (node != NULL)
3066 if (gtk_text_btree_node_has_tag (node, tag))
3074 g_assert (node != NULL); /* The tag summaries said some node had
3077 g_assert (node->level == 0);
3079 /* Find the last line in this node */
3080 line = node->children.line;
3081 while (line->next != NULL)
3088 /* This search can't be done efficiently at the moment,
3089 at least not without complexity.
3090 So, we just return the last line.
3092 return _gtk_text_btree_get_end_iter_line (tree);
3102 _gtk_text_line_get_number (GtkTextLine *line)
3105 GtkTextBTreeNode *node, *parent, *node2;
3109 * First count how many lines precede this one in its level-0
3113 node = line->parent;
3115 for (line2 = node->children.line; line2 != line;
3116 line2 = line2->next)
3120 g_error ("gtk_text_btree_line_number couldn't find line");
3126 * Now work up through the levels of the tree one at a time,
3127 * counting how many lines are in GtkTextBTreeNodes preceding the current
3131 for (parent = node->parent ; parent != NULL;
3132 node = parent, parent = parent->parent)
3134 for (node2 = parent->children.node; node2 != node;
3135 node2 = node2->next)
3139 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3141 index += node2->num_lines;
3147 static GtkTextLineSegment*
3148 find_toggle_segment_before_char (GtkTextLine *line,
3152 GtkTextLineSegment *seg;
3153 GtkTextLineSegment *toggle_seg;
3158 seg = line->segments;
3159 while ( (index + seg->char_count) <= char_in_line )
3161 if (((seg->type == >k_text_toggle_on_type)
3162 || (seg->type == >k_text_toggle_off_type))
3163 && (seg->body.toggle.info->tag == tag))
3166 index += seg->char_count;
3173 static GtkTextLineSegment*
3174 find_toggle_segment_before_byte (GtkTextLine *line,
3178 GtkTextLineSegment *seg;
3179 GtkTextLineSegment *toggle_seg;
3184 seg = line->segments;
3185 while ( (index + seg->byte_count) <= byte_in_line )
3187 if (((seg->type == >k_text_toggle_on_type)
3188 || (seg->type == >k_text_toggle_off_type))
3189 && (seg->body.toggle.info->tag == tag))
3192 index += seg->byte_count;
3200 find_toggle_outside_current_line (GtkTextLine *line,
3204 GtkTextBTreeNode *node;
3205 GtkTextLine *sibling_line;
3206 GtkTextLineSegment *seg;
3207 GtkTextLineSegment *toggle_seg;
3209 GtkTextTagInfo *info = NULL;
3212 * No toggle in this line. Look for toggles for the tag in lines
3213 * that are predecessors of line but under the same
3214 * level-0 GtkTextBTreeNode.
3217 sibling_line = line->parent->children.line;
3218 while (sibling_line != line)
3220 seg = sibling_line->segments;
3223 if (((seg->type == >k_text_toggle_on_type)
3224 || (seg->type == >k_text_toggle_off_type))
3225 && (seg->body.toggle.info->tag == tag))
3231 sibling_line = sibling_line->next;
3234 if (toggle_seg != NULL)
3235 return (toggle_seg->type == >k_text_toggle_on_type);
3238 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3239 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3240 * siblings that precede that GtkTextBTreeNode.
3243 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3249 node = line->parent;
3250 while (node->parent != NULL)
3252 GtkTextBTreeNode *sibling_node;
3254 sibling_node = node->parent->children.node;
3255 while (sibling_node != node)
3259 summary = sibling_node->summary;
3260 while (summary != NULL)
3262 if (summary->info == info)
3263 toggles += summary->toggle_count;
3265 summary = summary->next;
3268 sibling_node = sibling_node->next;
3271 if (node == info->tag_root)
3274 node = node->parent;
3278 * An odd number of toggles means that the tag is present at the
3282 return (toggles & 1) != 0;
3285 /* FIXME this function is far too slow, for no good reason. */
3287 _gtk_text_line_char_has_tag (GtkTextLine *line,
3292 GtkTextLineSegment *toggle_seg;
3294 g_return_val_if_fail (line != NULL, FALSE);
3297 * Check for toggles for the tag in the line but before
3298 * the char. If there is one, its type indicates whether or
3299 * not the character is tagged.
3302 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3304 if (toggle_seg != NULL)
3305 return (toggle_seg->type == >k_text_toggle_on_type);
3307 return find_toggle_outside_current_line (line, tree, tag);
3311 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3316 GtkTextLineSegment *toggle_seg;
3318 g_return_val_if_fail (line != NULL, FALSE);
3321 * Check for toggles for the tag in the line but before
3322 * the char. If there is one, its type indicates whether or
3323 * not the character is tagged.
3326 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3328 if (toggle_seg != NULL)
3329 return (toggle_seg->type == >k_text_toggle_on_type);
3331 return find_toggle_outside_current_line (line, tree, tag);
3335 _gtk_text_line_is_last (GtkTextLine *line,
3338 return line == get_last_line (tree);
3342 ensure_end_iter_line (GtkTextBTree *tree)
3344 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3348 /* n_lines is without the magic line at the end */
3349 g_assert (_gtk_text_btree_line_count (tree) >= 1);
3351 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3353 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3358 ensure_end_iter_segment (GtkTextBTree *tree)
3360 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3362 GtkTextLineSegment *seg;
3363 GtkTextLineSegment *last_with_chars;
3365 ensure_end_iter_line (tree);
3367 last_with_chars = NULL;
3369 seg = tree->end_iter_line->segments;
3372 if (seg->char_count > 0)
3373 last_with_chars = seg;
3377 tree->end_iter_segment = last_with_chars;
3379 /* We know the last char in the last line is '\n' */
3380 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3381 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3383 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3385 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3386 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3391 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3394 ensure_end_iter_line (tree);
3396 return line == tree->end_iter_line;
3400 _gtk_text_btree_is_end (GtkTextBTree *tree,
3402 GtkTextLineSegment *seg,
3406 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3408 /* Do this first to avoid walking segments in most cases */
3409 if (!_gtk_text_line_contains_end_iter (line, tree))
3412 ensure_end_iter_segment (tree);
3414 if (seg != tree->end_iter_segment)
3417 if (byte_index >= 0)
3418 return byte_index == tree->end_iter_segment_byte_index;
3420 return char_offset == tree->end_iter_segment_char_offset;
3424 _gtk_text_line_next (GtkTextLine *line)
3426 GtkTextBTreeNode *node;
3428 if (line->next != NULL)
3433 * This was the last line associated with the particular parent
3434 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3435 * then search down from that GtkTextBTreeNode to find the first
3439 node = line->parent;
3440 while (node != NULL && node->next == NULL)
3441 node = node->parent;
3447 while (node->level > 0)
3449 node = node->children.node;
3452 g_assert (node->children.line != line);
3454 return node->children.line;
3459 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3463 next = _gtk_text_line_next (line);
3465 /* If we were on the end iter line, we can't go to
3468 if (next && next->next == NULL && /* these checks are optimization only */
3469 _gtk_text_line_next (next) == NULL)
3476 _gtk_text_line_previous (GtkTextLine *line)
3478 GtkTextBTreeNode *node;
3479 GtkTextBTreeNode *node2;
3483 * Find the line under this GtkTextBTreeNode just before the starting line.
3485 prev = line->parent->children.line; /* First line at leaf */
3486 while (prev != line)
3488 if (prev->next == line)
3494 g_error ("gtk_text_btree_previous_line ran out of lines");
3498 * This was the first line associated with the particular parent
3499 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3500 * then search down from that GtkTextBTreeNode to find its last line.
3502 for (node = line->parent; ; node = node->parent)
3504 if (node == NULL || node->parent == NULL)
3506 else if (node != node->parent->children.node)
3510 for (node2 = node->parent->children.node; ;
3511 node2 = node2->children.node)
3513 while (node2->next != node)
3514 node2 = node2->next;
3516 if (node2->level == 0)
3522 for (prev = node2->children.line ; ; prev = prev->next)
3524 if (prev->next == NULL)
3528 g_assert_not_reached ();
3534 _gtk_text_line_data_new (GtkTextLayout *layout,
3537 GtkTextLineData *line_data;
3539 line_data = g_new (GtkTextLineData, 1);
3541 line_data->view_id = layout;
3542 line_data->next = NULL;
3543 line_data->width = 0;
3544 line_data->height = 0;
3545 line_data->valid = FALSE;
3551 _gtk_text_line_add_data (GtkTextLine *line,
3552 GtkTextLineData *data)
3554 g_return_if_fail (line != NULL);
3555 g_return_if_fail (data != NULL);
3556 g_return_if_fail (data->view_id != NULL);
3560 data->next = line->views;
3570 _gtk_text_line_remove_data (GtkTextLine *line,
3573 GtkTextLineData *prev;
3574 GtkTextLineData *iter;
3576 g_return_val_if_fail (line != NULL, NULL);
3577 g_return_val_if_fail (view_id != NULL, NULL);
3581 while (iter != NULL)
3583 if (iter->view_id == view_id)
3592 prev->next = iter->next;
3594 line->views = iter->next;
3603 _gtk_text_line_get_data (GtkTextLine *line,
3606 GtkTextLineData *iter;
3608 g_return_val_if_fail (line != NULL, NULL);
3609 g_return_val_if_fail (view_id != NULL, NULL);
3612 while (iter != NULL)
3614 if (iter->view_id == view_id)
3623 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3624 GtkTextLineData *ld)
3626 /* For now this is totally unoptimized. FIXME?
3628 We could probably optimize the case where the width removed
3629 is less than the max width for the parent node,
3630 and the case where the height is unchanged when we re-wrap.
3633 g_return_if_fail (ld != NULL);
3636 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3640 _gtk_text_line_char_count (GtkTextLine *line)
3642 GtkTextLineSegment *seg;
3646 seg = line->segments;
3649 size += seg->char_count;
3656 _gtk_text_line_byte_count (GtkTextLine *line)
3658 GtkTextLineSegment *seg;
3662 seg = line->segments;
3665 size += seg->byte_count;
3673 _gtk_text_line_char_index (GtkTextLine *target_line)
3675 GSList *node_stack = NULL;
3676 GtkTextBTreeNode *iter;
3680 /* Push all our parent nodes onto a stack */
3681 iter = target_line->parent;
3683 g_assert (iter != NULL);
3685 while (iter != NULL)
3687 node_stack = g_slist_prepend (node_stack, iter);
3689 iter = iter->parent;
3692 /* Check that we have the root node on top of the stack. */
3693 g_assert (node_stack != NULL &&
3694 node_stack->data != NULL &&
3695 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3697 /* Add up chars in all nodes before the nodes in our stack.
3701 iter = node_stack->data;
3702 while (iter != NULL)
3704 GtkTextBTreeNode *child_iter;
3705 GtkTextBTreeNode *next_node;
3707 next_node = node_stack->next ?
3708 node_stack->next->data : NULL;
3709 node_stack = g_slist_remove (node_stack, node_stack->data);
3711 if (iter->level == 0)
3713 /* stack should be empty when we're on the last node */
3714 g_assert (node_stack == NULL);
3715 break; /* Our children are now lines */
3718 g_assert (next_node != NULL);
3719 g_assert (iter != NULL);
3720 g_assert (next_node->parent == iter);
3722 /* Add up chars before us in the tree */
3723 child_iter = iter->children.node;
3724 while (child_iter != next_node)
3726 g_assert (child_iter != NULL);
3728 num_chars += child_iter->num_chars;
3730 child_iter = child_iter->next;
3736 g_assert (iter != NULL);
3737 g_assert (iter == target_line->parent);
3739 /* Since we don't store char counts in lines, only in segments, we
3740 have to iterate over the lines adding up segment char counts
3741 until we find our line. */
3742 line = iter->children.line;
3743 while (line != target_line)
3745 g_assert (line != NULL);
3747 num_chars += _gtk_text_line_char_count (line);
3752 g_assert (line == target_line);
3758 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3762 GtkTextLineSegment *seg;
3765 g_return_val_if_fail (line != NULL, NULL);
3767 offset = byte_offset;
3768 seg = line->segments;
3770 while (offset >= seg->byte_count)
3772 offset -= seg->byte_count;
3774 g_assert (seg != NULL); /* means an invalid byte index */
3778 *seg_offset = offset;
3784 _gtk_text_line_char_to_segment (GtkTextLine *line,
3788 GtkTextLineSegment *seg;
3791 g_return_val_if_fail (line != NULL, NULL);
3793 offset = char_offset;
3794 seg = line->segments;
3796 while (offset >= seg->char_count)
3798 offset -= seg->char_count;
3800 g_assert (seg != NULL); /* means an invalid char index */
3804 *seg_offset = offset;
3810 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3814 GtkTextLineSegment *seg;
3817 g_return_val_if_fail (line != NULL, NULL);
3819 offset = byte_offset;
3820 seg = line->segments;
3822 while (offset > 0 && offset >= seg->byte_count)
3824 offset -= seg->byte_count;
3826 g_assert (seg != NULL); /* means an invalid byte index */
3830 *seg_offset = offset;
3836 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3840 GtkTextLineSegment *seg;
3843 g_return_val_if_fail (line != NULL, NULL);
3845 offset = char_offset;
3846 seg = line->segments;
3848 while (offset > 0 && offset >= seg->char_count)
3850 offset -= seg->char_count;
3852 g_assert (seg != NULL); /* means an invalid byte index */
3856 *seg_offset = offset;
3862 _gtk_text_line_byte_to_char (GtkTextLine *line,
3866 GtkTextLineSegment *seg;
3868 g_return_val_if_fail (line != NULL, 0);
3869 g_return_val_if_fail (byte_offset >= 0, 0);
3872 seg = line->segments;
3873 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3874 the next segment) */
3876 byte_offset -= seg->byte_count;
3877 char_offset += seg->char_count;
3879 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3882 g_assert (seg != NULL);
3884 /* Now byte_offset is the offset into the current segment,
3885 and char_offset is the start of the current segment.
3886 Optimize the case where no chars use > 1 byte */
3887 if (seg->byte_count == seg->char_count)
3888 return char_offset + byte_offset;
3891 if (seg->type == >k_text_char_type)
3892 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3895 g_assert (seg->char_count == 1);
3896 g_assert (byte_offset == 0);
3904 _gtk_text_line_char_to_byte (GtkTextLine *line,
3907 g_warning ("FIXME not implemented");
3912 /* FIXME sync with char_locate (or figure out a clean
3913 way to merge the two functions) */
3915 _gtk_text_line_byte_locate (GtkTextLine *line,
3917 GtkTextLineSegment **segment,
3918 GtkTextLineSegment **any_segment,
3919 gint *seg_byte_offset,
3920 gint *line_byte_offset)
3922 GtkTextLineSegment *seg;
3923 GtkTextLineSegment *after_last_indexable;
3924 GtkTextLineSegment *last_indexable;
3928 g_return_val_if_fail (line != NULL, FALSE);
3929 g_return_val_if_fail (byte_offset >= 0, FALSE);
3932 *any_segment = NULL;
3935 offset = byte_offset;
3937 last_indexable = NULL;
3938 after_last_indexable = line->segments;
3939 seg = line->segments;
3941 /* The loop ends when we're inside a segment;
3942 last_indexable refers to the last segment
3943 we passed entirely. */
3944 while (seg && offset >= seg->byte_count)
3946 if (seg->char_count > 0)
3948 offset -= seg->byte_count;
3949 bytes_in_line += seg->byte_count;
3950 last_indexable = seg;
3951 after_last_indexable = last_indexable->next;
3959 /* We went off the end of the line */
3961 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3968 if (after_last_indexable != NULL)
3969 *any_segment = after_last_indexable;
3971 *any_segment = *segment;
3974 /* Override any_segment if we're in the middle of a segment. */
3976 *any_segment = *segment;
3978 *seg_byte_offset = offset;
3980 g_assert (*segment != NULL);
3981 g_assert (*any_segment != NULL);
3982 g_assert (*seg_byte_offset < (*segment)->byte_count);
3984 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3989 /* FIXME sync with byte_locate (or figure out a clean
3990 way to merge the two functions) */
3992 _gtk_text_line_char_locate (GtkTextLine *line,
3994 GtkTextLineSegment **segment,
3995 GtkTextLineSegment **any_segment,
3996 gint *seg_char_offset,
3997 gint *line_char_offset)
3999 GtkTextLineSegment *seg;
4000 GtkTextLineSegment *after_last_indexable;
4001 GtkTextLineSegment *last_indexable;
4005 g_return_val_if_fail (line != NULL, FALSE);
4006 g_return_val_if_fail (char_offset >= 0, FALSE);
4009 *any_segment = NULL;
4012 offset = char_offset;
4014 last_indexable = NULL;
4015 after_last_indexable = line->segments;
4016 seg = line->segments;
4018 /* The loop ends when we're inside a segment;
4019 last_indexable refers to the last segment
4020 we passed entirely. */
4021 while (seg && offset >= seg->char_count)
4023 if (seg->char_count > 0)
4025 offset -= seg->char_count;
4026 chars_in_line += seg->char_count;
4027 last_indexable = seg;
4028 after_last_indexable = last_indexable->next;
4036 /* end of the line */
4038 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4045 if (after_last_indexable != NULL)
4046 *any_segment = after_last_indexable;
4048 *any_segment = *segment;
4051 /* Override any_segment if we're in the middle of a segment. */
4053 *any_segment = *segment;
4055 *seg_char_offset = offset;
4057 g_assert (*segment != NULL);
4058 g_assert (*any_segment != NULL);
4059 g_assert (*seg_char_offset < (*segment)->char_count);
4061 *line_char_offset = chars_in_line + *seg_char_offset;
4067 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4069 gint *line_char_offset,
4070 gint *seg_char_offset)
4072 GtkTextLineSegment *seg;
4075 g_return_if_fail (line != NULL);
4076 g_return_if_fail (byte_offset >= 0);
4078 *line_char_offset = 0;
4080 offset = byte_offset;
4081 seg = line->segments;
4083 while (offset >= seg->byte_count)
4085 offset -= seg->byte_count;
4086 *line_char_offset += seg->char_count;
4088 g_assert (seg != NULL); /* means an invalid char offset */
4091 g_assert (seg->char_count > 0); /* indexable. */
4093 /* offset is now the number of bytes into the current segment we
4094 * want to go. Count chars into the current segment.
4097 if (seg->type == >k_text_char_type)
4099 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4101 g_assert (*seg_char_offset < seg->char_count);
4103 *line_char_offset += *seg_char_offset;
4107 g_assert (offset == 0);
4108 *seg_char_offset = 0;
4113 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4115 gint *line_byte_offset,
4116 gint *seg_byte_offset)
4118 GtkTextLineSegment *seg;
4121 g_return_if_fail (line != NULL);
4122 g_return_if_fail (char_offset >= 0);
4124 *line_byte_offset = 0;
4126 offset = char_offset;
4127 seg = line->segments;
4129 while (offset >= seg->char_count)
4131 offset -= seg->char_count;
4132 *line_byte_offset += seg->byte_count;
4134 g_assert (seg != NULL); /* means an invalid char offset */
4137 g_assert (seg->char_count > 0); /* indexable. */
4139 /* offset is now the number of chars into the current segment we
4140 want to go. Count bytes into the current segment. */
4142 if (seg->type == >k_text_char_type)
4146 /* if in the last fourth of the segment walk backwards */
4147 if (seg->char_count - offset < seg->char_count / 4)
4148 p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4149 offset - seg->char_count);
4151 p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4153 *seg_byte_offset = p - seg->body.chars;
4155 g_assert (*seg_byte_offset < seg->byte_count);
4157 *line_byte_offset += *seg_byte_offset;
4161 g_assert (offset == 0);
4162 *seg_byte_offset = 0;
4167 node_compare (GtkTextBTreeNode *lhs,
4168 GtkTextBTreeNode *rhs)
4170 GtkTextBTreeNode *iter;
4171 GtkTextBTreeNode *node;
4172 GtkTextBTreeNode *common_parent;
4173 GtkTextBTreeNode *parent_of_lower;
4174 GtkTextBTreeNode *parent_of_higher;
4175 gboolean lhs_is_lower;
4176 GtkTextBTreeNode *lower;
4177 GtkTextBTreeNode *higher;
4179 /* This function assumes that lhs and rhs are not underneath each
4186 if (lhs->level < rhs->level)
4188 lhs_is_lower = TRUE;
4194 lhs_is_lower = FALSE;
4199 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4200 * of the common parent we used to reach the common parent; the
4201 * ordering of these child nodes in the child list is the ordering
4205 /* Get on the same level (may be on same level already) */
4207 while (node->level < higher->level)
4208 node = node->parent;
4210 g_assert (node->level == higher->level);
4212 g_assert (node != higher); /* Happens if lower is underneath higher */
4214 /* Go up until we have two children with a common parent.
4216 parent_of_lower = node;
4217 parent_of_higher = higher;
4219 while (parent_of_lower->parent != parent_of_higher->parent)
4221 parent_of_lower = parent_of_lower->parent;
4222 parent_of_higher = parent_of_higher->parent;
4225 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4227 common_parent = parent_of_lower->parent;
4229 g_assert (common_parent != NULL);
4231 /* See which is first in the list of common_parent's children */
4232 iter = common_parent->children.node;
4233 while (iter != NULL)
4235 if (iter == parent_of_higher)
4237 /* higher is less than lower */
4240 return 1; /* lhs > rhs */
4244 else if (iter == parent_of_lower)
4246 /* lower is less than higher */
4249 return -1; /* lhs < rhs */
4257 g_assert_not_reached ();
4261 /* remember that tag == NULL means "any tag" */
4263 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4267 GtkTextBTreeNode *node;
4268 GtkTextTagInfo *info;
4269 gboolean below_tag_root;
4271 g_return_val_if_fail (line != NULL, NULL);
4273 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4274 _gtk_text_btree_check (tree);
4278 /* Right now we can only offer linear-search if the user wants
4279 * to know about any tag toggle at all.
4281 return _gtk_text_line_next_excluding_last (line);
4284 /* Our tag summaries only have node precision, not line
4285 * precision. This means that if any line under a node could contain a
4286 * tag, then any of the others could also contain a tag.
4288 * In the future we could have some mechanism to keep track of how
4289 * many toggles we've found under a node so far, since we have a
4290 * count of toggles under the node. But for now I'm going with KISS.
4293 /* return same-node line, if any. */
4297 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4301 if (info->tag_root == NULL)
4304 if (info->tag_root == line->parent)
4305 return NULL; /* we were at the last line under the tag root */
4307 /* We need to go up out of this node, and on to the next one with
4308 toggles for the target tag. If we're below the tag root, we need to
4309 find the next node below the tag root that has tag summaries. If
4310 we're not below the tag root, we need to see if the tag root is
4311 after us in the tree, and if so, return the first line underneath
4314 node = line->parent;
4315 below_tag_root = FALSE;
4316 while (node != NULL)
4318 if (node == info->tag_root)
4320 below_tag_root = TRUE;
4324 node = node->parent;
4329 node = line->parent;
4330 while (node != info->tag_root)
4332 if (node->next == NULL)
4333 node = node->parent;
4338 if (gtk_text_btree_node_has_tag (node, tag))
4348 ordering = node_compare (line->parent, info->tag_root);
4352 /* Tag root is ahead of us, so search there. */
4353 node = info->tag_root;
4358 /* Tag root is after us, so no more lines that
4359 * could contain the tag.
4364 g_assert_not_reached ();
4369 g_assert (node != NULL);
4371 /* We have to find the first sub-node of this node that contains
4375 while (node->level > 0)
4377 g_assert (node != NULL); /* If this fails, it likely means an
4378 incorrect tag summary led us on a
4379 wild goose chase down this branch of
4381 node = node->children.node;
4382 while (node != NULL)
4384 if (gtk_text_btree_node_has_tag (node, tag))
4390 g_assert (node != NULL);
4391 g_assert (node->level == 0);
4393 return node->children.line;
4397 prev_line_under_node (GtkTextBTreeNode *node,
4402 prev = node->children.line;
4408 while (prev->next != line)
4418 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4422 GtkTextBTreeNode *node;
4423 GtkTextBTreeNode *found_node = NULL;
4424 GtkTextTagInfo *info;
4425 gboolean below_tag_root;
4427 GtkTextBTreeNode *line_ancestor;
4428 GtkTextBTreeNode *line_ancestor_parent;
4430 /* See next_could_contain_tag () for more extensive comments
4431 * on what's going on here.
4434 g_return_val_if_fail (line != NULL, NULL);
4436 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4437 _gtk_text_btree_check (tree);
4441 /* Right now we can only offer linear-search if the user wants
4442 * to know about any tag toggle at all.
4444 return _gtk_text_line_previous (line);
4447 /* Return same-node line, if any. */
4448 prev = prev_line_under_node (line->parent, line);
4452 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4456 if (info->tag_root == NULL)
4459 if (info->tag_root == line->parent)
4460 return NULL; /* we were at the first line under the tag root */
4462 /* Are we below the tag root */
4463 node = line->parent;
4464 below_tag_root = FALSE;
4465 while (node != NULL)
4467 if (node == info->tag_root)
4469 below_tag_root = TRUE;
4473 node = node->parent;
4478 /* Look for a previous node under this tag root that has our
4482 /* this assertion holds because line->parent is not the
4483 * tag root, we are below the tag root, and the tag
4486 g_assert (line->parent->parent != NULL);
4488 line_ancestor = line->parent;
4489 line_ancestor_parent = line->parent->parent;
4491 while (line_ancestor != info->tag_root)
4493 GSList *child_nodes = NULL;
4496 /* Create reverse-order list of nodes before
4499 if (line_ancestor_parent != NULL)
4500 node = line_ancestor_parent->children.node;
4502 node = line_ancestor;
4504 while (node != line_ancestor && node != NULL)
4506 child_nodes = g_slist_prepend (child_nodes, node);
4511 /* Try to find a node with our tag on it in the list */
4515 GtkTextBTreeNode *this_node = tmp->data;
4517 g_assert (this_node != line_ancestor);
4519 if (gtk_text_btree_node_has_tag (this_node, tag))
4521 found_node = this_node;
4522 g_slist_free (child_nodes);
4526 tmp = g_slist_next (tmp);
4529 g_slist_free (child_nodes);
4531 /* Didn't find anything on this level; go up one level. */
4532 line_ancestor = line_ancestor_parent;
4533 line_ancestor_parent = line_ancestor->parent;
4543 ordering = node_compare (line->parent, info->tag_root);
4547 /* Tag root is ahead of us, so no more lines
4554 /* Tag root is after us, so grab last tagged
4555 * line underneath the tag root.
4557 found_node = info->tag_root;
4561 g_assert_not_reached ();
4566 g_assert (found_node != NULL);
4568 /* We have to find the last sub-node of this node that contains
4573 while (node->level > 0)
4575 GSList *child_nodes = NULL;
4577 g_assert (node != NULL); /* If this fails, it likely means an
4578 incorrect tag summary led us on a
4579 wild goose chase down this branch of
4582 node = node->children.node;
4583 while (node != NULL)
4585 child_nodes = g_slist_prepend (child_nodes, node);
4589 node = NULL; /* detect failure to find a child node. */
4592 while (iter != NULL)
4594 if (gtk_text_btree_node_has_tag (iter->data, tag))
4596 /* recurse into this node. */
4601 iter = g_slist_next (iter);
4604 g_slist_free (child_nodes);
4606 g_assert (node != NULL);
4609 g_assert (node != NULL);
4610 g_assert (node->level == 0);
4612 /* this assertion is correct, but slow. */
4613 /* g_assert (node_compare (node, line->parent) < 0); */
4615 /* Return last line in this node. */
4617 prev = node->children.line;
4625 * Non-public function implementations
4629 summary_list_destroy (Summary *summary)
4631 g_slice_free_chain (Summary, summary, next);
4635 get_last_line (GtkTextBTree *tree)
4637 if (tree->last_line_stamp != tree->chars_changed_stamp)
4643 n_lines = _gtk_text_btree_line_count (tree);
4645 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4647 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4649 tree->last_line_stamp = tree->chars_changed_stamp;
4650 tree->last_line = line;
4653 return tree->last_line;
4661 gtk_text_line_new (void)
4665 line = g_new0(GtkTextLine, 1);
4666 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4667 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4668 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4674 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4676 GtkTextLineData *ld;
4677 GtkTextLineData *next;
4679 g_return_if_fail (line != NULL);
4686 view = gtk_text_btree_get_view (tree, ld->view_id);
4688 g_assert (view != NULL);
4691 gtk_text_layout_free_line_data (view->layout, line, ld);
4700 gtk_text_line_set_parent (GtkTextLine *line,
4701 GtkTextBTreeNode *node)
4703 if (line->parent == node)
4705 line->parent = node;
4706 gtk_text_btree_node_invalidate_upward (node, NULL);
4710 cleanup_line (GtkTextLine *line)
4712 GtkTextLineSegment *seg, **prev_p;
4716 * Make a pass over all of the segments in the line, giving each
4717 * a chance to clean itself up. This could potentially change
4718 * the structure of the line, e.g. by merging two segments
4719 * together or having two segments cancel themselves; if so,
4720 * then repeat the whole process again, since the first structure
4721 * change might make other structure changes possible. Repeat
4722 * until eventually there are no changes.
4729 prev_p = &line->segments;
4730 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4732 if (seg->type->cleanupFunc != NULL)
4734 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4742 prev_p = &(*prev_p)->next;
4752 node_data_new (gpointer view_id)
4756 nd = g_slice_new (NodeData);
4758 nd->view_id = view_id;
4768 node_data_destroy (NodeData *nd)
4770 g_slice_free (NodeData, nd);
4774 node_data_list_destroy (NodeData *nd)
4776 g_slice_free_chain (NodeData, nd, next);
4780 node_data_find (NodeData *nd,
4785 if (nd->view_id == view_id)
4793 summary_destroy (Summary *summary)
4795 /* Fill with error-triggering garbage */
4796 summary->info = (void*)0x1;
4797 summary->toggle_count = 567;
4798 summary->next = (void*)0x1;
4799 g_slice_free (Summary, summary);
4802 static GtkTextBTreeNode*
4803 gtk_text_btree_node_new (void)
4805 GtkTextBTreeNode *node;
4807 node = g_new (GtkTextBTreeNode, 1);
4809 node->node_data = NULL;
4815 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4816 GtkTextTagInfo *info,
4821 summary = node->summary;
4822 while (summary != NULL)
4824 if (summary->info == info)
4826 summary->toggle_count += adjust;
4830 summary = summary->next;
4833 if (summary == NULL)
4835 /* didn't find a summary for our tag. */
4836 g_return_if_fail (adjust > 0);
4837 summary = g_slice_new (Summary);
4838 summary->info = info;
4839 summary->toggle_count = adjust;
4840 summary->next = node->summary;
4841 node->summary = summary;
4845 /* Note that the tag root and above do not have summaries
4846 for the tag; only nodes below the tag root have
4849 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4853 summary = node->summary;
4854 while (summary != NULL)
4857 summary->info->tag == tag)
4860 summary = summary->next;
4866 /* Add node and all children to the damage region. */
4868 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4872 nd = node->node_data;
4879 if (node->level == 0)
4883 line = node->children.line;
4884 while (line != NULL)
4886 GtkTextLineData *ld;
4900 GtkTextBTreeNode *child;
4902 child = node->children.node;
4904 while (child != NULL)
4906 gtk_text_btree_node_invalidate_downward (child);
4908 child = child->next;
4914 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4916 GtkTextBTreeNode *iter;
4919 while (iter != NULL)
4925 nd = node_data_find (iter->node_data, view_id);
4927 if (nd == NULL || !nd->valid)
4928 break; /* Once a node is invalid, we know its parents are as well. */
4934 gboolean should_continue = FALSE;
4936 nd = iter->node_data;
4941 should_continue = TRUE;
4948 if (!should_continue)
4949 break; /* This node was totally invalidated, so are its
4953 iter = iter->parent;
4959 * _gtk_text_btree_is_valid:
4960 * @tree: a #GtkTextBTree
4961 * @view_id: ID for the view
4963 * Check to see if the entire #GtkTextBTree is valid or not for
4966 * Return value: %TRUE if the entire #GtkTextBTree is valid
4969 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4973 g_return_val_if_fail (tree != NULL, FALSE);
4975 nd = node_data_find (tree->root_node->node_data, view_id);
4976 return (nd && nd->valid);
4979 typedef struct _ValidateState ValidateState;
4981 struct _ValidateState
4983 gint remaining_pixels;
4984 gboolean in_validation;
4991 gtk_text_btree_node_validate (BTreeView *view,
4992 GtkTextBTreeNode *node,
4994 ValidateState *state)
4996 gint node_valid = TRUE;
4997 gint node_width = 0;
4998 gint node_height = 0;
5000 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5001 g_return_if_fail (!nd->valid);
5003 if (node->level == 0)
5005 GtkTextLine *line = node->children.line;
5006 GtkTextLineData *ld;
5008 /* Iterate over leading valid lines */
5009 while (line != NULL)
5011 ld = _gtk_text_line_get_data (line, view_id);
5013 if (!ld || !ld->valid)
5015 else if (state->in_validation)
5017 state->in_validation = FALSE;
5022 state->y += ld->height;
5023 node_width = MAX (ld->width, node_width);
5024 node_height += ld->height;
5030 state->in_validation = TRUE;
5032 /* Iterate over invalid lines */
5033 while (line != NULL)
5035 ld = _gtk_text_line_get_data (line, view_id);
5037 if (ld && ld->valid)
5042 state->old_height += ld->height;
5043 ld = gtk_text_layout_wrap (view->layout, line, ld);
5044 state->new_height += ld->height;
5046 node_width = MAX (ld->width, node_width);
5047 node_height += ld->height;
5049 state->remaining_pixels -= ld->height;
5050 if (state->remaining_pixels <= 0)
5060 /* Iterate over the remaining lines */
5061 while (line != NULL)
5063 ld = _gtk_text_line_get_data (line, view_id);
5064 state->in_validation = FALSE;
5066 if (!ld || !ld->valid)
5071 node_width = MAX (ld->width, node_width);
5072 node_height += ld->height;
5080 GtkTextBTreeNode *child;
5083 child = node->children.node;
5085 /* Iterate over leading valid nodes */
5088 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5090 if (!child_nd->valid)
5092 else if (state->in_validation)
5094 state->in_validation = FALSE;
5099 state->y += child_nd->height;
5100 node_width = MAX (node_width, child_nd->width);
5101 node_height += child_nd->height;
5104 child = child->next;
5107 /* Iterate over invalid nodes */
5110 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5112 if (child_nd->valid)
5116 gtk_text_btree_node_validate (view, child, view_id, state);
5118 if (!child_nd->valid)
5120 node_width = MAX (node_width, child_nd->width);
5121 node_height += child_nd->height;
5123 if (!state->in_validation || state->remaining_pixels <= 0)
5125 child = child->next;
5130 child = child->next;
5133 /* Iterate over the remaining lines */
5136 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5137 state->in_validation = FALSE;
5139 if (!child_nd->valid)
5142 node_width = MAX (child_nd->width, node_width);
5143 node_height += child_nd->height;
5145 child = child->next;
5149 nd->width = node_width;
5150 nd->height = node_height;
5151 nd->valid = node_valid;
5155 * _gtk_text_btree_validate:
5156 * @tree: a #GtkTextBTree
5158 * @max_pixels: the maximum number of pixels to validate. (No more
5159 * than one paragraph beyond this limit will be validated)
5160 * @y: location to store starting y coordinate of validated region
5161 * @old_height: location to store old height of validated region
5162 * @new_height: location to store new height of validated region
5164 * Validate a single contiguous invalid region of a #GtkTextBTree for
5167 * Return value: %TRUE if a region has been validated, %FALSE if the
5168 * entire tree was already valid.
5171 _gtk_text_btree_validate (GtkTextBTree *tree,
5180 g_return_val_if_fail (tree != NULL, FALSE);
5182 view = gtk_text_btree_get_view (tree, view_id);
5183 g_return_val_if_fail (view != NULL, FALSE);
5185 if (!_gtk_text_btree_is_valid (tree, view_id))
5187 ValidateState state;
5189 state.remaining_pixels = max_pixels;
5190 state.in_validation = FALSE;
5192 state.old_height = 0;
5193 state.new_height = 0;
5195 gtk_text_btree_node_validate (view,
5202 *old_height = state.old_height;
5204 *new_height = state.new_height;
5206 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5207 _gtk_text_btree_check (tree);
5216 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5220 gboolean *valid_out)
5224 gboolean valid = TRUE;
5226 if (node->level == 0)
5228 GtkTextLine *line = node->children.line;
5230 while (line != NULL)
5232 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5234 if (!ld || !ld->valid)
5239 width = MAX (ld->width, width);
5240 height += ld->height;
5248 GtkTextBTreeNode *child = node->children.node;
5252 NodeData *child_nd = node_data_find (child->node_data, view_id);
5254 if (!child_nd || !child_nd->valid)
5259 width = MAX (child_nd->width, width);
5260 height += child_nd->height;
5263 child = child->next;
5268 *height_out = height;
5273 /* Recompute the validity and size of the view data for a given
5274 * view at this node from the immediate children of the node
5277 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5280 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5285 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5286 &width, &height, &valid);
5288 nd->height = height;
5295 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5300 gtk_text_btree_node_check_valid (node, view_id);
5301 node = node->parent;
5306 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5309 if (node->level == 0)
5311 return gtk_text_btree_node_check_valid (node, view_id);
5315 GtkTextBTreeNode *child = node->children.node;
5317 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5325 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5327 if (!child_nd->valid)
5329 nd->width = MAX (child_nd->width, nd->width);
5330 nd->height += child_nd->height;
5332 child = child->next;
5341 * _gtk_text_btree_validate_line:
5342 * @tree: a #GtkTextBTree
5343 * @line: line to validate
5344 * @view_id: view ID for the view to validate
5346 * Revalidate a single line of the btree for the given view, propagate
5347 * results up through the entire tree.
5350 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5354 GtkTextLineData *ld;
5357 g_return_if_fail (tree != NULL);
5358 g_return_if_fail (line != NULL);
5360 view = gtk_text_btree_get_view (tree, view_id);
5361 g_return_if_fail (view != NULL);
5363 ld = _gtk_text_line_get_data (line, view_id);
5364 if (!ld || !ld->valid)
5366 ld = gtk_text_layout_wrap (view->layout, line, ld);
5368 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5373 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5375 if (node->level == 0)
5379 line = node->children.line;
5380 while (line != NULL)
5382 GtkTextLineData *ld;
5384 ld = _gtk_text_line_remove_data (line, view_id);
5387 gtk_text_layout_free_line_data (view->layout, line, ld);
5394 GtkTextBTreeNode *child;
5396 child = node->children.node;
5398 while (child != NULL)
5401 gtk_text_btree_node_remove_view (view, child, view_id);
5403 child = child->next;
5407 gtk_text_btree_node_remove_data (node, view_id);
5411 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5413 if (node->level == 0)
5416 GtkTextLineSegment *seg;
5418 while (node->children.line != NULL)
5420 line = node->children.line;
5421 node->children.line = line->next;
5422 while (line->segments != NULL)
5424 seg = line->segments;
5425 line->segments = seg->next;
5427 (*seg->type->deleteFunc) (seg, line, TRUE);
5429 gtk_text_line_destroy (tree, line);
5434 GtkTextBTreeNode *childPtr;
5436 while (node->children.node != NULL)
5438 childPtr = node->children.node;
5439 node->children.node = childPtr->next;
5440 gtk_text_btree_node_destroy (tree, childPtr);
5444 gtk_text_btree_node_free_empty (tree, node);
5448 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5449 GtkTextBTreeNode *node)
5451 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5452 (node->level == 0 && node->children.line == NULL));
5454 summary_list_destroy (node->summary);
5455 node_data_list_destroy (node->node_data);
5460 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5464 nd = node->node_data;
5467 if (nd->view_id == view_id)
5475 nd = node_data_new (view_id);
5477 if (node->node_data)
5478 nd->next = node->node_data;
5480 node->node_data = nd;
5487 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5493 nd = node->node_data;
5496 if (nd->view_id == view_id)
5507 prev->next = nd->next;
5509 if (node->node_data == nd)
5510 node->node_data = nd->next;
5514 node_data_destroy (nd);
5518 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5519 gint *width, gint *height)
5523 g_return_if_fail (width != NULL);
5524 g_return_if_fail (height != NULL);
5526 nd = gtk_text_btree_node_ensure_data (node, view_id);
5531 *height = nd->height;
5534 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5535 * here isn't quite right, since for a lot of operations we want to
5536 * know which children of the common parent correspond to the two nodes
5537 * (e.g., when computing the order of two iters)
5539 static GtkTextBTreeNode *
5540 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5541 GtkTextBTreeNode *node2)
5543 while (node1->level < node2->level)
5544 node1 = node1->parent;
5545 while (node2->level < node1->level)
5546 node2 = node2->parent;
5547 while (node1 != node2)
5549 node1 = node1->parent;
5550 node2 = node2->parent;
5561 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5566 while (view != NULL)
5568 if (view->view_id == view_id)
5577 get_tree_bounds (GtkTextBTree *tree,
5581 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5582 _gtk_text_btree_get_end_iter (tree, end);
5586 tag_changed_cb (GtkTextTagTable *table,
5588 gboolean size_changed,
5593 /* We need to queue a relayout on all regions that are tagged with
5600 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5602 /* Must be a last toggle if there was a first one. */
5603 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5604 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5605 _gtk_text_btree_invalidate_region (tree,
5612 /* We only need to queue a redraw, not a relayout */
5617 while (view != NULL)
5621 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5622 gtk_text_layout_changed (view->layout, 0, height, height);
5630 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5633 /* Remove the tag from the tree */
5638 get_tree_bounds (tree, &start, &end);
5640 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5641 gtk_text_btree_remove_tag_info (tree, tag);
5645 /* Rebalance the out-of-whack node "node" */
5647 gtk_text_btree_rebalance (GtkTextBTree *tree,
5648 GtkTextBTreeNode *node)
5651 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5652 * up through the tree one GtkTextBTreeNode at a time until the root
5653 * GtkTextBTreeNode has been processed.
5656 while (node != NULL)
5658 GtkTextBTreeNode *new_node, *child;
5663 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5664 * then split off all but the first MIN_CHILDREN into a separate
5665 * GtkTextBTreeNode following the original one. Then repeat until the
5666 * GtkTextBTreeNode has a decent size.
5669 if (node->num_children > MAX_CHILDREN)
5674 * If the GtkTextBTreeNode being split is the root
5675 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5679 if (node->parent == NULL)
5681 new_node = gtk_text_btree_node_new ();
5682 new_node->parent = NULL;
5683 new_node->next = NULL;
5684 new_node->summary = NULL;
5685 new_node->level = node->level + 1;
5686 new_node->children.node = node;
5687 recompute_node_counts (tree, new_node);
5688 tree->root_node = new_node;
5690 new_node = gtk_text_btree_node_new ();
5691 new_node->parent = node->parent;
5692 new_node->next = node->next;
5693 node->next = new_node;
5694 new_node->summary = NULL;
5695 new_node->level = node->level;
5696 new_node->num_children = node->num_children - MIN_CHILDREN;
5697 if (node->level == 0)
5699 for (i = MIN_CHILDREN-1,
5700 line = node->children.line;
5701 i > 0; i--, line = line->next)
5703 /* Empty loop body. */
5705 new_node->children.line = line->next;
5710 for (i = MIN_CHILDREN-1,
5711 child = node->children.node;
5712 i > 0; i--, child = child->next)
5714 /* Empty loop body. */
5716 new_node->children.node = child->next;
5719 recompute_node_counts (tree, node);
5720 node->parent->num_children++;
5722 if (node->num_children <= MAX_CHILDREN)
5724 recompute_node_counts (tree, node);
5730 while (node->num_children < MIN_CHILDREN)
5732 GtkTextBTreeNode *other;
5733 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5734 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5735 int total_children, first_children, i;
5738 * Too few children for this GtkTextBTreeNode. If this is the root then,
5739 * it's OK for it to have less than MIN_CHILDREN children
5740 * as long as it's got at least two. If it has only one
5741 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5742 * the tree and use its child as the new root.
5745 if (node->parent == NULL)
5747 if ((node->num_children == 1) && (node->level > 0))
5749 tree->root_node = node->children.node;
5750 tree->root_node->parent = NULL;
5752 node->children.node = NULL;
5753 gtk_text_btree_node_free_empty (tree, node);
5759 * Not the root. Make sure that there are siblings to
5763 if (node->parent->num_children < 2)
5765 gtk_text_btree_rebalance (tree, node->parent);
5770 * Find a sibling neighbor to borrow from, and arrange for
5771 * node to be the earlier of the pair.
5774 if (node->next == NULL)
5776 for (other = node->parent->children.node;
5777 other->next != node;
5778 other = other->next)
5780 /* Empty loop body. */
5787 * We're going to either merge the two siblings together
5788 * into one GtkTextBTreeNode or redivide the children among them to
5789 * balance their loads. As preparation, join their two
5790 * child lists into a single list and remember the half-way
5791 * point in the list.
5794 total_children = node->num_children + other->num_children;
5795 first_children = total_children/2;
5796 if (node->children.node == NULL)
5798 node->children = other->children;
5799 other->children.node = NULL;
5800 other->children.line = NULL;
5802 if (node->level == 0)
5806 for (line = node->children.line, i = 1;
5808 line = line->next, i++)
5810 if (i == first_children)
5815 line->next = other->children.line;
5816 while (i <= first_children)
5825 GtkTextBTreeNode *child;
5827 for (child = node->children.node, i = 1;
5828 child->next != NULL;
5829 child = child->next, i++)
5831 if (i <= first_children)
5833 if (i == first_children)
5835 halfwaynode = child;
5839 child->next = other->children.node;
5840 while (i <= first_children)
5842 halfwaynode = child;
5843 child = child->next;
5849 * If the two siblings can simply be merged together, do it.
5852 if (total_children <= MAX_CHILDREN)
5854 recompute_node_counts (tree, node);
5855 node->next = other->next;
5856 node->parent->num_children--;
5858 other->children.node = NULL;
5859 other->children.line = NULL;
5860 gtk_text_btree_node_free_empty (tree, other);
5865 * The siblings can't be merged, so just divide their
5866 * children evenly between them.
5869 if (node->level == 0)
5871 other->children.line = halfwayline->next;
5872 halfwayline->next = NULL;
5876 other->children.node = halfwaynode->next;
5877 halfwaynode->next = NULL;
5880 recompute_node_counts (tree, node);
5881 recompute_node_counts (tree, other);
5884 node = node->parent;
5889 post_insert_fixup (GtkTextBTree *tree,
5891 gint line_count_delta,
5892 gint char_count_delta)
5895 GtkTextBTreeNode *node;
5898 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5899 * point, then rebalance the tree if necessary.
5902 for (node = line->parent ; node != NULL;
5903 node = node->parent)
5905 node->num_lines += line_count_delta;
5906 node->num_chars += char_count_delta;
5908 node = line->parent;
5909 node->num_children += line_count_delta;
5911 if (node->num_children > MAX_CHILDREN)
5913 gtk_text_btree_rebalance (tree, node);
5916 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5917 _gtk_text_btree_check (tree);
5920 static GtkTextTagInfo*
5921 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5924 GtkTextTagInfo *info;
5928 list = tree->tag_infos;
5929 while (list != NULL)
5932 if (info->tag == tag)
5935 list = g_slist_next (list);
5941 static GtkTextTagInfo*
5942 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5945 GtkTextTagInfo *info;
5947 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5951 /* didn't find it, create. */
5953 info = g_slice_new (GtkTextTagInfo);
5957 info->tag_root = NULL;
5958 info->toggle_count = 0;
5960 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5963 g_print ("Created tag info %p for tag %s(%p)\n",
5964 info, info->tag->name ? info->tag->name : "anon",
5973 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5976 GtkTextTagInfo *info;
5981 list = tree->tag_infos;
5982 while (list != NULL)
5985 if (info->tag == tag)
5988 g_print ("Removing tag info %p for tag %s(%p)\n",
5989 info, info->tag->name ? info->tag->name : "anon",
5995 prev->next = list->next;
5999 tree->tag_infos = list->next;
6002 g_slist_free (list);
6004 g_object_unref (info->tag);
6006 g_slice_free (GtkTextTagInfo, info);
6011 list = g_slist_next (list);
6016 recompute_level_zero_counts (GtkTextBTreeNode *node)
6019 GtkTextLineSegment *seg;
6021 g_assert (node->level == 0);
6023 line = node->children.line;
6024 while (line != NULL)
6026 node->num_children++;
6029 if (line->parent != node)
6030 gtk_text_line_set_parent (line, node);
6032 seg = line->segments;
6036 node->num_chars += seg->char_count;
6038 if (((seg->type != >k_text_toggle_on_type)
6039 && (seg->type != >k_text_toggle_off_type))
6040 || !(seg->body.toggle.inNodeCounts))
6046 GtkTextTagInfo *info;
6048 info = seg->body.toggle.info;
6050 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6061 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6064 GtkTextBTreeNode *child;
6066 g_assert (node->level > 0);
6068 child = node->children.node;
6069 while (child != NULL)
6071 node->num_children += 1;
6072 node->num_lines += child->num_lines;
6073 node->num_chars += child->num_chars;
6075 if (child->parent != node)
6077 child->parent = node;
6078 gtk_text_btree_node_invalidate_upward (node, NULL);
6081 summary = child->summary;
6082 while (summary != NULL)
6084 gtk_text_btree_node_adjust_toggle_count (node,
6086 summary->toggle_count);
6088 summary = summary->next;
6091 child = child->next;
6096 *----------------------------------------------------------------------
6098 * recompute_node_counts --
6100 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6101 * (tags, child information, etc.) by scanning the information in
6102 * its descendants. This procedure is called during rebalancing
6103 * when a GtkTextBTreeNode's child structure has changed.
6109 * The tag counts for node are modified to reflect its current
6110 * child structure, as are its num_children, num_lines, num_chars fields.
6111 * Also, all of the childrens' parent fields are made to point
6114 *----------------------------------------------------------------------
6118 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6121 Summary *summary, *summary2;
6124 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6125 * the existing Summary records (most of them will probably be reused).
6128 summary = node->summary;
6129 while (summary != NULL)
6131 summary->toggle_count = 0;
6132 summary = summary->next;
6135 node->num_children = 0;
6136 node->num_lines = 0;
6137 node->num_chars = 0;
6140 * Scan through the children, adding the childrens' tag counts into
6141 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6145 if (node->level == 0)
6146 recompute_level_zero_counts (node);
6148 recompute_level_nonzero_counts (node);
6153 gtk_text_btree_node_check_valid (node, view->view_id);
6158 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6159 * records that still have a zero count, or that have all the toggles.
6160 * The GtkTextBTreeNode with the children that account for all the tags toggles
6161 * have no summary information, and they become the tag_root for the tag.
6165 for (summary = node->summary; summary != NULL; )
6167 if (summary->toggle_count > 0 &&
6168 summary->toggle_count < summary->info->toggle_count)
6170 if (node->level == summary->info->tag_root->level)
6173 * The tag's root GtkTextBTreeNode split and some toggles left.
6174 * The tag root must move up a level.
6176 summary->info->tag_root = node->parent;
6179 summary = summary->next;
6182 if (summary->toggle_count == summary->info->toggle_count)
6185 * A GtkTextBTreeNode merge has collected all the toggles under
6186 * one GtkTextBTreeNode. Push the root down to this level.
6188 summary->info->tag_root = node;
6190 if (summary2 != NULL)
6192 summary2->next = summary->next;
6193 summary_destroy (summary);
6194 summary = summary2->next;
6198 node->summary = summary->next;
6199 summary_destroy (summary);
6200 summary = node->summary;
6206 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6207 GtkTextTagInfo *info,
6208 gint delta) /* may be negative */
6210 Summary *summary, *prevPtr;
6211 GtkTextBTreeNode *node2Ptr;
6212 int rootLevel; /* Level of original tag root */
6214 info->toggle_count += delta;
6216 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6218 info->tag_root = node;
6223 * Note the level of the existing root for the tag so we can detect
6224 * if it needs to be moved because of the toggle count change.
6227 rootLevel = info->tag_root->level;
6230 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6231 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6235 for ( ; node != info->tag_root; node = node->parent)
6238 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6239 * perhaps all we have to do is adjust its count.
6242 for (prevPtr = NULL, summary = node->summary;
6244 prevPtr = summary, summary = summary->next)
6246 if (summary->info == info)
6251 if (summary != NULL)
6253 summary->toggle_count += delta;
6254 if (summary->toggle_count > 0 &&
6255 summary->toggle_count < info->toggle_count)
6259 if (summary->toggle_count != 0)
6262 * Should never find a GtkTextBTreeNode with max toggle count at this
6263 * point (there shouldn't have been a summary entry in the
6267 g_error ("%s: bad toggle count (%d) max (%d)",
6268 G_STRLOC, summary->toggle_count, info->toggle_count);
6272 * Zero toggle count; must remove this tag from the list.
6275 if (prevPtr == NULL)
6277 node->summary = summary->next;
6281 prevPtr->next = summary->next;
6283 summary_destroy (summary);
6288 * This tag isn't currently in the summary information list.
6291 if (rootLevel == node->level)
6295 * The old tag root is at the same level in the tree as this
6296 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6297 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6298 * as well as the old root (if not, we'll move it up again
6299 * the next time through the loop). To push it up one level
6300 * we copy the original toggle count into the summary
6301 * information at the old root and change the root to its
6302 * parent GtkTextBTreeNode.
6305 GtkTextBTreeNode *rootnode = info->tag_root;
6306 summary = g_slice_new (Summary);
6307 summary->info = info;
6308 summary->toggle_count = info->toggle_count - delta;
6309 summary->next = rootnode->summary;
6310 rootnode->summary = summary;
6311 rootnode = rootnode->parent;
6312 rootLevel = rootnode->level;
6313 info->tag_root = rootnode;
6315 summary = g_slice_new (Summary);
6316 summary->info = info;
6317 summary->toggle_count = delta;
6318 summary->next = node->summary;
6319 node->summary = summary;
6324 * If we've decremented the toggle count, then it may be necessary
6325 * to push the tag root down one or more levels.
6332 if (info->toggle_count == 0)
6334 info->tag_root = (GtkTextBTreeNode *) NULL;
6337 node = info->tag_root;
6338 while (node->level > 0)
6341 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6342 * toggles. If so, push the root down one level.
6345 for (node2Ptr = node->children.node;
6346 node2Ptr != (GtkTextBTreeNode *)NULL ;
6347 node2Ptr = node2Ptr->next)
6349 for (prevPtr = NULL, summary = node2Ptr->summary;
6351 prevPtr = summary, summary = summary->next)
6353 if (summary->info == info)
6358 if (summary == NULL)
6362 if (summary->toggle_count != info->toggle_count)
6365 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6372 * This GtkTextBTreeNode has all the toggles, so push down the root.
6375 if (prevPtr == NULL)
6377 node2Ptr->summary = summary->next;
6381 prevPtr->next = summary->next;
6383 summary_destroy (summary);
6384 info->tag_root = node2Ptr;
6387 node = info->tag_root;
6392 *----------------------------------------------------------------------
6396 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6397 * increments the count for a particular tag, adding a new
6398 * entry for that tag if there wasn't one previously.
6404 * The information at *tagInfoPtr may be modified, and the arrays
6405 * may be reallocated to make them larger.
6407 *----------------------------------------------------------------------
6411 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6416 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6417 count > 0; tag_p++, count--)
6421 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6427 * There isn't currently an entry for this tag, so we have to
6428 * make a new one. If the arrays are full, then enlarge the
6432 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6434 GtkTextTag **newTags;
6435 int *newCounts, newSize;
6437 newSize = 2*tagInfoPtr->arraySize;
6438 newTags = (GtkTextTag **) g_malloc ((unsigned)
6439 (newSize*sizeof (GtkTextTag *)));
6440 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6441 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6442 g_free ((char *) tagInfoPtr->tags);
6443 tagInfoPtr->tags = newTags;
6444 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6445 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6446 tagInfoPtr->arraySize *sizeof (int));
6447 g_free ((char *) tagInfoPtr->counts);
6448 tagInfoPtr->counts = newCounts;
6449 tagInfoPtr->arraySize = newSize;
6452 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6453 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6454 tagInfoPtr->numTags++;
6458 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6459 const GtkTextIter *iter)
6461 GtkTextLineSegment *prev;
6465 line = _gtk_text_iter_get_text_line (iter);
6466 tree = _gtk_text_iter_get_btree (iter);
6468 prev = gtk_text_line_segment_split (iter);
6471 seg->next = line->segments;
6472 line->segments = seg;
6476 seg->next = prev->next;
6479 cleanup_line (line);
6480 segments_changed (tree);
6482 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6483 _gtk_text_btree_check (tree);
6487 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6488 GtkTextLineSegment *seg,
6491 GtkTextLineSegment *prev;
6493 if (line->segments == seg)
6495 line->segments = seg->next;
6499 for (prev = line->segments; prev->next != seg;
6502 /* Empty loop body. */
6504 prev->next = seg->next;
6506 cleanup_line (line);
6507 segments_changed (tree);
6511 * This is here because it requires BTree internals, it logically
6512 * belongs in gtktextsegment.c
6517 *--------------------------------------------------------------
6519 * _gtk_toggle_segment_check_func --
6521 * This procedure is invoked to perform consistency checks
6522 * on toggle segments.
6528 * If a consistency problem is found the procedure g_errors.
6530 *--------------------------------------------------------------
6534 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6540 if (segPtr->byte_count != 0)
6542 g_error ("toggle_segment_check_func: segment had non-zero size");
6544 if (!segPtr->body.toggle.inNodeCounts)
6546 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6548 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6549 for (summary = line->parent->summary; ;
6550 summary = summary->next)
6552 if (summary == NULL)
6556 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6563 if (summary->info == segPtr->body.toggle.info)
6567 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6579 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6580 GtkTextBTreeNode *node,
6590 while (view != NULL)
6592 if (view->view_id == nd->view_id)
6599 g_error ("Node has data for a view %p no longer attached to the tree",
6602 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6603 &width, &height, &valid);
6605 /* valid aggregate not checked the same as width/height, because on
6606 * btree rebalance we can have invalid nodes where all lines below
6607 * them are actually valid, due to moving lines around between
6610 * The guarantee is that if there are invalid lines the node is
6611 * invalid - we don't guarantee that if the node is invalid there
6612 * are invalid lines.
6615 if (nd->width != width ||
6616 nd->height != height ||
6617 (nd->valid && !valid))
6619 g_error ("Node aggregates for view %p are invalid:\n"
6620 "Are (%d,%d,%s), should be (%d,%d,%s)",
6622 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6623 width, height, valid ? "TRUE" : "FALSE");
6628 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6629 GtkTextBTreeNode *node)
6631 GtkTextBTreeNode *childnode;
6632 Summary *summary, *summary2;
6634 GtkTextLineSegment *segPtr;
6635 int num_children, num_lines, num_chars, toggle_count, min_children;
6636 GtkTextLineData *ld;
6639 if (node->parent != NULL)
6641 min_children = MIN_CHILDREN;
6643 else if (node->level > 0)
6650 if ((node->num_children < min_children)
6651 || (node->num_children > MAX_CHILDREN))
6653 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6654 node->num_children);
6657 nd = node->node_data;
6660 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6667 if (node->level == 0)
6669 for (line = node->children.line; line != NULL;
6672 if (line->parent != node)
6674 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6676 if (line->segments == NULL)
6678 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6684 /* Just ensuring we don't segv while doing this loop */
6689 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6691 if (segPtr->type->checkFunc != NULL)
6693 (*segPtr->type->checkFunc)(segPtr, line);
6695 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6696 && (segPtr->next != NULL)
6697 && (segPtr->next->byte_count == 0)
6698 && (segPtr->next->type->leftGravity))
6700 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6702 if ((segPtr->next == NULL)
6703 && (segPtr->type != >k_text_char_type))
6705 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6708 num_chars += segPtr->char_count;
6717 for (childnode = node->children.node; childnode != NULL;
6718 childnode = childnode->next)
6720 if (childnode->parent != node)
6722 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6724 if (childnode->level != (node->level-1))
6726 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6727 node->level, childnode->level);
6729 gtk_text_btree_node_check_consistency (tree, childnode);
6730 for (summary = childnode->summary; summary != NULL;
6731 summary = summary->next)
6733 for (summary2 = node->summary; ;
6734 summary2 = summary2->next)
6736 if (summary2 == NULL)
6738 if (summary->info->tag_root == node)
6742 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6743 summary->info->tag->name,
6744 "present in parent summaries");
6746 if (summary->info == summary2->info)
6753 num_lines += childnode->num_lines;
6754 num_chars += childnode->num_chars;
6757 if (num_children != node->num_children)
6759 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6760 num_children, node->num_children);
6762 if (num_lines != node->num_lines)
6764 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6765 num_lines, node->num_lines);
6767 if (num_chars != node->num_chars)
6769 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6770 num_chars, node->num_chars);
6773 for (summary = node->summary; summary != NULL;
6774 summary = summary->next)
6776 if (summary->info->toggle_count == summary->toggle_count)
6778 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6779 summary->info->tag->name);
6782 if (node->level == 0)
6784 for (line = node->children.line; line != NULL;
6787 for (segPtr = line->segments; segPtr != NULL;
6788 segPtr = segPtr->next)
6790 if ((segPtr->type != >k_text_toggle_on_type)
6791 && (segPtr->type != >k_text_toggle_off_type))
6795 if (segPtr->body.toggle.info == summary->info)
6797 if (!segPtr->body.toggle.inNodeCounts)
6798 g_error ("Toggle segment not in the node counts");
6807 for (childnode = node->children.node;
6809 childnode = childnode->next)
6811 for (summary2 = childnode->summary;
6813 summary2 = summary2->next)
6815 if (summary2->info == summary->info)
6817 toggle_count += summary2->toggle_count;
6822 if (toggle_count != summary->toggle_count)
6824 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6825 toggle_count, summary->toggle_count);
6827 for (summary2 = summary->next; summary2 != NULL;
6828 summary2 = summary2->next)
6830 if (summary2->info == summary->info)
6832 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6833 summary->info->tag->name);
6840 listify_foreach (GtkTextTag *tag, gpointer user_data)
6842 GSList** listp = user_data;
6844 *listp = g_slist_prepend (*listp, tag);
6848 list_of_tags (GtkTextTagTable *table)
6850 GSList *list = NULL;
6852 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6858 _gtk_text_btree_check (GtkTextBTree *tree)
6861 GtkTextBTreeNode *node;
6863 GtkTextLineSegment *seg;
6865 GSList *all_tags, *taglist = NULL;
6867 GtkTextTagInfo *info;
6870 * Make sure that the tag toggle counts and the tag root pointers are OK.
6872 all_tags = list_of_tags (tree->table);
6873 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6875 tag = taglist->data;
6876 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6879 node = info->tag_root;
6882 if (info->toggle_count != 0)
6884 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6885 tag->name, info->toggle_count);
6887 continue; /* no ranges for the tag */
6889 else if (info->toggle_count == 0)
6891 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6894 else if (info->toggle_count & 1)
6896 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6897 tag->name, info->toggle_count);
6899 for (summary = node->summary; summary != NULL;
6900 summary = summary->next)
6902 if (summary->info->tag == tag)
6904 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6908 if (node->level > 0)
6910 for (node = node->children.node ; node != NULL ;
6913 for (summary = node->summary; summary != NULL;
6914 summary = summary->next)
6916 if (summary->info->tag == tag)
6918 count += summary->toggle_count;
6925 const GtkTextLineSegmentClass *last = NULL;
6927 for (line = node->children.line ; line != NULL ;
6930 for (seg = line->segments; seg != NULL;
6933 if ((seg->type == >k_text_toggle_on_type ||
6934 seg->type == >k_text_toggle_off_type) &&
6935 seg->body.toggle.info->tag == tag)
6937 if (last == seg->type)
6938 g_error ("Two consecutive toggles on or off weren't merged");
6939 if (!seg->body.toggle.inNodeCounts)
6940 g_error ("Toggle segment not in the node counts");
6949 if (count != info->toggle_count)
6951 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6952 info->toggle_count, tag->name, count);
6957 g_slist_free (all_tags);
6960 * Call a recursive procedure to do the main body of checks.
6963 node = tree->root_node;
6964 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6967 * Make sure that there are at least two lines in the text and
6968 * that the last line has no characters except a newline.
6971 if (node->num_lines < 2)
6973 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6975 if (node->num_chars < 2)
6977 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6979 while (node->level > 0)
6981 node = node->children.node;
6982 while (node->next != NULL)
6987 line = node->children.line;
6988 while (line->next != NULL)
6992 seg = line->segments;
6993 while ((seg->type == >k_text_toggle_off_type)
6994 || (seg->type == >k_text_right_mark_type)
6995 || (seg->type == >k_text_left_mark_type))
6998 * It's OK to toggle a tag off in the last line, but
6999 * not to start a new range. It's also OK to have marks
7005 if (seg->type != >k_text_char_type)
7007 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7009 if (seg->next != NULL)
7011 g_error ("_gtk_text_btree_check: last line has too many segments");
7013 if (seg->byte_count != 1)
7015 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7018 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7020 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7025 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7026 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7027 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7028 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7031 _gtk_text_btree_spew (GtkTextBTree *tree)
7036 printf ("%d lines in tree %p\n",
7037 _gtk_text_btree_line_count (tree), tree);
7039 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7041 while (line != NULL)
7043 _gtk_text_btree_spew_line (tree, line);
7044 line = _gtk_text_line_next (line);
7047 printf ("=================== Tag information\n");
7052 list = tree->tag_infos;
7054 while (list != NULL)
7056 GtkTextTagInfo *info;
7060 printf (" tag `%s': root at %p, toggle count %d\n",
7061 info->tag->name, info->tag_root, info->toggle_count);
7063 list = g_slist_next (list);
7066 if (tree->tag_infos == NULL)
7068 printf (" (no tags in the tree)\n");
7072 printf ("=================== Tree nodes\n");
7075 _gtk_text_btree_spew_node (tree->root_node, 0);
7080 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7083 GtkTextLineSegment *seg;
7085 spaces = g_strnfill (indent, ' ');
7087 printf ("%sline %p chars %d bytes %d\n",
7089 _gtk_text_line_char_count (line),
7090 _gtk_text_line_byte_count (line));
7092 seg = line->segments;
7095 if (seg->type == >k_text_char_type)
7097 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7102 if (*s == '\n' || *s == '\r')
7106 printf ("%s chars `%s'...\n", spaces, str);
7109 else if (seg->type == >k_text_right_mark_type)
7111 printf ("%s right mark `%s' visible: %d\n",
7113 seg->body.mark.name,
7114 seg->body.mark.visible);
7116 else if (seg->type == >k_text_left_mark_type)
7118 printf ("%s left mark `%s' visible: %d\n",
7120 seg->body.mark.name,
7121 seg->body.mark.visible);
7123 else if (seg->type == >k_text_toggle_on_type ||
7124 seg->type == >k_text_toggle_off_type)
7126 printf ("%s tag `%s' %s\n",
7127 spaces, seg->body.toggle.info->tag->name,
7128 seg->type == >k_text_toggle_off_type ? "off" : "on");
7138 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7141 GtkTextBTreeNode *iter;
7144 spaces = g_strnfill (indent, ' ');
7146 printf ("%snode %p level %d children %d lines %d chars %d\n",
7147 spaces, node, node->level,
7148 node->num_children, node->num_lines, node->num_chars);
7153 printf ("%s %d toggles of `%s' below this node\n",
7154 spaces, s->toggle_count, s->info->tag->name);
7160 if (node->level > 0)
7162 iter = node->children.node;
7163 while (iter != NULL)
7165 _gtk_text_btree_spew_node (iter, indent + 2);
7172 GtkTextLine *line = node->children.line;
7173 while (line != NULL)
7175 _gtk_text_btree_spew_line_short (line, indent + 2);
7183 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7185 GtkTextLineSegment * seg;
7187 printf ("%4d| line: %p parent: %p next: %p\n",
7188 _gtk_text_line_get_number (line), line, line->parent, line->next);
7190 seg = line->segments;
7194 _gtk_text_btree_spew_segment (tree, seg);
7200 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7202 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7203 seg, seg->type->name, seg->byte_count, seg->char_count);
7205 if (seg->type == >k_text_char_type)
7207 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7208 printf (" `%s'\n", str);
7211 else if (seg->type == >k_text_right_mark_type)
7213 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7214 seg->body.mark.name,
7215 seg->body.mark.visible,
7216 seg->body.mark.not_deleteable);
7218 else if (seg->type == >k_text_left_mark_type)
7220 printf (" left 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_toggle_on_type ||
7226 seg->type == >k_text_toggle_off_type)
7228 printf (" tag `%s' priority %d\n",
7229 seg->body.toggle.info->tag->name,
7230 seg->body.toggle.info->tag->priority);
7234 #define __GTK_TEXT_BTREE_C__
7235 #include "gtkaliasdef.c"