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
58 #include "gtktextbtree.h"
62 #include "gtktexttag.h"
63 #include "gtktexttagtable.h"
64 #include "gtktextlayout.h"
65 #include "gtktextiterprivate.h"
67 #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->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;
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;
890 seg->next = prev_seg->next;
891 prev_seg->next = seg;
893 if (seg->type->leftGravity)
900 /* Segment is gone. Decrement the char count of the node and
902 for (node = curnode; node != NULL;
905 node->num_chars -= char_count;
913 * If the beginning and end of the deletion range are in different
914 * lines, join the two lines together and discard the ending line.
917 if (start_line != end_line)
920 GtkTextBTreeNode *ancestor_node;
921 GtkTextLine *prevline;
924 /* last_seg was appended to start_line up at the top of this function */
926 for (seg = last_seg; seg != NULL;
929 chars_moved += seg->char_count;
930 if (seg->type->lineChangeFunc != NULL)
932 (*seg->type->lineChangeFunc)(seg, end_line);
936 for (node = start_line->parent; node != NULL;
939 node->num_chars += chars_moved;
942 curnode = end_line->parent;
943 for (node = curnode; node != NULL;
946 node->num_chars -= chars_moved;
949 curnode->num_children--;
950 prevline = curnode->children.line;
951 if (prevline == end_line)
953 curnode->children.line = end_line->next;
957 while (prevline->next != end_line)
959 prevline = prevline->next;
961 prevline->next = end_line->next;
963 end_line->next = deleted_lines;
964 deleted_lines = end_line;
966 /* We now fix up the per-view aggregates. We add all the height and
967 * width for the deleted lines to the start line, so that when revalidation
968 * occurs, the correct change in size is seen.
970 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
977 gint deleted_width = 0;
978 gint deleted_height = 0;
980 line = deleted_lines;
983 GtkTextLine *next_line = line->next;
984 ld = _gtk_text_line_get_data (line, view->view_id);
988 deleted_width = MAX (deleted_width, ld->width);
989 deleted_height += ld->height;
993 gtk_text_line_destroy (tree, line);
998 if (deleted_width > 0 || deleted_height > 0)
1000 ld = _gtk_text_line_get_data (start_line, view->view_id);
1004 /* This means that start_line has never been validated.
1005 * We don't really want to do the validation here but
1006 * we do need to store our temporary sizes. So we
1007 * create the line data and assume a line w/h of 0.
1009 ld = _gtk_text_line_data_new (view->layout, start_line);
1010 _gtk_text_line_add_data (start_line, ld);
1016 ld->width = MAX (deleted_width, ld->width);
1017 ld->height += deleted_height;
1021 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1022 if (ancestor_node->parent)
1023 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1028 /* avoid dangling pointer */
1029 deleted_lines = NULL;
1031 gtk_text_btree_rebalance (tree, curnode);
1035 * Cleanup the segments in the new line.
1038 cleanup_line (start_line);
1041 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1044 gtk_text_btree_rebalance (tree, start_line->parent);
1046 /* Notify outstanding iterators that they
1048 chars_changed (tree);
1049 segments_changed (tree);
1051 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1052 _gtk_text_btree_check (tree);
1054 /* Re-initialize our iterators */
1055 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1058 gtk_text_btree_resolve_bidi (start, end);
1062 _gtk_text_btree_insert (GtkTextIter *iter,
1066 GtkTextLineSegment *prev_seg; /* The segment just before the first
1067 * new segment (NULL means new segment
1068 * is at beginning of line). */
1069 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1070 * are inserted just after this one.
1071 * NULL means insert at beginning of
1073 GtkTextLine *line; /* Current line (new segments are
1074 * added to this line). */
1075 GtkTextLineSegment *seg;
1076 GtkTextLine *newline;
1077 int chunk_len; /* # characters in current chunk. */
1078 gint sol; /* start of line */
1079 gint eol; /* Pointer to character just after last
1080 * one in current chunk.
1082 gint delim; /* index of paragraph delimiter */
1083 int line_count_delta; /* Counts change to total number of
1087 int char_count_delta; /* change to number of chars */
1089 gint start_byte_index;
1090 GtkTextLine *start_line;
1092 g_return_if_fail (text != NULL);
1093 g_return_if_fail (iter != NULL);
1096 len = strlen (text);
1098 /* extract iterator info */
1099 tree = _gtk_text_iter_get_btree (iter);
1100 line = _gtk_text_iter_get_text_line (iter);
1103 start_byte_index = gtk_text_iter_get_line_index (iter);
1105 /* Get our insertion segment split. Note this assumes line allows
1106 * char insertions, which isn't true of the "last" line. But iter
1107 * should not be on that line, as we assert here.
1109 g_assert (!_gtk_text_line_is_last (line, tree));
1110 prev_seg = gtk_text_line_segment_split (iter);
1113 /* Invalidate all iterators */
1114 chars_changed (tree);
1115 segments_changed (tree);
1118 * Chop the text up into lines and create a new segment for
1119 * each line, plus a new line for the leftovers from the
1125 line_count_delta = 0;
1126 char_count_delta = 0;
1131 pango_find_paragraph_boundary (text + sol,
1136 /* make these relative to the start of the text */
1140 g_assert (eol >= sol);
1141 g_assert (delim >= sol);
1142 g_assert (eol >= delim);
1143 g_assert (sol >= 0);
1144 g_assert (eol <= len);
1146 chunk_len = eol - sol;
1148 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1149 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1151 char_count_delta += seg->char_count;
1153 if (cur_seg == NULL)
1155 seg->next = line->segments;
1156 line->segments = seg;
1160 seg->next = cur_seg->next;
1161 cur_seg->next = seg;
1166 /* chunk didn't end with a paragraph separator */
1167 g_assert (eol == len);
1172 * The chunk ended with a newline, so create a new GtkTextLine
1173 * and move the remainder of the old line to it.
1176 newline = gtk_text_line_new ();
1177 gtk_text_line_set_parent (newline, line->parent);
1178 newline->next = line->next;
1179 line->next = newline;
1180 newline->segments = seg->next;
1188 * Cleanup the starting line for the insertion, plus the ending
1189 * line if it's different.
1192 cleanup_line (start_line);
1193 if (line != start_line)
1195 cleanup_line (line);
1198 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1200 /* Invalidate our region, and reset the iterator the user
1201 passed in to point to the end of the inserted text. */
1207 _gtk_text_btree_get_iter_at_line (tree,
1213 /* We could almost certainly be more efficient here
1214 by saving the information from the insertion loop
1216 gtk_text_iter_forward_chars (&end, char_count_delta);
1218 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1219 _gtk_text_btree_invalidate_region (tree,
1223 /* Convenience for the user */
1226 gtk_text_btree_resolve_bidi (&start, &end);
1231 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1232 GtkTextLineSegment *seg)
1236 GtkTextLineSegment *prevPtr;
1239 gint start_byte_offset;
1241 line = _gtk_text_iter_get_text_line (iter);
1242 tree = _gtk_text_iter_get_btree (iter);
1243 start_byte_offset = gtk_text_iter_get_line_index (iter);
1245 prevPtr = gtk_text_line_segment_split (iter);
1246 if (prevPtr == NULL)
1248 seg->next = line->segments;
1249 line->segments = seg;
1253 seg->next = prevPtr->next;
1254 prevPtr->next = seg;
1257 post_insert_fixup (tree, line, 0, seg->char_count);
1259 chars_changed (tree);
1260 segments_changed (tree);
1262 /* reset *iter for the user, and invalidate tree nodes */
1264 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1267 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1269 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1270 _gtk_text_btree_invalidate_region (tree, &start, iter);
1274 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1277 GtkTextLineSegment *seg;
1279 seg = _gtk_pixbuf_segment_new (pixbuf);
1281 insert_pixbuf_or_widget_segment (iter, seg);
1285 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1286 GtkTextChildAnchor *anchor)
1288 GtkTextLineSegment *seg;
1291 if (anchor->segment != NULL)
1293 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1297 seg = _gtk_widget_segment_new (anchor);
1299 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1300 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1302 insert_pixbuf_or_widget_segment (iter, seg);
1304 if (tree->child_anchor_table == NULL)
1305 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1307 g_hash_table_insert (tree->child_anchor_table,
1308 seg->body.child.obj,
1309 seg->body.child.obj);
1313 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1315 GtkTextLineSegment *seg;
1317 seg = anchor->segment;
1319 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1328 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1329 GtkTextBTreeNode *node, gint y, gint *line_top,
1330 GtkTextLine *last_line)
1334 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1335 _gtk_text_btree_check (tree);
1337 if (node->level == 0)
1341 line = node->children.line;
1343 while (line != NULL && line != last_line)
1345 GtkTextLineData *ld;
1347 ld = _gtk_text_line_get_data (line, view->view_id);
1351 if (y < (current_y + (ld ? ld->height : 0)))
1354 current_y += ld->height;
1355 *line_top += ld->height;
1364 GtkTextBTreeNode *child;
1366 child = node->children.node;
1368 while (child != NULL)
1373 gtk_text_btree_node_get_size (child, view->view_id,
1376 if (y < (current_y + height))
1377 return find_line_by_y (tree, view, child,
1378 y - current_y, line_top,
1381 current_y += height;
1382 *line_top += height;
1384 child = child->next;
1392 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1399 GtkTextLine *last_line;
1402 view = gtk_text_btree_get_view (tree, view_id);
1403 g_return_val_if_fail (view != NULL, NULL);
1405 last_line = get_last_line (tree);
1407 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1411 *line_top_out = line_top;
1417 find_line_top_in_line_list (GtkTextBTree *tree,
1420 GtkTextLine *target_line,
1423 while (line != NULL)
1425 GtkTextLineData *ld;
1427 if (line == target_line)
1430 ld = _gtk_text_line_get_data (line, view->view_id);
1437 g_assert_not_reached (); /* If we get here, our
1438 target line didn't exist
1439 under its parent node */
1444 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1445 GtkTextLine *target_line,
1452 GtkTextBTreeNode *node;
1454 view = gtk_text_btree_get_view (tree, view_id);
1456 g_return_val_if_fail (view != NULL, 0);
1459 node = target_line->parent;
1460 while (node != NULL)
1462 nodes = g_slist_prepend (nodes, node);
1463 node = node->parent;
1467 while (iter != NULL)
1471 if (node->level == 0)
1473 g_slist_free (nodes);
1474 return find_line_top_in_line_list (tree, view,
1475 node->children.line,
1480 GtkTextBTreeNode *child;
1481 GtkTextBTreeNode *target_node;
1483 g_assert (iter->next != NULL); /* not at level 0 */
1484 target_node = iter->next->data;
1486 child = node->children.node;
1488 while (child != NULL)
1493 if (child == target_node)
1497 gtk_text_btree_node_get_size (child, view->view_id,
1501 child = child->next;
1503 g_assert (child != NULL); /* should have broken out before we
1507 iter = g_slist_next (iter);
1510 g_assert_not_reached (); /* we return when we find the target line */
1515 _gtk_text_btree_add_view (GtkTextBTree *tree,
1516 GtkTextLayout *layout)
1519 GtkTextLine *last_line;
1520 GtkTextLineData *line_data;
1522 g_return_if_fail (tree != NULL);
1524 view = g_new (BTreeView, 1);
1526 view->view_id = layout;
1527 view->layout = layout;
1529 view->next = tree->views;
1534 g_assert (tree->views->prev == NULL);
1535 tree->views->prev = view;
1540 /* The last line in the buffer has identity values for the per-view
1541 * data so that we can avoid special case checks for it in a large
1544 last_line = get_last_line (tree);
1546 line_data = g_new (GtkTextLineData, 1);
1547 line_data->view_id = layout;
1548 line_data->next = NULL;
1549 line_data->width = 0;
1550 line_data->height = 0;
1551 line_data->valid = TRUE;
1553 _gtk_text_line_add_data (last_line, line_data);
1557 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1561 GtkTextLine *last_line;
1562 GtkTextLineData *line_data;
1564 g_return_if_fail (tree != NULL);
1568 while (view != NULL)
1570 if (view->view_id == view_id)
1576 g_return_if_fail (view != NULL);
1579 view->next->prev = view->prev;
1582 view->prev->next = view->next;
1584 if (view == tree->views)
1585 tree->views = view->next;
1587 /* Remove the line data for the last line which we added ourselves.
1588 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1590 last_line = get_last_line (tree);
1591 line_data = _gtk_text_line_remove_data (last_line, view_id);
1594 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1596 view->layout = (gpointer) 0xdeadbeef;
1597 view->view_id = (gpointer) 0xdeadbeef;
1603 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1604 const GtkTextIter *start,
1605 const GtkTextIter *end)
1611 while (view != NULL)
1613 gtk_text_layout_invalidate (view->layout, start, end);
1620 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1625 g_return_if_fail (tree != NULL);
1626 g_return_if_fail (view_id != NULL);
1628 gtk_text_btree_node_get_size (tree->root_node, view_id,
1643 iter_stack_new (void)
1646 stack = g_new (IterStack, 1);
1647 stack->iters = NULL;
1654 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1657 if (stack->count > stack->alloced)
1659 stack->alloced = stack->count*2;
1660 stack->iters = g_realloc (stack->iters,
1661 stack->alloced*sizeof (GtkTextIter));
1663 stack->iters[stack->count-1] = *iter;
1667 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1669 if (stack->count == 0)
1674 *iter = stack->iters[stack->count];
1680 iter_stack_free (IterStack *stack)
1682 g_free (stack->iters);
1687 iter_stack_invert (IterStack *stack)
1689 if (stack->count > 0)
1692 guint j = stack->count - 1;
1697 tmp = stack->iters[i];
1698 stack->iters[i] = stack->iters[j];
1699 stack->iters[j] = tmp;
1708 queue_tag_redisplay (GtkTextBTree *tree,
1710 const GtkTextIter *start,
1711 const GtkTextIter *end)
1713 if (_gtk_text_tag_affects_size (tag))
1715 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1716 _gtk_text_btree_invalidate_region (tree, start, end);
1718 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1720 /* We only need to queue a redraw, not a relayout */
1721 redisplay_region (tree, start, end);
1724 /* We don't need to do anything if the tag doesn't affect display */
1728 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1729 const GtkTextIter *end_orig,
1733 GtkTextLineSegment *seg, *prev;
1734 GtkTextLine *cleanupline;
1735 gboolean toggled_on;
1736 GtkTextLine *start_line;
1737 GtkTextLine *end_line;
1739 GtkTextIter start, end;
1742 GtkTextTagInfo *info;
1744 g_return_if_fail (start_orig != NULL);
1745 g_return_if_fail (end_orig != NULL);
1746 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1747 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1748 _gtk_text_iter_get_btree (end_orig));
1749 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1752 printf ("%s tag %s from %d to %d\n",
1753 add ? "Adding" : "Removing",
1755 gtk_text_buffer_get_offset (start_orig),
1756 gtk_text_buffer_get_offset (end_orig));
1759 if (gtk_text_iter_equal (start_orig, end_orig))
1762 start = *start_orig;
1765 gtk_text_iter_order (&start, &end);
1767 tree = _gtk_text_iter_get_btree (&start);
1769 queue_tag_redisplay (tree, tag, &start, &end);
1771 info = gtk_text_btree_get_tag_info (tree, tag);
1773 start_line = _gtk_text_iter_get_text_line (&start);
1774 end_line = _gtk_text_iter_get_text_line (&end);
1776 /* Find all tag toggles in the region; we are going to delete them.
1777 We need to find them in advance, because
1778 forward_find_tag_toggle () won't work once we start playing around
1780 stack = iter_stack_new ();
1783 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1784 * which is deliberate - we don't want to delete a toggle at the
1787 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1789 if (gtk_text_iter_compare (&iter, &end) >= 0)
1792 iter_stack_push (stack, &iter);
1795 /* We need to traverse the toggles in order. */
1796 iter_stack_invert (stack);
1799 * See whether the tag is present at the start of the range. If
1800 * the state doesn't already match what we want then add a toggle
1804 toggled_on = gtk_text_iter_has_tag (&start, tag);
1805 if ( (add && !toggled_on) ||
1806 (!add && toggled_on) )
1808 /* This could create a second toggle at the start position;
1809 cleanup_line () will remove it if so. */
1810 seg = _gtk_toggle_segment_new (info, add);
1812 prev = gtk_text_line_segment_split (&start);
1815 seg->next = start_line->segments;
1816 start_line->segments = seg;
1820 seg->next = prev->next;
1824 /* cleanup_line adds the new toggle to the node counts. */
1826 printf ("added toggle at start\n");
1828 /* we should probably call segments_changed, but in theory
1829 any still-cached segments in the iters we are about to
1830 use are still valid, since they're in front
1836 * Scan the range of characters and delete any internal tag
1837 * transitions. Keep track of what the old state was at the end
1838 * of the range, and add a toggle there if it's needed.
1842 cleanupline = start_line;
1843 while (iter_stack_pop (stack, &iter))
1845 GtkTextLineSegment *indexable_seg;
1848 line = _gtk_text_iter_get_text_line (&iter);
1849 seg = _gtk_text_iter_get_any_segment (&iter);
1850 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1852 g_assert (seg != NULL);
1853 g_assert (indexable_seg != NULL);
1854 g_assert (seg != indexable_seg);
1856 prev = line->segments;
1858 /* Find the segment that actually toggles this tag. */
1859 while (seg != indexable_seg)
1861 g_assert (seg != NULL);
1862 g_assert (indexable_seg != NULL);
1863 g_assert (seg != indexable_seg);
1865 if ( (seg->type == >k_text_toggle_on_type ||
1866 seg->type == >k_text_toggle_off_type) &&
1867 (seg->body.toggle.info == info) )
1873 g_assert (seg != NULL);
1874 g_assert (indexable_seg != NULL);
1876 g_assert (seg != indexable_seg); /* If this happens, then
1877 forward_to_tag_toggle was
1879 g_assert (seg->body.toggle.info->tag == tag);
1881 /* If this happens, when previously tagging we didn't merge
1882 overlapping tags. */
1883 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1884 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1886 toggled_on = !toggled_on;
1889 printf ("deleting %s toggle\n",
1890 seg->type == >k_text_toggle_on_type ? "on" : "off");
1892 /* Remove toggle segment from the list. */
1895 line->segments = seg->next;
1899 while (prev->next != seg)
1903 prev->next = seg->next;
1906 /* Inform iterators we've hosed them. This actually reflects a
1907 bit of inefficiency; if you have the same tag toggled on and
1908 off a lot in a single line, we keep having the rescan from
1909 the front of the line. Of course we have to do that to get
1910 "prev" anyway, but here we are doing it an additional
1912 segments_changed (tree);
1914 /* Update node counts */
1915 if (seg->body.toggle.inNodeCounts)
1917 _gtk_change_node_toggle_count (line->parent,
1919 seg->body.toggle.inNodeCounts = FALSE;
1924 /* We only clean up lines when we're done with them, saves some
1925 gratuitous line-segment-traversals */
1927 if (cleanupline != line)
1929 cleanup_line (cleanupline);
1934 iter_stack_free (stack);
1936 /* toggled_on now reflects the toggle state _just before_ the
1937 end iterator. The end iterator could already have a toggle
1938 on or a toggle off. */
1939 if ( (add && !toggled_on) ||
1940 (!add && toggled_on) )
1942 /* This could create a second toggle at the start position;
1943 cleanup_line () will remove it if so. */
1945 seg = _gtk_toggle_segment_new (info, !add);
1947 prev = gtk_text_line_segment_split (&end);
1950 seg->next = end_line->segments;
1951 end_line->segments = seg;
1955 seg->next = prev->next;
1958 /* cleanup_line adds the new toggle to the node counts. */
1959 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1961 printf ("added toggle at end\n");
1966 * Cleanup cleanupline and the last line of the range, if
1967 * these are different.
1970 cleanup_line (cleanupline);
1971 if (cleanupline != end_line)
1973 cleanup_line (end_line);
1976 segments_changed (tree);
1978 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1979 _gtk_text_btree_check (tree);
1988 get_line_internal (GtkTextBTree *tree,
1990 gint *real_line_number,
1991 gboolean include_last)
1993 GtkTextBTreeNode *node;
1998 line_count = _gtk_text_btree_line_count (tree);
2002 if (line_number < 0)
2004 line_number = line_count;
2006 else if (line_number > line_count)
2008 line_number = line_count;
2011 if (real_line_number)
2012 *real_line_number = line_number;
2014 node = tree->root_node;
2015 lines_left = line_number;
2018 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2022 while (node->level != 0)
2024 for (node = node->children.node;
2025 node->num_lines <= lines_left;
2031 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2034 lines_left -= node->num_lines;
2039 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2042 for (line = node->children.line; lines_left > 0;
2048 g_error ("gtk_text_btree_find_line ran out of lines");
2057 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2060 _gtk_text_btree_get_line (tree,
2061 _gtk_text_btree_line_count (tree) - 1,
2066 _gtk_text_btree_get_line (GtkTextBTree *tree,
2068 gint *real_line_number)
2070 return get_line_internal (tree, line_number, real_line_number, TRUE);
2074 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2076 gint *real_line_number)
2078 return get_line_internal (tree, line_number, real_line_number, FALSE);
2082 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2084 gint *line_start_index,
2085 gint *real_char_index)
2087 GtkTextBTreeNode *node;
2089 GtkTextLineSegment *seg;
2094 node = tree->root_node;
2096 /* Clamp to valid indexes (-1 is magic for "highest index"),
2097 * node->num_chars includes the two newlines that aren't really
2100 if (char_index < 0 || char_index >= (node->num_chars - 1))
2102 char_index = node->num_chars - 2;
2105 *real_char_index = char_index;
2108 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2112 chars_left = char_index;
2113 while (node->level != 0)
2115 for (node = node->children.node;
2116 chars_left >= node->num_chars;
2119 chars_left -= node->num_chars;
2121 g_assert (chars_left >= 0);
2125 if (chars_left == 0)
2127 /* Start of a line */
2129 *line_start_index = char_index;
2130 return node->children.line;
2134 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2140 for (line = node->children.line; line != NULL; line = line->next)
2142 seg = line->segments;
2145 if (chars_in_line + seg->char_count > chars_left)
2146 goto found; /* found our line/segment */
2148 chars_in_line += seg->char_count;
2153 chars_left -= chars_in_line;
2161 g_assert (line != NULL); /* hosage, ran out of lines */
2162 g_assert (seg != NULL);
2164 *line_start_index = char_index - chars_left;
2169 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2172 GtkTextBTreeNode *node;
2173 GtkTextLine *siblingline;
2174 GtkTextLineSegment *seg;
2175 int src, dst, index;
2181 #define NUM_TAG_INFOS 10
2183 line = _gtk_text_iter_get_text_line (iter);
2184 tree = _gtk_text_iter_get_btree (iter);
2185 byte_index = gtk_text_iter_get_line_index (iter);
2187 tagInfo.numTags = 0;
2188 tagInfo.arraySize = NUM_TAG_INFOS;
2189 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2190 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2193 * Record tag toggles within the line of indexPtr but preceding
2194 * indexPtr. Note that if this loop segfaults, your
2195 * byte_index probably points past the sum of all
2196 * seg->byte_count */
2198 for (index = 0, seg = line->segments;
2199 (index + seg->byte_count) <= byte_index;
2200 index += seg->byte_count, seg = seg->next)
2202 if ((seg->type == >k_text_toggle_on_type)
2203 || (seg->type == >k_text_toggle_off_type))
2205 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2210 * Record toggles for tags in lines that are predecessors of
2211 * line but under the same level-0 GtkTextBTreeNode.
2214 for (siblingline = line->parent->children.line;
2215 siblingline != line;
2216 siblingline = siblingline->next)
2218 for (seg = siblingline->segments; seg != NULL;
2221 if ((seg->type == >k_text_toggle_on_type)
2222 || (seg->type == >k_text_toggle_off_type))
2224 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2230 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2231 * toggles for all siblings that precede that GtkTextBTreeNode.
2234 for (node = line->parent; node->parent != NULL;
2235 node = node->parent)
2237 GtkTextBTreeNode *siblingPtr;
2240 for (siblingPtr = node->parent->children.node;
2241 siblingPtr != node; siblingPtr = siblingPtr->next)
2243 for (summary = siblingPtr->summary; summary != NULL;
2244 summary = summary->next)
2246 if (summary->toggle_count & 1)
2248 inc_count (summary->info->tag, summary->toggle_count,
2256 * Go through the tag information and squash out all of the tags
2257 * that have even toggle counts (these tags exist before the point
2258 * of interest, but not at the desired character itself).
2261 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2263 if (tagInfo.counts[src] & 1)
2265 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2266 tagInfo.tags[dst] = tagInfo.tags[src];
2272 g_free (tagInfo.counts);
2275 g_free (tagInfo.tags);
2278 return tagInfo.tags;
2282 copy_segment (GString *string,
2283 gboolean include_hidden,
2284 gboolean include_nonchars,
2285 const GtkTextIter *start,
2286 const GtkTextIter *end)
2288 GtkTextLineSegment *end_seg;
2289 GtkTextLineSegment *seg;
2291 if (gtk_text_iter_equal (start, end))
2294 seg = _gtk_text_iter_get_indexable_segment (start);
2295 end_seg = _gtk_text_iter_get_indexable_segment (end);
2297 if (seg->type == >k_text_char_type)
2299 gboolean copy = TRUE;
2300 gint copy_bytes = 0;
2301 gint copy_start = 0;
2303 /* Don't copy if we're invisible; segments are invisible/not
2304 as a whole, no need to check each char */
2305 if (!include_hidden &&
2306 _gtk_text_btree_char_is_invisible (start))
2309 /* printf (" <invisible>\n"); */
2312 copy_start = _gtk_text_iter_get_segment_byte (start);
2316 /* End is in the same segment; need to copy fewer bytes. */
2317 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2319 copy_bytes = end_byte - copy_start;
2322 copy_bytes = seg->byte_count - copy_start;
2324 g_assert (copy_bytes != 0); /* Due to iter equality check at
2325 front of this function. */
2329 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2331 g_string_append_len (string,
2332 seg->body.chars + copy_start,
2336 /* printf (" :%s\n", string->str); */
2338 else if (seg->type == >k_text_pixbuf_type ||
2339 seg->type == >k_text_child_type)
2341 gboolean copy = TRUE;
2343 if (!include_nonchars)
2347 else if (!include_hidden &&
2348 _gtk_text_btree_char_is_invisible (start))
2355 g_string_append_len (string,
2356 gtk_text_unknown_char_utf8,
2364 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2365 const GtkTextIter *end_orig,
2366 gboolean include_hidden,
2367 gboolean include_nonchars)
2369 GtkTextLineSegment *seg;
2370 GtkTextLineSegment *end_seg;
2378 g_return_val_if_fail (start_orig != NULL, NULL);
2379 g_return_val_if_fail (end_orig != NULL, NULL);
2380 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2381 _gtk_text_iter_get_btree (end_orig), NULL);
2383 start = *start_orig;
2386 gtk_text_iter_order (&start, &end);
2388 retval = g_string_new (NULL);
2390 tree = _gtk_text_iter_get_btree (&start);
2392 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2394 seg = _gtk_text_iter_get_indexable_segment (&iter);
2395 while (seg != end_seg)
2397 copy_segment (retval, include_hidden, include_nonchars,
2400 _gtk_text_iter_forward_indexable_segment (&iter);
2402 seg = _gtk_text_iter_get_indexable_segment (&iter);
2405 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2408 g_string_free (retval, FALSE);
2413 _gtk_text_btree_line_count (GtkTextBTree *tree)
2415 /* Subtract bogus line at the end; we return a count
2417 return tree->root_node->num_lines - 1;
2421 _gtk_text_btree_char_count (GtkTextBTree *tree)
2423 /* Exclude newline in bogus last line and the
2424 * one in the last line that is after the end iterator
2426 return tree->root_node->num_chars - 2;
2429 #define LOTSA_TAGS 1000
2431 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2433 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2435 int deftagCnts[LOTSA_TAGS];
2436 int *tagCnts = deftagCnts;
2437 GtkTextTag *deftags[LOTSA_TAGS];
2438 GtkTextTag **tags = deftags;
2440 GtkTextBTreeNode *node;
2441 GtkTextLine *siblingline;
2442 GtkTextLineSegment *seg;
2449 line = _gtk_text_iter_get_text_line (iter);
2450 tree = _gtk_text_iter_get_btree (iter);
2451 byte_index = gtk_text_iter_get_line_index (iter);
2453 numTags = gtk_text_tag_table_get_size (tree->table);
2455 /* almost always avoid malloc, so stay out of system calls */
2456 if (LOTSA_TAGS < numTags)
2458 tagCnts = g_new (int, numTags);
2459 tags = g_new (GtkTextTag*, numTags);
2462 for (i=0; i<numTags; i++)
2468 * Record tag toggles within the line of indexPtr but preceding
2472 for (index = 0, seg = line->segments;
2473 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2474 index += seg->byte_count, seg = seg->next)
2476 if ((seg->type == >k_text_toggle_on_type)
2477 || (seg->type == >k_text_toggle_off_type))
2479 tag = seg->body.toggle.info->tag;
2480 if (tag->invisible_set && tag->values->invisible)
2482 tags[tag->priority] = tag;
2483 tagCnts[tag->priority]++;
2489 * Record toggles for tags in lines that are predecessors of
2490 * line but under the same level-0 GtkTextBTreeNode.
2493 for (siblingline = line->parent->children.line;
2494 siblingline != line;
2495 siblingline = siblingline->next)
2497 for (seg = siblingline->segments; seg != NULL;
2500 if ((seg->type == >k_text_toggle_on_type)
2501 || (seg->type == >k_text_toggle_off_type))
2503 tag = seg->body.toggle.info->tag;
2504 if (tag->invisible_set && tag->values->invisible)
2506 tags[tag->priority] = tag;
2507 tagCnts[tag->priority]++;
2514 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2515 * for all siblings that precede that GtkTextBTreeNode.
2518 for (node = line->parent; node->parent != NULL;
2519 node = node->parent)
2521 GtkTextBTreeNode *siblingPtr;
2524 for (siblingPtr = node->parent->children.node;
2525 siblingPtr != node; siblingPtr = siblingPtr->next)
2527 for (summary = siblingPtr->summary; summary != NULL;
2528 summary = summary->next)
2530 if (summary->toggle_count & 1)
2532 tag = summary->info->tag;
2533 if (tag->invisible_set && tag->values->invisible)
2535 tags[tag->priority] = tag;
2536 tagCnts[tag->priority] += summary->toggle_count;
2544 * Now traverse from highest priority to lowest,
2545 * take invisible value from first odd count (= on)
2548 for (i = numTags-1; i >=0; i--)
2552 /* FIXME not sure this should be if 0 */
2554 #ifndef ALWAYS_SHOW_SELECTION
2555 /* who would make the selection invisible? */
2556 if ((tag == tkxt->seltag)
2557 && !(tkxt->flags & GOT_FOCUS))
2563 invisible = tags[i]->values->invisible;
2568 if (LOTSA_TAGS < numTags)
2583 redisplay_region (GtkTextBTree *tree,
2584 const GtkTextIter *start,
2585 const GtkTextIter *end)
2588 GtkTextLine *start_line, *end_line;
2590 if (gtk_text_iter_compare (start, end) > 0)
2592 const GtkTextIter *tmp = start;
2597 start_line = _gtk_text_iter_get_text_line (start);
2598 end_line = _gtk_text_iter_get_text_line (end);
2601 while (view != NULL)
2603 gint start_y, end_y;
2604 GtkTextLineData *ld;
2606 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2608 if (end_line == start_line)
2611 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2613 ld = _gtk_text_line_get_data (end_line, view->view_id);
2615 end_y += ld->height;
2617 gtk_text_layout_changed (view->layout, start_y,
2626 redisplay_mark (GtkTextLineSegment *mark)
2631 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2633 mark->body.mark.obj);
2636 gtk_text_iter_forward_char (&end);
2638 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2639 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2644 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2646 if (!mark->body.mark.visible)
2649 redisplay_mark (mark);
2653 ensure_not_off_end (GtkTextBTree *tree,
2654 GtkTextLineSegment *mark,
2657 if (gtk_text_iter_get_line (iter) ==
2658 _gtk_text_btree_line_count (tree))
2659 gtk_text_iter_backward_char (iter);
2662 static GtkTextLineSegment*
2663 real_set_mark (GtkTextBTree *tree,
2664 GtkTextMark *existing_mark,
2666 gboolean left_gravity,
2667 const GtkTextIter *where,
2668 gboolean should_exist,
2669 gboolean redraw_selections)
2671 GtkTextLineSegment *mark;
2674 g_return_val_if_fail (tree != NULL, NULL);
2675 g_return_val_if_fail (where != NULL, NULL);
2676 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2679 mark = existing_mark->segment;
2680 else if (name != NULL)
2681 mark = g_hash_table_lookup (tree->mark_table,
2686 if (should_exist && mark == NULL)
2688 g_warning ("No mark `%s' exists!", name);
2692 /* OK if !should_exist and it does already exist, in that case
2698 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2699 _gtk_text_iter_check (&iter);
2703 if (redraw_selections &&
2704 (mark == tree->insert_mark->segment ||
2705 mark == tree->selection_bound_mark->segment))
2707 GtkTextIter old_pos;
2709 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2710 mark->body.mark.obj);
2711 redisplay_region (tree, &old_pos, where);
2715 * don't let visible marks be after the final newline of the
2719 if (mark->body.mark.visible)
2721 ensure_not_off_end (tree, mark, &iter);
2724 /* Redraw the mark's old location. */
2725 redisplay_mark_if_visible (mark);
2727 /* Unlink mark from its current location.
2728 This could hose our iterator... */
2729 gtk_text_btree_unlink_segment (tree, mark,
2730 mark->body.mark.line);
2731 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2732 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2734 segments_changed (tree); /* make sure the iterator recomputes its
2739 mark = _gtk_mark_segment_new (tree,
2743 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2745 if (mark->body.mark.name)
2746 g_hash_table_insert (tree->mark_table,
2747 mark->body.mark.name,
2751 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2752 _gtk_text_iter_check (&iter);
2754 /* Link mark into new location */
2755 gtk_text_btree_link_segment (mark, &iter);
2757 /* Invalidate some iterators. */
2758 segments_changed (tree);
2761 * update the screen at the mark's new location.
2764 redisplay_mark_if_visible (mark);
2766 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2767 _gtk_text_iter_check (&iter);
2769 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2770 _gtk_text_btree_check (tree);
2777 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2778 GtkTextMark *existing_mark,
2780 gboolean left_gravity,
2781 const GtkTextIter *iter,
2782 gboolean should_exist)
2784 GtkTextLineSegment *seg;
2786 seg = real_set_mark (tree, existing_mark,
2787 name, left_gravity, iter, should_exist,
2790 return seg ? seg->body.mark.obj : NULL;
2794 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2798 GtkTextIter tmp_start, tmp_end;
2800 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2802 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2803 tree->selection_bound_mark);
2805 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2817 gtk_text_iter_order (&tmp_start, &tmp_end);
2830 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2831 const GtkTextIter *iter)
2833 _gtk_text_btree_select_range (tree, iter, iter);
2837 _gtk_text_btree_select_range (GtkTextBTree *tree,
2838 const GtkTextIter *ins,
2839 const GtkTextIter *bound)
2841 GtkTextIter start, end;
2843 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2844 redisplay_region (tree, &start, &end);
2846 /* Move insert AND selection_bound before we redisplay */
2847 real_set_mark (tree, tree->insert_mark,
2848 "insert", FALSE, ins, TRUE, FALSE);
2849 real_set_mark (tree, tree->selection_bound_mark,
2850 "selection_bound", FALSE, bound, TRUE, FALSE);
2855 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2860 g_return_if_fail (tree != NULL);
2861 g_return_if_fail (name != NULL);
2863 mark = g_hash_table_lookup (tree->mark_table,
2866 _gtk_text_btree_remove_mark (tree, mark);
2870 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2871 GtkTextLineSegment *segment)
2874 if (segment->body.mark.name)
2875 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2877 segment->body.mark.tree = NULL;
2878 segment->body.mark.line = NULL;
2880 /* Remove the ref on the mark, which frees segment as a side effect
2881 * if this is the last reference.
2883 g_object_unref (segment->body.mark.obj);
2887 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2890 GtkTextLineSegment *segment;
2892 g_return_if_fail (mark != NULL);
2893 g_return_if_fail (tree != NULL);
2895 segment = mark->segment;
2897 if (segment->body.mark.not_deleteable)
2899 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2903 /* This calls cleanup_line and segments_changed */
2904 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2906 _gtk_text_btree_release_mark_segment (tree, segment);
2910 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2911 GtkTextMark *segment)
2913 return segment == tree->insert_mark;
2917 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2918 GtkTextMark *segment)
2920 return segment == tree->selection_bound_mark;
2924 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2927 GtkTextLineSegment *seg;
2929 g_return_val_if_fail (tree != NULL, NULL);
2930 g_return_val_if_fail (name != NULL, NULL);
2932 seg = g_hash_table_lookup (tree->mark_table, name);
2934 return seg ? seg->body.mark.obj : NULL;
2938 * gtk_text_mark_set_visible:
2939 * @mark: a #GtkTextMark
2940 * @setting: visibility of mark
2942 * Sets the visibility of @mark; the insertion point is normally
2943 * visible, i.e. you can see it as a vertical bar. Also, the text
2944 * widget uses a visible mark to indicate where a drop will occur when
2945 * dragging-and-dropping text. Most other marks are not visible.
2946 * Marks are not visible by default.
2950 gtk_text_mark_set_visible (GtkTextMark *mark,
2953 GtkTextLineSegment *seg;
2955 g_return_if_fail (mark != NULL);
2957 seg = mark->segment;
2959 if (seg->body.mark.visible == setting)
2963 seg->body.mark.visible = setting;
2965 redisplay_mark (seg);
2970 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2973 GtkTextBTreeNode *node;
2974 GtkTextTagInfo *info;
2976 g_return_val_if_fail (tree != NULL, NULL);
2980 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2985 if (info->tag_root == NULL)
2988 node = info->tag_root;
2990 /* We know the tag root has instances of the given
2993 continue_outer_loop:
2994 g_assert (node != NULL);
2995 while (node->level > 0)
2997 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2998 node = node->children.node;
2999 while (node != NULL)
3001 if (gtk_text_btree_node_has_tag (node, tag))
3002 goto continue_outer_loop;
3006 g_assert (node != NULL);
3009 g_assert (node != NULL); /* The tag summaries said some node had
3012 g_assert (node->level == 0);
3014 return node->children.line;
3018 /* Looking for any tag at all (tag == NULL).
3019 Unfortunately this can't be done in a simple and efficient way
3020 right now; so I'm just going to return the
3021 first line in the btree. FIXME */
3022 return _gtk_text_btree_get_line (tree, 0, NULL);
3027 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3030 GtkTextBTreeNode *node;
3031 GtkTextBTreeNode *last_node;
3033 GtkTextTagInfo *info;
3035 g_return_val_if_fail (tree != NULL, NULL);
3039 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3041 if (info->tag_root == NULL)
3044 node = info->tag_root;
3045 /* We know the tag root has instances of the given
3048 while (node->level > 0)
3050 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3052 node = node->children.node;
3053 while (node != NULL)
3055 if (gtk_text_btree_node_has_tag (node, tag))
3063 g_assert (node != NULL); /* The tag summaries said some node had
3066 g_assert (node->level == 0);
3068 /* Find the last line in this node */
3069 line = node->children.line;
3070 while (line->next != NULL)
3077 /* This search can't be done efficiently at the moment,
3078 at least not without complexity.
3079 So, we just return the last line.
3081 return _gtk_text_btree_get_end_iter_line (tree);
3091 _gtk_text_line_get_number (GtkTextLine *line)
3094 GtkTextBTreeNode *node, *parent, *node2;
3098 * First count how many lines precede this one in its level-0
3102 node = line->parent;
3104 for (line2 = node->children.line; line2 != line;
3105 line2 = line2->next)
3109 g_error ("gtk_text_btree_line_number couldn't find line");
3115 * Now work up through the levels of the tree one at a time,
3116 * counting how many lines are in GtkTextBTreeNodes preceding the current
3120 for (parent = node->parent ; parent != NULL;
3121 node = parent, parent = parent->parent)
3123 for (node2 = parent->children.node; node2 != node;
3124 node2 = node2->next)
3128 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3130 index += node2->num_lines;
3136 static GtkTextLineSegment*
3137 find_toggle_segment_before_char (GtkTextLine *line,
3141 GtkTextLineSegment *seg;
3142 GtkTextLineSegment *toggle_seg;
3147 seg = line->segments;
3148 while ( (index + seg->char_count) <= char_in_line )
3150 if (((seg->type == >k_text_toggle_on_type)
3151 || (seg->type == >k_text_toggle_off_type))
3152 && (seg->body.toggle.info->tag == tag))
3155 index += seg->char_count;
3162 static GtkTextLineSegment*
3163 find_toggle_segment_before_byte (GtkTextLine *line,
3167 GtkTextLineSegment *seg;
3168 GtkTextLineSegment *toggle_seg;
3173 seg = line->segments;
3174 while ( (index + seg->byte_count) <= byte_in_line )
3176 if (((seg->type == >k_text_toggle_on_type)
3177 || (seg->type == >k_text_toggle_off_type))
3178 && (seg->body.toggle.info->tag == tag))
3181 index += seg->byte_count;
3189 find_toggle_outside_current_line (GtkTextLine *line,
3193 GtkTextBTreeNode *node;
3194 GtkTextLine *sibling_line;
3195 GtkTextLineSegment *seg;
3196 GtkTextLineSegment *toggle_seg;
3198 GtkTextTagInfo *info = NULL;
3201 * No toggle in this line. Look for toggles for the tag in lines
3202 * that are predecessors of line but under the same
3203 * level-0 GtkTextBTreeNode.
3206 sibling_line = line->parent->children.line;
3207 while (sibling_line != line)
3209 seg = sibling_line->segments;
3212 if (((seg->type == >k_text_toggle_on_type)
3213 || (seg->type == >k_text_toggle_off_type))
3214 && (seg->body.toggle.info->tag == tag))
3220 sibling_line = sibling_line->next;
3223 if (toggle_seg != NULL)
3224 return (toggle_seg->type == >k_text_toggle_on_type);
3227 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3228 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3229 * siblings that precede that GtkTextBTreeNode.
3232 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3238 node = line->parent;
3239 while (node->parent != NULL)
3241 GtkTextBTreeNode *sibling_node;
3243 sibling_node = node->parent->children.node;
3244 while (sibling_node != node)
3248 summary = sibling_node->summary;
3249 while (summary != NULL)
3251 if (summary->info == info)
3252 toggles += summary->toggle_count;
3254 summary = summary->next;
3257 sibling_node = sibling_node->next;
3260 if (node == info->tag_root)
3263 node = node->parent;
3267 * An odd number of toggles means that the tag is present at the
3271 return (toggles & 1) != 0;
3274 /* FIXME this function is far too slow, for no good reason. */
3276 _gtk_text_line_char_has_tag (GtkTextLine *line,
3281 GtkTextLineSegment *toggle_seg;
3283 g_return_val_if_fail (line != NULL, FALSE);
3286 * Check for toggles for the tag in the line but before
3287 * the char. If there is one, its type indicates whether or
3288 * not the character is tagged.
3291 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3293 if (toggle_seg != NULL)
3294 return (toggle_seg->type == >k_text_toggle_on_type);
3296 return find_toggle_outside_current_line (line, tree, tag);
3300 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3305 GtkTextLineSegment *toggle_seg;
3307 g_return_val_if_fail (line != NULL, FALSE);
3310 * Check for toggles for the tag in the line but before
3311 * the char. If there is one, its type indicates whether or
3312 * not the character is tagged.
3315 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3317 if (toggle_seg != NULL)
3318 return (toggle_seg->type == >k_text_toggle_on_type);
3320 return find_toggle_outside_current_line (line, tree, tag);
3324 _gtk_text_line_is_last (GtkTextLine *line,
3327 return line == get_last_line (tree);
3331 ensure_end_iter_line (GtkTextBTree *tree)
3333 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3338 /* n_lines is without the magic line at the end */
3339 n_lines = _gtk_text_btree_line_count (tree);
3341 g_assert (n_lines >= 1);
3343 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3345 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3350 ensure_end_iter_segment (GtkTextBTree *tree)
3352 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3354 GtkTextLineSegment *seg;
3355 GtkTextLineSegment *last_with_chars;
3357 ensure_end_iter_line (tree);
3359 last_with_chars = NULL;
3361 seg = tree->end_iter_line->segments;
3364 if (seg->char_count > 0)
3365 last_with_chars = seg;
3369 tree->end_iter_segment = last_with_chars;
3371 /* We know the last char in the last line is '\n' */
3372 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3373 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3375 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3377 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3378 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3383 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3386 ensure_end_iter_line (tree);
3388 return line == tree->end_iter_line;
3392 _gtk_text_btree_is_end (GtkTextBTree *tree,
3394 GtkTextLineSegment *seg,
3398 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3400 /* Do this first to avoid walking segments in most cases */
3401 if (!_gtk_text_line_contains_end_iter (line, tree))
3404 ensure_end_iter_segment (tree);
3406 if (seg != tree->end_iter_segment)
3409 if (byte_index >= 0)
3410 return byte_index == tree->end_iter_segment_byte_index;
3412 return char_offset == tree->end_iter_segment_char_offset;
3416 _gtk_text_line_next (GtkTextLine *line)
3418 GtkTextBTreeNode *node;
3420 if (line->next != NULL)
3425 * This was the last line associated with the particular parent
3426 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3427 * then search down from that GtkTextBTreeNode to find the first
3431 node = line->parent;
3432 while (node != NULL && node->next == NULL)
3433 node = node->parent;
3439 while (node->level > 0)
3441 node = node->children.node;
3444 g_assert (node->children.line != line);
3446 return node->children.line;
3451 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3455 next = _gtk_text_line_next (line);
3457 /* If we were on the end iter line, we can't go to
3460 if (next && next->next == NULL && /* these checks are optimization only */
3461 _gtk_text_line_next (next) == NULL)
3468 _gtk_text_line_previous (GtkTextLine *line)
3470 GtkTextBTreeNode *node;
3471 GtkTextBTreeNode *node2;
3475 * Find the line under this GtkTextBTreeNode just before the starting line.
3477 prev = line->parent->children.line; /* First line at leaf */
3478 while (prev != line)
3480 if (prev->next == line)
3486 g_error ("gtk_text_btree_previous_line ran out of lines");
3490 * This was the first line associated with the particular parent
3491 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3492 * then search down from that GtkTextBTreeNode to find its last line.
3494 for (node = line->parent; ; node = node->parent)
3496 if (node == NULL || node->parent == NULL)
3498 else if (node != node->parent->children.node)
3502 for (node2 = node->parent->children.node; ;
3503 node2 = node2->children.node)
3505 while (node2->next != node)
3506 node2 = node2->next;
3508 if (node2->level == 0)
3514 for (prev = node2->children.line ; ; prev = prev->next)
3516 if (prev->next == NULL)
3520 g_assert_not_reached ();
3526 _gtk_text_line_data_new (GtkTextLayout *layout,
3529 GtkTextLineData *line_data;
3531 line_data = g_new (GtkTextLineData, 1);
3533 line_data->view_id = layout;
3534 line_data->next = NULL;
3535 line_data->width = 0;
3536 line_data->height = 0;
3537 line_data->valid = FALSE;
3543 _gtk_text_line_add_data (GtkTextLine *line,
3544 GtkTextLineData *data)
3546 g_return_if_fail (line != NULL);
3547 g_return_if_fail (data != NULL);
3548 g_return_if_fail (data->view_id != NULL);
3552 data->next = line->views;
3562 _gtk_text_line_remove_data (GtkTextLine *line,
3565 GtkTextLineData *prev;
3566 GtkTextLineData *iter;
3568 g_return_val_if_fail (line != NULL, NULL);
3569 g_return_val_if_fail (view_id != NULL, NULL);
3573 while (iter != NULL)
3575 if (iter->view_id == view_id)
3584 prev->next = iter->next;
3586 line->views = iter->next;
3595 _gtk_text_line_get_data (GtkTextLine *line,
3598 GtkTextLineData *iter;
3600 g_return_val_if_fail (line != NULL, NULL);
3601 g_return_val_if_fail (view_id != NULL, NULL);
3604 while (iter != NULL)
3606 if (iter->view_id == view_id)
3615 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3616 GtkTextLineData *ld)
3618 /* For now this is totally unoptimized. FIXME?
3620 We could probably optimize the case where the width removed
3621 is less than the max width for the parent node,
3622 and the case where the height is unchanged when we re-wrap.
3625 g_return_if_fail (ld != NULL);
3628 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3632 _gtk_text_line_char_count (GtkTextLine *line)
3634 GtkTextLineSegment *seg;
3638 seg = line->segments;
3641 size += seg->char_count;
3648 _gtk_text_line_byte_count (GtkTextLine *line)
3650 GtkTextLineSegment *seg;
3654 seg = line->segments;
3657 size += seg->byte_count;
3665 _gtk_text_line_char_index (GtkTextLine *target_line)
3667 GSList *node_stack = NULL;
3668 GtkTextBTreeNode *iter;
3672 /* Push all our parent nodes onto a stack */
3673 iter = target_line->parent;
3675 g_assert (iter != NULL);
3677 while (iter != NULL)
3679 node_stack = g_slist_prepend (node_stack, iter);
3681 iter = iter->parent;
3684 /* Check that we have the root node on top of the stack. */
3685 g_assert (node_stack != NULL &&
3686 node_stack->data != NULL &&
3687 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3689 /* Add up chars in all nodes before the nodes in our stack.
3693 iter = node_stack->data;
3694 while (iter != NULL)
3696 GtkTextBTreeNode *child_iter;
3697 GtkTextBTreeNode *next_node;
3699 next_node = node_stack->next ?
3700 node_stack->next->data : NULL;
3701 node_stack = g_slist_remove (node_stack, node_stack->data);
3703 if (iter->level == 0)
3705 /* stack should be empty when we're on the last node */
3706 g_assert (node_stack == NULL);
3707 break; /* Our children are now lines */
3710 g_assert (next_node != NULL);
3711 g_assert (iter != NULL);
3712 g_assert (next_node->parent == iter);
3714 /* Add up chars before us in the tree */
3715 child_iter = iter->children.node;
3716 while (child_iter != next_node)
3718 g_assert (child_iter != NULL);
3720 num_chars += child_iter->num_chars;
3722 child_iter = child_iter->next;
3728 g_assert (iter != NULL);
3729 g_assert (iter == target_line->parent);
3731 /* Since we don't store char counts in lines, only in segments, we
3732 have to iterate over the lines adding up segment char counts
3733 until we find our line. */
3734 line = iter->children.line;
3735 while (line != target_line)
3737 g_assert (line != NULL);
3739 num_chars += _gtk_text_line_char_count (line);
3744 g_assert (line == target_line);
3750 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3754 GtkTextLineSegment *seg;
3757 g_return_val_if_fail (line != NULL, NULL);
3759 offset = byte_offset;
3760 seg = line->segments;
3762 while (offset >= seg->byte_count)
3764 g_assert (seg != NULL); /* means an invalid byte index */
3765 offset -= seg->byte_count;
3770 *seg_offset = offset;
3776 _gtk_text_line_char_to_segment (GtkTextLine *line,
3780 GtkTextLineSegment *seg;
3783 g_return_val_if_fail (line != NULL, NULL);
3785 offset = char_offset;
3786 seg = line->segments;
3788 while (offset >= seg->char_count)
3790 g_assert (seg != NULL); /* means an invalid char index */
3791 offset -= seg->char_count;
3796 *seg_offset = offset;
3802 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3806 GtkTextLineSegment *seg;
3809 g_return_val_if_fail (line != NULL, NULL);
3811 offset = byte_offset;
3812 seg = line->segments;
3814 while (offset > 0 && offset >= seg->byte_count)
3816 g_assert (seg != NULL); /* means an invalid byte index */
3817 offset -= seg->byte_count;
3822 *seg_offset = offset;
3828 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3832 GtkTextLineSegment *seg;
3835 g_return_val_if_fail (line != NULL, NULL);
3837 offset = char_offset;
3838 seg = line->segments;
3840 while (offset > 0 && offset >= seg->char_count)
3842 g_assert (seg != NULL); /* means an invalid byte index */
3843 offset -= seg->char_count;
3848 *seg_offset = offset;
3854 _gtk_text_line_byte_to_char (GtkTextLine *line,
3858 GtkTextLineSegment *seg;
3860 g_return_val_if_fail (line != NULL, 0);
3861 g_return_val_if_fail (byte_offset >= 0, 0);
3864 seg = line->segments;
3865 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3866 the next segment) */
3868 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3870 byte_offset -= seg->byte_count;
3871 char_offset += seg->char_count;
3876 g_assert (seg != NULL);
3878 /* Now byte_offset is the offset into the current segment,
3879 and char_offset is the start of the current segment.
3880 Optimize the case where no chars use > 1 byte */
3881 if (seg->byte_count == seg->char_count)
3882 return char_offset + byte_offset;
3885 if (seg->type == >k_text_char_type)
3886 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3889 g_assert (seg->char_count == 1);
3890 g_assert (byte_offset == 0);
3898 _gtk_text_line_char_to_byte (GtkTextLine *line,
3901 g_warning ("FIXME not implemented");
3906 /* FIXME sync with char_locate (or figure out a clean
3907 way to merge the two functions) */
3909 _gtk_text_line_byte_locate (GtkTextLine *line,
3911 GtkTextLineSegment **segment,
3912 GtkTextLineSegment **any_segment,
3913 gint *seg_byte_offset,
3914 gint *line_byte_offset)
3916 GtkTextLineSegment *seg;
3917 GtkTextLineSegment *after_prev_indexable;
3918 GtkTextLineSegment *after_last_indexable;
3919 GtkTextLineSegment *last_indexable;
3923 g_return_val_if_fail (line != NULL, FALSE);
3924 g_return_val_if_fail (byte_offset >= 0, FALSE);
3927 *any_segment = NULL;
3930 offset = byte_offset;
3932 last_indexable = NULL;
3933 after_last_indexable = line->segments;
3934 after_prev_indexable = line->segments;
3935 seg = line->segments;
3937 /* The loop ends when we're inside a segment;
3938 last_indexable refers to the last segment
3939 we passed entirely. */
3940 while (seg && offset >= seg->byte_count)
3942 if (seg->char_count > 0)
3944 offset -= seg->byte_count;
3945 bytes_in_line += seg->byte_count;
3946 last_indexable = seg;
3947 after_prev_indexable = after_last_indexable;
3948 after_last_indexable = last_indexable->next;
3956 /* We went off the end of the line */
3958 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3965 if (after_last_indexable != NULL)
3966 *any_segment = after_last_indexable;
3968 *any_segment = *segment;
3971 /* Override any_segment if we're in the middle of a segment. */
3973 *any_segment = *segment;
3975 *seg_byte_offset = offset;
3977 g_assert (*segment != NULL);
3978 g_assert (*any_segment != NULL);
3979 g_assert (*seg_byte_offset < (*segment)->byte_count);
3981 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3986 /* FIXME sync with byte_locate (or figure out a clean
3987 way to merge the two functions) */
3989 _gtk_text_line_char_locate (GtkTextLine *line,
3991 GtkTextLineSegment **segment,
3992 GtkTextLineSegment **any_segment,
3993 gint *seg_char_offset,
3994 gint *line_char_offset)
3996 GtkTextLineSegment *seg;
3997 GtkTextLineSegment *after_prev_indexable;
3998 GtkTextLineSegment *after_last_indexable;
3999 GtkTextLineSegment *last_indexable;
4003 g_return_val_if_fail (line != NULL, FALSE);
4004 g_return_val_if_fail (char_offset >= 0, FALSE);
4007 *any_segment = NULL;
4010 offset = char_offset;
4012 last_indexable = NULL;
4013 after_last_indexable = line->segments;
4014 after_prev_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_prev_indexable = after_last_indexable;
4028 after_last_indexable = last_indexable->next;
4036 /* end of the line */
4038 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4045 if (after_last_indexable != NULL)
4046 *any_segment = after_last_indexable;
4048 *any_segment = *segment;
4051 /* Override any_segment if we're in the middle of a segment. */
4053 *any_segment = *segment;
4055 *seg_char_offset = offset;
4057 g_assert (*segment != NULL);
4058 g_assert (*any_segment != NULL);
4059 g_assert (*seg_char_offset < (*segment)->char_count);
4061 *line_char_offset = chars_in_line + *seg_char_offset;
4067 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4069 gint *line_char_offset,
4070 gint *seg_char_offset)
4072 GtkTextLineSegment *seg;
4075 g_return_if_fail (line != NULL);
4076 g_return_if_fail (byte_offset >= 0);
4078 *line_char_offset = 0;
4080 offset = byte_offset;
4081 seg = line->segments;
4083 while (offset >= seg->byte_count)
4085 offset -= seg->byte_count;
4086 *line_char_offset += seg->char_count;
4088 g_assert (seg != NULL); /* means an invalid char offset */
4091 g_assert (seg->char_count > 0); /* indexable. */
4093 /* offset is now the number of bytes into the current segment we
4094 * want to go. Count chars into the current segment.
4097 if (seg->type == >k_text_char_type)
4099 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4101 g_assert (*seg_char_offset < seg->char_count);
4103 *line_char_offset += *seg_char_offset;
4107 g_assert (offset == 0);
4108 *seg_char_offset = 0;
4113 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4115 gint *line_byte_offset,
4116 gint *seg_byte_offset)
4118 GtkTextLineSegment *seg;
4121 g_return_if_fail (line != NULL);
4122 g_return_if_fail (char_offset >= 0);
4124 *line_byte_offset = 0;
4126 offset = char_offset;
4127 seg = line->segments;
4129 while (offset >= seg->char_count)
4131 offset -= seg->char_count;
4132 *line_byte_offset += seg->byte_count;
4134 g_assert (seg != NULL); /* means an invalid char offset */
4137 g_assert (seg->char_count > 0); /* indexable. */
4139 /* offset is now the number of chars into the current segment we
4140 want to go. Count bytes into the current segment. */
4142 if (seg->type == >k_text_char_type)
4144 *seg_byte_offset = 0;
4148 const char * start = seg->body.chars + *seg_byte_offset;
4150 bytes = g_utf8_next_char (start) - start;
4151 *seg_byte_offset += bytes;
4155 g_assert (*seg_byte_offset < seg->byte_count);
4157 *line_byte_offset += *seg_byte_offset;
4161 g_assert (offset == 0);
4162 *seg_byte_offset = 0;
4167 node_compare (GtkTextBTreeNode *lhs,
4168 GtkTextBTreeNode *rhs)
4170 GtkTextBTreeNode *iter;
4171 GtkTextBTreeNode *node;
4172 GtkTextBTreeNode *common_parent;
4173 GtkTextBTreeNode *parent_of_lower;
4174 GtkTextBTreeNode *parent_of_higher;
4175 gboolean lhs_is_lower;
4176 GtkTextBTreeNode *lower;
4177 GtkTextBTreeNode *higher;
4179 /* This function assumes that lhs and rhs are not underneath each
4186 if (lhs->level < rhs->level)
4188 lhs_is_lower = TRUE;
4194 lhs_is_lower = FALSE;
4199 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4200 * of the common parent we used to reach the common parent; the
4201 * ordering of these child nodes in the child list is the ordering
4205 /* Get on the same level (may be on same level already) */
4207 while (node->level < higher->level)
4208 node = node->parent;
4210 g_assert (node->level == higher->level);
4212 g_assert (node != higher); /* Happens if lower is underneath higher */
4214 /* Go up until we have two children with a common parent.
4216 parent_of_lower = node;
4217 parent_of_higher = higher;
4219 while (parent_of_lower->parent != parent_of_higher->parent)
4221 parent_of_lower = parent_of_lower->parent;
4222 parent_of_higher = parent_of_higher->parent;
4225 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4227 common_parent = parent_of_lower->parent;
4229 g_assert (common_parent != NULL);
4231 /* See which is first in the list of common_parent's children */
4232 iter = common_parent->children.node;
4233 while (iter != NULL)
4235 if (iter == parent_of_higher)
4237 /* higher is less than lower */
4240 return 1; /* lhs > rhs */
4244 else if (iter == parent_of_lower)
4246 /* lower is less than higher */
4249 return -1; /* lhs < rhs */
4257 g_assert_not_reached ();
4261 /* remember that tag == NULL means "any tag" */
4263 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4267 GtkTextBTreeNode *node;
4268 GtkTextTagInfo *info;
4269 gboolean below_tag_root;
4271 g_return_val_if_fail (line != NULL, NULL);
4273 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4274 _gtk_text_btree_check (tree);
4278 /* Right now we can only offer linear-search if the user wants
4279 * to know about any tag toggle at all.
4281 return _gtk_text_line_next_excluding_last (line);
4284 /* Our tag summaries only have node precision, not line
4285 * precision. This means that if any line under a node could contain a
4286 * tag, then any of the others could also contain a tag.
4288 * In the future we could have some mechanism to keep track of how
4289 * many toggles we've found under a node so far, since we have a
4290 * count of toggles under the node. But for now I'm going with KISS.
4293 /* return same-node line, if any. */
4297 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4301 if (info->tag_root == NULL)
4304 if (info->tag_root == line->parent)
4305 return NULL; /* we were at the last line under the tag root */
4307 /* We need to go up out of this node, and on to the next one with
4308 toggles for the target tag. If we're below the tag root, we need to
4309 find the next node below the tag root that has tag summaries. If
4310 we're not below the tag root, we need to see if the tag root is
4311 after us in the tree, and if so, return the first line underneath
4314 node = line->parent;
4315 below_tag_root = FALSE;
4316 while (node != NULL)
4318 if (node == info->tag_root)
4320 below_tag_root = TRUE;
4324 node = node->parent;
4329 node = line->parent;
4330 while (node != info->tag_root)
4332 if (node->next == NULL)
4333 node = node->parent;
4338 if (gtk_text_btree_node_has_tag (node, tag))
4348 ordering = node_compare (line->parent, info->tag_root);
4352 /* Tag root is ahead of us, so search there. */
4353 node = info->tag_root;
4358 /* Tag root is after us, so no more lines that
4359 * could contain the tag.
4364 g_assert_not_reached ();
4369 g_assert (node != NULL);
4371 /* We have to find the first sub-node of this node that contains
4375 while (node->level > 0)
4377 g_assert (node != NULL); /* If this fails, it likely means an
4378 incorrect tag summary led us on a
4379 wild goose chase down this branch of
4381 node = node->children.node;
4382 while (node != NULL)
4384 if (gtk_text_btree_node_has_tag (node, tag))
4390 g_assert (node != NULL);
4391 g_assert (node->level == 0);
4393 return node->children.line;
4397 prev_line_under_node (GtkTextBTreeNode *node,
4402 prev = node->children.line;
4408 while (prev->next != line)
4418 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4422 GtkTextBTreeNode *node;
4423 GtkTextBTreeNode *found_node = NULL;
4424 GtkTextTagInfo *info;
4425 gboolean below_tag_root;
4427 GtkTextBTreeNode *line_ancestor;
4428 GtkTextBTreeNode *line_ancestor_parent;
4430 /* See next_could_contain_tag () for more extensive comments
4431 * on what's going on here.
4434 g_return_val_if_fail (line != NULL, NULL);
4436 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4437 _gtk_text_btree_check (tree);
4441 /* Right now we can only offer linear-search if the user wants
4442 * to know about any tag toggle at all.
4444 return _gtk_text_line_previous (line);
4447 /* Return same-node line, if any. */
4448 prev = prev_line_under_node (line->parent, line);
4452 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4456 if (info->tag_root == NULL)
4459 if (info->tag_root == line->parent)
4460 return NULL; /* we were at the first line under the tag root */
4462 /* Are we below the tag root */
4463 node = line->parent;
4464 below_tag_root = FALSE;
4465 while (node != NULL)
4467 if (node == info->tag_root)
4469 below_tag_root = TRUE;
4473 node = node->parent;
4478 /* Look for a previous node under this tag root that has our
4482 /* this assertion holds because line->parent is not the
4483 * tag root, we are below the tag root, and the tag
4486 g_assert (line->parent->parent != NULL);
4488 line_ancestor = line->parent;
4489 line_ancestor_parent = line->parent->parent;
4491 while (line_ancestor != info->tag_root)
4493 GSList *child_nodes = NULL;
4496 /* Create reverse-order list of nodes before
4499 if (line_ancestor_parent != NULL)
4500 node = line_ancestor_parent->children.node;
4502 node = line_ancestor;
4504 while (node != line_ancestor && node != NULL)
4506 child_nodes = g_slist_prepend (child_nodes, node);
4511 /* Try to find a node with our tag on it in the list */
4515 GtkTextBTreeNode *this_node = tmp->data;
4517 g_assert (this_node != line_ancestor);
4519 if (gtk_text_btree_node_has_tag (this_node, tag))
4521 found_node = this_node;
4522 g_slist_free (child_nodes);
4526 tmp = g_slist_next (tmp);
4529 g_slist_free (child_nodes);
4531 /* Didn't find anything on this level; go up one level. */
4532 line_ancestor = line_ancestor_parent;
4533 line_ancestor_parent = line_ancestor->parent;
4543 ordering = node_compare (line->parent, info->tag_root);
4547 /* Tag root is ahead of us, so no more lines
4554 /* Tag root is after us, so grab last tagged
4555 * line underneath the tag root.
4557 found_node = info->tag_root;
4561 g_assert_not_reached ();
4566 g_assert (found_node != NULL);
4568 /* We have to find the last sub-node of this node that contains
4573 while (node->level > 0)
4575 GSList *child_nodes = NULL;
4577 g_assert (node != NULL); /* If this fails, it likely means an
4578 incorrect tag summary led us on a
4579 wild goose chase down this branch of
4582 node = node->children.node;
4583 while (node != NULL)
4585 child_nodes = g_slist_prepend (child_nodes, node);
4589 node = NULL; /* detect failure to find a child node. */
4592 while (iter != NULL)
4594 if (gtk_text_btree_node_has_tag (iter->data, tag))
4596 /* recurse into this node. */
4601 iter = g_slist_next (iter);
4604 g_slist_free (child_nodes);
4606 g_assert (node != NULL);
4609 g_assert (node != NULL);
4610 g_assert (node->level == 0);
4612 /* this assertion is correct, but slow. */
4613 /* g_assert (node_compare (node, line->parent) < 0); */
4615 /* Return last line in this node. */
4617 prev = node->children.line;
4625 * Non-public function implementations
4629 summary_list_destroy (Summary *summary)
4632 while (summary != NULL)
4634 next = summary->next;
4635 summary_destroy (summary);
4641 get_last_line (GtkTextBTree *tree)
4643 if (tree->last_line_stamp != tree->chars_changed_stamp)
4649 n_lines = _gtk_text_btree_line_count (tree);
4651 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4653 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4655 tree->last_line_stamp = tree->chars_changed_stamp;
4656 tree->last_line = line;
4659 return tree->last_line;
4667 gtk_text_line_new (void)
4671 line = g_new0(GtkTextLine, 1);
4672 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4673 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4674 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4680 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4682 GtkTextLineData *ld;
4683 GtkTextLineData *next;
4685 g_return_if_fail (line != NULL);
4692 view = gtk_text_btree_get_view (tree, ld->view_id);
4694 g_assert (view != NULL);
4697 gtk_text_layout_free_line_data (view->layout, line, ld);
4706 gtk_text_line_set_parent (GtkTextLine *line,
4707 GtkTextBTreeNode *node)
4709 if (line->parent == node)
4711 line->parent = node;
4712 gtk_text_btree_node_invalidate_upward (node, NULL);
4716 cleanup_line (GtkTextLine *line)
4718 GtkTextLineSegment *seg, **prev_p;
4722 * Make a pass over all of the segments in the line, giving each
4723 * a chance to clean itself up. This could potentially change
4724 * the structure of the line, e.g. by merging two segments
4725 * together or having two segments cancel themselves; if so,
4726 * then repeat the whole process again, since the first structure
4727 * change might make other structure changes possible. Repeat
4728 * until eventually there are no changes.
4735 for (prev_p = &line->segments, seg = *prev_p;
4737 prev_p = &(*prev_p)->next, seg = *prev_p)
4739 if (seg->type->cleanupFunc != NULL)
4741 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4754 node_data_new (gpointer view_id)
4758 nd = g_new (NodeData, 1);
4760 nd->view_id = view_id;
4770 node_data_destroy (NodeData *nd)
4776 node_data_list_destroy (NodeData *nd)
4782 while (iter != NULL)
4785 node_data_destroy (iter);
4791 node_data_find (NodeData *nd, gpointer view_id)
4795 if (nd->view_id == view_id)
4803 summary_destroy (Summary *summary)
4805 /* Fill with error-triggering garbage */
4806 summary->info = (void*)0x1;
4807 summary->toggle_count = 567;
4808 summary->next = (void*)0x1;
4812 static GtkTextBTreeNode*
4813 gtk_text_btree_node_new (void)
4815 GtkTextBTreeNode *node;
4817 node = g_new (GtkTextBTreeNode, 1);
4819 node->node_data = NULL;
4825 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4826 GtkTextTagInfo *info,
4831 summary = node->summary;
4832 while (summary != NULL)
4834 if (summary->info == info)
4836 summary->toggle_count += adjust;
4840 summary = summary->next;
4843 if (summary == NULL)
4845 /* didn't find a summary for our tag. */
4846 g_return_if_fail (adjust > 0);
4847 summary = g_new (Summary, 1);
4848 summary->info = info;
4849 summary->toggle_count = adjust;
4850 summary->next = node->summary;
4851 node->summary = summary;
4855 /* Note that the tag root and above do not have summaries
4856 for the tag; only nodes below the tag root have
4859 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4863 summary = node->summary;
4864 while (summary != NULL)
4867 summary->info->tag == tag)
4870 summary = summary->next;
4876 /* Add node and all children to the damage region. */
4878 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4882 nd = node->node_data;
4889 if (node->level == 0)
4893 line = node->children.line;
4894 while (line != NULL)
4896 GtkTextLineData *ld;
4910 GtkTextBTreeNode *child;
4912 child = node->children.node;
4914 while (child != NULL)
4916 gtk_text_btree_node_invalidate_downward (child);
4918 child = child->next;
4924 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4926 GtkTextBTreeNode *iter;
4929 while (iter != NULL)
4935 nd = node_data_find (iter->node_data, view_id);
4937 if (nd == NULL || !nd->valid)
4938 break; /* Once a node is invalid, we know its parents are as well. */
4944 gboolean should_continue = FALSE;
4946 nd = iter->node_data;
4951 should_continue = TRUE;
4958 if (!should_continue)
4959 break; /* This node was totally invalidated, so are its
4963 iter = iter->parent;
4969 * _gtk_text_btree_is_valid:
4970 * @tree: a #GtkTextBTree
4971 * @view_id: ID for the view
4973 * Check to see if the entire #GtkTextBTree is valid or not for
4976 * Return value: %TRUE if the entire #GtkTextBTree is valid
4979 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4983 g_return_val_if_fail (tree != NULL, FALSE);
4985 nd = node_data_find (tree->root_node->node_data, view_id);
4986 return (nd && nd->valid);
4989 typedef struct _ValidateState ValidateState;
4991 struct _ValidateState
4993 gint remaining_pixels;
4994 gboolean in_validation;
5001 gtk_text_btree_node_validate (BTreeView *view,
5002 GtkTextBTreeNode *node,
5004 ValidateState *state)
5006 gint node_valid = TRUE;
5007 gint node_width = 0;
5008 gint node_height = 0;
5010 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5011 g_return_if_fail (!nd->valid);
5013 if (node->level == 0)
5015 GtkTextLine *line = node->children.line;
5016 GtkTextLineData *ld;
5018 /* Iterate over leading valid lines */
5019 while (line != NULL)
5021 ld = _gtk_text_line_get_data (line, view_id);
5023 if (!ld || !ld->valid)
5025 else if (state->in_validation)
5027 state->in_validation = FALSE;
5032 state->y += ld->height;
5033 node_width = MAX (ld->width, node_width);
5034 node_height += ld->height;
5040 state->in_validation = TRUE;
5042 /* Iterate over invalid lines */
5043 while (line != NULL)
5045 ld = _gtk_text_line_get_data (line, view_id);
5047 if (ld && ld->valid)
5052 state->old_height += ld->height;
5053 ld = gtk_text_layout_wrap (view->layout, line, ld);
5054 state->new_height += ld->height;
5056 node_width = MAX (ld->width, node_width);
5057 node_height += ld->height;
5059 state->remaining_pixels -= ld->height;
5060 if (state->remaining_pixels <= 0)
5070 /* Iterate over the remaining lines */
5071 while (line != NULL)
5073 ld = _gtk_text_line_get_data (line, view_id);
5074 state->in_validation = FALSE;
5076 if (!ld || !ld->valid)
5081 node_width = MAX (ld->width, node_width);
5082 node_height += ld->height;
5090 GtkTextBTreeNode *child;
5093 child = node->children.node;
5095 /* Iterate over leading valid nodes */
5098 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5100 if (!child_nd->valid)
5102 else if (state->in_validation)
5104 state->in_validation = FALSE;
5109 state->y += child_nd->height;
5110 node_width = MAX (node_width, child_nd->width);
5111 node_height += child_nd->height;
5114 child = child->next;
5117 /* Iterate over invalid nodes */
5120 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5122 if (child_nd->valid)
5126 gtk_text_btree_node_validate (view, child, view_id, state);
5128 if (!child_nd->valid)
5130 node_width = MAX (node_width, child_nd->width);
5131 node_height += child_nd->height;
5133 if (!state->in_validation || state->remaining_pixels <= 0)
5135 child = child->next;
5140 child = child->next;
5143 /* Iterate over the remaining lines */
5146 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5147 state->in_validation = FALSE;
5149 if (!child_nd->valid)
5152 node_width = MAX (child_nd->width, node_width);
5153 node_height += child_nd->height;
5155 child = child->next;
5159 nd->width = node_width;
5160 nd->height = node_height;
5161 nd->valid = node_valid;
5165 * _gtk_text_btree_validate:
5166 * @tree: a #GtkTextBTree
5168 * @max_pixels: the maximum number of pixels to validate. (No more
5169 * than one paragraph beyond this limit will be validated)
5170 * @y: location to store starting y coordinate of validated region
5171 * @old_height: location to store old height of validated region
5172 * @new_height: location to store new height of validated region
5174 * Validate a single contiguous invalid region of a #GtkTextBTree for
5177 * Return value: %TRUE if a region has been validated, %FALSE if the
5178 * entire tree was already valid.
5181 _gtk_text_btree_validate (GtkTextBTree *tree,
5190 g_return_val_if_fail (tree != NULL, FALSE);
5192 view = gtk_text_btree_get_view (tree, view_id);
5193 g_return_val_if_fail (view != NULL, FALSE);
5195 if (!_gtk_text_btree_is_valid (tree, view_id))
5197 ValidateState state;
5199 state.remaining_pixels = max_pixels;
5200 state.in_validation = FALSE;
5202 state.old_height = 0;
5203 state.new_height = 0;
5205 gtk_text_btree_node_validate (view,
5212 *old_height = state.old_height;
5214 *new_height = state.new_height;
5216 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5217 _gtk_text_btree_check (tree);
5226 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5230 gboolean *valid_out)
5234 gboolean valid = TRUE;
5236 if (node->level == 0)
5238 GtkTextLine *line = node->children.line;
5240 while (line != NULL)
5242 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5244 if (!ld || !ld->valid)
5249 width = MAX (ld->width, width);
5250 height += ld->height;
5258 GtkTextBTreeNode *child = node->children.node;
5262 NodeData *child_nd = node_data_find (child->node_data, view_id);
5264 if (!child_nd || !child_nd->valid)
5269 width = MAX (child_nd->width, width);
5270 height += child_nd->height;
5273 child = child->next;
5278 *height_out = height;
5283 /* Recompute the validity and size of the view data for a given
5284 * view at this node from the immediate children of the node
5287 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5290 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5295 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5296 &width, &height, &valid);
5298 nd->height = height;
5305 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5310 gtk_text_btree_node_check_valid (node, view_id);
5311 node = node->parent;
5316 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5319 if (node->level == 0)
5321 return gtk_text_btree_node_check_valid (node, view_id);
5325 GtkTextBTreeNode *child = node->children.node;
5327 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5335 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5337 if (!child_nd->valid)
5339 nd->width = MAX (child_nd->width, nd->width);
5340 nd->height += child_nd->height;
5342 child = child->next;
5351 * _gtk_text_btree_validate_line:
5352 * @tree: a #GtkTextBTree
5353 * @line: line to validate
5354 * @view_id: view ID for the view to validate
5356 * Revalidate a single line of the btree for the given view, propagate
5357 * results up through the entire tree.
5360 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5364 GtkTextLineData *ld;
5367 g_return_if_fail (tree != NULL);
5368 g_return_if_fail (line != NULL);
5370 view = gtk_text_btree_get_view (tree, view_id);
5371 g_return_if_fail (view != NULL);
5373 ld = _gtk_text_line_get_data (line, view_id);
5374 if (!ld || !ld->valid)
5376 ld = gtk_text_layout_wrap (view->layout, line, ld);
5378 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5383 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5385 if (node->level == 0)
5389 line = node->children.line;
5390 while (line != NULL)
5392 GtkTextLineData *ld;
5394 ld = _gtk_text_line_remove_data (line, view_id);
5397 gtk_text_layout_free_line_data (view->layout, line, ld);
5404 GtkTextBTreeNode *child;
5406 child = node->children.node;
5408 while (child != NULL)
5411 gtk_text_btree_node_remove_view (view, child, view_id);
5413 child = child->next;
5417 gtk_text_btree_node_remove_data (node, view_id);
5421 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5423 if (node->level == 0)
5426 GtkTextLineSegment *seg;
5428 while (node->children.line != NULL)
5430 line = node->children.line;
5431 node->children.line = line->next;
5432 while (line->segments != NULL)
5434 seg = line->segments;
5435 line->segments = seg->next;
5437 (*seg->type->deleteFunc) (seg, line, TRUE);
5439 gtk_text_line_destroy (tree, line);
5444 GtkTextBTreeNode *childPtr;
5446 while (node->children.node != NULL)
5448 childPtr = node->children.node;
5449 node->children.node = childPtr->next;
5450 gtk_text_btree_node_destroy (tree, childPtr);
5454 gtk_text_btree_node_free_empty (tree, node);
5458 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5459 GtkTextBTreeNode *node)
5461 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5462 (node->level == 0 && node->children.line == NULL));
5464 summary_list_destroy (node->summary);
5465 node_data_list_destroy (node->node_data);
5470 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5474 nd = node->node_data;
5477 if (nd->view_id == view_id)
5485 nd = node_data_new (view_id);
5487 if (node->node_data)
5488 nd->next = node->node_data;
5490 node->node_data = nd;
5497 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5503 nd = node->node_data;
5506 if (nd->view_id == view_id)
5517 prev->next = nd->next;
5519 if (node->node_data == nd)
5520 node->node_data = nd->next;
5524 node_data_destroy (nd);
5528 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5529 gint *width, gint *height)
5533 g_return_if_fail (width != NULL);
5534 g_return_if_fail (height != NULL);
5536 nd = gtk_text_btree_node_ensure_data (node, view_id);
5541 *height = nd->height;
5544 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5545 * here isn't quite right, since for a lot of operations we want to
5546 * know which children of the common parent correspond to the two nodes
5547 * (e.g., when computing the order of two iters)
5549 static GtkTextBTreeNode *
5550 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5551 GtkTextBTreeNode *node2)
5553 while (node1->level < node2->level)
5554 node1 = node1->parent;
5555 while (node2->level < node1->level)
5556 node2 = node2->parent;
5557 while (node1 != node2)
5559 node1 = node1->parent;
5560 node2 = node2->parent;
5571 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5576 while (view != NULL)
5578 if (view->view_id == view_id)
5587 get_tree_bounds (GtkTextBTree *tree,
5591 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5592 _gtk_text_btree_get_end_iter (tree, end);
5596 tag_changed_cb (GtkTextTagTable *table,
5598 gboolean size_changed,
5603 /* We need to queue a relayout on all regions that are tagged with
5610 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5612 /* Must be a last toggle if there was a first one. */
5613 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5614 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5615 _gtk_text_btree_invalidate_region (tree,
5622 /* We only need to queue a redraw, not a relayout */
5627 while (view != NULL)
5631 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5632 gtk_text_layout_changed (view->layout, 0, height, height);
5640 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5643 /* Remove the tag from the tree */
5648 get_tree_bounds (tree, &start, &end);
5650 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5651 gtk_text_btree_remove_tag_info (tree, tag);
5655 /* Rebalance the out-of-whack node "node" */
5657 gtk_text_btree_rebalance (GtkTextBTree *tree,
5658 GtkTextBTreeNode *node)
5661 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5662 * up through the tree one GtkTextBTreeNode at a time until the root
5663 * GtkTextBTreeNode has been processed.
5666 while (node != NULL)
5668 GtkTextBTreeNode *new_node, *child;
5673 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5674 * then split off all but the first MIN_CHILDREN into a separate
5675 * GtkTextBTreeNode following the original one. Then repeat until the
5676 * GtkTextBTreeNode has a decent size.
5679 if (node->num_children > MAX_CHILDREN)
5684 * If the GtkTextBTreeNode being split is the root
5685 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5689 if (node->parent == NULL)
5691 new_node = gtk_text_btree_node_new ();
5692 new_node->parent = NULL;
5693 new_node->next = NULL;
5694 new_node->summary = NULL;
5695 new_node->level = node->level + 1;
5696 new_node->children.node = node;
5697 recompute_node_counts (tree, new_node);
5698 tree->root_node = new_node;
5700 new_node = gtk_text_btree_node_new ();
5701 new_node->parent = node->parent;
5702 new_node->next = node->next;
5703 node->next = new_node;
5704 new_node->summary = NULL;
5705 new_node->level = node->level;
5706 new_node->num_children = node->num_children - MIN_CHILDREN;
5707 if (node->level == 0)
5709 for (i = MIN_CHILDREN-1,
5710 line = node->children.line;
5711 i > 0; i--, line = line->next)
5713 /* Empty loop body. */
5715 new_node->children.line = line->next;
5720 for (i = MIN_CHILDREN-1,
5721 child = node->children.node;
5722 i > 0; i--, child = child->next)
5724 /* Empty loop body. */
5726 new_node->children.node = child->next;
5729 recompute_node_counts (tree, node);
5730 node->parent->num_children++;
5732 if (node->num_children <= MAX_CHILDREN)
5734 recompute_node_counts (tree, node);
5740 while (node->num_children < MIN_CHILDREN)
5742 GtkTextBTreeNode *other;
5743 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5744 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5745 int total_children, first_children, i;
5748 * Too few children for this GtkTextBTreeNode. If this is the root then,
5749 * it's OK for it to have less than MIN_CHILDREN children
5750 * as long as it's got at least two. If it has only one
5751 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5752 * the tree and use its child as the new root.
5755 if (node->parent == NULL)
5757 if ((node->num_children == 1) && (node->level > 0))
5759 tree->root_node = node->children.node;
5760 tree->root_node->parent = NULL;
5762 node->children.node = NULL;
5763 gtk_text_btree_node_free_empty (tree, node);
5769 * Not the root. Make sure that there are siblings to
5773 if (node->parent->num_children < 2)
5775 gtk_text_btree_rebalance (tree, node->parent);
5780 * Find a sibling neighbor to borrow from, and arrange for
5781 * node to be the earlier of the pair.
5784 if (node->next == NULL)
5786 for (other = node->parent->children.node;
5787 other->next != node;
5788 other = other->next)
5790 /* Empty loop body. */
5797 * We're going to either merge the two siblings together
5798 * into one GtkTextBTreeNode or redivide the children among them to
5799 * balance their loads. As preparation, join their two
5800 * child lists into a single list and remember the half-way
5801 * point in the list.
5804 total_children = node->num_children + other->num_children;
5805 first_children = total_children/2;
5806 if (node->children.node == NULL)
5808 node->children = other->children;
5809 other->children.node = NULL;
5810 other->children.line = NULL;
5812 if (node->level == 0)
5816 for (line = node->children.line, i = 1;
5818 line = line->next, i++)
5820 if (i == first_children)
5825 line->next = other->children.line;
5826 while (i <= first_children)
5835 GtkTextBTreeNode *child;
5837 for (child = node->children.node, i = 1;
5838 child->next != NULL;
5839 child = child->next, i++)
5841 if (i <= first_children)
5843 if (i == first_children)
5845 halfwaynode = child;
5849 child->next = other->children.node;
5850 while (i <= first_children)
5852 halfwaynode = child;
5853 child = child->next;
5859 * If the two siblings can simply be merged together, do it.
5862 if (total_children <= MAX_CHILDREN)
5864 recompute_node_counts (tree, node);
5865 node->next = other->next;
5866 node->parent->num_children--;
5868 other->children.node = NULL;
5869 other->children.line = NULL;
5870 gtk_text_btree_node_free_empty (tree, other);
5875 * The siblings can't be merged, so just divide their
5876 * children evenly between them.
5879 if (node->level == 0)
5881 other->children.line = halfwayline->next;
5882 halfwayline->next = NULL;
5886 other->children.node = halfwaynode->next;
5887 halfwaynode->next = NULL;
5890 recompute_node_counts (tree, node);
5891 recompute_node_counts (tree, other);
5894 node = node->parent;
5899 post_insert_fixup (GtkTextBTree *tree,
5901 gint line_count_delta,
5902 gint char_count_delta)
5905 GtkTextBTreeNode *node;
5908 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5909 * point, then rebalance the tree if necessary.
5912 for (node = line->parent ; node != NULL;
5913 node = node->parent)
5915 node->num_lines += line_count_delta;
5916 node->num_chars += char_count_delta;
5918 node = line->parent;
5919 node->num_children += line_count_delta;
5921 if (node->num_children > MAX_CHILDREN)
5923 gtk_text_btree_rebalance (tree, node);
5926 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5927 _gtk_text_btree_check (tree);
5930 static GtkTextTagInfo*
5931 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5934 GtkTextTagInfo *info;
5938 list = tree->tag_infos;
5939 while (list != NULL)
5942 if (info->tag == tag)
5945 list = g_slist_next (list);
5951 static GtkTextTagInfo*
5952 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5955 GtkTextTagInfo *info;
5957 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5961 /* didn't find it, create. */
5963 info = g_new (GtkTextTagInfo, 1);
5967 info->tag_root = NULL;
5968 info->toggle_count = 0;
5970 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5973 g_print ("Created tag info %p for tag %s(%p)\n",
5974 info, info->tag->name ? info->tag->name : "anon",
5983 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5986 GtkTextTagInfo *info;
5991 list = tree->tag_infos;
5992 while (list != NULL)
5995 if (info->tag == tag)
5998 g_print ("Removing tag info %p for tag %s(%p)\n",
5999 info, info->tag->name ? info->tag->name : "anon",
6005 prev->next = list->next;
6009 tree->tag_infos = list->next;
6012 g_slist_free (list);
6014 g_object_unref (info->tag);
6021 list = g_slist_next (list);
6026 recompute_level_zero_counts (GtkTextBTreeNode *node)
6029 GtkTextLineSegment *seg;
6031 g_assert (node->level == 0);
6033 line = node->children.line;
6034 while (line != NULL)
6036 node->num_children++;
6039 if (line->parent != node)
6040 gtk_text_line_set_parent (line, node);
6042 seg = line->segments;
6046 node->num_chars += seg->char_count;
6048 if (((seg->type != >k_text_toggle_on_type)
6049 && (seg->type != >k_text_toggle_off_type))
6050 || !(seg->body.toggle.inNodeCounts))
6056 GtkTextTagInfo *info;
6058 info = seg->body.toggle.info;
6060 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6071 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6074 GtkTextBTreeNode *child;
6076 g_assert (node->level > 0);
6078 child = node->children.node;
6079 while (child != NULL)
6081 node->num_children += 1;
6082 node->num_lines += child->num_lines;
6083 node->num_chars += child->num_chars;
6085 if (child->parent != node)
6087 child->parent = node;
6088 gtk_text_btree_node_invalidate_upward (node, NULL);
6091 summary = child->summary;
6092 while (summary != NULL)
6094 gtk_text_btree_node_adjust_toggle_count (node,
6096 summary->toggle_count);
6098 summary = summary->next;
6101 child = child->next;
6106 *----------------------------------------------------------------------
6108 * recompute_node_counts --
6110 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6111 * (tags, child information, etc.) by scanning the information in
6112 * its descendants. This procedure is called during rebalancing
6113 * when a GtkTextBTreeNode's child structure has changed.
6119 * The tag counts for node are modified to reflect its current
6120 * child structure, as are its num_children, num_lines, num_chars fields.
6121 * Also, all of the childrens' parent fields are made to point
6124 *----------------------------------------------------------------------
6128 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6131 Summary *summary, *summary2;
6134 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6135 * the existing Summary records (most of them will probably be reused).
6138 summary = node->summary;
6139 while (summary != NULL)
6141 summary->toggle_count = 0;
6142 summary = summary->next;
6145 node->num_children = 0;
6146 node->num_lines = 0;
6147 node->num_chars = 0;
6150 * Scan through the children, adding the childrens' tag counts into
6151 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6155 if (node->level == 0)
6156 recompute_level_zero_counts (node);
6158 recompute_level_nonzero_counts (node);
6163 gtk_text_btree_node_check_valid (node, view->view_id);
6168 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6169 * records that still have a zero count, or that have all the toggles.
6170 * The GtkTextBTreeNode with the children that account for all the tags toggles
6171 * have no summary information, and they become the tag_root for the tag.
6175 for (summary = node->summary; summary != NULL; )
6177 if (summary->toggle_count > 0 &&
6178 summary->toggle_count < summary->info->toggle_count)
6180 if (node->level == summary->info->tag_root->level)
6183 * The tag's root GtkTextBTreeNode split and some toggles left.
6184 * The tag root must move up a level.
6186 summary->info->tag_root = node->parent;
6189 summary = summary->next;
6192 if (summary->toggle_count == summary->info->toggle_count)
6195 * A GtkTextBTreeNode merge has collected all the toggles under
6196 * one GtkTextBTreeNode. Push the root down to this level.
6198 summary->info->tag_root = node;
6200 if (summary2 != NULL)
6202 summary2->next = summary->next;
6203 summary_destroy (summary);
6204 summary = summary2->next;
6208 node->summary = summary->next;
6209 summary_destroy (summary);
6210 summary = node->summary;
6216 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6217 GtkTextTagInfo *info,
6218 gint delta) /* may be negative */
6220 Summary *summary, *prevPtr;
6221 GtkTextBTreeNode *node2Ptr;
6222 int rootLevel; /* Level of original tag root */
6224 info->toggle_count += delta;
6226 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6228 info->tag_root = node;
6233 * Note the level of the existing root for the tag so we can detect
6234 * if it needs to be moved because of the toggle count change.
6237 rootLevel = info->tag_root->level;
6240 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6241 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6245 for ( ; node != info->tag_root; node = node->parent)
6248 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6249 * perhaps all we have to do is adjust its count.
6252 for (prevPtr = NULL, summary = node->summary;
6254 prevPtr = summary, summary = summary->next)
6256 if (summary->info == info)
6261 if (summary != NULL)
6263 summary->toggle_count += delta;
6264 if (summary->toggle_count > 0 &&
6265 summary->toggle_count < info->toggle_count)
6269 if (summary->toggle_count != 0)
6272 * Should never find a GtkTextBTreeNode with max toggle count at this
6273 * point (there shouldn't have been a summary entry in the
6277 g_error ("%s: bad toggle count (%d) max (%d)",
6278 G_STRLOC, summary->toggle_count, info->toggle_count);
6282 * Zero toggle count; must remove this tag from the list.
6285 if (prevPtr == NULL)
6287 node->summary = summary->next;
6291 prevPtr->next = summary->next;
6293 summary_destroy (summary);
6298 * This tag isn't currently in the summary information list.
6301 if (rootLevel == node->level)
6305 * The old tag root is at the same level in the tree as this
6306 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6307 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6308 * as well as the old root (if not, we'll move it up again
6309 * the next time through the loop). To push it up one level
6310 * we copy the original toggle count into the summary
6311 * information at the old root and change the root to its
6312 * parent GtkTextBTreeNode.
6315 GtkTextBTreeNode *rootnode = info->tag_root;
6316 summary = (Summary *) g_malloc (sizeof (Summary));
6317 summary->info = info;
6318 summary->toggle_count = info->toggle_count - delta;
6319 summary->next = rootnode->summary;
6320 rootnode->summary = summary;
6321 rootnode = rootnode->parent;
6322 rootLevel = rootnode->level;
6323 info->tag_root = rootnode;
6325 summary = (Summary *) g_malloc (sizeof (Summary));
6326 summary->info = info;
6327 summary->toggle_count = delta;
6328 summary->next = node->summary;
6329 node->summary = summary;
6334 * If we've decremented the toggle count, then it may be necessary
6335 * to push the tag root down one or more levels.
6342 if (info->toggle_count == 0)
6344 info->tag_root = (GtkTextBTreeNode *) NULL;
6347 node = info->tag_root;
6348 while (node->level > 0)
6351 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6352 * toggles. If so, push the root down one level.
6355 for (node2Ptr = node->children.node;
6356 node2Ptr != (GtkTextBTreeNode *)NULL ;
6357 node2Ptr = node2Ptr->next)
6359 for (prevPtr = NULL, summary = node2Ptr->summary;
6361 prevPtr = summary, summary = summary->next)
6363 if (summary->info == info)
6368 if (summary == NULL)
6372 if (summary->toggle_count != info->toggle_count)
6375 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6382 * This GtkTextBTreeNode has all the toggles, so push down the root.
6385 if (prevPtr == NULL)
6387 node2Ptr->summary = summary->next;
6391 prevPtr->next = summary->next;
6393 summary_destroy (summary);
6394 info->tag_root = node2Ptr;
6397 node = info->tag_root;
6402 *----------------------------------------------------------------------
6406 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6407 * increments the count for a particular tag, adding a new
6408 * entry for that tag if there wasn't one previously.
6414 * The information at *tagInfoPtr may be modified, and the arrays
6415 * may be reallocated to make them larger.
6417 *----------------------------------------------------------------------
6421 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6426 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6427 count > 0; tag_p++, count--)
6431 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6437 * There isn't currently an entry for this tag, so we have to
6438 * make a new one. If the arrays are full, then enlarge the
6442 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6444 GtkTextTag **newTags;
6445 int *newCounts, newSize;
6447 newSize = 2*tagInfoPtr->arraySize;
6448 newTags = (GtkTextTag **) g_malloc ((unsigned)
6449 (newSize*sizeof (GtkTextTag *)));
6450 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6451 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6452 g_free ((char *) tagInfoPtr->tags);
6453 tagInfoPtr->tags = newTags;
6454 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6455 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6456 tagInfoPtr->arraySize *sizeof (int));
6457 g_free ((char *) tagInfoPtr->counts);
6458 tagInfoPtr->counts = newCounts;
6459 tagInfoPtr->arraySize = newSize;
6462 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6463 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6464 tagInfoPtr->numTags++;
6468 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6469 const GtkTextIter *iter)
6471 GtkTextLineSegment *prev;
6475 line = _gtk_text_iter_get_text_line (iter);
6476 tree = _gtk_text_iter_get_btree (iter);
6478 prev = gtk_text_line_segment_split (iter);
6481 seg->next = line->segments;
6482 line->segments = seg;
6486 seg->next = prev->next;
6489 cleanup_line (line);
6490 segments_changed (tree);
6492 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6493 _gtk_text_btree_check (tree);
6497 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6498 GtkTextLineSegment *seg,
6501 GtkTextLineSegment *prev;
6503 if (line->segments == seg)
6505 line->segments = seg->next;
6509 for (prev = line->segments; prev->next != seg;
6512 /* Empty loop body. */
6514 prev->next = seg->next;
6516 cleanup_line (line);
6517 segments_changed (tree);
6521 * This is here because it requires BTree internals, it logically
6522 * belongs in gtktextsegment.c
6527 *--------------------------------------------------------------
6529 * _gtk_toggle_segment_check_func --
6531 * This procedure is invoked to perform consistency checks
6532 * on toggle segments.
6538 * If a consistency problem is found the procedure g_errors.
6540 *--------------------------------------------------------------
6544 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6550 if (segPtr->byte_count != 0)
6552 g_error ("toggle_segment_check_func: segment had non-zero size");
6554 if (!segPtr->body.toggle.inNodeCounts)
6556 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6558 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6559 for (summary = line->parent->summary; ;
6560 summary = summary->next)
6562 if (summary == NULL)
6566 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6573 if (summary->info == segPtr->body.toggle.info)
6577 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6589 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6590 GtkTextBTreeNode *node,
6600 while (view != NULL)
6602 if (view->view_id == nd->view_id)
6609 g_error ("Node has data for a view %p no longer attached to the tree",
6612 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6613 &width, &height, &valid);
6615 /* valid aggregate not checked the same as width/height, because on
6616 * btree rebalance we can have invalid nodes where all lines below
6617 * them are actually valid, due to moving lines around between
6620 * The guarantee is that if there are invalid lines the node is
6621 * invalid - we don't guarantee that if the node is invalid there
6622 * are invalid lines.
6625 if (nd->width != width ||
6626 nd->height != height ||
6627 (nd->valid && !valid))
6629 g_error ("Node aggregates for view %p are invalid:\n"
6630 "Are (%d,%d,%s), should be (%d,%d,%s)",
6632 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6633 width, height, valid ? "TRUE" : "FALSE");
6638 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6639 GtkTextBTreeNode *node)
6641 GtkTextBTreeNode *childnode;
6642 Summary *summary, *summary2;
6644 GtkTextLineSegment *segPtr;
6645 int num_children, num_lines, num_chars, toggle_count, min_children;
6646 GtkTextLineData *ld;
6649 if (node->parent != NULL)
6651 min_children = MIN_CHILDREN;
6653 else if (node->level > 0)
6660 if ((node->num_children < min_children)
6661 || (node->num_children > MAX_CHILDREN))
6663 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6664 node->num_children);
6667 nd = node->node_data;
6670 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6677 if (node->level == 0)
6679 for (line = node->children.line; line != NULL;
6682 if (line->parent != node)
6684 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6686 if (line->segments == NULL)
6688 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6694 /* Just ensuring we don't segv while doing this loop */
6699 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6701 if (segPtr->type->checkFunc != NULL)
6703 (*segPtr->type->checkFunc)(segPtr, line);
6705 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6706 && (segPtr->next != NULL)
6707 && (segPtr->next->byte_count == 0)
6708 && (segPtr->next->type->leftGravity))
6710 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6712 if ((segPtr->next == NULL)
6713 && (segPtr->type != >k_text_char_type))
6715 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6718 num_chars += segPtr->char_count;
6727 for (childnode = node->children.node; childnode != NULL;
6728 childnode = childnode->next)
6730 if (childnode->parent != node)
6732 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6734 if (childnode->level != (node->level-1))
6736 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6737 node->level, childnode->level);
6739 gtk_text_btree_node_check_consistency (tree, childnode);
6740 for (summary = childnode->summary; summary != NULL;
6741 summary = summary->next)
6743 for (summary2 = node->summary; ;
6744 summary2 = summary2->next)
6746 if (summary2 == NULL)
6748 if (summary->info->tag_root == node)
6752 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6753 summary->info->tag->name,
6754 "present in parent summaries");
6756 if (summary->info == summary2->info)
6763 num_lines += childnode->num_lines;
6764 num_chars += childnode->num_chars;
6767 if (num_children != node->num_children)
6769 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6770 num_children, node->num_children);
6772 if (num_lines != node->num_lines)
6774 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6775 num_lines, node->num_lines);
6777 if (num_chars != node->num_chars)
6779 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6780 num_chars, node->num_chars);
6783 for (summary = node->summary; summary != NULL;
6784 summary = summary->next)
6786 if (summary->info->toggle_count == summary->toggle_count)
6788 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6789 summary->info->tag->name);
6792 if (node->level == 0)
6794 for (line = node->children.line; line != NULL;
6797 for (segPtr = line->segments; segPtr != NULL;
6798 segPtr = segPtr->next)
6800 if ((segPtr->type != >k_text_toggle_on_type)
6801 && (segPtr->type != >k_text_toggle_off_type))
6805 if (segPtr->body.toggle.info == summary->info)
6807 if (!segPtr->body.toggle.inNodeCounts)
6808 g_error ("Toggle segment not in the node counts");
6817 for (childnode = node->children.node;
6819 childnode = childnode->next)
6821 for (summary2 = childnode->summary;
6823 summary2 = summary2->next)
6825 if (summary2->info == summary->info)
6827 toggle_count += summary2->toggle_count;
6832 if (toggle_count != summary->toggle_count)
6834 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6835 toggle_count, summary->toggle_count);
6837 for (summary2 = summary->next; summary2 != NULL;
6838 summary2 = summary2->next)
6840 if (summary2->info == summary->info)
6842 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6843 summary->info->tag->name);
6850 listify_foreach (GtkTextTag *tag, gpointer user_data)
6852 GSList** listp = user_data;
6854 *listp = g_slist_prepend (*listp, tag);
6858 list_of_tags (GtkTextTagTable *table)
6860 GSList *list = NULL;
6862 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6868 _gtk_text_btree_check (GtkTextBTree *tree)
6871 GtkTextBTreeNode *node;
6873 GtkTextLineSegment *seg;
6875 GSList *all_tags, *taglist = NULL;
6877 GtkTextTagInfo *info;
6880 * Make sure that the tag toggle counts and the tag root pointers are OK.
6882 all_tags = list_of_tags (tree->table);
6883 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6885 tag = taglist->data;
6886 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6889 node = info->tag_root;
6892 if (info->toggle_count != 0)
6894 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6895 tag->name, info->toggle_count);
6897 continue; /* no ranges for the tag */
6899 else if (info->toggle_count == 0)
6901 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6904 else if (info->toggle_count & 1)
6906 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6907 tag->name, info->toggle_count);
6909 for (summary = node->summary; summary != NULL;
6910 summary = summary->next)
6912 if (summary->info->tag == tag)
6914 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6918 if (node->level > 0)
6920 for (node = node->children.node ; node != NULL ;
6923 for (summary = node->summary; summary != NULL;
6924 summary = summary->next)
6926 if (summary->info->tag == tag)
6928 count += summary->toggle_count;
6935 GtkTextLineSegmentClass * last = NULL;
6937 for (line = node->children.line ; line != NULL ;
6940 for (seg = line->segments; seg != NULL;
6943 if ((seg->type == >k_text_toggle_on_type ||
6944 seg->type == >k_text_toggle_off_type) &&
6945 seg->body.toggle.info->tag == tag)
6947 if (last == seg->type)
6948 g_error ("Two consecutive toggles on or off weren't merged");
6949 if (!seg->body.toggle.inNodeCounts)
6950 g_error ("Toggle segment not in the node counts");
6959 if (count != info->toggle_count)
6961 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6962 info->toggle_count, tag->name, count);
6967 g_slist_free (all_tags);
6970 * Call a recursive procedure to do the main body of checks.
6973 node = tree->root_node;
6974 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6977 * Make sure that there are at least two lines in the text and
6978 * that the last line has no characters except a newline.
6981 if (node->num_lines < 2)
6983 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6985 if (node->num_chars < 2)
6987 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6989 while (node->level > 0)
6991 node = node->children.node;
6992 while (node->next != NULL)
6997 line = node->children.line;
6998 while (line->next != NULL)
7002 seg = line->segments;
7003 while ((seg->type == >k_text_toggle_off_type)
7004 || (seg->type == >k_text_right_mark_type)
7005 || (seg->type == >k_text_left_mark_type))
7008 * It's OK to toggle a tag off in the last line, but
7009 * not to start a new range. It's also OK to have marks
7015 if (seg->type != >k_text_char_type)
7017 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7019 if (seg->next != NULL)
7021 g_error ("_gtk_text_btree_check: last line has too many segments");
7023 if (seg->byte_count != 1)
7025 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7028 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7030 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7035 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7036 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7037 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7038 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7041 _gtk_text_btree_spew (GtkTextBTree *tree)
7046 printf ("%d lines in tree %p\n",
7047 _gtk_text_btree_line_count (tree), tree);
7049 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7051 while (line != NULL)
7053 _gtk_text_btree_spew_line (tree, line);
7054 line = _gtk_text_line_next (line);
7057 printf ("=================== Tag information\n");
7062 list = tree->tag_infos;
7064 while (list != NULL)
7066 GtkTextTagInfo *info;
7070 printf (" tag `%s': root at %p, toggle count %d\n",
7071 info->tag->name, info->tag_root, info->toggle_count);
7073 list = g_slist_next (list);
7076 if (tree->tag_infos == NULL)
7078 printf (" (no tags in the tree)\n");
7082 printf ("=================== Tree nodes\n");
7085 _gtk_text_btree_spew_node (tree->root_node, 0);
7090 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7093 GtkTextLineSegment *seg;
7095 spaces = g_strnfill (indent, ' ');
7097 printf ("%sline %p chars %d bytes %d\n",
7099 _gtk_text_line_char_count (line),
7100 _gtk_text_line_byte_count (line));
7102 seg = line->segments;
7105 if (seg->type == >k_text_char_type)
7107 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7112 if (*s == '\n' || *s == '\r')
7116 printf ("%s chars `%s'...\n", spaces, str);
7119 else if (seg->type == >k_text_right_mark_type)
7121 printf ("%s right mark `%s' visible: %d\n",
7123 seg->body.mark.name,
7124 seg->body.mark.visible);
7126 else if (seg->type == >k_text_left_mark_type)
7128 printf ("%s left mark `%s' visible: %d\n",
7130 seg->body.mark.name,
7131 seg->body.mark.visible);
7133 else if (seg->type == >k_text_toggle_on_type ||
7134 seg->type == >k_text_toggle_off_type)
7136 printf ("%s tag `%s' %s\n",
7137 spaces, seg->body.toggle.info->tag->name,
7138 seg->type == >k_text_toggle_off_type ? "off" : "on");
7148 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7151 GtkTextBTreeNode *iter;
7154 spaces = g_strnfill (indent, ' ');
7156 printf ("%snode %p level %d children %d lines %d chars %d\n",
7157 spaces, node, node->level,
7158 node->num_children, node->num_lines, node->num_chars);
7163 printf ("%s %d toggles of `%s' below this node\n",
7164 spaces, s->toggle_count, s->info->tag->name);
7170 if (node->level > 0)
7172 iter = node->children.node;
7173 while (iter != NULL)
7175 _gtk_text_btree_spew_node (iter, indent + 2);
7182 GtkTextLine *line = node->children.line;
7183 while (line != NULL)
7185 _gtk_text_btree_spew_line_short (line, indent + 2);
7193 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7195 GtkTextLineSegment * seg;
7197 printf ("%4d| line: %p parent: %p next: %p\n",
7198 _gtk_text_line_get_number (line), line, line->parent, line->next);
7200 seg = line->segments;
7204 _gtk_text_btree_spew_segment (tree, seg);
7210 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7212 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7213 seg, seg->type->name, seg->byte_count, seg->char_count);
7215 if (seg->type == >k_text_char_type)
7217 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7218 printf (" `%s'\n", str);
7221 else if (seg->type == >k_text_right_mark_type)
7223 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7224 seg->body.mark.name,
7225 seg->body.mark.visible,
7226 seg->body.mark.not_deleteable);
7228 else if (seg->type == >k_text_left_mark_type)
7230 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7231 seg->body.mark.name,
7232 seg->body.mark.visible,
7233 seg->body.mark.not_deleteable);
7235 else if (seg->type == >k_text_toggle_on_type ||
7236 seg->type == >k_text_toggle_off_type)
7238 printf (" tag `%s' priority %d\n",
7239 seg->body.toggle.info->tag->name,
7240 seg->body.toggle.info->tag->priority);