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 seg->type == >k_text_toggle_off_type &&
890 prev_seg->next->type == >k_text_toggle_on_type &&
891 seg->body.toggle.info == prev_seg->next->body.toggle.info)
893 /* Try to match an off toggle with the matching on toggle
894 * if it immediately follows. This is a common case, and
895 * handling it here prevents quadratic blowup in
896 * cleanup_line() below. See bug 317125.
898 next2 = prev_seg->next->next;
899 g_free ((char *)prev_seg->next);
900 prev_seg->next = next2;
901 g_free ((char *)seg);
906 seg->next = prev_seg->next;
907 prev_seg->next = seg;
910 if (seg && seg->type->leftGravity)
917 /* Segment is gone. Decrement the char count of the node and
919 for (node = curnode; node != NULL;
922 node->num_chars -= char_count;
930 * If the beginning and end of the deletion range are in different
931 * lines, join the two lines together and discard the ending line.
934 if (start_line != end_line)
937 GtkTextBTreeNode *ancestor_node;
938 GtkTextLine *prevline;
941 /* last_seg was appended to start_line up at the top of this function */
943 for (seg = last_seg; seg != NULL;
946 chars_moved += seg->char_count;
947 if (seg->type->lineChangeFunc != NULL)
949 (*seg->type->lineChangeFunc)(seg, end_line);
953 for (node = start_line->parent; node != NULL;
956 node->num_chars += chars_moved;
959 curnode = end_line->parent;
960 for (node = curnode; node != NULL;
963 node->num_chars -= chars_moved;
966 curnode->num_children--;
967 prevline = curnode->children.line;
968 if (prevline == end_line)
970 curnode->children.line = end_line->next;
974 while (prevline->next != end_line)
976 prevline = prevline->next;
978 prevline->next = end_line->next;
980 end_line->next = deleted_lines;
981 deleted_lines = end_line;
983 /* We now fix up the per-view aggregates. We add all the height and
984 * width for the deleted lines to the start line, so that when revalidation
985 * occurs, the correct change in size is seen.
987 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
994 gint deleted_width = 0;
995 gint deleted_height = 0;
997 line = deleted_lines;
1000 GtkTextLine *next_line = line->next;
1001 ld = _gtk_text_line_get_data (line, view->view_id);
1005 deleted_width = MAX (deleted_width, ld->width);
1006 deleted_height += ld->height;
1010 gtk_text_line_destroy (tree, line);
1015 if (deleted_width > 0 || deleted_height > 0)
1017 ld = _gtk_text_line_get_data (start_line, view->view_id);
1021 /* This means that start_line has never been validated.
1022 * We don't really want to do the validation here but
1023 * we do need to store our temporary sizes. So we
1024 * create the line data and assume a line w/h of 0.
1026 ld = _gtk_text_line_data_new (view->layout, start_line);
1027 _gtk_text_line_add_data (start_line, ld);
1033 ld->width = MAX (deleted_width, ld->width);
1034 ld->height += deleted_height;
1038 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1039 if (ancestor_node->parent)
1040 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1045 /* avoid dangling pointer */
1046 deleted_lines = NULL;
1048 gtk_text_btree_rebalance (tree, curnode);
1052 * Cleanup the segments in the new line.
1055 cleanup_line (start_line);
1058 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1061 gtk_text_btree_rebalance (tree, start_line->parent);
1063 /* Notify outstanding iterators that they
1065 chars_changed (tree);
1066 segments_changed (tree);
1068 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1069 _gtk_text_btree_check (tree);
1071 /* Re-initialize our iterators */
1072 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1075 gtk_text_btree_resolve_bidi (start, end);
1079 _gtk_text_btree_insert (GtkTextIter *iter,
1083 GtkTextLineSegment *prev_seg; /* The segment just before the first
1084 * new segment (NULL means new segment
1085 * is at beginning of line). */
1086 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1087 * are inserted just after this one.
1088 * NULL means insert at beginning of
1090 GtkTextLine *line; /* Current line (new segments are
1091 * added to this line). */
1092 GtkTextLineSegment *seg;
1093 GtkTextLine *newline;
1094 int chunk_len; /* # characters in current chunk. */
1095 gint sol; /* start of line */
1096 gint eol; /* Pointer to character just after last
1097 * one in current chunk.
1099 gint delim; /* index of paragraph delimiter */
1100 int line_count_delta; /* Counts change to total number of
1104 int char_count_delta; /* change to number of chars */
1106 gint start_byte_index;
1107 GtkTextLine *start_line;
1109 g_return_if_fail (text != NULL);
1110 g_return_if_fail (iter != NULL);
1113 len = strlen (text);
1115 /* extract iterator info */
1116 tree = _gtk_text_iter_get_btree (iter);
1117 line = _gtk_text_iter_get_text_line (iter);
1120 start_byte_index = gtk_text_iter_get_line_index (iter);
1122 /* Get our insertion segment split. Note this assumes line allows
1123 * char insertions, which isn't true of the "last" line. But iter
1124 * should not be on that line, as we assert here.
1126 g_assert (!_gtk_text_line_is_last (line, tree));
1127 prev_seg = gtk_text_line_segment_split (iter);
1130 /* Invalidate all iterators */
1131 chars_changed (tree);
1132 segments_changed (tree);
1135 * Chop the text up into lines and create a new segment for
1136 * each line, plus a new line for the leftovers from the
1142 line_count_delta = 0;
1143 char_count_delta = 0;
1148 pango_find_paragraph_boundary (text + sol,
1153 /* make these relative to the start of the text */
1157 g_assert (eol >= sol);
1158 g_assert (delim >= sol);
1159 g_assert (eol >= delim);
1160 g_assert (sol >= 0);
1161 g_assert (eol <= len);
1163 chunk_len = eol - sol;
1165 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1166 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1168 char_count_delta += seg->char_count;
1170 if (cur_seg == NULL)
1172 seg->next = line->segments;
1173 line->segments = seg;
1177 seg->next = cur_seg->next;
1178 cur_seg->next = seg;
1183 /* chunk didn't end with a paragraph separator */
1184 g_assert (eol == len);
1189 * The chunk ended with a newline, so create a new GtkTextLine
1190 * and move the remainder of the old line to it.
1193 newline = gtk_text_line_new ();
1194 gtk_text_line_set_parent (newline, line->parent);
1195 newline->next = line->next;
1196 line->next = newline;
1197 newline->segments = seg->next;
1205 * Cleanup the starting line for the insertion, plus the ending
1206 * line if it's different.
1209 cleanup_line (start_line);
1210 if (line != start_line)
1212 cleanup_line (line);
1215 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1217 /* Invalidate our region, and reset the iterator the user
1218 passed in to point to the end of the inserted text. */
1224 _gtk_text_btree_get_iter_at_line (tree,
1230 /* We could almost certainly be more efficient here
1231 by saving the information from the insertion loop
1233 gtk_text_iter_forward_chars (&end, char_count_delta);
1235 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1236 _gtk_text_btree_invalidate_region (tree,
1240 /* Convenience for the user */
1243 gtk_text_btree_resolve_bidi (&start, &end);
1248 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1249 GtkTextLineSegment *seg)
1253 GtkTextLineSegment *prevPtr;
1256 gint start_byte_offset;
1258 line = _gtk_text_iter_get_text_line (iter);
1259 tree = _gtk_text_iter_get_btree (iter);
1260 start_byte_offset = gtk_text_iter_get_line_index (iter);
1262 prevPtr = gtk_text_line_segment_split (iter);
1263 if (prevPtr == NULL)
1265 seg->next = line->segments;
1266 line->segments = seg;
1270 seg->next = prevPtr->next;
1271 prevPtr->next = seg;
1274 post_insert_fixup (tree, line, 0, seg->char_count);
1276 chars_changed (tree);
1277 segments_changed (tree);
1279 /* reset *iter for the user, and invalidate tree nodes */
1281 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1284 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1286 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1287 _gtk_text_btree_invalidate_region (tree, &start, iter);
1291 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1294 GtkTextLineSegment *seg;
1296 seg = _gtk_pixbuf_segment_new (pixbuf);
1298 insert_pixbuf_or_widget_segment (iter, seg);
1302 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1303 GtkTextChildAnchor *anchor)
1305 GtkTextLineSegment *seg;
1308 if (anchor->segment != NULL)
1310 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1314 seg = _gtk_widget_segment_new (anchor);
1316 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1317 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1319 insert_pixbuf_or_widget_segment (iter, seg);
1321 if (tree->child_anchor_table == NULL)
1322 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1324 g_hash_table_insert (tree->child_anchor_table,
1325 seg->body.child.obj,
1326 seg->body.child.obj);
1330 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1332 GtkTextLineSegment *seg;
1334 seg = anchor->segment;
1336 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1345 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1346 GtkTextBTreeNode *node, gint y, gint *line_top,
1347 GtkTextLine *last_line)
1351 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1352 _gtk_text_btree_check (tree);
1354 if (node->level == 0)
1358 line = node->children.line;
1360 while (line != NULL && line != last_line)
1362 GtkTextLineData *ld;
1364 ld = _gtk_text_line_get_data (line, view->view_id);
1368 if (y < (current_y + (ld ? ld->height : 0)))
1371 current_y += ld->height;
1372 *line_top += ld->height;
1381 GtkTextBTreeNode *child;
1383 child = node->children.node;
1385 while (child != NULL)
1390 gtk_text_btree_node_get_size (child, view->view_id,
1393 if (y < (current_y + height))
1394 return find_line_by_y (tree, view, child,
1395 y - current_y, line_top,
1398 current_y += height;
1399 *line_top += height;
1401 child = child->next;
1409 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1416 GtkTextLine *last_line;
1419 view = gtk_text_btree_get_view (tree, view_id);
1420 g_return_val_if_fail (view != NULL, NULL);
1422 last_line = get_last_line (tree);
1424 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1428 *line_top_out = line_top;
1434 find_line_top_in_line_list (GtkTextBTree *tree,
1437 GtkTextLine *target_line,
1440 while (line != NULL)
1442 GtkTextLineData *ld;
1444 if (line == target_line)
1447 ld = _gtk_text_line_get_data (line, view->view_id);
1454 g_assert_not_reached (); /* If we get here, our
1455 target line didn't exist
1456 under its parent node */
1461 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1462 GtkTextLine *target_line,
1469 GtkTextBTreeNode *node;
1471 view = gtk_text_btree_get_view (tree, view_id);
1473 g_return_val_if_fail (view != NULL, 0);
1476 node = target_line->parent;
1477 while (node != NULL)
1479 nodes = g_slist_prepend (nodes, node);
1480 node = node->parent;
1484 while (iter != NULL)
1488 if (node->level == 0)
1490 g_slist_free (nodes);
1491 return find_line_top_in_line_list (tree, view,
1492 node->children.line,
1497 GtkTextBTreeNode *child;
1498 GtkTextBTreeNode *target_node;
1500 g_assert (iter->next != NULL); /* not at level 0 */
1501 target_node = iter->next->data;
1503 child = node->children.node;
1505 while (child != NULL)
1510 if (child == target_node)
1514 gtk_text_btree_node_get_size (child, view->view_id,
1518 child = child->next;
1520 g_assert (child != NULL); /* should have broken out before we
1524 iter = g_slist_next (iter);
1527 g_assert_not_reached (); /* we return when we find the target line */
1532 _gtk_text_btree_add_view (GtkTextBTree *tree,
1533 GtkTextLayout *layout)
1536 GtkTextLine *last_line;
1537 GtkTextLineData *line_data;
1539 g_return_if_fail (tree != NULL);
1541 view = g_new (BTreeView, 1);
1543 view->view_id = layout;
1544 view->layout = layout;
1546 view->next = tree->views;
1551 g_assert (tree->views->prev == NULL);
1552 tree->views->prev = view;
1557 /* The last line in the buffer has identity values for the per-view
1558 * data so that we can avoid special case checks for it in a large
1561 last_line = get_last_line (tree);
1563 line_data = g_new (GtkTextLineData, 1);
1564 line_data->view_id = layout;
1565 line_data->next = NULL;
1566 line_data->width = 0;
1567 line_data->height = 0;
1568 line_data->valid = TRUE;
1570 _gtk_text_line_add_data (last_line, line_data);
1574 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1578 GtkTextLine *last_line;
1579 GtkTextLineData *line_data;
1581 g_return_if_fail (tree != NULL);
1585 while (view != NULL)
1587 if (view->view_id == view_id)
1593 g_return_if_fail (view != NULL);
1596 view->next->prev = view->prev;
1599 view->prev->next = view->next;
1601 if (view == tree->views)
1602 tree->views = view->next;
1604 /* Remove the line data for the last line which we added ourselves.
1605 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1607 last_line = get_last_line (tree);
1608 line_data = _gtk_text_line_remove_data (last_line, view_id);
1611 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1613 view->layout = (gpointer) 0xdeadbeef;
1614 view->view_id = (gpointer) 0xdeadbeef;
1620 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1621 const GtkTextIter *start,
1622 const GtkTextIter *end)
1628 while (view != NULL)
1630 gtk_text_layout_invalidate (view->layout, start, end);
1637 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1642 g_return_if_fail (tree != NULL);
1643 g_return_if_fail (view_id != NULL);
1645 gtk_text_btree_node_get_size (tree->root_node, view_id,
1660 iter_stack_new (void)
1663 stack = g_slice_new (IterStack);
1664 stack->iters = NULL;
1671 iter_stack_push (IterStack *stack,
1672 const GtkTextIter *iter)
1675 if (stack->count > stack->alloced)
1677 stack->alloced = stack->count*2;
1678 stack->iters = g_realloc (stack->iters,
1679 stack->alloced * sizeof (GtkTextIter));
1681 stack->iters[stack->count-1] = *iter;
1685 iter_stack_pop (IterStack *stack,
1688 if (stack->count == 0)
1693 *iter = stack->iters[stack->count];
1699 iter_stack_free (IterStack *stack)
1701 g_free (stack->iters);
1702 g_slice_free (IterStack, stack);
1706 iter_stack_invert (IterStack *stack)
1708 if (stack->count > 0)
1711 guint j = stack->count - 1;
1716 tmp = stack->iters[i];
1717 stack->iters[i] = stack->iters[j];
1718 stack->iters[j] = tmp;
1727 queue_tag_redisplay (GtkTextBTree *tree,
1729 const GtkTextIter *start,
1730 const GtkTextIter *end)
1732 if (_gtk_text_tag_affects_size (tag))
1734 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1735 _gtk_text_btree_invalidate_region (tree, start, end);
1737 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1739 /* We only need to queue a redraw, not a relayout */
1740 redisplay_region (tree, start, end);
1743 /* We don't need to do anything if the tag doesn't affect display */
1747 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1748 const GtkTextIter *end_orig,
1752 GtkTextLineSegment *seg, *prev;
1753 GtkTextLine *cleanupline;
1754 gboolean toggled_on;
1755 GtkTextLine *start_line;
1756 GtkTextLine *end_line;
1758 GtkTextIter start, end;
1761 GtkTextTagInfo *info;
1763 g_return_if_fail (start_orig != NULL);
1764 g_return_if_fail (end_orig != NULL);
1765 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1766 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1767 _gtk_text_iter_get_btree (end_orig));
1768 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1771 printf ("%s tag %s from %d to %d\n",
1772 add ? "Adding" : "Removing",
1774 gtk_text_buffer_get_offset (start_orig),
1775 gtk_text_buffer_get_offset (end_orig));
1778 if (gtk_text_iter_equal (start_orig, end_orig))
1781 start = *start_orig;
1784 gtk_text_iter_order (&start, &end);
1786 tree = _gtk_text_iter_get_btree (&start);
1788 queue_tag_redisplay (tree, tag, &start, &end);
1790 info = gtk_text_btree_get_tag_info (tree, tag);
1792 start_line = _gtk_text_iter_get_text_line (&start);
1793 end_line = _gtk_text_iter_get_text_line (&end);
1795 /* Find all tag toggles in the region; we are going to delete them.
1796 We need to find them in advance, because
1797 forward_find_tag_toggle () won't work once we start playing around
1799 stack = iter_stack_new ();
1802 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1803 * which is deliberate - we don't want to delete a toggle at the
1806 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1808 if (gtk_text_iter_compare (&iter, &end) >= 0)
1811 iter_stack_push (stack, &iter);
1814 /* We need to traverse the toggles in order. */
1815 iter_stack_invert (stack);
1818 * See whether the tag is present at the start of the range. If
1819 * the state doesn't already match what we want then add a toggle
1823 toggled_on = gtk_text_iter_has_tag (&start, tag);
1824 if ( (add && !toggled_on) ||
1825 (!add && toggled_on) )
1827 /* This could create a second toggle at the start position;
1828 cleanup_line () will remove it if so. */
1829 seg = _gtk_toggle_segment_new (info, add);
1831 prev = gtk_text_line_segment_split (&start);
1834 seg->next = start_line->segments;
1835 start_line->segments = seg;
1839 seg->next = prev->next;
1843 /* cleanup_line adds the new toggle to the node counts. */
1845 printf ("added toggle at start\n");
1847 /* we should probably call segments_changed, but in theory
1848 any still-cached segments in the iters we are about to
1849 use are still valid, since they're in front
1855 * Scan the range of characters and delete any internal tag
1856 * transitions. Keep track of what the old state was at the end
1857 * of the range, and add a toggle there if it's needed.
1861 cleanupline = start_line;
1862 while (iter_stack_pop (stack, &iter))
1864 GtkTextLineSegment *indexable_seg;
1867 line = _gtk_text_iter_get_text_line (&iter);
1868 seg = _gtk_text_iter_get_any_segment (&iter);
1869 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1871 g_assert (seg != NULL);
1872 g_assert (indexable_seg != NULL);
1873 g_assert (seg != indexable_seg);
1875 prev = line->segments;
1877 /* Find the segment that actually toggles this tag. */
1878 while (seg != indexable_seg)
1880 g_assert (seg != NULL);
1881 g_assert (indexable_seg != NULL);
1882 g_assert (seg != indexable_seg);
1884 if ( (seg->type == >k_text_toggle_on_type ||
1885 seg->type == >k_text_toggle_off_type) &&
1886 (seg->body.toggle.info == info) )
1892 g_assert (seg != NULL);
1893 g_assert (indexable_seg != NULL);
1895 g_assert (seg != indexable_seg); /* If this happens, then
1896 forward_to_tag_toggle was
1898 g_assert (seg->body.toggle.info->tag == tag);
1900 /* If this happens, when previously tagging we didn't merge
1901 overlapping tags. */
1902 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1903 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1905 toggled_on = !toggled_on;
1908 printf ("deleting %s toggle\n",
1909 seg->type == >k_text_toggle_on_type ? "on" : "off");
1911 /* Remove toggle segment from the list. */
1914 line->segments = seg->next;
1918 while (prev->next != seg)
1922 prev->next = seg->next;
1925 /* Inform iterators we've hosed them. This actually reflects a
1926 bit of inefficiency; if you have the same tag toggled on and
1927 off a lot in a single line, we keep having the rescan from
1928 the front of the line. Of course we have to do that to get
1929 "prev" anyway, but here we are doing it an additional
1931 segments_changed (tree);
1933 /* Update node counts */
1934 if (seg->body.toggle.inNodeCounts)
1936 _gtk_change_node_toggle_count (line->parent,
1938 seg->body.toggle.inNodeCounts = FALSE;
1943 /* We only clean up lines when we're done with them, saves some
1944 gratuitous line-segment-traversals */
1946 if (cleanupline != line)
1948 cleanup_line (cleanupline);
1953 iter_stack_free (stack);
1955 /* toggled_on now reflects the toggle state _just before_ the
1956 end iterator. The end iterator could already have a toggle
1957 on or a toggle off. */
1958 if ( (add && !toggled_on) ||
1959 (!add && toggled_on) )
1961 /* This could create a second toggle at the start position;
1962 cleanup_line () will remove it if so. */
1964 seg = _gtk_toggle_segment_new (info, !add);
1966 prev = gtk_text_line_segment_split (&end);
1969 seg->next = end_line->segments;
1970 end_line->segments = seg;
1974 seg->next = prev->next;
1977 /* cleanup_line adds the new toggle to the node counts. */
1978 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1980 printf ("added toggle at end\n");
1985 * Cleanup cleanupline and the last line of the range, if
1986 * these are different.
1989 cleanup_line (cleanupline);
1990 if (cleanupline != end_line)
1992 cleanup_line (end_line);
1995 segments_changed (tree);
1997 queue_tag_redisplay (tree, tag, &start, &end);
1999 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2000 _gtk_text_btree_check (tree);
2009 get_line_internal (GtkTextBTree *tree,
2011 gint *real_line_number,
2012 gboolean include_last)
2014 GtkTextBTreeNode *node;
2019 line_count = _gtk_text_btree_line_count (tree);
2023 if (line_number < 0)
2025 line_number = line_count;
2027 else if (line_number > line_count)
2029 line_number = line_count;
2032 if (real_line_number)
2033 *real_line_number = line_number;
2035 node = tree->root_node;
2036 lines_left = line_number;
2039 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2043 while (node->level != 0)
2045 for (node = node->children.node;
2046 node->num_lines <= lines_left;
2052 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2055 lines_left -= node->num_lines;
2060 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2063 for (line = node->children.line; lines_left > 0;
2069 g_error ("gtk_text_btree_find_line ran out of lines");
2078 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2081 _gtk_text_btree_get_line (tree,
2082 _gtk_text_btree_line_count (tree) - 1,
2087 _gtk_text_btree_get_line (GtkTextBTree *tree,
2089 gint *real_line_number)
2091 return get_line_internal (tree, line_number, real_line_number, TRUE);
2095 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2097 gint *real_line_number)
2099 return get_line_internal (tree, line_number, real_line_number, FALSE);
2103 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2105 gint *line_start_index,
2106 gint *real_char_index)
2108 GtkTextBTreeNode *node;
2110 GtkTextLineSegment *seg;
2114 node = tree->root_node;
2116 /* Clamp to valid indexes (-1 is magic for "highest index"),
2117 * node->num_chars includes the two newlines that aren't really
2120 if (char_index < 0 || char_index >= (node->num_chars - 1))
2122 char_index = node->num_chars - 2;
2125 *real_char_index = char_index;
2128 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2132 chars_left = char_index;
2133 while (node->level != 0)
2135 for (node = node->children.node;
2136 chars_left >= node->num_chars;
2139 chars_left -= node->num_chars;
2141 g_assert (chars_left >= 0);
2145 if (chars_left == 0)
2147 /* Start of a line */
2149 *line_start_index = char_index;
2150 return node->children.line;
2154 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2159 for (line = node->children.line; line != NULL; line = line->next)
2161 seg = line->segments;
2164 if (chars_in_line + seg->char_count > chars_left)
2165 goto found; /* found our line/segment */
2167 chars_in_line += seg->char_count;
2172 chars_left -= chars_in_line;
2180 g_assert (line != NULL); /* hosage, ran out of lines */
2181 g_assert (seg != NULL);
2183 *line_start_index = char_index - chars_left;
2188 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2191 GtkTextBTreeNode *node;
2192 GtkTextLine *siblingline;
2193 GtkTextLineSegment *seg;
2194 int src, dst, index;
2199 #define NUM_TAG_INFOS 10
2201 line = _gtk_text_iter_get_text_line (iter);
2202 byte_index = gtk_text_iter_get_line_index (iter);
2204 tagInfo.numTags = 0;
2205 tagInfo.arraySize = NUM_TAG_INFOS;
2206 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2207 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2210 * Record tag toggles within the line of indexPtr but preceding
2211 * indexPtr. Note that if this loop segfaults, your
2212 * byte_index probably points past the sum of all
2213 * seg->byte_count */
2215 for (index = 0, seg = line->segments;
2216 (index + seg->byte_count) <= byte_index;
2217 index += seg->byte_count, seg = seg->next)
2219 if ((seg->type == >k_text_toggle_on_type)
2220 || (seg->type == >k_text_toggle_off_type))
2222 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2227 * Record toggles for tags in lines that are predecessors of
2228 * line but under the same level-0 GtkTextBTreeNode.
2231 for (siblingline = line->parent->children.line;
2232 siblingline != line;
2233 siblingline = siblingline->next)
2235 for (seg = siblingline->segments; seg != NULL;
2238 if ((seg->type == >k_text_toggle_on_type)
2239 || (seg->type == >k_text_toggle_off_type))
2241 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2247 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2248 * toggles for all siblings that precede that GtkTextBTreeNode.
2251 for (node = line->parent; node->parent != NULL;
2252 node = node->parent)
2254 GtkTextBTreeNode *siblingPtr;
2257 for (siblingPtr = node->parent->children.node;
2258 siblingPtr != node; siblingPtr = siblingPtr->next)
2260 for (summary = siblingPtr->summary; summary != NULL;
2261 summary = summary->next)
2263 if (summary->toggle_count & 1)
2265 inc_count (summary->info->tag, summary->toggle_count,
2273 * Go through the tag information and squash out all of the tags
2274 * that have even toggle counts (these tags exist before the point
2275 * of interest, but not at the desired character itself).
2278 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2280 if (tagInfo.counts[src] & 1)
2282 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2283 tagInfo.tags[dst] = tagInfo.tags[src];
2289 g_free (tagInfo.counts);
2292 g_free (tagInfo.tags);
2295 return tagInfo.tags;
2299 copy_segment (GString *string,
2300 gboolean include_hidden,
2301 gboolean include_nonchars,
2302 const GtkTextIter *start,
2303 const GtkTextIter *end)
2305 GtkTextLineSegment *end_seg;
2306 GtkTextLineSegment *seg;
2308 if (gtk_text_iter_equal (start, end))
2311 seg = _gtk_text_iter_get_indexable_segment (start);
2312 end_seg = _gtk_text_iter_get_indexable_segment (end);
2314 if (seg->type == >k_text_char_type)
2316 gboolean copy = TRUE;
2317 gint copy_bytes = 0;
2318 gint copy_start = 0;
2320 /* Don't copy if we're invisible; segments are invisible/not
2321 as a whole, no need to check each char */
2322 if (!include_hidden &&
2323 _gtk_text_btree_char_is_invisible (start))
2326 /* printf (" <invisible>\n"); */
2329 copy_start = _gtk_text_iter_get_segment_byte (start);
2333 /* End is in the same segment; need to copy fewer bytes. */
2334 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2336 copy_bytes = end_byte - copy_start;
2339 copy_bytes = seg->byte_count - copy_start;
2341 g_assert (copy_bytes != 0); /* Due to iter equality check at
2342 front of this function. */
2346 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2348 g_string_append_len (string,
2349 seg->body.chars + copy_start,
2353 /* printf (" :%s\n", string->str); */
2355 else if (seg->type == >k_text_pixbuf_type ||
2356 seg->type == >k_text_child_type)
2358 gboolean copy = TRUE;
2360 if (!include_nonchars)
2364 else if (!include_hidden &&
2365 _gtk_text_btree_char_is_invisible (start))
2372 g_string_append_len (string,
2373 gtk_text_unknown_char_utf8,
2381 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2382 const GtkTextIter *end_orig,
2383 gboolean include_hidden,
2384 gboolean include_nonchars)
2386 GtkTextLineSegment *seg;
2387 GtkTextLineSegment *end_seg;
2394 g_return_val_if_fail (start_orig != NULL, NULL);
2395 g_return_val_if_fail (end_orig != NULL, NULL);
2396 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2397 _gtk_text_iter_get_btree (end_orig), NULL);
2399 start = *start_orig;
2402 gtk_text_iter_order (&start, &end);
2404 retval = g_string_new (NULL);
2406 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2408 seg = _gtk_text_iter_get_indexable_segment (&iter);
2409 while (seg != end_seg)
2411 copy_segment (retval, include_hidden, include_nonchars,
2414 _gtk_text_iter_forward_indexable_segment (&iter);
2416 seg = _gtk_text_iter_get_indexable_segment (&iter);
2419 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2422 g_string_free (retval, FALSE);
2427 _gtk_text_btree_line_count (GtkTextBTree *tree)
2429 /* Subtract bogus line at the end; we return a count
2431 return tree->root_node->num_lines - 1;
2435 _gtk_text_btree_char_count (GtkTextBTree *tree)
2437 /* Exclude newline in bogus last line and the
2438 * one in the last line that is after the end iterator
2440 return tree->root_node->num_chars - 2;
2443 #define LOTSA_TAGS 1000
2445 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2447 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2449 int deftagCnts[LOTSA_TAGS] = { 0, };
2450 int *tagCnts = deftagCnts;
2451 GtkTextTag *deftags[LOTSA_TAGS];
2452 GtkTextTag **tags = deftags;
2454 GtkTextBTreeNode *node;
2455 GtkTextLine *siblingline;
2456 GtkTextLineSegment *seg;
2463 line = _gtk_text_iter_get_text_line (iter);
2464 tree = _gtk_text_iter_get_btree (iter);
2465 byte_index = gtk_text_iter_get_line_index (iter);
2467 numTags = gtk_text_tag_table_get_size (tree->table);
2469 /* almost always avoid malloc, so stay out of system calls */
2470 if (LOTSA_TAGS < numTags)
2472 tagCnts = g_new0 (int, numTags);
2473 tags = g_new (GtkTextTag*, numTags);
2477 * Record tag toggles within the line of indexPtr but preceding
2481 for (index = 0, seg = line->segments;
2482 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2483 index += seg->byte_count, seg = seg->next)
2485 if ((seg->type == >k_text_toggle_on_type)
2486 || (seg->type == >k_text_toggle_off_type))
2488 tag = seg->body.toggle.info->tag;
2489 if (tag->invisible_set && tag->values->invisible)
2491 tags[tag->priority] = tag;
2492 tagCnts[tag->priority]++;
2498 * Record toggles for tags in lines that are predecessors of
2499 * line but under the same level-0 GtkTextBTreeNode.
2502 for (siblingline = line->parent->children.line;
2503 siblingline != line;
2504 siblingline = siblingline->next)
2506 for (seg = siblingline->segments; seg != NULL;
2509 if ((seg->type == >k_text_toggle_on_type)
2510 || (seg->type == >k_text_toggle_off_type))
2512 tag = seg->body.toggle.info->tag;
2513 if (tag->invisible_set && tag->values->invisible)
2515 tags[tag->priority] = tag;
2516 tagCnts[tag->priority]++;
2523 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2524 * for all siblings that precede that GtkTextBTreeNode.
2527 for (node = line->parent; node->parent != NULL;
2528 node = node->parent)
2530 GtkTextBTreeNode *siblingPtr;
2533 for (siblingPtr = node->parent->children.node;
2534 siblingPtr != node; siblingPtr = siblingPtr->next)
2536 for (summary = siblingPtr->summary; summary != NULL;
2537 summary = summary->next)
2539 if (summary->toggle_count & 1)
2541 tag = summary->info->tag;
2542 if (tag->invisible_set && tag->values->invisible)
2544 tags[tag->priority] = tag;
2545 tagCnts[tag->priority] += summary->toggle_count;
2553 * Now traverse from highest priority to lowest,
2554 * take invisible value from first odd count (= on)
2557 for (i = numTags-1; i >=0; i--)
2561 /* FIXME not sure this should be if 0 */
2563 #ifndef ALWAYS_SHOW_SELECTION
2564 /* who would make the selection invisible? */
2565 if ((tag == tkxt->seltag)
2566 && !(tkxt->flags & GOT_FOCUS))
2572 invisible = tags[i]->values->invisible;
2577 if (LOTSA_TAGS < numTags)
2592 redisplay_region (GtkTextBTree *tree,
2593 const GtkTextIter *start,
2594 const GtkTextIter *end)
2597 GtkTextLine *start_line, *end_line;
2599 if (gtk_text_iter_compare (start, end) > 0)
2601 const GtkTextIter *tmp = start;
2606 start_line = _gtk_text_iter_get_text_line (start);
2607 end_line = _gtk_text_iter_get_text_line (end);
2610 while (view != NULL)
2612 gint start_y, end_y;
2613 GtkTextLineData *ld;
2615 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2617 if (end_line == start_line)
2620 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2622 ld = _gtk_text_line_get_data (end_line, view->view_id);
2624 end_y += ld->height;
2626 gtk_text_layout_changed (view->layout, start_y,
2635 redisplay_mark (GtkTextLineSegment *mark)
2640 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2642 mark->body.mark.obj);
2645 gtk_text_iter_forward_char (&end);
2647 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2648 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2653 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2655 if (!mark->body.mark.visible)
2658 redisplay_mark (mark);
2662 ensure_not_off_end (GtkTextBTree *tree,
2663 GtkTextLineSegment *mark,
2666 if (gtk_text_iter_get_line (iter) ==
2667 _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)
3349 /* n_lines is without the magic line at the end */
3350 n_lines = _gtk_text_btree_line_count (tree);
3352 g_assert (n_lines >= 1);
3354 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3356 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3361 ensure_end_iter_segment (GtkTextBTree *tree)
3363 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3365 GtkTextLineSegment *seg;
3366 GtkTextLineSegment *last_with_chars;
3368 ensure_end_iter_line (tree);
3370 last_with_chars = NULL;
3372 seg = tree->end_iter_line->segments;
3375 if (seg->char_count > 0)
3376 last_with_chars = seg;
3380 tree->end_iter_segment = last_with_chars;
3382 /* We know the last char in the last line is '\n' */
3383 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3384 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3386 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3388 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3389 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3394 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3397 ensure_end_iter_line (tree);
3399 return line == tree->end_iter_line;
3403 _gtk_text_btree_is_end (GtkTextBTree *tree,
3405 GtkTextLineSegment *seg,
3409 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3411 /* Do this first to avoid walking segments in most cases */
3412 if (!_gtk_text_line_contains_end_iter (line, tree))
3415 ensure_end_iter_segment (tree);
3417 if (seg != tree->end_iter_segment)
3420 if (byte_index >= 0)
3421 return byte_index == tree->end_iter_segment_byte_index;
3423 return char_offset == tree->end_iter_segment_char_offset;
3427 _gtk_text_line_next (GtkTextLine *line)
3429 GtkTextBTreeNode *node;
3431 if (line->next != NULL)
3436 * This was the last line associated with the particular parent
3437 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3438 * then search down from that GtkTextBTreeNode to find the first
3442 node = line->parent;
3443 while (node != NULL && node->next == NULL)
3444 node = node->parent;
3450 while (node->level > 0)
3452 node = node->children.node;
3455 g_assert (node->children.line != line);
3457 return node->children.line;
3462 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3466 next = _gtk_text_line_next (line);
3468 /* If we were on the end iter line, we can't go to
3471 if (next && next->next == NULL && /* these checks are optimization only */
3472 _gtk_text_line_next (next) == NULL)
3479 _gtk_text_line_previous (GtkTextLine *line)
3481 GtkTextBTreeNode *node;
3482 GtkTextBTreeNode *node2;
3486 * Find the line under this GtkTextBTreeNode just before the starting line.
3488 prev = line->parent->children.line; /* First line at leaf */
3489 while (prev != line)
3491 if (prev->next == line)
3497 g_error ("gtk_text_btree_previous_line ran out of lines");
3501 * This was the first line associated with the particular parent
3502 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3503 * then search down from that GtkTextBTreeNode to find its last line.
3505 for (node = line->parent; ; node = node->parent)
3507 if (node == NULL || node->parent == NULL)
3509 else if (node != node->parent->children.node)
3513 for (node2 = node->parent->children.node; ;
3514 node2 = node2->children.node)
3516 while (node2->next != node)
3517 node2 = node2->next;
3519 if (node2->level == 0)
3525 for (prev = node2->children.line ; ; prev = prev->next)
3527 if (prev->next == NULL)
3531 g_assert_not_reached ();
3537 _gtk_text_line_data_new (GtkTextLayout *layout,
3540 GtkTextLineData *line_data;
3542 line_data = g_new (GtkTextLineData, 1);
3544 line_data->view_id = layout;
3545 line_data->next = NULL;
3546 line_data->width = 0;
3547 line_data->height = 0;
3548 line_data->valid = FALSE;
3554 _gtk_text_line_add_data (GtkTextLine *line,
3555 GtkTextLineData *data)
3557 g_return_if_fail (line != NULL);
3558 g_return_if_fail (data != NULL);
3559 g_return_if_fail (data->view_id != NULL);
3563 data->next = line->views;
3573 _gtk_text_line_remove_data (GtkTextLine *line,
3576 GtkTextLineData *prev;
3577 GtkTextLineData *iter;
3579 g_return_val_if_fail (line != NULL, NULL);
3580 g_return_val_if_fail (view_id != NULL, NULL);
3584 while (iter != NULL)
3586 if (iter->view_id == view_id)
3595 prev->next = iter->next;
3597 line->views = iter->next;
3606 _gtk_text_line_get_data (GtkTextLine *line,
3609 GtkTextLineData *iter;
3611 g_return_val_if_fail (line != NULL, NULL);
3612 g_return_val_if_fail (view_id != NULL, NULL);
3615 while (iter != NULL)
3617 if (iter->view_id == view_id)
3626 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3627 GtkTextLineData *ld)
3629 /* For now this is totally unoptimized. FIXME?
3631 We could probably optimize the case where the width removed
3632 is less than the max width for the parent node,
3633 and the case where the height is unchanged when we re-wrap.
3636 g_return_if_fail (ld != NULL);
3639 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3643 _gtk_text_line_char_count (GtkTextLine *line)
3645 GtkTextLineSegment *seg;
3649 seg = line->segments;
3652 size += seg->char_count;
3659 _gtk_text_line_byte_count (GtkTextLine *line)
3661 GtkTextLineSegment *seg;
3665 seg = line->segments;
3668 size += seg->byte_count;
3676 _gtk_text_line_char_index (GtkTextLine *target_line)
3678 GSList *node_stack = NULL;
3679 GtkTextBTreeNode *iter;
3683 /* Push all our parent nodes onto a stack */
3684 iter = target_line->parent;
3686 g_assert (iter != NULL);
3688 while (iter != NULL)
3690 node_stack = g_slist_prepend (node_stack, iter);
3692 iter = iter->parent;
3695 /* Check that we have the root node on top of the stack. */
3696 g_assert (node_stack != NULL &&
3697 node_stack->data != NULL &&
3698 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3700 /* Add up chars in all nodes before the nodes in our stack.
3704 iter = node_stack->data;
3705 while (iter != NULL)
3707 GtkTextBTreeNode *child_iter;
3708 GtkTextBTreeNode *next_node;
3710 next_node = node_stack->next ?
3711 node_stack->next->data : NULL;
3712 node_stack = g_slist_remove (node_stack, node_stack->data);
3714 if (iter->level == 0)
3716 /* stack should be empty when we're on the last node */
3717 g_assert (node_stack == NULL);
3718 break; /* Our children are now lines */
3721 g_assert (next_node != NULL);
3722 g_assert (iter != NULL);
3723 g_assert (next_node->parent == iter);
3725 /* Add up chars before us in the tree */
3726 child_iter = iter->children.node;
3727 while (child_iter != next_node)
3729 g_assert (child_iter != NULL);
3731 num_chars += child_iter->num_chars;
3733 child_iter = child_iter->next;
3739 g_assert (iter != NULL);
3740 g_assert (iter == target_line->parent);
3742 /* Since we don't store char counts in lines, only in segments, we
3743 have to iterate over the lines adding up segment char counts
3744 until we find our line. */
3745 line = iter->children.line;
3746 while (line != target_line)
3748 g_assert (line != NULL);
3750 num_chars += _gtk_text_line_char_count (line);
3755 g_assert (line == target_line);
3761 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3765 GtkTextLineSegment *seg;
3768 g_return_val_if_fail (line != NULL, NULL);
3770 offset = byte_offset;
3771 seg = line->segments;
3773 while (offset >= seg->byte_count)
3775 g_assert (seg != NULL); /* means an invalid byte index */
3776 offset -= seg->byte_count;
3781 *seg_offset = offset;
3787 _gtk_text_line_char_to_segment (GtkTextLine *line,
3791 GtkTextLineSegment *seg;
3794 g_return_val_if_fail (line != NULL, NULL);
3796 offset = char_offset;
3797 seg = line->segments;
3799 while (offset >= seg->char_count)
3801 g_assert (seg != NULL); /* means an invalid char index */
3802 offset -= seg->char_count;
3807 *seg_offset = offset;
3813 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3817 GtkTextLineSegment *seg;
3820 g_return_val_if_fail (line != NULL, NULL);
3822 offset = byte_offset;
3823 seg = line->segments;
3825 while (offset > 0 && offset >= seg->byte_count)
3827 g_assert (seg != NULL); /* means an invalid byte index */
3828 offset -= seg->byte_count;
3833 *seg_offset = offset;
3839 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3843 GtkTextLineSegment *seg;
3846 g_return_val_if_fail (line != NULL, NULL);
3848 offset = char_offset;
3849 seg = line->segments;
3851 while (offset > 0 && offset >= seg->char_count)
3853 g_assert (seg != NULL); /* means an invalid byte index */
3854 offset -= seg->char_count;
3859 *seg_offset = offset;
3865 _gtk_text_line_byte_to_char (GtkTextLine *line,
3869 GtkTextLineSegment *seg;
3871 g_return_val_if_fail (line != NULL, 0);
3872 g_return_val_if_fail (byte_offset >= 0, 0);
3875 seg = line->segments;
3876 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3877 the next segment) */
3879 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3881 byte_offset -= seg->byte_count;
3882 char_offset += seg->char_count;
3887 g_assert (seg != NULL);
3889 /* Now byte_offset is the offset into the current segment,
3890 and char_offset is the start of the current segment.
3891 Optimize the case where no chars use > 1 byte */
3892 if (seg->byte_count == seg->char_count)
3893 return char_offset + byte_offset;
3896 if (seg->type == >k_text_char_type)
3897 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3900 g_assert (seg->char_count == 1);
3901 g_assert (byte_offset == 0);
3909 _gtk_text_line_char_to_byte (GtkTextLine *line,
3912 g_warning ("FIXME not implemented");
3917 /* FIXME sync with char_locate (or figure out a clean
3918 way to merge the two functions) */
3920 _gtk_text_line_byte_locate (GtkTextLine *line,
3922 GtkTextLineSegment **segment,
3923 GtkTextLineSegment **any_segment,
3924 gint *seg_byte_offset,
3925 gint *line_byte_offset)
3927 GtkTextLineSegment *seg;
3928 GtkTextLineSegment *after_last_indexable;
3929 GtkTextLineSegment *last_indexable;
3933 g_return_val_if_fail (line != NULL, FALSE);
3934 g_return_val_if_fail (byte_offset >= 0, FALSE);
3937 *any_segment = NULL;
3940 offset = byte_offset;
3942 last_indexable = NULL;
3943 after_last_indexable = line->segments;
3944 seg = line->segments;
3946 /* The loop ends when we're inside a segment;
3947 last_indexable refers to the last segment
3948 we passed entirely. */
3949 while (seg && offset >= seg->byte_count)
3951 if (seg->char_count > 0)
3953 offset -= seg->byte_count;
3954 bytes_in_line += seg->byte_count;
3955 last_indexable = seg;
3956 after_last_indexable = last_indexable->next;
3964 /* We went off the end of the line */
3966 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3973 if (after_last_indexable != NULL)
3974 *any_segment = after_last_indexable;
3976 *any_segment = *segment;
3979 /* Override any_segment if we're in the middle of a segment. */
3981 *any_segment = *segment;
3983 *seg_byte_offset = offset;
3985 g_assert (*segment != NULL);
3986 g_assert (*any_segment != NULL);
3987 g_assert (*seg_byte_offset < (*segment)->byte_count);
3989 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3994 /* FIXME sync with byte_locate (or figure out a clean
3995 way to merge the two functions) */
3997 _gtk_text_line_char_locate (GtkTextLine *line,
3999 GtkTextLineSegment **segment,
4000 GtkTextLineSegment **any_segment,
4001 gint *seg_char_offset,
4002 gint *line_char_offset)
4004 GtkTextLineSegment *seg;
4005 GtkTextLineSegment *after_last_indexable;
4006 GtkTextLineSegment *last_indexable;
4010 g_return_val_if_fail (line != NULL, FALSE);
4011 g_return_val_if_fail (char_offset >= 0, FALSE);
4014 *any_segment = NULL;
4017 offset = char_offset;
4019 last_indexable = NULL;
4020 after_last_indexable = line->segments;
4021 seg = line->segments;
4023 /* The loop ends when we're inside a segment;
4024 last_indexable refers to the last segment
4025 we passed entirely. */
4026 while (seg && offset >= seg->char_count)
4028 if (seg->char_count > 0)
4030 offset -= seg->char_count;
4031 chars_in_line += seg->char_count;
4032 last_indexable = seg;
4033 after_last_indexable = last_indexable->next;
4041 /* end of the line */
4043 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4050 if (after_last_indexable != NULL)
4051 *any_segment = after_last_indexable;
4053 *any_segment = *segment;
4056 /* Override any_segment if we're in the middle of a segment. */
4058 *any_segment = *segment;
4060 *seg_char_offset = offset;
4062 g_assert (*segment != NULL);
4063 g_assert (*any_segment != NULL);
4064 g_assert (*seg_char_offset < (*segment)->char_count);
4066 *line_char_offset = chars_in_line + *seg_char_offset;
4072 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4074 gint *line_char_offset,
4075 gint *seg_char_offset)
4077 GtkTextLineSegment *seg;
4080 g_return_if_fail (line != NULL);
4081 g_return_if_fail (byte_offset >= 0);
4083 *line_char_offset = 0;
4085 offset = byte_offset;
4086 seg = line->segments;
4088 while (offset >= seg->byte_count)
4090 offset -= seg->byte_count;
4091 *line_char_offset += seg->char_count;
4093 g_assert (seg != NULL); /* means an invalid char offset */
4096 g_assert (seg->char_count > 0); /* indexable. */
4098 /* offset is now the number of bytes into the current segment we
4099 * want to go. Count chars into the current segment.
4102 if (seg->type == >k_text_char_type)
4104 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4106 g_assert (*seg_char_offset < seg->char_count);
4108 *line_char_offset += *seg_char_offset;
4112 g_assert (offset == 0);
4113 *seg_char_offset = 0;
4118 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4120 gint *line_byte_offset,
4121 gint *seg_byte_offset)
4123 GtkTextLineSegment *seg;
4126 g_return_if_fail (line != NULL);
4127 g_return_if_fail (char_offset >= 0);
4129 *line_byte_offset = 0;
4131 offset = char_offset;
4132 seg = line->segments;
4134 while (offset >= seg->char_count)
4136 offset -= seg->char_count;
4137 *line_byte_offset += seg->byte_count;
4139 g_assert (seg != NULL); /* means an invalid char offset */
4142 g_assert (seg->char_count > 0); /* indexable. */
4144 /* offset is now the number of chars into the current segment we
4145 want to go. Count bytes into the current segment. */
4147 if (seg->type == >k_text_char_type)
4151 /* if in the last fourth of the segment walk backwards */
4152 if (seg->char_count - offset < seg->char_count / 4)
4153 p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4154 offset - seg->char_count);
4156 p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4158 *seg_byte_offset = p - seg->body.chars;
4160 g_assert (*seg_byte_offset < seg->byte_count);
4162 *line_byte_offset += *seg_byte_offset;
4166 g_assert (offset == 0);
4167 *seg_byte_offset = 0;
4172 node_compare (GtkTextBTreeNode *lhs,
4173 GtkTextBTreeNode *rhs)
4175 GtkTextBTreeNode *iter;
4176 GtkTextBTreeNode *node;
4177 GtkTextBTreeNode *common_parent;
4178 GtkTextBTreeNode *parent_of_lower;
4179 GtkTextBTreeNode *parent_of_higher;
4180 gboolean lhs_is_lower;
4181 GtkTextBTreeNode *lower;
4182 GtkTextBTreeNode *higher;
4184 /* This function assumes that lhs and rhs are not underneath each
4191 if (lhs->level < rhs->level)
4193 lhs_is_lower = TRUE;
4199 lhs_is_lower = FALSE;
4204 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4205 * of the common parent we used to reach the common parent; the
4206 * ordering of these child nodes in the child list is the ordering
4210 /* Get on the same level (may be on same level already) */
4212 while (node->level < higher->level)
4213 node = node->parent;
4215 g_assert (node->level == higher->level);
4217 g_assert (node != higher); /* Happens if lower is underneath higher */
4219 /* Go up until we have two children with a common parent.
4221 parent_of_lower = node;
4222 parent_of_higher = higher;
4224 while (parent_of_lower->parent != parent_of_higher->parent)
4226 parent_of_lower = parent_of_lower->parent;
4227 parent_of_higher = parent_of_higher->parent;
4230 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4232 common_parent = parent_of_lower->parent;
4234 g_assert (common_parent != NULL);
4236 /* See which is first in the list of common_parent's children */
4237 iter = common_parent->children.node;
4238 while (iter != NULL)
4240 if (iter == parent_of_higher)
4242 /* higher is less than lower */
4245 return 1; /* lhs > rhs */
4249 else if (iter == parent_of_lower)
4251 /* lower is less than higher */
4254 return -1; /* lhs < rhs */
4262 g_assert_not_reached ();
4266 /* remember that tag == NULL means "any tag" */
4268 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4272 GtkTextBTreeNode *node;
4273 GtkTextTagInfo *info;
4274 gboolean below_tag_root;
4276 g_return_val_if_fail (line != NULL, NULL);
4278 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4279 _gtk_text_btree_check (tree);
4283 /* Right now we can only offer linear-search if the user wants
4284 * to know about any tag toggle at all.
4286 return _gtk_text_line_next_excluding_last (line);
4289 /* Our tag summaries only have node precision, not line
4290 * precision. This means that if any line under a node could contain a
4291 * tag, then any of the others could also contain a tag.
4293 * In the future we could have some mechanism to keep track of how
4294 * many toggles we've found under a node so far, since we have a
4295 * count of toggles under the node. But for now I'm going with KISS.
4298 /* return same-node line, if any. */
4302 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4306 if (info->tag_root == NULL)
4309 if (info->tag_root == line->parent)
4310 return NULL; /* we were at the last line under the tag root */
4312 /* We need to go up out of this node, and on to the next one with
4313 toggles for the target tag. If we're below the tag root, we need to
4314 find the next node below the tag root that has tag summaries. If
4315 we're not below the tag root, we need to see if the tag root is
4316 after us in the tree, and if so, return the first line underneath
4319 node = line->parent;
4320 below_tag_root = FALSE;
4321 while (node != NULL)
4323 if (node == info->tag_root)
4325 below_tag_root = TRUE;
4329 node = node->parent;
4334 node = line->parent;
4335 while (node != info->tag_root)
4337 if (node->next == NULL)
4338 node = node->parent;
4343 if (gtk_text_btree_node_has_tag (node, tag))
4353 ordering = node_compare (line->parent, info->tag_root);
4357 /* Tag root is ahead of us, so search there. */
4358 node = info->tag_root;
4363 /* Tag root is after us, so no more lines that
4364 * could contain the tag.
4369 g_assert_not_reached ();
4374 g_assert (node != NULL);
4376 /* We have to find the first sub-node of this node that contains
4380 while (node->level > 0)
4382 g_assert (node != NULL); /* If this fails, it likely means an
4383 incorrect tag summary led us on a
4384 wild goose chase down this branch of
4386 node = node->children.node;
4387 while (node != NULL)
4389 if (gtk_text_btree_node_has_tag (node, tag))
4395 g_assert (node != NULL);
4396 g_assert (node->level == 0);
4398 return node->children.line;
4402 prev_line_under_node (GtkTextBTreeNode *node,
4407 prev = node->children.line;
4413 while (prev->next != line)
4423 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4427 GtkTextBTreeNode *node;
4428 GtkTextBTreeNode *found_node = NULL;
4429 GtkTextTagInfo *info;
4430 gboolean below_tag_root;
4432 GtkTextBTreeNode *line_ancestor;
4433 GtkTextBTreeNode *line_ancestor_parent;
4435 /* See next_could_contain_tag () for more extensive comments
4436 * on what's going on here.
4439 g_return_val_if_fail (line != NULL, NULL);
4441 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4442 _gtk_text_btree_check (tree);
4446 /* Right now we can only offer linear-search if the user wants
4447 * to know about any tag toggle at all.
4449 return _gtk_text_line_previous (line);
4452 /* Return same-node line, if any. */
4453 prev = prev_line_under_node (line->parent, line);
4457 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4461 if (info->tag_root == NULL)
4464 if (info->tag_root == line->parent)
4465 return NULL; /* we were at the first line under the tag root */
4467 /* Are we below the tag root */
4468 node = line->parent;
4469 below_tag_root = FALSE;
4470 while (node != NULL)
4472 if (node == info->tag_root)
4474 below_tag_root = TRUE;
4478 node = node->parent;
4483 /* Look for a previous node under this tag root that has our
4487 /* this assertion holds because line->parent is not the
4488 * tag root, we are below the tag root, and the tag
4491 g_assert (line->parent->parent != NULL);
4493 line_ancestor = line->parent;
4494 line_ancestor_parent = line->parent->parent;
4496 while (line_ancestor != info->tag_root)
4498 GSList *child_nodes = NULL;
4501 /* Create reverse-order list of nodes before
4504 if (line_ancestor_parent != NULL)
4505 node = line_ancestor_parent->children.node;
4507 node = line_ancestor;
4509 while (node != line_ancestor && node != NULL)
4511 child_nodes = g_slist_prepend (child_nodes, node);
4516 /* Try to find a node with our tag on it in the list */
4520 GtkTextBTreeNode *this_node = tmp->data;
4522 g_assert (this_node != line_ancestor);
4524 if (gtk_text_btree_node_has_tag (this_node, tag))
4526 found_node = this_node;
4527 g_slist_free (child_nodes);
4531 tmp = g_slist_next (tmp);
4534 g_slist_free (child_nodes);
4536 /* Didn't find anything on this level; go up one level. */
4537 line_ancestor = line_ancestor_parent;
4538 line_ancestor_parent = line_ancestor->parent;
4548 ordering = node_compare (line->parent, info->tag_root);
4552 /* Tag root is ahead of us, so no more lines
4559 /* Tag root is after us, so grab last tagged
4560 * line underneath the tag root.
4562 found_node = info->tag_root;
4566 g_assert_not_reached ();
4571 g_assert (found_node != NULL);
4573 /* We have to find the last sub-node of this node that contains
4578 while (node->level > 0)
4580 GSList *child_nodes = NULL;
4582 g_assert (node != NULL); /* If this fails, it likely means an
4583 incorrect tag summary led us on a
4584 wild goose chase down this branch of
4587 node = node->children.node;
4588 while (node != NULL)
4590 child_nodes = g_slist_prepend (child_nodes, node);
4594 node = NULL; /* detect failure to find a child node. */
4597 while (iter != NULL)
4599 if (gtk_text_btree_node_has_tag (iter->data, tag))
4601 /* recurse into this node. */
4606 iter = g_slist_next (iter);
4609 g_slist_free (child_nodes);
4611 g_assert (node != NULL);
4614 g_assert (node != NULL);
4615 g_assert (node->level == 0);
4617 /* this assertion is correct, but slow. */
4618 /* g_assert (node_compare (node, line->parent) < 0); */
4620 /* Return last line in this node. */
4622 prev = node->children.line;
4630 * Non-public function implementations
4634 summary_list_destroy (Summary *summary)
4636 g_slice_free_chain (Summary, summary, next);
4640 get_last_line (GtkTextBTree *tree)
4642 if (tree->last_line_stamp != tree->chars_changed_stamp)
4648 n_lines = _gtk_text_btree_line_count (tree);
4650 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4652 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4654 tree->last_line_stamp = tree->chars_changed_stamp;
4655 tree->last_line = line;
4658 return tree->last_line;
4666 gtk_text_line_new (void)
4670 line = g_new0(GtkTextLine, 1);
4671 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4672 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4673 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4679 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4681 GtkTextLineData *ld;
4682 GtkTextLineData *next;
4684 g_return_if_fail (line != NULL);
4691 view = gtk_text_btree_get_view (tree, ld->view_id);
4693 g_assert (view != NULL);
4696 gtk_text_layout_free_line_data (view->layout, line, ld);
4705 gtk_text_line_set_parent (GtkTextLine *line,
4706 GtkTextBTreeNode *node)
4708 if (line->parent == node)
4710 line->parent = node;
4711 gtk_text_btree_node_invalidate_upward (node, NULL);
4715 cleanup_line (GtkTextLine *line)
4717 GtkTextLineSegment *seg, **prev_p;
4721 * Make a pass over all of the segments in the line, giving each
4722 * a chance to clean itself up. This could potentially change
4723 * the structure of the line, e.g. by merging two segments
4724 * together or having two segments cancel themselves; if so,
4725 * then repeat the whole process again, since the first structure
4726 * change might make other structure changes possible. Repeat
4727 * until eventually there are no changes.
4734 prev_p = &line->segments;
4735 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4737 if (seg->type->cleanupFunc != NULL)
4739 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4747 prev_p = &(*prev_p)->next;
4757 node_data_new (gpointer view_id)
4761 nd = g_slice_new (NodeData);
4763 nd->view_id = view_id;
4773 node_data_destroy (NodeData *nd)
4775 g_slice_free (NodeData, nd);
4779 node_data_list_destroy (NodeData *nd)
4781 g_slice_free_chain (NodeData, nd, next);
4785 node_data_find (NodeData *nd,
4790 if (nd->view_id == view_id)
4798 summary_destroy (Summary *summary)
4800 /* Fill with error-triggering garbage */
4801 summary->info = (void*)0x1;
4802 summary->toggle_count = 567;
4803 summary->next = (void*)0x1;
4804 g_slice_free (Summary, summary);
4807 static GtkTextBTreeNode*
4808 gtk_text_btree_node_new (void)
4810 GtkTextBTreeNode *node;
4812 node = g_new (GtkTextBTreeNode, 1);
4814 node->node_data = NULL;
4820 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4821 GtkTextTagInfo *info,
4826 summary = node->summary;
4827 while (summary != NULL)
4829 if (summary->info == info)
4831 summary->toggle_count += adjust;
4835 summary = summary->next;
4838 if (summary == NULL)
4840 /* didn't find a summary for our tag. */
4841 g_return_if_fail (adjust > 0);
4842 summary = g_slice_new (Summary);
4843 summary->info = info;
4844 summary->toggle_count = adjust;
4845 summary->next = node->summary;
4846 node->summary = summary;
4850 /* Note that the tag root and above do not have summaries
4851 for the tag; only nodes below the tag root have
4854 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4858 summary = node->summary;
4859 while (summary != NULL)
4862 summary->info->tag == tag)
4865 summary = summary->next;
4871 /* Add node and all children to the damage region. */
4873 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4877 nd = node->node_data;
4884 if (node->level == 0)
4888 line = node->children.line;
4889 while (line != NULL)
4891 GtkTextLineData *ld;
4905 GtkTextBTreeNode *child;
4907 child = node->children.node;
4909 while (child != NULL)
4911 gtk_text_btree_node_invalidate_downward (child);
4913 child = child->next;
4919 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4921 GtkTextBTreeNode *iter;
4924 while (iter != NULL)
4930 nd = node_data_find (iter->node_data, view_id);
4932 if (nd == NULL || !nd->valid)
4933 break; /* Once a node is invalid, we know its parents are as well. */
4939 gboolean should_continue = FALSE;
4941 nd = iter->node_data;
4946 should_continue = TRUE;
4953 if (!should_continue)
4954 break; /* This node was totally invalidated, so are its
4958 iter = iter->parent;
4964 * _gtk_text_btree_is_valid:
4965 * @tree: a #GtkTextBTree
4966 * @view_id: ID for the view
4968 * Check to see if the entire #GtkTextBTree is valid or not for
4971 * Return value: %TRUE if the entire #GtkTextBTree is valid
4974 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4978 g_return_val_if_fail (tree != NULL, FALSE);
4980 nd = node_data_find (tree->root_node->node_data, view_id);
4981 return (nd && nd->valid);
4984 typedef struct _ValidateState ValidateState;
4986 struct _ValidateState
4988 gint remaining_pixels;
4989 gboolean in_validation;
4996 gtk_text_btree_node_validate (BTreeView *view,
4997 GtkTextBTreeNode *node,
4999 ValidateState *state)
5001 gint node_valid = TRUE;
5002 gint node_width = 0;
5003 gint node_height = 0;
5005 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5006 g_return_if_fail (!nd->valid);
5008 if (node->level == 0)
5010 GtkTextLine *line = node->children.line;
5011 GtkTextLineData *ld;
5013 /* Iterate over leading valid lines */
5014 while (line != NULL)
5016 ld = _gtk_text_line_get_data (line, view_id);
5018 if (!ld || !ld->valid)
5020 else if (state->in_validation)
5022 state->in_validation = FALSE;
5027 state->y += ld->height;
5028 node_width = MAX (ld->width, node_width);
5029 node_height += ld->height;
5035 state->in_validation = TRUE;
5037 /* Iterate over invalid lines */
5038 while (line != NULL)
5040 ld = _gtk_text_line_get_data (line, view_id);
5042 if (ld && ld->valid)
5047 state->old_height += ld->height;
5048 ld = gtk_text_layout_wrap (view->layout, line, ld);
5049 state->new_height += ld->height;
5051 node_width = MAX (ld->width, node_width);
5052 node_height += ld->height;
5054 state->remaining_pixels -= ld->height;
5055 if (state->remaining_pixels <= 0)
5065 /* Iterate over the remaining lines */
5066 while (line != NULL)
5068 ld = _gtk_text_line_get_data (line, view_id);
5069 state->in_validation = FALSE;
5071 if (!ld || !ld->valid)
5076 node_width = MAX (ld->width, node_width);
5077 node_height += ld->height;
5085 GtkTextBTreeNode *child;
5088 child = node->children.node;
5090 /* Iterate over leading valid nodes */
5093 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5095 if (!child_nd->valid)
5097 else if (state->in_validation)
5099 state->in_validation = FALSE;
5104 state->y += child_nd->height;
5105 node_width = MAX (node_width, child_nd->width);
5106 node_height += child_nd->height;
5109 child = child->next;
5112 /* Iterate over invalid nodes */
5115 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5117 if (child_nd->valid)
5121 gtk_text_btree_node_validate (view, child, view_id, state);
5123 if (!child_nd->valid)
5125 node_width = MAX (node_width, child_nd->width);
5126 node_height += child_nd->height;
5128 if (!state->in_validation || state->remaining_pixels <= 0)
5130 child = child->next;
5135 child = child->next;
5138 /* Iterate over the remaining lines */
5141 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5142 state->in_validation = FALSE;
5144 if (!child_nd->valid)
5147 node_width = MAX (child_nd->width, node_width);
5148 node_height += child_nd->height;
5150 child = child->next;
5154 nd->width = node_width;
5155 nd->height = node_height;
5156 nd->valid = node_valid;
5160 * _gtk_text_btree_validate:
5161 * @tree: a #GtkTextBTree
5163 * @max_pixels: the maximum number of pixels to validate. (No more
5164 * than one paragraph beyond this limit will be validated)
5165 * @y: location to store starting y coordinate of validated region
5166 * @old_height: location to store old height of validated region
5167 * @new_height: location to store new height of validated region
5169 * Validate a single contiguous invalid region of a #GtkTextBTree for
5172 * Return value: %TRUE if a region has been validated, %FALSE if the
5173 * entire tree was already valid.
5176 _gtk_text_btree_validate (GtkTextBTree *tree,
5185 g_return_val_if_fail (tree != NULL, FALSE);
5187 view = gtk_text_btree_get_view (tree, view_id);
5188 g_return_val_if_fail (view != NULL, FALSE);
5190 if (!_gtk_text_btree_is_valid (tree, view_id))
5192 ValidateState state;
5194 state.remaining_pixels = max_pixels;
5195 state.in_validation = FALSE;
5197 state.old_height = 0;
5198 state.new_height = 0;
5200 gtk_text_btree_node_validate (view,
5207 *old_height = state.old_height;
5209 *new_height = state.new_height;
5211 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5212 _gtk_text_btree_check (tree);
5221 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5225 gboolean *valid_out)
5229 gboolean valid = TRUE;
5231 if (node->level == 0)
5233 GtkTextLine *line = node->children.line;
5235 while (line != NULL)
5237 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5239 if (!ld || !ld->valid)
5244 width = MAX (ld->width, width);
5245 height += ld->height;
5253 GtkTextBTreeNode *child = node->children.node;
5257 NodeData *child_nd = node_data_find (child->node_data, view_id);
5259 if (!child_nd || !child_nd->valid)
5264 width = MAX (child_nd->width, width);
5265 height += child_nd->height;
5268 child = child->next;
5273 *height_out = height;
5278 /* Recompute the validity and size of the view data for a given
5279 * view at this node from the immediate children of the node
5282 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5285 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5290 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5291 &width, &height, &valid);
5293 nd->height = height;
5300 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5305 gtk_text_btree_node_check_valid (node, view_id);
5306 node = node->parent;
5311 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5314 if (node->level == 0)
5316 return gtk_text_btree_node_check_valid (node, view_id);
5320 GtkTextBTreeNode *child = node->children.node;
5322 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5330 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5332 if (!child_nd->valid)
5334 nd->width = MAX (child_nd->width, nd->width);
5335 nd->height += child_nd->height;
5337 child = child->next;
5346 * _gtk_text_btree_validate_line:
5347 * @tree: a #GtkTextBTree
5348 * @line: line to validate
5349 * @view_id: view ID for the view to validate
5351 * Revalidate a single line of the btree for the given view, propagate
5352 * results up through the entire tree.
5355 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5359 GtkTextLineData *ld;
5362 g_return_if_fail (tree != NULL);
5363 g_return_if_fail (line != NULL);
5365 view = gtk_text_btree_get_view (tree, view_id);
5366 g_return_if_fail (view != NULL);
5368 ld = _gtk_text_line_get_data (line, view_id);
5369 if (!ld || !ld->valid)
5371 ld = gtk_text_layout_wrap (view->layout, line, ld);
5373 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5378 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5380 if (node->level == 0)
5384 line = node->children.line;
5385 while (line != NULL)
5387 GtkTextLineData *ld;
5389 ld = _gtk_text_line_remove_data (line, view_id);
5392 gtk_text_layout_free_line_data (view->layout, line, ld);
5399 GtkTextBTreeNode *child;
5401 child = node->children.node;
5403 while (child != NULL)
5406 gtk_text_btree_node_remove_view (view, child, view_id);
5408 child = child->next;
5412 gtk_text_btree_node_remove_data (node, view_id);
5416 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5418 if (node->level == 0)
5421 GtkTextLineSegment *seg;
5423 while (node->children.line != NULL)
5425 line = node->children.line;
5426 node->children.line = line->next;
5427 while (line->segments != NULL)
5429 seg = line->segments;
5430 line->segments = seg->next;
5432 (*seg->type->deleteFunc) (seg, line, TRUE);
5434 gtk_text_line_destroy (tree, line);
5439 GtkTextBTreeNode *childPtr;
5441 while (node->children.node != NULL)
5443 childPtr = node->children.node;
5444 node->children.node = childPtr->next;
5445 gtk_text_btree_node_destroy (tree, childPtr);
5449 gtk_text_btree_node_free_empty (tree, node);
5453 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5454 GtkTextBTreeNode *node)
5456 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5457 (node->level == 0 && node->children.line == NULL));
5459 summary_list_destroy (node->summary);
5460 node_data_list_destroy (node->node_data);
5465 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5469 nd = node->node_data;
5472 if (nd->view_id == view_id)
5480 nd = node_data_new (view_id);
5482 if (node->node_data)
5483 nd->next = node->node_data;
5485 node->node_data = nd;
5492 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5498 nd = node->node_data;
5501 if (nd->view_id == view_id)
5512 prev->next = nd->next;
5514 if (node->node_data == nd)
5515 node->node_data = nd->next;
5519 node_data_destroy (nd);
5523 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5524 gint *width, gint *height)
5528 g_return_if_fail (width != NULL);
5529 g_return_if_fail (height != NULL);
5531 nd = gtk_text_btree_node_ensure_data (node, view_id);
5536 *height = nd->height;
5539 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5540 * here isn't quite right, since for a lot of operations we want to
5541 * know which children of the common parent correspond to the two nodes
5542 * (e.g., when computing the order of two iters)
5544 static GtkTextBTreeNode *
5545 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5546 GtkTextBTreeNode *node2)
5548 while (node1->level < node2->level)
5549 node1 = node1->parent;
5550 while (node2->level < node1->level)
5551 node2 = node2->parent;
5552 while (node1 != node2)
5554 node1 = node1->parent;
5555 node2 = node2->parent;
5566 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5571 while (view != NULL)
5573 if (view->view_id == view_id)
5582 get_tree_bounds (GtkTextBTree *tree,
5586 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5587 _gtk_text_btree_get_end_iter (tree, end);
5591 tag_changed_cb (GtkTextTagTable *table,
5593 gboolean size_changed,
5598 /* We need to queue a relayout on all regions that are tagged with
5605 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5607 /* Must be a last toggle if there was a first one. */
5608 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5609 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5610 _gtk_text_btree_invalidate_region (tree,
5617 /* We only need to queue a redraw, not a relayout */
5622 while (view != NULL)
5626 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5627 gtk_text_layout_changed (view->layout, 0, height, height);
5635 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5638 /* Remove the tag from the tree */
5643 get_tree_bounds (tree, &start, &end);
5645 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5646 gtk_text_btree_remove_tag_info (tree, tag);
5650 /* Rebalance the out-of-whack node "node" */
5652 gtk_text_btree_rebalance (GtkTextBTree *tree,
5653 GtkTextBTreeNode *node)
5656 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5657 * up through the tree one GtkTextBTreeNode at a time until the root
5658 * GtkTextBTreeNode has been processed.
5661 while (node != NULL)
5663 GtkTextBTreeNode *new_node, *child;
5668 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5669 * then split off all but the first MIN_CHILDREN into a separate
5670 * GtkTextBTreeNode following the original one. Then repeat until the
5671 * GtkTextBTreeNode has a decent size.
5674 if (node->num_children > MAX_CHILDREN)
5679 * If the GtkTextBTreeNode being split is the root
5680 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5684 if (node->parent == NULL)
5686 new_node = gtk_text_btree_node_new ();
5687 new_node->parent = NULL;
5688 new_node->next = NULL;
5689 new_node->summary = NULL;
5690 new_node->level = node->level + 1;
5691 new_node->children.node = node;
5692 recompute_node_counts (tree, new_node);
5693 tree->root_node = new_node;
5695 new_node = gtk_text_btree_node_new ();
5696 new_node->parent = node->parent;
5697 new_node->next = node->next;
5698 node->next = new_node;
5699 new_node->summary = NULL;
5700 new_node->level = node->level;
5701 new_node->num_children = node->num_children - MIN_CHILDREN;
5702 if (node->level == 0)
5704 for (i = MIN_CHILDREN-1,
5705 line = node->children.line;
5706 i > 0; i--, line = line->next)
5708 /* Empty loop body. */
5710 new_node->children.line = line->next;
5715 for (i = MIN_CHILDREN-1,
5716 child = node->children.node;
5717 i > 0; i--, child = child->next)
5719 /* Empty loop body. */
5721 new_node->children.node = child->next;
5724 recompute_node_counts (tree, node);
5725 node->parent->num_children++;
5727 if (node->num_children <= MAX_CHILDREN)
5729 recompute_node_counts (tree, node);
5735 while (node->num_children < MIN_CHILDREN)
5737 GtkTextBTreeNode *other;
5738 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5739 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5740 int total_children, first_children, i;
5743 * Too few children for this GtkTextBTreeNode. If this is the root then,
5744 * it's OK for it to have less than MIN_CHILDREN children
5745 * as long as it's got at least two. If it has only one
5746 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5747 * the tree and use its child as the new root.
5750 if (node->parent == NULL)
5752 if ((node->num_children == 1) && (node->level > 0))
5754 tree->root_node = node->children.node;
5755 tree->root_node->parent = NULL;
5757 node->children.node = NULL;
5758 gtk_text_btree_node_free_empty (tree, node);
5764 * Not the root. Make sure that there are siblings to
5768 if (node->parent->num_children < 2)
5770 gtk_text_btree_rebalance (tree, node->parent);
5775 * Find a sibling neighbor to borrow from, and arrange for
5776 * node to be the earlier of the pair.
5779 if (node->next == NULL)
5781 for (other = node->parent->children.node;
5782 other->next != node;
5783 other = other->next)
5785 /* Empty loop body. */
5792 * We're going to either merge the two siblings together
5793 * into one GtkTextBTreeNode or redivide the children among them to
5794 * balance their loads. As preparation, join their two
5795 * child lists into a single list and remember the half-way
5796 * point in the list.
5799 total_children = node->num_children + other->num_children;
5800 first_children = total_children/2;
5801 if (node->children.node == NULL)
5803 node->children = other->children;
5804 other->children.node = NULL;
5805 other->children.line = NULL;
5807 if (node->level == 0)
5811 for (line = node->children.line, i = 1;
5813 line = line->next, i++)
5815 if (i == first_children)
5820 line->next = other->children.line;
5821 while (i <= first_children)
5830 GtkTextBTreeNode *child;
5832 for (child = node->children.node, i = 1;
5833 child->next != NULL;
5834 child = child->next, i++)
5836 if (i <= first_children)
5838 if (i == first_children)
5840 halfwaynode = child;
5844 child->next = other->children.node;
5845 while (i <= first_children)
5847 halfwaynode = child;
5848 child = child->next;
5854 * If the two siblings can simply be merged together, do it.
5857 if (total_children <= MAX_CHILDREN)
5859 recompute_node_counts (tree, node);
5860 node->next = other->next;
5861 node->parent->num_children--;
5863 other->children.node = NULL;
5864 other->children.line = NULL;
5865 gtk_text_btree_node_free_empty (tree, other);
5870 * The siblings can't be merged, so just divide their
5871 * children evenly between them.
5874 if (node->level == 0)
5876 other->children.line = halfwayline->next;
5877 halfwayline->next = NULL;
5881 other->children.node = halfwaynode->next;
5882 halfwaynode->next = NULL;
5885 recompute_node_counts (tree, node);
5886 recompute_node_counts (tree, other);
5889 node = node->parent;
5894 post_insert_fixup (GtkTextBTree *tree,
5896 gint line_count_delta,
5897 gint char_count_delta)
5900 GtkTextBTreeNode *node;
5903 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5904 * point, then rebalance the tree if necessary.
5907 for (node = line->parent ; node != NULL;
5908 node = node->parent)
5910 node->num_lines += line_count_delta;
5911 node->num_chars += char_count_delta;
5913 node = line->parent;
5914 node->num_children += line_count_delta;
5916 if (node->num_children > MAX_CHILDREN)
5918 gtk_text_btree_rebalance (tree, node);
5921 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5922 _gtk_text_btree_check (tree);
5925 static GtkTextTagInfo*
5926 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5929 GtkTextTagInfo *info;
5933 list = tree->tag_infos;
5934 while (list != NULL)
5937 if (info->tag == tag)
5940 list = g_slist_next (list);
5946 static GtkTextTagInfo*
5947 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5950 GtkTextTagInfo *info;
5952 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5956 /* didn't find it, create. */
5958 info = g_slice_new (GtkTextTagInfo);
5962 info->tag_root = NULL;
5963 info->toggle_count = 0;
5965 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5968 g_print ("Created tag info %p for tag %s(%p)\n",
5969 info, info->tag->name ? info->tag->name : "anon",
5978 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5981 GtkTextTagInfo *info;
5986 list = tree->tag_infos;
5987 while (list != NULL)
5990 if (info->tag == tag)
5993 g_print ("Removing tag info %p for tag %s(%p)\n",
5994 info, info->tag->name ? info->tag->name : "anon",
6000 prev->next = list->next;
6004 tree->tag_infos = list->next;
6007 g_slist_free (list);
6009 g_object_unref (info->tag);
6011 g_slice_free (GtkTextTagInfo, info);
6016 list = g_slist_next (list);
6021 recompute_level_zero_counts (GtkTextBTreeNode *node)
6024 GtkTextLineSegment *seg;
6026 g_assert (node->level == 0);
6028 line = node->children.line;
6029 while (line != NULL)
6031 node->num_children++;
6034 if (line->parent != node)
6035 gtk_text_line_set_parent (line, node);
6037 seg = line->segments;
6041 node->num_chars += seg->char_count;
6043 if (((seg->type != >k_text_toggle_on_type)
6044 && (seg->type != >k_text_toggle_off_type))
6045 || !(seg->body.toggle.inNodeCounts))
6051 GtkTextTagInfo *info;
6053 info = seg->body.toggle.info;
6055 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6066 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6069 GtkTextBTreeNode *child;
6071 g_assert (node->level > 0);
6073 child = node->children.node;
6074 while (child != NULL)
6076 node->num_children += 1;
6077 node->num_lines += child->num_lines;
6078 node->num_chars += child->num_chars;
6080 if (child->parent != node)
6082 child->parent = node;
6083 gtk_text_btree_node_invalidate_upward (node, NULL);
6086 summary = child->summary;
6087 while (summary != NULL)
6089 gtk_text_btree_node_adjust_toggle_count (node,
6091 summary->toggle_count);
6093 summary = summary->next;
6096 child = child->next;
6101 *----------------------------------------------------------------------
6103 * recompute_node_counts --
6105 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6106 * (tags, child information, etc.) by scanning the information in
6107 * its descendants. This procedure is called during rebalancing
6108 * when a GtkTextBTreeNode's child structure has changed.
6114 * The tag counts for node are modified to reflect its current
6115 * child structure, as are its num_children, num_lines, num_chars fields.
6116 * Also, all of the childrens' parent fields are made to point
6119 *----------------------------------------------------------------------
6123 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6126 Summary *summary, *summary2;
6129 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6130 * the existing Summary records (most of them will probably be reused).
6133 summary = node->summary;
6134 while (summary != NULL)
6136 summary->toggle_count = 0;
6137 summary = summary->next;
6140 node->num_children = 0;
6141 node->num_lines = 0;
6142 node->num_chars = 0;
6145 * Scan through the children, adding the childrens' tag counts into
6146 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6150 if (node->level == 0)
6151 recompute_level_zero_counts (node);
6153 recompute_level_nonzero_counts (node);
6158 gtk_text_btree_node_check_valid (node, view->view_id);
6163 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6164 * records that still have a zero count, or that have all the toggles.
6165 * The GtkTextBTreeNode with the children that account for all the tags toggles
6166 * have no summary information, and they become the tag_root for the tag.
6170 for (summary = node->summary; summary != NULL; )
6172 if (summary->toggle_count > 0 &&
6173 summary->toggle_count < summary->info->toggle_count)
6175 if (node->level == summary->info->tag_root->level)
6178 * The tag's root GtkTextBTreeNode split and some toggles left.
6179 * The tag root must move up a level.
6181 summary->info->tag_root = node->parent;
6184 summary = summary->next;
6187 if (summary->toggle_count == summary->info->toggle_count)
6190 * A GtkTextBTreeNode merge has collected all the toggles under
6191 * one GtkTextBTreeNode. Push the root down to this level.
6193 summary->info->tag_root = node;
6195 if (summary2 != NULL)
6197 summary2->next = summary->next;
6198 summary_destroy (summary);
6199 summary = summary2->next;
6203 node->summary = summary->next;
6204 summary_destroy (summary);
6205 summary = node->summary;
6211 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6212 GtkTextTagInfo *info,
6213 gint delta) /* may be negative */
6215 Summary *summary, *prevPtr;
6216 GtkTextBTreeNode *node2Ptr;
6217 int rootLevel; /* Level of original tag root */
6219 info->toggle_count += delta;
6221 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6223 info->tag_root = node;
6228 * Note the level of the existing root for the tag so we can detect
6229 * if it needs to be moved because of the toggle count change.
6232 rootLevel = info->tag_root->level;
6235 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6236 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6240 for ( ; node != info->tag_root; node = node->parent)
6243 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6244 * perhaps all we have to do is adjust its count.
6247 for (prevPtr = NULL, summary = node->summary;
6249 prevPtr = summary, summary = summary->next)
6251 if (summary->info == info)
6256 if (summary != NULL)
6258 summary->toggle_count += delta;
6259 if (summary->toggle_count > 0 &&
6260 summary->toggle_count < info->toggle_count)
6264 if (summary->toggle_count != 0)
6267 * Should never find a GtkTextBTreeNode with max toggle count at this
6268 * point (there shouldn't have been a summary entry in the
6272 g_error ("%s: bad toggle count (%d) max (%d)",
6273 G_STRLOC, summary->toggle_count, info->toggle_count);
6277 * Zero toggle count; must remove this tag from the list.
6280 if (prevPtr == NULL)
6282 node->summary = summary->next;
6286 prevPtr->next = summary->next;
6288 summary_destroy (summary);
6293 * This tag isn't currently in the summary information list.
6296 if (rootLevel == node->level)
6300 * The old tag root is at the same level in the tree as this
6301 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6302 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6303 * as well as the old root (if not, we'll move it up again
6304 * the next time through the loop). To push it up one level
6305 * we copy the original toggle count into the summary
6306 * information at the old root and change the root to its
6307 * parent GtkTextBTreeNode.
6310 GtkTextBTreeNode *rootnode = info->tag_root;
6311 summary = g_slice_new (Summary);
6312 summary->info = info;
6313 summary->toggle_count = info->toggle_count - delta;
6314 summary->next = rootnode->summary;
6315 rootnode->summary = summary;
6316 rootnode = rootnode->parent;
6317 rootLevel = rootnode->level;
6318 info->tag_root = rootnode;
6320 summary = g_slice_new (Summary);
6321 summary->info = info;
6322 summary->toggle_count = delta;
6323 summary->next = node->summary;
6324 node->summary = summary;
6329 * If we've decremented the toggle count, then it may be necessary
6330 * to push the tag root down one or more levels.
6337 if (info->toggle_count == 0)
6339 info->tag_root = (GtkTextBTreeNode *) NULL;
6342 node = info->tag_root;
6343 while (node->level > 0)
6346 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6347 * toggles. If so, push the root down one level.
6350 for (node2Ptr = node->children.node;
6351 node2Ptr != (GtkTextBTreeNode *)NULL ;
6352 node2Ptr = node2Ptr->next)
6354 for (prevPtr = NULL, summary = node2Ptr->summary;
6356 prevPtr = summary, summary = summary->next)
6358 if (summary->info == info)
6363 if (summary == NULL)
6367 if (summary->toggle_count != info->toggle_count)
6370 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6377 * This GtkTextBTreeNode has all the toggles, so push down the root.
6380 if (prevPtr == NULL)
6382 node2Ptr->summary = summary->next;
6386 prevPtr->next = summary->next;
6388 summary_destroy (summary);
6389 info->tag_root = node2Ptr;
6392 node = info->tag_root;
6397 *----------------------------------------------------------------------
6401 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6402 * increments the count for a particular tag, adding a new
6403 * entry for that tag if there wasn't one previously.
6409 * The information at *tagInfoPtr may be modified, and the arrays
6410 * may be reallocated to make them larger.
6412 *----------------------------------------------------------------------
6416 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6421 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6422 count > 0; tag_p++, count--)
6426 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6432 * There isn't currently an entry for this tag, so we have to
6433 * make a new one. If the arrays are full, then enlarge the
6437 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6439 GtkTextTag **newTags;
6440 int *newCounts, newSize;
6442 newSize = 2*tagInfoPtr->arraySize;
6443 newTags = (GtkTextTag **) g_malloc ((unsigned)
6444 (newSize*sizeof (GtkTextTag *)));
6445 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6446 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6447 g_free ((char *) tagInfoPtr->tags);
6448 tagInfoPtr->tags = newTags;
6449 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6450 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6451 tagInfoPtr->arraySize *sizeof (int));
6452 g_free ((char *) tagInfoPtr->counts);
6453 tagInfoPtr->counts = newCounts;
6454 tagInfoPtr->arraySize = newSize;
6457 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6458 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6459 tagInfoPtr->numTags++;
6463 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6464 const GtkTextIter *iter)
6466 GtkTextLineSegment *prev;
6470 line = _gtk_text_iter_get_text_line (iter);
6471 tree = _gtk_text_iter_get_btree (iter);
6473 prev = gtk_text_line_segment_split (iter);
6476 seg->next = line->segments;
6477 line->segments = seg;
6481 seg->next = prev->next;
6484 cleanup_line (line);
6485 segments_changed (tree);
6487 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6488 _gtk_text_btree_check (tree);
6492 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6493 GtkTextLineSegment *seg,
6496 GtkTextLineSegment *prev;
6498 if (line->segments == seg)
6500 line->segments = seg->next;
6504 for (prev = line->segments; prev->next != seg;
6507 /* Empty loop body. */
6509 prev->next = seg->next;
6511 cleanup_line (line);
6512 segments_changed (tree);
6516 * This is here because it requires BTree internals, it logically
6517 * belongs in gtktextsegment.c
6522 *--------------------------------------------------------------
6524 * _gtk_toggle_segment_check_func --
6526 * This procedure is invoked to perform consistency checks
6527 * on toggle segments.
6533 * If a consistency problem is found the procedure g_errors.
6535 *--------------------------------------------------------------
6539 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6545 if (segPtr->byte_count != 0)
6547 g_error ("toggle_segment_check_func: segment had non-zero size");
6549 if (!segPtr->body.toggle.inNodeCounts)
6551 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6553 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6554 for (summary = line->parent->summary; ;
6555 summary = summary->next)
6557 if (summary == NULL)
6561 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6568 if (summary->info == segPtr->body.toggle.info)
6572 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6584 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6585 GtkTextBTreeNode *node,
6595 while (view != NULL)
6597 if (view->view_id == nd->view_id)
6604 g_error ("Node has data for a view %p no longer attached to the tree",
6607 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6608 &width, &height, &valid);
6610 /* valid aggregate not checked the same as width/height, because on
6611 * btree rebalance we can have invalid nodes where all lines below
6612 * them are actually valid, due to moving lines around between
6615 * The guarantee is that if there are invalid lines the node is
6616 * invalid - we don't guarantee that if the node is invalid there
6617 * are invalid lines.
6620 if (nd->width != width ||
6621 nd->height != height ||
6622 (nd->valid && !valid))
6624 g_error ("Node aggregates for view %p are invalid:\n"
6625 "Are (%d,%d,%s), should be (%d,%d,%s)",
6627 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6628 width, height, valid ? "TRUE" : "FALSE");
6633 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6634 GtkTextBTreeNode *node)
6636 GtkTextBTreeNode *childnode;
6637 Summary *summary, *summary2;
6639 GtkTextLineSegment *segPtr;
6640 int num_children, num_lines, num_chars, toggle_count, min_children;
6641 GtkTextLineData *ld;
6644 if (node->parent != NULL)
6646 min_children = MIN_CHILDREN;
6648 else if (node->level > 0)
6655 if ((node->num_children < min_children)
6656 || (node->num_children > MAX_CHILDREN))
6658 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6659 node->num_children);
6662 nd = node->node_data;
6665 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6672 if (node->level == 0)
6674 for (line = node->children.line; line != NULL;
6677 if (line->parent != node)
6679 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6681 if (line->segments == NULL)
6683 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6689 /* Just ensuring we don't segv while doing this loop */
6694 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6696 if (segPtr->type->checkFunc != NULL)
6698 (*segPtr->type->checkFunc)(segPtr, line);
6700 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6701 && (segPtr->next != NULL)
6702 && (segPtr->next->byte_count == 0)
6703 && (segPtr->next->type->leftGravity))
6705 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6707 if ((segPtr->next == NULL)
6708 && (segPtr->type != >k_text_char_type))
6710 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6713 num_chars += segPtr->char_count;
6722 for (childnode = node->children.node; childnode != NULL;
6723 childnode = childnode->next)
6725 if (childnode->parent != node)
6727 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6729 if (childnode->level != (node->level-1))
6731 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6732 node->level, childnode->level);
6734 gtk_text_btree_node_check_consistency (tree, childnode);
6735 for (summary = childnode->summary; summary != NULL;
6736 summary = summary->next)
6738 for (summary2 = node->summary; ;
6739 summary2 = summary2->next)
6741 if (summary2 == NULL)
6743 if (summary->info->tag_root == node)
6747 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6748 summary->info->tag->name,
6749 "present in parent summaries");
6751 if (summary->info == summary2->info)
6758 num_lines += childnode->num_lines;
6759 num_chars += childnode->num_chars;
6762 if (num_children != node->num_children)
6764 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6765 num_children, node->num_children);
6767 if (num_lines != node->num_lines)
6769 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6770 num_lines, node->num_lines);
6772 if (num_chars != node->num_chars)
6774 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6775 num_chars, node->num_chars);
6778 for (summary = node->summary; summary != NULL;
6779 summary = summary->next)
6781 if (summary->info->toggle_count == summary->toggle_count)
6783 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6784 summary->info->tag->name);
6787 if (node->level == 0)
6789 for (line = node->children.line; line != NULL;
6792 for (segPtr = line->segments; segPtr != NULL;
6793 segPtr = segPtr->next)
6795 if ((segPtr->type != >k_text_toggle_on_type)
6796 && (segPtr->type != >k_text_toggle_off_type))
6800 if (segPtr->body.toggle.info == summary->info)
6802 if (!segPtr->body.toggle.inNodeCounts)
6803 g_error ("Toggle segment not in the node counts");
6812 for (childnode = node->children.node;
6814 childnode = childnode->next)
6816 for (summary2 = childnode->summary;
6818 summary2 = summary2->next)
6820 if (summary2->info == summary->info)
6822 toggle_count += summary2->toggle_count;
6827 if (toggle_count != summary->toggle_count)
6829 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6830 toggle_count, summary->toggle_count);
6832 for (summary2 = summary->next; summary2 != NULL;
6833 summary2 = summary2->next)
6835 if (summary2->info == summary->info)
6837 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6838 summary->info->tag->name);
6845 listify_foreach (GtkTextTag *tag, gpointer user_data)
6847 GSList** listp = user_data;
6849 *listp = g_slist_prepend (*listp, tag);
6853 list_of_tags (GtkTextTagTable *table)
6855 GSList *list = NULL;
6857 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6863 _gtk_text_btree_check (GtkTextBTree *tree)
6866 GtkTextBTreeNode *node;
6868 GtkTextLineSegment *seg;
6870 GSList *all_tags, *taglist = NULL;
6872 GtkTextTagInfo *info;
6875 * Make sure that the tag toggle counts and the tag root pointers are OK.
6877 all_tags = list_of_tags (tree->table);
6878 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6880 tag = taglist->data;
6881 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6884 node = info->tag_root;
6887 if (info->toggle_count != 0)
6889 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6890 tag->name, info->toggle_count);
6892 continue; /* no ranges for the tag */
6894 else if (info->toggle_count == 0)
6896 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6899 else if (info->toggle_count & 1)
6901 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6902 tag->name, info->toggle_count);
6904 for (summary = node->summary; summary != NULL;
6905 summary = summary->next)
6907 if (summary->info->tag == tag)
6909 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6913 if (node->level > 0)
6915 for (node = node->children.node ; node != NULL ;
6918 for (summary = node->summary; summary != NULL;
6919 summary = summary->next)
6921 if (summary->info->tag == tag)
6923 count += summary->toggle_count;
6930 GtkTextLineSegmentClass * last = NULL;
6932 for (line = node->children.line ; line != NULL ;
6935 for (seg = line->segments; seg != NULL;
6938 if ((seg->type == >k_text_toggle_on_type ||
6939 seg->type == >k_text_toggle_off_type) &&
6940 seg->body.toggle.info->tag == tag)
6942 if (last == seg->type)
6943 g_error ("Two consecutive toggles on or off weren't merged");
6944 if (!seg->body.toggle.inNodeCounts)
6945 g_error ("Toggle segment not in the node counts");
6954 if (count != info->toggle_count)
6956 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6957 info->toggle_count, tag->name, count);
6962 g_slist_free (all_tags);
6965 * Call a recursive procedure to do the main body of checks.
6968 node = tree->root_node;
6969 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6972 * Make sure that there are at least two lines in the text and
6973 * that the last line has no characters except a newline.
6976 if (node->num_lines < 2)
6978 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6980 if (node->num_chars < 2)
6982 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6984 while (node->level > 0)
6986 node = node->children.node;
6987 while (node->next != NULL)
6992 line = node->children.line;
6993 while (line->next != NULL)
6997 seg = line->segments;
6998 while ((seg->type == >k_text_toggle_off_type)
6999 || (seg->type == >k_text_right_mark_type)
7000 || (seg->type == >k_text_left_mark_type))
7003 * It's OK to toggle a tag off in the last line, but
7004 * not to start a new range. It's also OK to have marks
7010 if (seg->type != >k_text_char_type)
7012 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7014 if (seg->next != NULL)
7016 g_error ("_gtk_text_btree_check: last line has too many segments");
7018 if (seg->byte_count != 1)
7020 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7023 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7025 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7030 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7031 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7032 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7033 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7036 _gtk_text_btree_spew (GtkTextBTree *tree)
7041 printf ("%d lines in tree %p\n",
7042 _gtk_text_btree_line_count (tree), tree);
7044 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7046 while (line != NULL)
7048 _gtk_text_btree_spew_line (tree, line);
7049 line = _gtk_text_line_next (line);
7052 printf ("=================== Tag information\n");
7057 list = tree->tag_infos;
7059 while (list != NULL)
7061 GtkTextTagInfo *info;
7065 printf (" tag `%s': root at %p, toggle count %d\n",
7066 info->tag->name, info->tag_root, info->toggle_count);
7068 list = g_slist_next (list);
7071 if (tree->tag_infos == NULL)
7073 printf (" (no tags in the tree)\n");
7077 printf ("=================== Tree nodes\n");
7080 _gtk_text_btree_spew_node (tree->root_node, 0);
7085 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7088 GtkTextLineSegment *seg;
7090 spaces = g_strnfill (indent, ' ');
7092 printf ("%sline %p chars %d bytes %d\n",
7094 _gtk_text_line_char_count (line),
7095 _gtk_text_line_byte_count (line));
7097 seg = line->segments;
7100 if (seg->type == >k_text_char_type)
7102 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7107 if (*s == '\n' || *s == '\r')
7111 printf ("%s chars `%s'...\n", spaces, str);
7114 else if (seg->type == >k_text_right_mark_type)
7116 printf ("%s right mark `%s' visible: %d\n",
7118 seg->body.mark.name,
7119 seg->body.mark.visible);
7121 else if (seg->type == >k_text_left_mark_type)
7123 printf ("%s left mark `%s' visible: %d\n",
7125 seg->body.mark.name,
7126 seg->body.mark.visible);
7128 else if (seg->type == >k_text_toggle_on_type ||
7129 seg->type == >k_text_toggle_off_type)
7131 printf ("%s tag `%s' %s\n",
7132 spaces, seg->body.toggle.info->tag->name,
7133 seg->type == >k_text_toggle_off_type ? "off" : "on");
7143 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7146 GtkTextBTreeNode *iter;
7149 spaces = g_strnfill (indent, ' ');
7151 printf ("%snode %p level %d children %d lines %d chars %d\n",
7152 spaces, node, node->level,
7153 node->num_children, node->num_lines, node->num_chars);
7158 printf ("%s %d toggles of `%s' below this node\n",
7159 spaces, s->toggle_count, s->info->tag->name);
7165 if (node->level > 0)
7167 iter = node->children.node;
7168 while (iter != NULL)
7170 _gtk_text_btree_spew_node (iter, indent + 2);
7177 GtkTextLine *line = node->children.line;
7178 while (line != NULL)
7180 _gtk_text_btree_spew_line_short (line, indent + 2);
7188 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7190 GtkTextLineSegment * seg;
7192 printf ("%4d| line: %p parent: %p next: %p\n",
7193 _gtk_text_line_get_number (line), line, line->parent, line->next);
7195 seg = line->segments;
7199 _gtk_text_btree_spew_segment (tree, seg);
7205 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7207 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7208 seg, seg->type->name, seg->byte_count, seg->char_count);
7210 if (seg->type == >k_text_char_type)
7212 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7213 printf (" `%s'\n", str);
7216 else if (seg->type == >k_text_right_mark_type)
7218 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7219 seg->body.mark.name,
7220 seg->body.mark.visible,
7221 seg->body.mark.not_deleteable);
7223 else if (seg->type == >k_text_left_mark_type)
7225 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7226 seg->body.mark.name,
7227 seg->body.mark.visible,
7228 seg->body.mark.not_deleteable);
7230 else if (seg->type == >k_text_toggle_on_type ||
7231 seg->type == >k_text_toggle_off_type)
7233 printf (" tag `%s' priority %d\n",
7234 seg->body.toggle.info->tag->name,
7235 seg->body.toggle.info->tag->priority);
7239 #define __GTK_TEXT_BTREE_C__
7240 #include "gtkaliasdef.c"