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) == _gtk_text_btree_line_count (tree))
2667 gtk_text_iter_backward_char (iter);
2670 static GtkTextLineSegment*
2671 real_set_mark (GtkTextBTree *tree,
2672 GtkTextMark *existing_mark,
2674 gboolean left_gravity,
2675 const GtkTextIter *where,
2676 gboolean should_exist,
2677 gboolean redraw_selections)
2679 GtkTextLineSegment *mark;
2682 g_return_val_if_fail (tree != NULL, NULL);
2683 g_return_val_if_fail (where != NULL, NULL);
2684 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2687 mark = existing_mark->segment;
2688 else if (name != NULL)
2689 mark = g_hash_table_lookup (tree->mark_table,
2694 if (should_exist && mark == NULL)
2696 g_warning ("No mark `%s' exists!", name);
2700 /* OK if !should_exist and it does already exist, in that case
2706 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2707 _gtk_text_iter_check (&iter);
2711 if (redraw_selections &&
2712 (mark == tree->insert_mark->segment ||
2713 mark == tree->selection_bound_mark->segment))
2715 GtkTextIter old_pos;
2717 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2718 mark->body.mark.obj);
2719 redisplay_region (tree, &old_pos, where);
2723 * don't let visible marks be after the final newline of the
2727 if (mark->body.mark.visible)
2729 ensure_not_off_end (tree, mark, &iter);
2732 /* Redraw the mark's old location. */
2733 redisplay_mark_if_visible (mark);
2735 /* Unlink mark from its current location.
2736 This could hose our iterator... */
2737 gtk_text_btree_unlink_segment (tree, mark,
2738 mark->body.mark.line);
2739 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2740 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2742 segments_changed (tree); /* make sure the iterator recomputes its
2747 mark = _gtk_mark_segment_new (tree,
2751 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2753 if (mark->body.mark.name)
2754 g_hash_table_insert (tree->mark_table,
2755 mark->body.mark.name,
2759 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2760 _gtk_text_iter_check (&iter);
2762 /* Link mark into new location */
2763 gtk_text_btree_link_segment (mark, &iter);
2765 /* Invalidate some iterators. */
2766 segments_changed (tree);
2769 * update the screen at the mark's new location.
2772 redisplay_mark_if_visible (mark);
2774 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2775 _gtk_text_iter_check (&iter);
2777 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2778 _gtk_text_btree_check (tree);
2785 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2786 GtkTextMark *existing_mark,
2788 gboolean left_gravity,
2789 const GtkTextIter *iter,
2790 gboolean should_exist)
2792 GtkTextLineSegment *seg;
2794 seg = real_set_mark (tree, existing_mark,
2795 name, left_gravity, iter, should_exist,
2798 return seg ? seg->body.mark.obj : NULL;
2802 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2806 GtkTextIter tmp_start, tmp_end;
2808 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2810 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2811 tree->selection_bound_mark);
2813 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2825 gtk_text_iter_order (&tmp_start, &tmp_end);
2838 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2839 const GtkTextIter *iter)
2841 _gtk_text_btree_select_range (tree, iter, iter);
2845 _gtk_text_btree_select_range (GtkTextBTree *tree,
2846 const GtkTextIter *ins,
2847 const GtkTextIter *bound)
2849 GtkTextIter start, end;
2851 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2852 redisplay_region (tree, &start, &end);
2854 /* Move insert AND selection_bound before we redisplay */
2855 real_set_mark (tree, tree->insert_mark,
2856 "insert", FALSE, ins, TRUE, FALSE);
2857 real_set_mark (tree, tree->selection_bound_mark,
2858 "selection_bound", FALSE, bound, TRUE, FALSE);
2860 redisplay_region (tree, ins, bound);
2865 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2870 g_return_if_fail (tree != NULL);
2871 g_return_if_fail (name != NULL);
2873 mark = g_hash_table_lookup (tree->mark_table,
2876 _gtk_text_btree_remove_mark (tree, mark);
2880 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2881 GtkTextLineSegment *segment)
2884 if (segment->body.mark.name)
2885 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2887 segment->body.mark.tree = NULL;
2888 segment->body.mark.line = NULL;
2890 /* Remove the ref on the mark, which frees segment as a side effect
2891 * if this is the last reference.
2893 g_object_unref (segment->body.mark.obj);
2897 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2900 GtkTextLineSegment *segment;
2902 g_return_if_fail (mark != NULL);
2903 g_return_if_fail (tree != NULL);
2905 segment = mark->segment;
2907 if (segment->body.mark.not_deleteable)
2909 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2913 /* This calls cleanup_line and segments_changed */
2914 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2916 _gtk_text_btree_release_mark_segment (tree, segment);
2920 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2921 GtkTextMark *segment)
2923 return segment == tree->insert_mark;
2927 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2928 GtkTextMark *segment)
2930 return segment == tree->selection_bound_mark;
2934 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2937 GtkTextLineSegment *seg;
2939 g_return_val_if_fail (tree != NULL, NULL);
2940 g_return_val_if_fail (name != NULL, NULL);
2942 seg = g_hash_table_lookup (tree->mark_table, name);
2944 return seg ? seg->body.mark.obj : NULL;
2948 * gtk_text_mark_set_visible:
2949 * @mark: a #GtkTextMark
2950 * @setting: visibility of mark
2952 * Sets the visibility of @mark; the insertion point is normally
2953 * visible, i.e. you can see it as a vertical bar. Also, the text
2954 * widget uses a visible mark to indicate where a drop will occur when
2955 * dragging-and-dropping text. Most other marks are not visible.
2956 * Marks are not visible by default.
2960 gtk_text_mark_set_visible (GtkTextMark *mark,
2963 GtkTextLineSegment *seg;
2965 g_return_if_fail (mark != NULL);
2967 seg = mark->segment;
2969 if (seg->body.mark.visible == setting)
2973 seg->body.mark.visible = setting;
2975 redisplay_mark (seg);
2980 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2983 GtkTextBTreeNode *node;
2984 GtkTextTagInfo *info;
2986 g_return_val_if_fail (tree != NULL, NULL);
2990 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2995 if (info->tag_root == NULL)
2998 node = info->tag_root;
3000 /* We know the tag root has instances of the given
3003 continue_outer_loop:
3004 g_assert (node != NULL);
3005 while (node->level > 0)
3007 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3008 node = node->children.node;
3009 while (node != NULL)
3011 if (gtk_text_btree_node_has_tag (node, tag))
3012 goto continue_outer_loop;
3016 g_assert (node != NULL);
3019 g_assert (node != NULL); /* The tag summaries said some node had
3022 g_assert (node->level == 0);
3024 return node->children.line;
3028 /* Looking for any tag at all (tag == NULL).
3029 Unfortunately this can't be done in a simple and efficient way
3030 right now; so I'm just going to return the
3031 first line in the btree. FIXME */
3032 return _gtk_text_btree_get_line (tree, 0, NULL);
3037 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3040 GtkTextBTreeNode *node;
3041 GtkTextBTreeNode *last_node;
3043 GtkTextTagInfo *info;
3045 g_return_val_if_fail (tree != NULL, NULL);
3049 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3051 if (info->tag_root == NULL)
3054 node = info->tag_root;
3055 /* We know the tag root has instances of the given
3058 while (node->level > 0)
3060 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3062 node = node->children.node;
3063 while (node != NULL)
3065 if (gtk_text_btree_node_has_tag (node, tag))
3073 g_assert (node != NULL); /* The tag summaries said some node had
3076 g_assert (node->level == 0);
3078 /* Find the last line in this node */
3079 line = node->children.line;
3080 while (line->next != NULL)
3087 /* This search can't be done efficiently at the moment,
3088 at least not without complexity.
3089 So, we just return the last line.
3091 return _gtk_text_btree_get_end_iter_line (tree);
3101 _gtk_text_line_get_number (GtkTextLine *line)
3104 GtkTextBTreeNode *node, *parent, *node2;
3108 * First count how many lines precede this one in its level-0
3112 node = line->parent;
3114 for (line2 = node->children.line; line2 != line;
3115 line2 = line2->next)
3119 g_error ("gtk_text_btree_line_number couldn't find line");
3125 * Now work up through the levels of the tree one at a time,
3126 * counting how many lines are in GtkTextBTreeNodes preceding the current
3130 for (parent = node->parent ; parent != NULL;
3131 node = parent, parent = parent->parent)
3133 for (node2 = parent->children.node; node2 != node;
3134 node2 = node2->next)
3138 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3140 index += node2->num_lines;
3146 static GtkTextLineSegment*
3147 find_toggle_segment_before_char (GtkTextLine *line,
3151 GtkTextLineSegment *seg;
3152 GtkTextLineSegment *toggle_seg;
3157 seg = line->segments;
3158 while ( (index + seg->char_count) <= char_in_line )
3160 if (((seg->type == >k_text_toggle_on_type)
3161 || (seg->type == >k_text_toggle_off_type))
3162 && (seg->body.toggle.info->tag == tag))
3165 index += seg->char_count;
3172 static GtkTextLineSegment*
3173 find_toggle_segment_before_byte (GtkTextLine *line,
3177 GtkTextLineSegment *seg;
3178 GtkTextLineSegment *toggle_seg;
3183 seg = line->segments;
3184 while ( (index + seg->byte_count) <= byte_in_line )
3186 if (((seg->type == >k_text_toggle_on_type)
3187 || (seg->type == >k_text_toggle_off_type))
3188 && (seg->body.toggle.info->tag == tag))
3191 index += seg->byte_count;
3199 find_toggle_outside_current_line (GtkTextLine *line,
3203 GtkTextBTreeNode *node;
3204 GtkTextLine *sibling_line;
3205 GtkTextLineSegment *seg;
3206 GtkTextLineSegment *toggle_seg;
3208 GtkTextTagInfo *info = NULL;
3211 * No toggle in this line. Look for toggles for the tag in lines
3212 * that are predecessors of line but under the same
3213 * level-0 GtkTextBTreeNode.
3216 sibling_line = line->parent->children.line;
3217 while (sibling_line != line)
3219 seg = sibling_line->segments;
3222 if (((seg->type == >k_text_toggle_on_type)
3223 || (seg->type == >k_text_toggle_off_type))
3224 && (seg->body.toggle.info->tag == tag))
3230 sibling_line = sibling_line->next;
3233 if (toggle_seg != NULL)
3234 return (toggle_seg->type == >k_text_toggle_on_type);
3237 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3238 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3239 * siblings that precede that GtkTextBTreeNode.
3242 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3248 node = line->parent;
3249 while (node->parent != NULL)
3251 GtkTextBTreeNode *sibling_node;
3253 sibling_node = node->parent->children.node;
3254 while (sibling_node != node)
3258 summary = sibling_node->summary;
3259 while (summary != NULL)
3261 if (summary->info == info)
3262 toggles += summary->toggle_count;
3264 summary = summary->next;
3267 sibling_node = sibling_node->next;
3270 if (node == info->tag_root)
3273 node = node->parent;
3277 * An odd number of toggles means that the tag is present at the
3281 return (toggles & 1) != 0;
3284 /* FIXME this function is far too slow, for no good reason. */
3286 _gtk_text_line_char_has_tag (GtkTextLine *line,
3291 GtkTextLineSegment *toggle_seg;
3293 g_return_val_if_fail (line != NULL, FALSE);
3296 * Check for toggles for the tag in the line but before
3297 * the char. If there is one, its type indicates whether or
3298 * not the character is tagged.
3301 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3303 if (toggle_seg != NULL)
3304 return (toggle_seg->type == >k_text_toggle_on_type);
3306 return find_toggle_outside_current_line (line, tree, tag);
3310 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3315 GtkTextLineSegment *toggle_seg;
3317 g_return_val_if_fail (line != NULL, FALSE);
3320 * Check for toggles for the tag in the line but before
3321 * the char. If there is one, its type indicates whether or
3322 * not the character is tagged.
3325 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3327 if (toggle_seg != NULL)
3328 return (toggle_seg->type == >k_text_toggle_on_type);
3330 return find_toggle_outside_current_line (line, tree, tag);
3334 _gtk_text_line_is_last (GtkTextLine *line,
3337 return line == get_last_line (tree);
3341 ensure_end_iter_line (GtkTextBTree *tree)
3343 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3345 /* n_lines is without the magic line at the end */
3346 g_assert (_gtk_text_btree_line_count (tree) >= 1);
3348 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3350 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3355 ensure_end_iter_segment (GtkTextBTree *tree)
3357 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3359 GtkTextLineSegment *seg;
3360 GtkTextLineSegment *last_with_chars;
3362 ensure_end_iter_line (tree);
3364 last_with_chars = NULL;
3366 seg = tree->end_iter_line->segments;
3369 if (seg->char_count > 0)
3370 last_with_chars = seg;
3374 tree->end_iter_segment = last_with_chars;
3376 /* We know the last char in the last line is '\n' */
3377 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3378 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3380 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3382 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3383 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3388 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3391 ensure_end_iter_line (tree);
3393 return line == tree->end_iter_line;
3397 _gtk_text_btree_is_end (GtkTextBTree *tree,
3399 GtkTextLineSegment *seg,
3403 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3405 /* Do this first to avoid walking segments in most cases */
3406 if (!_gtk_text_line_contains_end_iter (line, tree))
3409 ensure_end_iter_segment (tree);
3411 if (seg != tree->end_iter_segment)
3414 if (byte_index >= 0)
3415 return byte_index == tree->end_iter_segment_byte_index;
3417 return char_offset == tree->end_iter_segment_char_offset;
3421 _gtk_text_line_next (GtkTextLine *line)
3423 GtkTextBTreeNode *node;
3425 if (line->next != NULL)
3430 * This was the last line associated with the particular parent
3431 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3432 * then search down from that GtkTextBTreeNode to find the first
3436 node = line->parent;
3437 while (node != NULL && node->next == NULL)
3438 node = node->parent;
3444 while (node->level > 0)
3446 node = node->children.node;
3449 g_assert (node->children.line != line);
3451 return node->children.line;
3456 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3460 next = _gtk_text_line_next (line);
3462 /* If we were on the end iter line, we can't go to
3465 if (next && next->next == NULL && /* these checks are optimization only */
3466 _gtk_text_line_next (next) == NULL)
3473 _gtk_text_line_previous (GtkTextLine *line)
3475 GtkTextBTreeNode *node;
3476 GtkTextBTreeNode *node2;
3480 * Find the line under this GtkTextBTreeNode just before the starting line.
3482 prev = line->parent->children.line; /* First line at leaf */
3483 while (prev != line)
3485 if (prev->next == line)
3491 g_error ("gtk_text_btree_previous_line ran out of lines");
3495 * This was the first line associated with the particular parent
3496 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3497 * then search down from that GtkTextBTreeNode to find its last line.
3499 for (node = line->parent; ; node = node->parent)
3501 if (node == NULL || node->parent == NULL)
3503 else if (node != node->parent->children.node)
3507 for (node2 = node->parent->children.node; ;
3508 node2 = node2->children.node)
3510 while (node2->next != node)
3511 node2 = node2->next;
3513 if (node2->level == 0)
3519 for (prev = node2->children.line ; ; prev = prev->next)
3521 if (prev->next == NULL)
3525 g_assert_not_reached ();
3531 _gtk_text_line_data_new (GtkTextLayout *layout,
3534 GtkTextLineData *line_data;
3536 line_data = g_new (GtkTextLineData, 1);
3538 line_data->view_id = layout;
3539 line_data->next = NULL;
3540 line_data->width = 0;
3541 line_data->height = 0;
3542 line_data->valid = FALSE;
3548 _gtk_text_line_add_data (GtkTextLine *line,
3549 GtkTextLineData *data)
3551 g_return_if_fail (line != NULL);
3552 g_return_if_fail (data != NULL);
3553 g_return_if_fail (data->view_id != NULL);
3557 data->next = line->views;
3567 _gtk_text_line_remove_data (GtkTextLine *line,
3570 GtkTextLineData *prev;
3571 GtkTextLineData *iter;
3573 g_return_val_if_fail (line != NULL, NULL);
3574 g_return_val_if_fail (view_id != NULL, NULL);
3578 while (iter != NULL)
3580 if (iter->view_id == view_id)
3589 prev->next = iter->next;
3591 line->views = iter->next;
3600 _gtk_text_line_get_data (GtkTextLine *line,
3603 GtkTextLineData *iter;
3605 g_return_val_if_fail (line != NULL, NULL);
3606 g_return_val_if_fail (view_id != NULL, NULL);
3609 while (iter != NULL)
3611 if (iter->view_id == view_id)
3620 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3621 GtkTextLineData *ld)
3623 /* For now this is totally unoptimized. FIXME?
3625 We could probably optimize the case where the width removed
3626 is less than the max width for the parent node,
3627 and the case where the height is unchanged when we re-wrap.
3630 g_return_if_fail (ld != NULL);
3633 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3637 _gtk_text_line_char_count (GtkTextLine *line)
3639 GtkTextLineSegment *seg;
3643 seg = line->segments;
3646 size += seg->char_count;
3653 _gtk_text_line_byte_count (GtkTextLine *line)
3655 GtkTextLineSegment *seg;
3659 seg = line->segments;
3662 size += seg->byte_count;
3670 _gtk_text_line_char_index (GtkTextLine *target_line)
3672 GSList *node_stack = NULL;
3673 GtkTextBTreeNode *iter;
3677 /* Push all our parent nodes onto a stack */
3678 iter = target_line->parent;
3680 g_assert (iter != NULL);
3682 while (iter != NULL)
3684 node_stack = g_slist_prepend (node_stack, iter);
3686 iter = iter->parent;
3689 /* Check that we have the root node on top of the stack. */
3690 g_assert (node_stack != NULL &&
3691 node_stack->data != NULL &&
3692 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3694 /* Add up chars in all nodes before the nodes in our stack.
3698 iter = node_stack->data;
3699 while (iter != NULL)
3701 GtkTextBTreeNode *child_iter;
3702 GtkTextBTreeNode *next_node;
3704 next_node = node_stack->next ?
3705 node_stack->next->data : NULL;
3706 node_stack = g_slist_remove (node_stack, node_stack->data);
3708 if (iter->level == 0)
3710 /* stack should be empty when we're on the last node */
3711 g_assert (node_stack == NULL);
3712 break; /* Our children are now lines */
3715 g_assert (next_node != NULL);
3716 g_assert (iter != NULL);
3717 g_assert (next_node->parent == iter);
3719 /* Add up chars before us in the tree */
3720 child_iter = iter->children.node;
3721 while (child_iter != next_node)
3723 g_assert (child_iter != NULL);
3725 num_chars += child_iter->num_chars;
3727 child_iter = child_iter->next;
3733 g_assert (iter != NULL);
3734 g_assert (iter == target_line->parent);
3736 /* Since we don't store char counts in lines, only in segments, we
3737 have to iterate over the lines adding up segment char counts
3738 until we find our line. */
3739 line = iter->children.line;
3740 while (line != target_line)
3742 g_assert (line != NULL);
3744 num_chars += _gtk_text_line_char_count (line);
3749 g_assert (line == target_line);
3755 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3759 GtkTextLineSegment *seg;
3762 g_return_val_if_fail (line != NULL, NULL);
3764 offset = byte_offset;
3765 seg = line->segments;
3767 while (offset >= seg->byte_count)
3769 g_assert (seg != NULL); /* means an invalid byte index */
3770 offset -= seg->byte_count;
3775 *seg_offset = offset;
3781 _gtk_text_line_char_to_segment (GtkTextLine *line,
3785 GtkTextLineSegment *seg;
3788 g_return_val_if_fail (line != NULL, NULL);
3790 offset = char_offset;
3791 seg = line->segments;
3793 while (offset >= seg->char_count)
3795 g_assert (seg != NULL); /* means an invalid char index */
3796 offset -= seg->char_count;
3801 *seg_offset = offset;
3807 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3811 GtkTextLineSegment *seg;
3814 g_return_val_if_fail (line != NULL, NULL);
3816 offset = byte_offset;
3817 seg = line->segments;
3819 while (offset > 0 && offset >= seg->byte_count)
3821 g_assert (seg != NULL); /* means an invalid byte index */
3822 offset -= seg->byte_count;
3827 *seg_offset = offset;
3833 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3837 GtkTextLineSegment *seg;
3840 g_return_val_if_fail (line != NULL, NULL);
3842 offset = char_offset;
3843 seg = line->segments;
3845 while (offset > 0 && offset >= seg->char_count)
3847 g_assert (seg != NULL); /* means an invalid byte index */
3848 offset -= seg->char_count;
3853 *seg_offset = offset;
3859 _gtk_text_line_byte_to_char (GtkTextLine *line,
3863 GtkTextLineSegment *seg;
3865 g_return_val_if_fail (line != NULL, 0);
3866 g_return_val_if_fail (byte_offset >= 0, 0);
3869 seg = line->segments;
3870 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3871 the next segment) */
3873 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3875 byte_offset -= seg->byte_count;
3876 char_offset += seg->char_count;
3881 g_assert (seg != NULL);
3883 /* Now byte_offset is the offset into the current segment,
3884 and char_offset is the start of the current segment.
3885 Optimize the case where no chars use > 1 byte */
3886 if (seg->byte_count == seg->char_count)
3887 return char_offset + byte_offset;
3890 if (seg->type == >k_text_char_type)
3891 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3894 g_assert (seg->char_count == 1);
3895 g_assert (byte_offset == 0);
3903 _gtk_text_line_char_to_byte (GtkTextLine *line,
3906 g_warning ("FIXME not implemented");
3911 /* FIXME sync with char_locate (or figure out a clean
3912 way to merge the two functions) */
3914 _gtk_text_line_byte_locate (GtkTextLine *line,
3916 GtkTextLineSegment **segment,
3917 GtkTextLineSegment **any_segment,
3918 gint *seg_byte_offset,
3919 gint *line_byte_offset)
3921 GtkTextLineSegment *seg;
3922 GtkTextLineSegment *after_last_indexable;
3923 GtkTextLineSegment *last_indexable;
3927 g_return_val_if_fail (line != NULL, FALSE);
3928 g_return_val_if_fail (byte_offset >= 0, FALSE);
3931 *any_segment = NULL;
3934 offset = byte_offset;
3936 last_indexable = NULL;
3937 after_last_indexable = line->segments;
3938 seg = line->segments;
3940 /* The loop ends when we're inside a segment;
3941 last_indexable refers to the last segment
3942 we passed entirely. */
3943 while (seg && offset >= seg->byte_count)
3945 if (seg->char_count > 0)
3947 offset -= seg->byte_count;
3948 bytes_in_line += seg->byte_count;
3949 last_indexable = seg;
3950 after_last_indexable = last_indexable->next;
3958 /* We went off the end of the line */
3960 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3967 if (after_last_indexable != NULL)
3968 *any_segment = after_last_indexable;
3970 *any_segment = *segment;
3973 /* Override any_segment if we're in the middle of a segment. */
3975 *any_segment = *segment;
3977 *seg_byte_offset = offset;
3979 g_assert (*segment != NULL);
3980 g_assert (*any_segment != NULL);
3981 g_assert (*seg_byte_offset < (*segment)->byte_count);
3983 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3988 /* FIXME sync with byte_locate (or figure out a clean
3989 way to merge the two functions) */
3991 _gtk_text_line_char_locate (GtkTextLine *line,
3993 GtkTextLineSegment **segment,
3994 GtkTextLineSegment **any_segment,
3995 gint *seg_char_offset,
3996 gint *line_char_offset)
3998 GtkTextLineSegment *seg;
3999 GtkTextLineSegment *after_last_indexable;
4000 GtkTextLineSegment *last_indexable;
4004 g_return_val_if_fail (line != NULL, FALSE);
4005 g_return_val_if_fail (char_offset >= 0, FALSE);
4008 *any_segment = NULL;
4011 offset = char_offset;
4013 last_indexable = NULL;
4014 after_last_indexable = line->segments;
4015 seg = line->segments;
4017 /* The loop ends when we're inside a segment;
4018 last_indexable refers to the last segment
4019 we passed entirely. */
4020 while (seg && offset >= seg->char_count)
4022 if (seg->char_count > 0)
4024 offset -= seg->char_count;
4025 chars_in_line += seg->char_count;
4026 last_indexable = seg;
4027 after_last_indexable = last_indexable->next;
4035 /* end of the line */
4037 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4044 if (after_last_indexable != NULL)
4045 *any_segment = after_last_indexable;
4047 *any_segment = *segment;
4050 /* Override any_segment if we're in the middle of a segment. */
4052 *any_segment = *segment;
4054 *seg_char_offset = offset;
4056 g_assert (*segment != NULL);
4057 g_assert (*any_segment != NULL);
4058 g_assert (*seg_char_offset < (*segment)->char_count);
4060 *line_char_offset = chars_in_line + *seg_char_offset;
4066 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4068 gint *line_char_offset,
4069 gint *seg_char_offset)
4071 GtkTextLineSegment *seg;
4074 g_return_if_fail (line != NULL);
4075 g_return_if_fail (byte_offset >= 0);
4077 *line_char_offset = 0;
4079 offset = byte_offset;
4080 seg = line->segments;
4082 while (offset >= seg->byte_count)
4084 offset -= seg->byte_count;
4085 *line_char_offset += seg->char_count;
4087 g_assert (seg != NULL); /* means an invalid char offset */
4090 g_assert (seg->char_count > 0); /* indexable. */
4092 /* offset is now the number of bytes into the current segment we
4093 * want to go. Count chars into the current segment.
4096 if (seg->type == >k_text_char_type)
4098 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4100 g_assert (*seg_char_offset < seg->char_count);
4102 *line_char_offset += *seg_char_offset;
4106 g_assert (offset == 0);
4107 *seg_char_offset = 0;
4112 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4114 gint *line_byte_offset,
4115 gint *seg_byte_offset)
4117 GtkTextLineSegment *seg;
4120 g_return_if_fail (line != NULL);
4121 g_return_if_fail (char_offset >= 0);
4123 *line_byte_offset = 0;
4125 offset = char_offset;
4126 seg = line->segments;
4128 while (offset >= seg->char_count)
4130 offset -= seg->char_count;
4131 *line_byte_offset += seg->byte_count;
4133 g_assert (seg != NULL); /* means an invalid char offset */
4136 g_assert (seg->char_count > 0); /* indexable. */
4138 /* offset is now the number of chars into the current segment we
4139 want to go. Count bytes into the current segment. */
4141 if (seg->type == >k_text_char_type)
4145 /* if in the last fourth of the segment walk backwards */
4146 if (seg->char_count - offset < seg->char_count / 4)
4147 p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4148 offset - seg->char_count);
4150 p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4152 *seg_byte_offset = p - seg->body.chars;
4154 g_assert (*seg_byte_offset < seg->byte_count);
4156 *line_byte_offset += *seg_byte_offset;
4160 g_assert (offset == 0);
4161 *seg_byte_offset = 0;
4166 node_compare (GtkTextBTreeNode *lhs,
4167 GtkTextBTreeNode *rhs)
4169 GtkTextBTreeNode *iter;
4170 GtkTextBTreeNode *node;
4171 GtkTextBTreeNode *common_parent;
4172 GtkTextBTreeNode *parent_of_lower;
4173 GtkTextBTreeNode *parent_of_higher;
4174 gboolean lhs_is_lower;
4175 GtkTextBTreeNode *lower;
4176 GtkTextBTreeNode *higher;
4178 /* This function assumes that lhs and rhs are not underneath each
4185 if (lhs->level < rhs->level)
4187 lhs_is_lower = TRUE;
4193 lhs_is_lower = FALSE;
4198 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4199 * of the common parent we used to reach the common parent; the
4200 * ordering of these child nodes in the child list is the ordering
4204 /* Get on the same level (may be on same level already) */
4206 while (node->level < higher->level)
4207 node = node->parent;
4209 g_assert (node->level == higher->level);
4211 g_assert (node != higher); /* Happens if lower is underneath higher */
4213 /* Go up until we have two children with a common parent.
4215 parent_of_lower = node;
4216 parent_of_higher = higher;
4218 while (parent_of_lower->parent != parent_of_higher->parent)
4220 parent_of_lower = parent_of_lower->parent;
4221 parent_of_higher = parent_of_higher->parent;
4224 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4226 common_parent = parent_of_lower->parent;
4228 g_assert (common_parent != NULL);
4230 /* See which is first in the list of common_parent's children */
4231 iter = common_parent->children.node;
4232 while (iter != NULL)
4234 if (iter == parent_of_higher)
4236 /* higher is less than lower */
4239 return 1; /* lhs > rhs */
4243 else if (iter == parent_of_lower)
4245 /* lower is less than higher */
4248 return -1; /* lhs < rhs */
4256 g_assert_not_reached ();
4260 /* remember that tag == NULL means "any tag" */
4262 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4266 GtkTextBTreeNode *node;
4267 GtkTextTagInfo *info;
4268 gboolean below_tag_root;
4270 g_return_val_if_fail (line != NULL, NULL);
4272 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4273 _gtk_text_btree_check (tree);
4277 /* Right now we can only offer linear-search if the user wants
4278 * to know about any tag toggle at all.
4280 return _gtk_text_line_next_excluding_last (line);
4283 /* Our tag summaries only have node precision, not line
4284 * precision. This means that if any line under a node could contain a
4285 * tag, then any of the others could also contain a tag.
4287 * In the future we could have some mechanism to keep track of how
4288 * many toggles we've found under a node so far, since we have a
4289 * count of toggles under the node. But for now I'm going with KISS.
4292 /* return same-node line, if any. */
4296 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4300 if (info->tag_root == NULL)
4303 if (info->tag_root == line->parent)
4304 return NULL; /* we were at the last line under the tag root */
4306 /* We need to go up out of this node, and on to the next one with
4307 toggles for the target tag. If we're below the tag root, we need to
4308 find the next node below the tag root that has tag summaries. If
4309 we're not below the tag root, we need to see if the tag root is
4310 after us in the tree, and if so, return the first line underneath
4313 node = line->parent;
4314 below_tag_root = FALSE;
4315 while (node != NULL)
4317 if (node == info->tag_root)
4319 below_tag_root = TRUE;
4323 node = node->parent;
4328 node = line->parent;
4329 while (node != info->tag_root)
4331 if (node->next == NULL)
4332 node = node->parent;
4337 if (gtk_text_btree_node_has_tag (node, tag))
4347 ordering = node_compare (line->parent, info->tag_root);
4351 /* Tag root is ahead of us, so search there. */
4352 node = info->tag_root;
4357 /* Tag root is after us, so no more lines that
4358 * could contain the tag.
4363 g_assert_not_reached ();
4368 g_assert (node != NULL);
4370 /* We have to find the first sub-node of this node that contains
4374 while (node->level > 0)
4376 g_assert (node != NULL); /* If this fails, it likely means an
4377 incorrect tag summary led us on a
4378 wild goose chase down this branch of
4380 node = node->children.node;
4381 while (node != NULL)
4383 if (gtk_text_btree_node_has_tag (node, tag))
4389 g_assert (node != NULL);
4390 g_assert (node->level == 0);
4392 return node->children.line;
4396 prev_line_under_node (GtkTextBTreeNode *node,
4401 prev = node->children.line;
4407 while (prev->next != line)
4417 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4421 GtkTextBTreeNode *node;
4422 GtkTextBTreeNode *found_node = NULL;
4423 GtkTextTagInfo *info;
4424 gboolean below_tag_root;
4426 GtkTextBTreeNode *line_ancestor;
4427 GtkTextBTreeNode *line_ancestor_parent;
4429 /* See next_could_contain_tag () for more extensive comments
4430 * on what's going on here.
4433 g_return_val_if_fail (line != NULL, NULL);
4435 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4436 _gtk_text_btree_check (tree);
4440 /* Right now we can only offer linear-search if the user wants
4441 * to know about any tag toggle at all.
4443 return _gtk_text_line_previous (line);
4446 /* Return same-node line, if any. */
4447 prev = prev_line_under_node (line->parent, line);
4451 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4455 if (info->tag_root == NULL)
4458 if (info->tag_root == line->parent)
4459 return NULL; /* we were at the first line under the tag root */
4461 /* Are we below the tag root */
4462 node = line->parent;
4463 below_tag_root = FALSE;
4464 while (node != NULL)
4466 if (node == info->tag_root)
4468 below_tag_root = TRUE;
4472 node = node->parent;
4477 /* Look for a previous node under this tag root that has our
4481 /* this assertion holds because line->parent is not the
4482 * tag root, we are below the tag root, and the tag
4485 g_assert (line->parent->parent != NULL);
4487 line_ancestor = line->parent;
4488 line_ancestor_parent = line->parent->parent;
4490 while (line_ancestor != info->tag_root)
4492 GSList *child_nodes = NULL;
4495 /* Create reverse-order list of nodes before
4498 if (line_ancestor_parent != NULL)
4499 node = line_ancestor_parent->children.node;
4501 node = line_ancestor;
4503 while (node != line_ancestor && node != NULL)
4505 child_nodes = g_slist_prepend (child_nodes, node);
4510 /* Try to find a node with our tag on it in the list */
4514 GtkTextBTreeNode *this_node = tmp->data;
4516 g_assert (this_node != line_ancestor);
4518 if (gtk_text_btree_node_has_tag (this_node, tag))
4520 found_node = this_node;
4521 g_slist_free (child_nodes);
4525 tmp = g_slist_next (tmp);
4528 g_slist_free (child_nodes);
4530 /* Didn't find anything on this level; go up one level. */
4531 line_ancestor = line_ancestor_parent;
4532 line_ancestor_parent = line_ancestor->parent;
4542 ordering = node_compare (line->parent, info->tag_root);
4546 /* Tag root is ahead of us, so no more lines
4553 /* Tag root is after us, so grab last tagged
4554 * line underneath the tag root.
4556 found_node = info->tag_root;
4560 g_assert_not_reached ();
4565 g_assert (found_node != NULL);
4567 /* We have to find the last sub-node of this node that contains
4572 while (node->level > 0)
4574 GSList *child_nodes = NULL;
4576 g_assert (node != NULL); /* If this fails, it likely means an
4577 incorrect tag summary led us on a
4578 wild goose chase down this branch of
4581 node = node->children.node;
4582 while (node != NULL)
4584 child_nodes = g_slist_prepend (child_nodes, node);
4588 node = NULL; /* detect failure to find a child node. */
4591 while (iter != NULL)
4593 if (gtk_text_btree_node_has_tag (iter->data, tag))
4595 /* recurse into this node. */
4600 iter = g_slist_next (iter);
4603 g_slist_free (child_nodes);
4605 g_assert (node != NULL);
4608 g_assert (node != NULL);
4609 g_assert (node->level == 0);
4611 /* this assertion is correct, but slow. */
4612 /* g_assert (node_compare (node, line->parent) < 0); */
4614 /* Return last line in this node. */
4616 prev = node->children.line;
4624 * Non-public function implementations
4628 summary_list_destroy (Summary *summary)
4630 g_slice_free_chain (Summary, summary, next);
4634 get_last_line (GtkTextBTree *tree)
4636 if (tree->last_line_stamp != tree->chars_changed_stamp)
4642 n_lines = _gtk_text_btree_line_count (tree);
4644 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4646 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4648 tree->last_line_stamp = tree->chars_changed_stamp;
4649 tree->last_line = line;
4652 return tree->last_line;
4660 gtk_text_line_new (void)
4664 line = g_new0(GtkTextLine, 1);
4665 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4666 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4667 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4673 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4675 GtkTextLineData *ld;
4676 GtkTextLineData *next;
4678 g_return_if_fail (line != NULL);
4685 view = gtk_text_btree_get_view (tree, ld->view_id);
4687 g_assert (view != NULL);
4690 gtk_text_layout_free_line_data (view->layout, line, ld);
4699 gtk_text_line_set_parent (GtkTextLine *line,
4700 GtkTextBTreeNode *node)
4702 if (line->parent == node)
4704 line->parent = node;
4705 gtk_text_btree_node_invalidate_upward (node, NULL);
4709 cleanup_line (GtkTextLine *line)
4711 GtkTextLineSegment *seg, **prev_p;
4715 * Make a pass over all of the segments in the line, giving each
4716 * a chance to clean itself up. This could potentially change
4717 * the structure of the line, e.g. by merging two segments
4718 * together or having two segments cancel themselves; if so,
4719 * then repeat the whole process again, since the first structure
4720 * change might make other structure changes possible. Repeat
4721 * until eventually there are no changes.
4728 prev_p = &line->segments;
4729 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4731 if (seg->type->cleanupFunc != NULL)
4733 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4741 prev_p = &(*prev_p)->next;
4751 node_data_new (gpointer view_id)
4755 nd = g_slice_new (NodeData);
4757 nd->view_id = view_id;
4767 node_data_destroy (NodeData *nd)
4769 g_slice_free (NodeData, nd);
4773 node_data_list_destroy (NodeData *nd)
4775 g_slice_free_chain (NodeData, nd, next);
4779 node_data_find (NodeData *nd,
4784 if (nd->view_id == view_id)
4792 summary_destroy (Summary *summary)
4794 /* Fill with error-triggering garbage */
4795 summary->info = (void*)0x1;
4796 summary->toggle_count = 567;
4797 summary->next = (void*)0x1;
4798 g_slice_free (Summary, summary);
4801 static GtkTextBTreeNode*
4802 gtk_text_btree_node_new (void)
4804 GtkTextBTreeNode *node;
4806 node = g_new (GtkTextBTreeNode, 1);
4808 node->node_data = NULL;
4814 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4815 GtkTextTagInfo *info,
4820 summary = node->summary;
4821 while (summary != NULL)
4823 if (summary->info == info)
4825 summary->toggle_count += adjust;
4829 summary = summary->next;
4832 if (summary == NULL)
4834 /* didn't find a summary for our tag. */
4835 g_return_if_fail (adjust > 0);
4836 summary = g_slice_new (Summary);
4837 summary->info = info;
4838 summary->toggle_count = adjust;
4839 summary->next = node->summary;
4840 node->summary = summary;
4844 /* Note that the tag root and above do not have summaries
4845 for the tag; only nodes below the tag root have
4848 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4852 summary = node->summary;
4853 while (summary != NULL)
4856 summary->info->tag == tag)
4859 summary = summary->next;
4865 /* Add node and all children to the damage region. */
4867 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4871 nd = node->node_data;
4878 if (node->level == 0)
4882 line = node->children.line;
4883 while (line != NULL)
4885 GtkTextLineData *ld;
4899 GtkTextBTreeNode *child;
4901 child = node->children.node;
4903 while (child != NULL)
4905 gtk_text_btree_node_invalidate_downward (child);
4907 child = child->next;
4913 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4915 GtkTextBTreeNode *iter;
4918 while (iter != NULL)
4924 nd = node_data_find (iter->node_data, view_id);
4926 if (nd == NULL || !nd->valid)
4927 break; /* Once a node is invalid, we know its parents are as well. */
4933 gboolean should_continue = FALSE;
4935 nd = iter->node_data;
4940 should_continue = TRUE;
4947 if (!should_continue)
4948 break; /* This node was totally invalidated, so are its
4952 iter = iter->parent;
4958 * _gtk_text_btree_is_valid:
4959 * @tree: a #GtkTextBTree
4960 * @view_id: ID for the view
4962 * Check to see if the entire #GtkTextBTree is valid or not for
4965 * Return value: %TRUE if the entire #GtkTextBTree is valid
4968 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4972 g_return_val_if_fail (tree != NULL, FALSE);
4974 nd = node_data_find (tree->root_node->node_data, view_id);
4975 return (nd && nd->valid);
4978 typedef struct _ValidateState ValidateState;
4980 struct _ValidateState
4982 gint remaining_pixels;
4983 gboolean in_validation;
4990 gtk_text_btree_node_validate (BTreeView *view,
4991 GtkTextBTreeNode *node,
4993 ValidateState *state)
4995 gint node_valid = TRUE;
4996 gint node_width = 0;
4997 gint node_height = 0;
4999 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5000 g_return_if_fail (!nd->valid);
5002 if (node->level == 0)
5004 GtkTextLine *line = node->children.line;
5005 GtkTextLineData *ld;
5007 /* Iterate over leading valid lines */
5008 while (line != NULL)
5010 ld = _gtk_text_line_get_data (line, view_id);
5012 if (!ld || !ld->valid)
5014 else if (state->in_validation)
5016 state->in_validation = FALSE;
5021 state->y += ld->height;
5022 node_width = MAX (ld->width, node_width);
5023 node_height += ld->height;
5029 state->in_validation = TRUE;
5031 /* Iterate over invalid lines */
5032 while (line != NULL)
5034 ld = _gtk_text_line_get_data (line, view_id);
5036 if (ld && ld->valid)
5041 state->old_height += ld->height;
5042 ld = gtk_text_layout_wrap (view->layout, line, ld);
5043 state->new_height += ld->height;
5045 node_width = MAX (ld->width, node_width);
5046 node_height += ld->height;
5048 state->remaining_pixels -= ld->height;
5049 if (state->remaining_pixels <= 0)
5059 /* Iterate over the remaining lines */
5060 while (line != NULL)
5062 ld = _gtk_text_line_get_data (line, view_id);
5063 state->in_validation = FALSE;
5065 if (!ld || !ld->valid)
5070 node_width = MAX (ld->width, node_width);
5071 node_height += ld->height;
5079 GtkTextBTreeNode *child;
5082 child = node->children.node;
5084 /* Iterate over leading valid nodes */
5087 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5089 if (!child_nd->valid)
5091 else if (state->in_validation)
5093 state->in_validation = FALSE;
5098 state->y += child_nd->height;
5099 node_width = MAX (node_width, child_nd->width);
5100 node_height += child_nd->height;
5103 child = child->next;
5106 /* Iterate over invalid nodes */
5109 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5111 if (child_nd->valid)
5115 gtk_text_btree_node_validate (view, child, view_id, state);
5117 if (!child_nd->valid)
5119 node_width = MAX (node_width, child_nd->width);
5120 node_height += child_nd->height;
5122 if (!state->in_validation || state->remaining_pixels <= 0)
5124 child = child->next;
5129 child = child->next;
5132 /* Iterate over the remaining lines */
5135 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5136 state->in_validation = FALSE;
5138 if (!child_nd->valid)
5141 node_width = MAX (child_nd->width, node_width);
5142 node_height += child_nd->height;
5144 child = child->next;
5148 nd->width = node_width;
5149 nd->height = node_height;
5150 nd->valid = node_valid;
5154 * _gtk_text_btree_validate:
5155 * @tree: a #GtkTextBTree
5157 * @max_pixels: the maximum number of pixels to validate. (No more
5158 * than one paragraph beyond this limit will be validated)
5159 * @y: location to store starting y coordinate of validated region
5160 * @old_height: location to store old height of validated region
5161 * @new_height: location to store new height of validated region
5163 * Validate a single contiguous invalid region of a #GtkTextBTree for
5166 * Return value: %TRUE if a region has been validated, %FALSE if the
5167 * entire tree was already valid.
5170 _gtk_text_btree_validate (GtkTextBTree *tree,
5179 g_return_val_if_fail (tree != NULL, FALSE);
5181 view = gtk_text_btree_get_view (tree, view_id);
5182 g_return_val_if_fail (view != NULL, FALSE);
5184 if (!_gtk_text_btree_is_valid (tree, view_id))
5186 ValidateState state;
5188 state.remaining_pixels = max_pixels;
5189 state.in_validation = FALSE;
5191 state.old_height = 0;
5192 state.new_height = 0;
5194 gtk_text_btree_node_validate (view,
5201 *old_height = state.old_height;
5203 *new_height = state.new_height;
5205 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5206 _gtk_text_btree_check (tree);
5215 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5219 gboolean *valid_out)
5223 gboolean valid = TRUE;
5225 if (node->level == 0)
5227 GtkTextLine *line = node->children.line;
5229 while (line != NULL)
5231 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5233 if (!ld || !ld->valid)
5238 width = MAX (ld->width, width);
5239 height += ld->height;
5247 GtkTextBTreeNode *child = node->children.node;
5251 NodeData *child_nd = node_data_find (child->node_data, view_id);
5253 if (!child_nd || !child_nd->valid)
5258 width = MAX (child_nd->width, width);
5259 height += child_nd->height;
5262 child = child->next;
5267 *height_out = height;
5272 /* Recompute the validity and size of the view data for a given
5273 * view at this node from the immediate children of the node
5276 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5279 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5284 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5285 &width, &height, &valid);
5287 nd->height = height;
5294 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5299 gtk_text_btree_node_check_valid (node, view_id);
5300 node = node->parent;
5305 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5308 if (node->level == 0)
5310 return gtk_text_btree_node_check_valid (node, view_id);
5314 GtkTextBTreeNode *child = node->children.node;
5316 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5324 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5326 if (!child_nd->valid)
5328 nd->width = MAX (child_nd->width, nd->width);
5329 nd->height += child_nd->height;
5331 child = child->next;
5340 * _gtk_text_btree_validate_line:
5341 * @tree: a #GtkTextBTree
5342 * @line: line to validate
5343 * @view_id: view ID for the view to validate
5345 * Revalidate a single line of the btree for the given view, propagate
5346 * results up through the entire tree.
5349 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5353 GtkTextLineData *ld;
5356 g_return_if_fail (tree != NULL);
5357 g_return_if_fail (line != NULL);
5359 view = gtk_text_btree_get_view (tree, view_id);
5360 g_return_if_fail (view != NULL);
5362 ld = _gtk_text_line_get_data (line, view_id);
5363 if (!ld || !ld->valid)
5365 ld = gtk_text_layout_wrap (view->layout, line, ld);
5367 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5372 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5374 if (node->level == 0)
5378 line = node->children.line;
5379 while (line != NULL)
5381 GtkTextLineData *ld;
5383 ld = _gtk_text_line_remove_data (line, view_id);
5386 gtk_text_layout_free_line_data (view->layout, line, ld);
5393 GtkTextBTreeNode *child;
5395 child = node->children.node;
5397 while (child != NULL)
5400 gtk_text_btree_node_remove_view (view, child, view_id);
5402 child = child->next;
5406 gtk_text_btree_node_remove_data (node, view_id);
5410 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5412 if (node->level == 0)
5415 GtkTextLineSegment *seg;
5417 while (node->children.line != NULL)
5419 line = node->children.line;
5420 node->children.line = line->next;
5421 while (line->segments != NULL)
5423 seg = line->segments;
5424 line->segments = seg->next;
5426 (*seg->type->deleteFunc) (seg, line, TRUE);
5428 gtk_text_line_destroy (tree, line);
5433 GtkTextBTreeNode *childPtr;
5435 while (node->children.node != NULL)
5437 childPtr = node->children.node;
5438 node->children.node = childPtr->next;
5439 gtk_text_btree_node_destroy (tree, childPtr);
5443 gtk_text_btree_node_free_empty (tree, node);
5447 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5448 GtkTextBTreeNode *node)
5450 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5451 (node->level == 0 && node->children.line == NULL));
5453 summary_list_destroy (node->summary);
5454 node_data_list_destroy (node->node_data);
5459 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5463 nd = node->node_data;
5466 if (nd->view_id == view_id)
5474 nd = node_data_new (view_id);
5476 if (node->node_data)
5477 nd->next = node->node_data;
5479 node->node_data = nd;
5486 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5492 nd = node->node_data;
5495 if (nd->view_id == view_id)
5506 prev->next = nd->next;
5508 if (node->node_data == nd)
5509 node->node_data = nd->next;
5513 node_data_destroy (nd);
5517 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5518 gint *width, gint *height)
5522 g_return_if_fail (width != NULL);
5523 g_return_if_fail (height != NULL);
5525 nd = gtk_text_btree_node_ensure_data (node, view_id);
5530 *height = nd->height;
5533 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5534 * here isn't quite right, since for a lot of operations we want to
5535 * know which children of the common parent correspond to the two nodes
5536 * (e.g., when computing the order of two iters)
5538 static GtkTextBTreeNode *
5539 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5540 GtkTextBTreeNode *node2)
5542 while (node1->level < node2->level)
5543 node1 = node1->parent;
5544 while (node2->level < node1->level)
5545 node2 = node2->parent;
5546 while (node1 != node2)
5548 node1 = node1->parent;
5549 node2 = node2->parent;
5560 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5565 while (view != NULL)
5567 if (view->view_id == view_id)
5576 get_tree_bounds (GtkTextBTree *tree,
5580 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5581 _gtk_text_btree_get_end_iter (tree, end);
5585 tag_changed_cb (GtkTextTagTable *table,
5587 gboolean size_changed,
5592 /* We need to queue a relayout on all regions that are tagged with
5599 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5601 /* Must be a last toggle if there was a first one. */
5602 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5603 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5604 _gtk_text_btree_invalidate_region (tree,
5611 /* We only need to queue a redraw, not a relayout */
5616 while (view != NULL)
5620 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5621 gtk_text_layout_changed (view->layout, 0, height, height);
5629 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5632 /* Remove the tag from the tree */
5637 get_tree_bounds (tree, &start, &end);
5639 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5640 gtk_text_btree_remove_tag_info (tree, tag);
5644 /* Rebalance the out-of-whack node "node" */
5646 gtk_text_btree_rebalance (GtkTextBTree *tree,
5647 GtkTextBTreeNode *node)
5650 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5651 * up through the tree one GtkTextBTreeNode at a time until the root
5652 * GtkTextBTreeNode has been processed.
5655 while (node != NULL)
5657 GtkTextBTreeNode *new_node, *child;
5662 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5663 * then split off all but the first MIN_CHILDREN into a separate
5664 * GtkTextBTreeNode following the original one. Then repeat until the
5665 * GtkTextBTreeNode has a decent size.
5668 if (node->num_children > MAX_CHILDREN)
5673 * If the GtkTextBTreeNode being split is the root
5674 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5678 if (node->parent == NULL)
5680 new_node = gtk_text_btree_node_new ();
5681 new_node->parent = NULL;
5682 new_node->next = NULL;
5683 new_node->summary = NULL;
5684 new_node->level = node->level + 1;
5685 new_node->children.node = node;
5686 recompute_node_counts (tree, new_node);
5687 tree->root_node = new_node;
5689 new_node = gtk_text_btree_node_new ();
5690 new_node->parent = node->parent;
5691 new_node->next = node->next;
5692 node->next = new_node;
5693 new_node->summary = NULL;
5694 new_node->level = node->level;
5695 new_node->num_children = node->num_children - MIN_CHILDREN;
5696 if (node->level == 0)
5698 for (i = MIN_CHILDREN-1,
5699 line = node->children.line;
5700 i > 0; i--, line = line->next)
5702 /* Empty loop body. */
5704 new_node->children.line = line->next;
5709 for (i = MIN_CHILDREN-1,
5710 child = node->children.node;
5711 i > 0; i--, child = child->next)
5713 /* Empty loop body. */
5715 new_node->children.node = child->next;
5718 recompute_node_counts (tree, node);
5719 node->parent->num_children++;
5721 if (node->num_children <= MAX_CHILDREN)
5723 recompute_node_counts (tree, node);
5729 while (node->num_children < MIN_CHILDREN)
5731 GtkTextBTreeNode *other;
5732 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5733 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5734 int total_children, first_children, i;
5737 * Too few children for this GtkTextBTreeNode. If this is the root then,
5738 * it's OK for it to have less than MIN_CHILDREN children
5739 * as long as it's got at least two. If it has only one
5740 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5741 * the tree and use its child as the new root.
5744 if (node->parent == NULL)
5746 if ((node->num_children == 1) && (node->level > 0))
5748 tree->root_node = node->children.node;
5749 tree->root_node->parent = NULL;
5751 node->children.node = NULL;
5752 gtk_text_btree_node_free_empty (tree, node);
5758 * Not the root. Make sure that there are siblings to
5762 if (node->parent->num_children < 2)
5764 gtk_text_btree_rebalance (tree, node->parent);
5769 * Find a sibling neighbor to borrow from, and arrange for
5770 * node to be the earlier of the pair.
5773 if (node->next == NULL)
5775 for (other = node->parent->children.node;
5776 other->next != node;
5777 other = other->next)
5779 /* Empty loop body. */
5786 * We're going to either merge the two siblings together
5787 * into one GtkTextBTreeNode or redivide the children among them to
5788 * balance their loads. As preparation, join their two
5789 * child lists into a single list and remember the half-way
5790 * point in the list.
5793 total_children = node->num_children + other->num_children;
5794 first_children = total_children/2;
5795 if (node->children.node == NULL)
5797 node->children = other->children;
5798 other->children.node = NULL;
5799 other->children.line = NULL;
5801 if (node->level == 0)
5805 for (line = node->children.line, i = 1;
5807 line = line->next, i++)
5809 if (i == first_children)
5814 line->next = other->children.line;
5815 while (i <= first_children)
5824 GtkTextBTreeNode *child;
5826 for (child = node->children.node, i = 1;
5827 child->next != NULL;
5828 child = child->next, i++)
5830 if (i <= first_children)
5832 if (i == first_children)
5834 halfwaynode = child;
5838 child->next = other->children.node;
5839 while (i <= first_children)
5841 halfwaynode = child;
5842 child = child->next;
5848 * If the two siblings can simply be merged together, do it.
5851 if (total_children <= MAX_CHILDREN)
5853 recompute_node_counts (tree, node);
5854 node->next = other->next;
5855 node->parent->num_children--;
5857 other->children.node = NULL;
5858 other->children.line = NULL;
5859 gtk_text_btree_node_free_empty (tree, other);
5864 * The siblings can't be merged, so just divide their
5865 * children evenly between them.
5868 if (node->level == 0)
5870 other->children.line = halfwayline->next;
5871 halfwayline->next = NULL;
5875 other->children.node = halfwaynode->next;
5876 halfwaynode->next = NULL;
5879 recompute_node_counts (tree, node);
5880 recompute_node_counts (tree, other);
5883 node = node->parent;
5888 post_insert_fixup (GtkTextBTree *tree,
5890 gint line_count_delta,
5891 gint char_count_delta)
5894 GtkTextBTreeNode *node;
5897 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5898 * point, then rebalance the tree if necessary.
5901 for (node = line->parent ; node != NULL;
5902 node = node->parent)
5904 node->num_lines += line_count_delta;
5905 node->num_chars += char_count_delta;
5907 node = line->parent;
5908 node->num_children += line_count_delta;
5910 if (node->num_children > MAX_CHILDREN)
5912 gtk_text_btree_rebalance (tree, node);
5915 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5916 _gtk_text_btree_check (tree);
5919 static GtkTextTagInfo*
5920 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5923 GtkTextTagInfo *info;
5927 list = tree->tag_infos;
5928 while (list != NULL)
5931 if (info->tag == tag)
5934 list = g_slist_next (list);
5940 static GtkTextTagInfo*
5941 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5944 GtkTextTagInfo *info;
5946 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5950 /* didn't find it, create. */
5952 info = g_slice_new (GtkTextTagInfo);
5956 info->tag_root = NULL;
5957 info->toggle_count = 0;
5959 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5962 g_print ("Created tag info %p for tag %s(%p)\n",
5963 info, info->tag->name ? info->tag->name : "anon",
5972 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5975 GtkTextTagInfo *info;
5980 list = tree->tag_infos;
5981 while (list != NULL)
5984 if (info->tag == tag)
5987 g_print ("Removing tag info %p for tag %s(%p)\n",
5988 info, info->tag->name ? info->tag->name : "anon",
5994 prev->next = list->next;
5998 tree->tag_infos = list->next;
6001 g_slist_free (list);
6003 g_object_unref (info->tag);
6005 g_slice_free (GtkTextTagInfo, info);
6010 list = g_slist_next (list);
6015 recompute_level_zero_counts (GtkTextBTreeNode *node)
6018 GtkTextLineSegment *seg;
6020 g_assert (node->level == 0);
6022 line = node->children.line;
6023 while (line != NULL)
6025 node->num_children++;
6028 if (line->parent != node)
6029 gtk_text_line_set_parent (line, node);
6031 seg = line->segments;
6035 node->num_chars += seg->char_count;
6037 if (((seg->type != >k_text_toggle_on_type)
6038 && (seg->type != >k_text_toggle_off_type))
6039 || !(seg->body.toggle.inNodeCounts))
6045 GtkTextTagInfo *info;
6047 info = seg->body.toggle.info;
6049 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6060 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6063 GtkTextBTreeNode *child;
6065 g_assert (node->level > 0);
6067 child = node->children.node;
6068 while (child != NULL)
6070 node->num_children += 1;
6071 node->num_lines += child->num_lines;
6072 node->num_chars += child->num_chars;
6074 if (child->parent != node)
6076 child->parent = node;
6077 gtk_text_btree_node_invalidate_upward (node, NULL);
6080 summary = child->summary;
6081 while (summary != NULL)
6083 gtk_text_btree_node_adjust_toggle_count (node,
6085 summary->toggle_count);
6087 summary = summary->next;
6090 child = child->next;
6095 *----------------------------------------------------------------------
6097 * recompute_node_counts --
6099 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6100 * (tags, child information, etc.) by scanning the information in
6101 * its descendants. This procedure is called during rebalancing
6102 * when a GtkTextBTreeNode's child structure has changed.
6108 * The tag counts for node are modified to reflect its current
6109 * child structure, as are its num_children, num_lines, num_chars fields.
6110 * Also, all of the childrens' parent fields are made to point
6113 *----------------------------------------------------------------------
6117 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6120 Summary *summary, *summary2;
6123 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6124 * the existing Summary records (most of them will probably be reused).
6127 summary = node->summary;
6128 while (summary != NULL)
6130 summary->toggle_count = 0;
6131 summary = summary->next;
6134 node->num_children = 0;
6135 node->num_lines = 0;
6136 node->num_chars = 0;
6139 * Scan through the children, adding the childrens' tag counts into
6140 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6144 if (node->level == 0)
6145 recompute_level_zero_counts (node);
6147 recompute_level_nonzero_counts (node);
6152 gtk_text_btree_node_check_valid (node, view->view_id);
6157 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6158 * records that still have a zero count, or that have all the toggles.
6159 * The GtkTextBTreeNode with the children that account for all the tags toggles
6160 * have no summary information, and they become the tag_root for the tag.
6164 for (summary = node->summary; summary != NULL; )
6166 if (summary->toggle_count > 0 &&
6167 summary->toggle_count < summary->info->toggle_count)
6169 if (node->level == summary->info->tag_root->level)
6172 * The tag's root GtkTextBTreeNode split and some toggles left.
6173 * The tag root must move up a level.
6175 summary->info->tag_root = node->parent;
6178 summary = summary->next;
6181 if (summary->toggle_count == summary->info->toggle_count)
6184 * A GtkTextBTreeNode merge has collected all the toggles under
6185 * one GtkTextBTreeNode. Push the root down to this level.
6187 summary->info->tag_root = node;
6189 if (summary2 != NULL)
6191 summary2->next = summary->next;
6192 summary_destroy (summary);
6193 summary = summary2->next;
6197 node->summary = summary->next;
6198 summary_destroy (summary);
6199 summary = node->summary;
6205 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6206 GtkTextTagInfo *info,
6207 gint delta) /* may be negative */
6209 Summary *summary, *prevPtr;
6210 GtkTextBTreeNode *node2Ptr;
6211 int rootLevel; /* Level of original tag root */
6213 info->toggle_count += delta;
6215 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6217 info->tag_root = node;
6222 * Note the level of the existing root for the tag so we can detect
6223 * if it needs to be moved because of the toggle count change.
6226 rootLevel = info->tag_root->level;
6229 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6230 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6234 for ( ; node != info->tag_root; node = node->parent)
6237 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6238 * perhaps all we have to do is adjust its count.
6241 for (prevPtr = NULL, summary = node->summary;
6243 prevPtr = summary, summary = summary->next)
6245 if (summary->info == info)
6250 if (summary != NULL)
6252 summary->toggle_count += delta;
6253 if (summary->toggle_count > 0 &&
6254 summary->toggle_count < info->toggle_count)
6258 if (summary->toggle_count != 0)
6261 * Should never find a GtkTextBTreeNode with max toggle count at this
6262 * point (there shouldn't have been a summary entry in the
6266 g_error ("%s: bad toggle count (%d) max (%d)",
6267 G_STRLOC, summary->toggle_count, info->toggle_count);
6271 * Zero toggle count; must remove this tag from the list.
6274 if (prevPtr == NULL)
6276 node->summary = summary->next;
6280 prevPtr->next = summary->next;
6282 summary_destroy (summary);
6287 * This tag isn't currently in the summary information list.
6290 if (rootLevel == node->level)
6294 * The old tag root is at the same level in the tree as this
6295 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6296 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6297 * as well as the old root (if not, we'll move it up again
6298 * the next time through the loop). To push it up one level
6299 * we copy the original toggle count into the summary
6300 * information at the old root and change the root to its
6301 * parent GtkTextBTreeNode.
6304 GtkTextBTreeNode *rootnode = info->tag_root;
6305 summary = g_slice_new (Summary);
6306 summary->info = info;
6307 summary->toggle_count = info->toggle_count - delta;
6308 summary->next = rootnode->summary;
6309 rootnode->summary = summary;
6310 rootnode = rootnode->parent;
6311 rootLevel = rootnode->level;
6312 info->tag_root = rootnode;
6314 summary = g_slice_new (Summary);
6315 summary->info = info;
6316 summary->toggle_count = delta;
6317 summary->next = node->summary;
6318 node->summary = summary;
6323 * If we've decremented the toggle count, then it may be necessary
6324 * to push the tag root down one or more levels.
6331 if (info->toggle_count == 0)
6333 info->tag_root = (GtkTextBTreeNode *) NULL;
6336 node = info->tag_root;
6337 while (node->level > 0)
6340 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6341 * toggles. If so, push the root down one level.
6344 for (node2Ptr = node->children.node;
6345 node2Ptr != (GtkTextBTreeNode *)NULL ;
6346 node2Ptr = node2Ptr->next)
6348 for (prevPtr = NULL, summary = node2Ptr->summary;
6350 prevPtr = summary, summary = summary->next)
6352 if (summary->info == info)
6357 if (summary == NULL)
6361 if (summary->toggle_count != info->toggle_count)
6364 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6371 * This GtkTextBTreeNode has all the toggles, so push down the root.
6374 if (prevPtr == NULL)
6376 node2Ptr->summary = summary->next;
6380 prevPtr->next = summary->next;
6382 summary_destroy (summary);
6383 info->tag_root = node2Ptr;
6386 node = info->tag_root;
6391 *----------------------------------------------------------------------
6395 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6396 * increments the count for a particular tag, adding a new
6397 * entry for that tag if there wasn't one previously.
6403 * The information at *tagInfoPtr may be modified, and the arrays
6404 * may be reallocated to make them larger.
6406 *----------------------------------------------------------------------
6410 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6415 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6416 count > 0; tag_p++, count--)
6420 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6426 * There isn't currently an entry for this tag, so we have to
6427 * make a new one. If the arrays are full, then enlarge the
6431 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6433 GtkTextTag **newTags;
6434 int *newCounts, newSize;
6436 newSize = 2*tagInfoPtr->arraySize;
6437 newTags = (GtkTextTag **) g_malloc ((unsigned)
6438 (newSize*sizeof (GtkTextTag *)));
6439 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6440 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6441 g_free ((char *) tagInfoPtr->tags);
6442 tagInfoPtr->tags = newTags;
6443 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6444 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6445 tagInfoPtr->arraySize *sizeof (int));
6446 g_free ((char *) tagInfoPtr->counts);
6447 tagInfoPtr->counts = newCounts;
6448 tagInfoPtr->arraySize = newSize;
6451 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6452 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6453 tagInfoPtr->numTags++;
6457 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6458 const GtkTextIter *iter)
6460 GtkTextLineSegment *prev;
6464 line = _gtk_text_iter_get_text_line (iter);
6465 tree = _gtk_text_iter_get_btree (iter);
6467 prev = gtk_text_line_segment_split (iter);
6470 seg->next = line->segments;
6471 line->segments = seg;
6475 seg->next = prev->next;
6478 cleanup_line (line);
6479 segments_changed (tree);
6481 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6482 _gtk_text_btree_check (tree);
6486 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6487 GtkTextLineSegment *seg,
6490 GtkTextLineSegment *prev;
6492 if (line->segments == seg)
6494 line->segments = seg->next;
6498 for (prev = line->segments; prev->next != seg;
6501 /* Empty loop body. */
6503 prev->next = seg->next;
6505 cleanup_line (line);
6506 segments_changed (tree);
6510 * This is here because it requires BTree internals, it logically
6511 * belongs in gtktextsegment.c
6516 *--------------------------------------------------------------
6518 * _gtk_toggle_segment_check_func --
6520 * This procedure is invoked to perform consistency checks
6521 * on toggle segments.
6527 * If a consistency problem is found the procedure g_errors.
6529 *--------------------------------------------------------------
6533 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6539 if (segPtr->byte_count != 0)
6541 g_error ("toggle_segment_check_func: segment had non-zero size");
6543 if (!segPtr->body.toggle.inNodeCounts)
6545 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6547 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6548 for (summary = line->parent->summary; ;
6549 summary = summary->next)
6551 if (summary == NULL)
6555 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6562 if (summary->info == segPtr->body.toggle.info)
6566 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6578 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6579 GtkTextBTreeNode *node,
6589 while (view != NULL)
6591 if (view->view_id == nd->view_id)
6598 g_error ("Node has data for a view %p no longer attached to the tree",
6601 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6602 &width, &height, &valid);
6604 /* valid aggregate not checked the same as width/height, because on
6605 * btree rebalance we can have invalid nodes where all lines below
6606 * them are actually valid, due to moving lines around between
6609 * The guarantee is that if there are invalid lines the node is
6610 * invalid - we don't guarantee that if the node is invalid there
6611 * are invalid lines.
6614 if (nd->width != width ||
6615 nd->height != height ||
6616 (nd->valid && !valid))
6618 g_error ("Node aggregates for view %p are invalid:\n"
6619 "Are (%d,%d,%s), should be (%d,%d,%s)",
6621 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6622 width, height, valid ? "TRUE" : "FALSE");
6627 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6628 GtkTextBTreeNode *node)
6630 GtkTextBTreeNode *childnode;
6631 Summary *summary, *summary2;
6633 GtkTextLineSegment *segPtr;
6634 int num_children, num_lines, num_chars, toggle_count, min_children;
6635 GtkTextLineData *ld;
6638 if (node->parent != NULL)
6640 min_children = MIN_CHILDREN;
6642 else if (node->level > 0)
6649 if ((node->num_children < min_children)
6650 || (node->num_children > MAX_CHILDREN))
6652 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6653 node->num_children);
6656 nd = node->node_data;
6659 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6666 if (node->level == 0)
6668 for (line = node->children.line; line != NULL;
6671 if (line->parent != node)
6673 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6675 if (line->segments == NULL)
6677 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6683 /* Just ensuring we don't segv while doing this loop */
6688 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6690 if (segPtr->type->checkFunc != NULL)
6692 (*segPtr->type->checkFunc)(segPtr, line);
6694 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6695 && (segPtr->next != NULL)
6696 && (segPtr->next->byte_count == 0)
6697 && (segPtr->next->type->leftGravity))
6699 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6701 if ((segPtr->next == NULL)
6702 && (segPtr->type != >k_text_char_type))
6704 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6707 num_chars += segPtr->char_count;
6716 for (childnode = node->children.node; childnode != NULL;
6717 childnode = childnode->next)
6719 if (childnode->parent != node)
6721 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6723 if (childnode->level != (node->level-1))
6725 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6726 node->level, childnode->level);
6728 gtk_text_btree_node_check_consistency (tree, childnode);
6729 for (summary = childnode->summary; summary != NULL;
6730 summary = summary->next)
6732 for (summary2 = node->summary; ;
6733 summary2 = summary2->next)
6735 if (summary2 == NULL)
6737 if (summary->info->tag_root == node)
6741 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6742 summary->info->tag->name,
6743 "present in parent summaries");
6745 if (summary->info == summary2->info)
6752 num_lines += childnode->num_lines;
6753 num_chars += childnode->num_chars;
6756 if (num_children != node->num_children)
6758 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6759 num_children, node->num_children);
6761 if (num_lines != node->num_lines)
6763 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6764 num_lines, node->num_lines);
6766 if (num_chars != node->num_chars)
6768 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6769 num_chars, node->num_chars);
6772 for (summary = node->summary; summary != NULL;
6773 summary = summary->next)
6775 if (summary->info->toggle_count == summary->toggle_count)
6777 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6778 summary->info->tag->name);
6781 if (node->level == 0)
6783 for (line = node->children.line; line != NULL;
6786 for (segPtr = line->segments; segPtr != NULL;
6787 segPtr = segPtr->next)
6789 if ((segPtr->type != >k_text_toggle_on_type)
6790 && (segPtr->type != >k_text_toggle_off_type))
6794 if (segPtr->body.toggle.info == summary->info)
6796 if (!segPtr->body.toggle.inNodeCounts)
6797 g_error ("Toggle segment not in the node counts");
6806 for (childnode = node->children.node;
6808 childnode = childnode->next)
6810 for (summary2 = childnode->summary;
6812 summary2 = summary2->next)
6814 if (summary2->info == summary->info)
6816 toggle_count += summary2->toggle_count;
6821 if (toggle_count != summary->toggle_count)
6823 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6824 toggle_count, summary->toggle_count);
6826 for (summary2 = summary->next; summary2 != NULL;
6827 summary2 = summary2->next)
6829 if (summary2->info == summary->info)
6831 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6832 summary->info->tag->name);
6839 listify_foreach (GtkTextTag *tag, gpointer user_data)
6841 GSList** listp = user_data;
6843 *listp = g_slist_prepend (*listp, tag);
6847 list_of_tags (GtkTextTagTable *table)
6849 GSList *list = NULL;
6851 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6857 _gtk_text_btree_check (GtkTextBTree *tree)
6860 GtkTextBTreeNode *node;
6862 GtkTextLineSegment *seg;
6864 GSList *all_tags, *taglist = NULL;
6866 GtkTextTagInfo *info;
6869 * Make sure that the tag toggle counts and the tag root pointers are OK.
6871 all_tags = list_of_tags (tree->table);
6872 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6874 tag = taglist->data;
6875 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6878 node = info->tag_root;
6881 if (info->toggle_count != 0)
6883 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6884 tag->name, info->toggle_count);
6886 continue; /* no ranges for the tag */
6888 else if (info->toggle_count == 0)
6890 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6893 else if (info->toggle_count & 1)
6895 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6896 tag->name, info->toggle_count);
6898 for (summary = node->summary; summary != NULL;
6899 summary = summary->next)
6901 if (summary->info->tag == tag)
6903 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6907 if (node->level > 0)
6909 for (node = node->children.node ; node != NULL ;
6912 for (summary = node->summary; summary != NULL;
6913 summary = summary->next)
6915 if (summary->info->tag == tag)
6917 count += summary->toggle_count;
6924 GtkTextLineSegmentClass * last = NULL;
6926 for (line = node->children.line ; line != NULL ;
6929 for (seg = line->segments; seg != NULL;
6932 if ((seg->type == >k_text_toggle_on_type ||
6933 seg->type == >k_text_toggle_off_type) &&
6934 seg->body.toggle.info->tag == tag)
6936 if (last == seg->type)
6937 g_error ("Two consecutive toggles on or off weren't merged");
6938 if (!seg->body.toggle.inNodeCounts)
6939 g_error ("Toggle segment not in the node counts");
6948 if (count != info->toggle_count)
6950 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6951 info->toggle_count, tag->name, count);
6956 g_slist_free (all_tags);
6959 * Call a recursive procedure to do the main body of checks.
6962 node = tree->root_node;
6963 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6966 * Make sure that there are at least two lines in the text and
6967 * that the last line has no characters except a newline.
6970 if (node->num_lines < 2)
6972 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6974 if (node->num_chars < 2)
6976 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6978 while (node->level > 0)
6980 node = node->children.node;
6981 while (node->next != NULL)
6986 line = node->children.line;
6987 while (line->next != NULL)
6991 seg = line->segments;
6992 while ((seg->type == >k_text_toggle_off_type)
6993 || (seg->type == >k_text_right_mark_type)
6994 || (seg->type == >k_text_left_mark_type))
6997 * It's OK to toggle a tag off in the last line, but
6998 * not to start a new range. It's also OK to have marks
7004 if (seg->type != >k_text_char_type)
7006 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7008 if (seg->next != NULL)
7010 g_error ("_gtk_text_btree_check: last line has too many segments");
7012 if (seg->byte_count != 1)
7014 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7017 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7019 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7024 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7025 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7026 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7027 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7030 _gtk_text_btree_spew (GtkTextBTree *tree)
7035 printf ("%d lines in tree %p\n",
7036 _gtk_text_btree_line_count (tree), tree);
7038 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7040 while (line != NULL)
7042 _gtk_text_btree_spew_line (tree, line);
7043 line = _gtk_text_line_next (line);
7046 printf ("=================== Tag information\n");
7051 list = tree->tag_infos;
7053 while (list != NULL)
7055 GtkTextTagInfo *info;
7059 printf (" tag `%s': root at %p, toggle count %d\n",
7060 info->tag->name, info->tag_root, info->toggle_count);
7062 list = g_slist_next (list);
7065 if (tree->tag_infos == NULL)
7067 printf (" (no tags in the tree)\n");
7071 printf ("=================== Tree nodes\n");
7074 _gtk_text_btree_spew_node (tree->root_node, 0);
7079 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7082 GtkTextLineSegment *seg;
7084 spaces = g_strnfill (indent, ' ');
7086 printf ("%sline %p chars %d bytes %d\n",
7088 _gtk_text_line_char_count (line),
7089 _gtk_text_line_byte_count (line));
7091 seg = line->segments;
7094 if (seg->type == >k_text_char_type)
7096 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7101 if (*s == '\n' || *s == '\r')
7105 printf ("%s chars `%s'...\n", spaces, str);
7108 else if (seg->type == >k_text_right_mark_type)
7110 printf ("%s right mark `%s' visible: %d\n",
7112 seg->body.mark.name,
7113 seg->body.mark.visible);
7115 else if (seg->type == >k_text_left_mark_type)
7117 printf ("%s left mark `%s' visible: %d\n",
7119 seg->body.mark.name,
7120 seg->body.mark.visible);
7122 else if (seg->type == >k_text_toggle_on_type ||
7123 seg->type == >k_text_toggle_off_type)
7125 printf ("%s tag `%s' %s\n",
7126 spaces, seg->body.toggle.info->tag->name,
7127 seg->type == >k_text_toggle_off_type ? "off" : "on");
7137 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7140 GtkTextBTreeNode *iter;
7143 spaces = g_strnfill (indent, ' ');
7145 printf ("%snode %p level %d children %d lines %d chars %d\n",
7146 spaces, node, node->level,
7147 node->num_children, node->num_lines, node->num_chars);
7152 printf ("%s %d toggles of `%s' below this node\n",
7153 spaces, s->toggle_count, s->info->tag->name);
7159 if (node->level > 0)
7161 iter = node->children.node;
7162 while (iter != NULL)
7164 _gtk_text_btree_spew_node (iter, indent + 2);
7171 GtkTextLine *line = node->children.line;
7172 while (line != NULL)
7174 _gtk_text_btree_spew_line_short (line, indent + 2);
7182 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7184 GtkTextLineSegment * seg;
7186 printf ("%4d| line: %p parent: %p next: %p\n",
7187 _gtk_text_line_get_number (line), line, line->parent, line->next);
7189 seg = line->segments;
7193 _gtk_text_btree_spew_segment (tree, seg);
7199 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7201 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7202 seg, seg->type->name, seg->byte_count, seg->char_count);
7204 if (seg->type == >k_text_char_type)
7206 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7207 printf (" `%s'\n", str);
7210 else if (seg->type == >k_text_right_mark_type)
7212 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7213 seg->body.mark.name,
7214 seg->body.mark.visible,
7215 seg->body.mark.not_deleteable);
7217 else if (seg->type == >k_text_left_mark_type)
7219 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7220 seg->body.mark.name,
7221 seg->body.mark.visible,
7222 seg->body.mark.not_deleteable);
7224 else if (seg->type == >k_text_toggle_on_type ||
7225 seg->type == >k_text_toggle_off_type)
7227 printf (" tag `%s' priority %d\n",
7228 seg->body.toggle.info->tag->name,
7229 seg->body.toggle.info->tag->priority);
7233 #define __GTK_TEXT_BTREE_C__
7234 #include "gtkaliasdef.c"