4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
57 #include "gtktextbtree.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
75 * The structure below is used to pass information between
76 * _gtk_text_btree_get_tags and inc_count:
79 typedef struct TagInfo {
80 int numTags; /* Number of tags for which there
81 * is currently information in
83 int arraySize; /* Number of entries allocated for
85 GtkTextTag **tags; /* Array of tags seen so far.
87 int *counts; /* Toggle count (so far) for each
88 * entry in tags. Malloc-ed. */
93 * This is used to store per-view width/height info at the tree nodes.
96 typedef struct _NodeData NodeData;
102 /* Height and width of this node */
104 signed int width : 24;
106 /* boolean indicating whether the lines below this node are in need of validation.
107 * However, width/height should always represent the current total width and
108 * max height for lines below this node; the valid flag indicates whether the
109 * width/height on the lines needs recomputing, not whether the totals
112 guint valid : 8; /* Actually a boolean */
117 * The data structure below keeps summary information about one tag as part
118 * of the tag information in a node.
121 typedef struct Summary {
122 GtkTextTagInfo *info; /* Handle for tag. */
123 int toggle_count; /* Number of transitions into or
124 * out of this tag that occur in
125 * the subtree rooted at this node. */
126 struct Summary *next; /* Next in list of all tags for same
127 * node, or NULL if at end of list. */
131 * The data structure below defines a node in the B-tree.
134 struct _GtkTextBTreeNode {
135 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
136 * this is the root. */
137 GtkTextBTreeNode *next; /* Next in list of siblings with the
138 * same parent node, or NULL for end
140 Summary *summary; /* First in malloc-ed list of info
141 * about tags in this subtree (NULL if
142 * no tag info in the subtree). */
143 int level; /* Level of this node in the B-tree.
144 * 0 refers to the bottom of the tree
145 * (children are lines, not nodes). */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
150 int num_children; /* Number of children of this node. */
151 int num_lines; /* Total number of lines (leaves) in
152 * the subtree rooted here. */
153 int num_chars; /* Number of chars below here */
160 * Used to store the list of views in our btree
163 typedef struct _BTreeView BTreeView;
167 GtkTextLayout *layout;
173 * And the tree itself
176 struct _GtkTextBTree {
177 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
178 GtkTextTagTable *table;
179 GHashTable *mark_table;
181 GtkTextMark *insert_mark;
182 GtkTextMark *selection_bound_mark;
183 GtkTextBuffer *buffer;
186 gulong tag_changed_handler;
188 /* Incremented when a segment with a byte size > 0
189 * is added to or removed from the tree (i.e. the
190 * length of a line may have changed, and lines may
191 * have been added or removed). This invalidates
192 * all outstanding iterators.
194 guint chars_changed_stamp;
195 /* Incremented when any segments are added or deleted;
196 * this makes outstanding iterators recalculate their
197 * pointed-to segment and segment offset.
199 guint segments_changed_stamp;
201 /* Cache the last line in the buffer */
202 GtkTextLine *last_line;
203 guint last_line_stamp;
205 /* Cache the next-to-last line in the buffer,
206 * containing the end iterator
208 GtkTextLine *end_iter_line;
209 GtkTextLineSegment *end_iter_segment;
210 int end_iter_segment_byte_index;
211 int end_iter_segment_char_offset;
212 guint end_iter_line_stamp;
213 guint end_iter_segment_stamp;
215 GHashTable *child_anchor_table;
220 * Upper and lower bounds on how many children a node may have:
221 * rebalance when either of these limits is exceeded. MAX_CHILDREN
222 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
225 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
226 shallow. It appears to be faster to locate a particular line number
227 if the tree is narrow and deep, since it is more finely sorted. I
228 guess this may increase memory use though, and make it slower to
229 walk the tree in order, or locate a particular byte index (which
230 is done by walking the tree in order).
232 There's basically a tradeoff here. However I'm thinking we want to
233 add pixels, byte counts, and char counts to the tree nodes,
234 at that point narrow and deep should speed up all operations,
235 not just the line number searches.
239 #define MAX_CHILDREN 12
240 #define MIN_CHILDREN 6
242 #define MAX_CHILDREN 6
243 #define MIN_CHILDREN 3
250 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
252 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
253 GtkTextBTreeNode *node);
254 static GtkTextLine * get_last_line (GtkTextBTree *tree);
255 static void post_insert_fixup (GtkTextBTree *tree,
256 GtkTextLine *insert_line,
257 gint char_count_delta,
258 gint line_count_delta);
259 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
260 GtkTextTagInfo *info,
262 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
265 static void segments_changed (GtkTextBTree *tree);
266 static void chars_changed (GtkTextBTree *tree);
267 static void summary_list_destroy (Summary *summary);
268 static GtkTextLine *gtk_text_line_new (void);
269 static void gtk_text_line_destroy (GtkTextBTree *tree,
271 static void gtk_text_line_set_parent (GtkTextLine *line,
272 GtkTextBTreeNode *node);
273 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277 static NodeData *node_data_new (gpointer view_id);
278 static void node_data_destroy (NodeData *nd);
279 static void node_data_list_destroy (NodeData *nd);
280 static NodeData *node_data_find (NodeData *nd,
283 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
284 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
285 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
289 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
291 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
294 static void gtk_text_btree_node_remove_view (BTreeView *view,
295 GtkTextBTreeNode *node,
297 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
300 GtkTextBTreeNode *node);
301 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
305 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
309 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
310 GtkTextBTreeNode *node2);
311 static void get_tree_bounds (GtkTextBTree *tree,
314 static void tag_changed_cb (GtkTextTagTable *table,
316 gboolean size_changed,
318 static void cleanup_line (GtkTextLine *line);
319 static void recompute_node_counts (GtkTextBTree *tree,
320 GtkTextBTreeNode *node);
321 static void inc_count (GtkTextTag *tag,
323 TagInfo *tagInfoPtr);
325 static void summary_destroy (Summary *summary);
327 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
328 const GtkTextIter *iter);
329 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
330 GtkTextLineSegment *seg,
334 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
336 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
338 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
341 static void redisplay_region (GtkTextBTree *tree,
342 const GtkTextIter *start,
343 const GtkTextIter *end);
345 /* Inline thingies */
348 segments_changed (GtkTextBTree *tree)
350 tree->segments_changed_stamp += 1;
354 chars_changed (GtkTextBTree *tree)
356 tree->chars_changed_stamp += 1;
364 _gtk_text_btree_new (GtkTextTagTable *table,
365 GtkTextBuffer *buffer)
368 GtkTextBTreeNode *root_node;
369 GtkTextLine *line, *line2;
371 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
372 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
375 * The tree will initially have two empty lines. The second line
376 * isn't actually part of the tree's contents, but its presence
377 * makes several operations easier. The tree will have one GtkTextBTreeNode,
378 * which is also the root of the tree.
381 /* Create the root node. */
383 root_node = gtk_text_btree_node_new ();
385 line = gtk_text_line_new ();
386 line2 = gtk_text_line_new ();
388 root_node->parent = NULL;
389 root_node->next = NULL;
390 root_node->summary = NULL;
391 root_node->level = 0;
392 root_node->children.line = line;
393 root_node->num_children = 2;
394 root_node->num_lines = 2;
395 root_node->num_chars = 2;
397 line->parent = root_node;
400 line->segments = _gtk_char_segment_new ("\n", 1);
402 line2->parent = root_node;
404 line2->segments = _gtk_char_segment_new ("\n", 1);
406 /* Create the tree itself */
408 tree = g_new0(GtkTextBTree, 1);
409 tree->root_node = root_node;
413 /* Set these to values that are unlikely to be found
414 * in random memory garbage, and also avoid
415 * duplicates between tree instances.
417 tree->chars_changed_stamp = g_random_int ();
418 tree->segments_changed_stamp = g_random_int ();
420 tree->last_line_stamp = tree->chars_changed_stamp - 1;
421 tree->last_line = NULL;
423 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
424 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
425 tree->end_iter_line = NULL;
426 tree->end_iter_segment_byte_index = 0;
427 tree->end_iter_segment_char_offset = 0;
429 g_object_ref (tree->table);
431 tree->tag_changed_handler = g_signal_connect (tree->table,
433 G_CALLBACK (tag_changed_cb),
436 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
437 tree->child_anchor_table = NULL;
439 /* We don't ref the buffer, since the buffer owns us;
440 * we'd have some circularity issues. The buffer always
441 * lasts longer than the BTree
443 tree->buffer = buffer;
447 GtkTextLineSegment *seg;
449 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
452 tree->insert_mark = _gtk_text_btree_set_mark (tree,
459 seg = tree->insert_mark->segment;
461 seg->body.mark.not_deleteable = TRUE;
462 seg->body.mark.visible = TRUE;
464 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
471 seg = tree->selection_bound_mark->segment;
473 seg->body.mark.not_deleteable = TRUE;
475 g_object_ref (tree->insert_mark);
476 g_object_ref (tree->selection_bound_mark);
485 _gtk_text_btree_ref (GtkTextBTree *tree)
487 g_return_if_fail (tree != NULL);
488 g_return_if_fail (tree->refcount > 0);
494 _gtk_text_btree_unref (GtkTextBTree *tree)
496 g_return_if_fail (tree != NULL);
497 g_return_if_fail (tree->refcount > 0);
501 if (tree->refcount == 0)
503 g_signal_handler_disconnect (tree->table,
504 tree->tag_changed_handler);
506 g_object_unref (tree->table);
509 gtk_text_btree_node_destroy (tree, tree->root_node);
510 tree->root_node = NULL;
512 g_assert (g_hash_table_size (tree->mark_table) == 0);
513 g_hash_table_destroy (tree->mark_table);
514 tree->mark_table = NULL;
515 if (tree->child_anchor_table != NULL)
517 g_hash_table_destroy (tree->child_anchor_table);
518 tree->child_anchor_table = NULL;
521 g_object_unref (tree->insert_mark);
522 tree->insert_mark = NULL;
523 g_object_unref (tree->selection_bound_mark);
524 tree->selection_bound_mark = NULL;
531 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
537 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
539 return tree->chars_changed_stamp;
543 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
545 return tree->segments_changed_stamp;
549 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
551 g_return_if_fail (tree != NULL);
552 segments_changed (tree);
556 * Indexable segment mutation
560 * The following function is responsible for resolving the bidi direction
561 * for the lines between start and end. But it also calculates any
562 * dependent bidi direction for surrounding lines that change as a result
563 * of the bidi direction decisions within the range. The function is
564 * trying to do as little propagation as is needed.
567 gtk_text_btree_resolve_bidi (GtkTextIter *start,
570 GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
571 GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
572 PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
574 /* Resolve the strong bidi direction for all lines between
577 start_line = _gtk_text_iter_get_text_line (start);
578 start_line_prev = _gtk_text_line_previous (start_line);
579 end_line = _gtk_text_iter_get_text_line (end);
580 end_line_next = _gtk_text_line_next (end_line);
583 while (line && line != end_line_next)
585 /* Loop through the segments and search for a strong character
587 GtkTextLineSegment *seg = line->segments;
588 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
592 if (seg->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);
2852 redisplay_region (tree, ins, bound);
2857 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2862 g_return_if_fail (tree != NULL);
2863 g_return_if_fail (name != NULL);
2865 mark = g_hash_table_lookup (tree->mark_table,
2868 _gtk_text_btree_remove_mark (tree, mark);
2872 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2873 GtkTextLineSegment *segment)
2876 if (segment->body.mark.name)
2877 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2879 segment->body.mark.tree = NULL;
2880 segment->body.mark.line = NULL;
2882 /* Remove the ref on the mark, which frees segment as a side effect
2883 * if this is the last reference.
2885 g_object_unref (segment->body.mark.obj);
2889 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2892 GtkTextLineSegment *segment;
2894 g_return_if_fail (mark != NULL);
2895 g_return_if_fail (tree != NULL);
2897 segment = mark->segment;
2899 if (segment->body.mark.not_deleteable)
2901 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2905 /* This calls cleanup_line and segments_changed */
2906 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2908 _gtk_text_btree_release_mark_segment (tree, segment);
2912 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2913 GtkTextMark *segment)
2915 return segment == tree->insert_mark;
2919 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2920 GtkTextMark *segment)
2922 return segment == tree->selection_bound_mark;
2926 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2929 GtkTextLineSegment *seg;
2931 g_return_val_if_fail (tree != NULL, NULL);
2932 g_return_val_if_fail (name != NULL, NULL);
2934 seg = g_hash_table_lookup (tree->mark_table, name);
2936 return seg ? seg->body.mark.obj : NULL;
2940 * gtk_text_mark_set_visible:
2941 * @mark: a #GtkTextMark
2942 * @setting: visibility of mark
2944 * Sets the visibility of @mark; the insertion point is normally
2945 * visible, i.e. you can see it as a vertical bar. Also, the text
2946 * widget uses a visible mark to indicate where a drop will occur when
2947 * dragging-and-dropping text. Most other marks are not visible.
2948 * Marks are not visible by default.
2952 gtk_text_mark_set_visible (GtkTextMark *mark,
2955 GtkTextLineSegment *seg;
2957 g_return_if_fail (mark != NULL);
2959 seg = mark->segment;
2961 if (seg->body.mark.visible == setting)
2965 seg->body.mark.visible = setting;
2967 redisplay_mark (seg);
2972 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2975 GtkTextBTreeNode *node;
2976 GtkTextTagInfo *info;
2978 g_return_val_if_fail (tree != NULL, NULL);
2982 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2987 if (info->tag_root == NULL)
2990 node = info->tag_root;
2992 /* We know the tag root has instances of the given
2995 continue_outer_loop:
2996 g_assert (node != NULL);
2997 while (node->level > 0)
2999 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3000 node = node->children.node;
3001 while (node != NULL)
3003 if (gtk_text_btree_node_has_tag (node, tag))
3004 goto continue_outer_loop;
3008 g_assert (node != NULL);
3011 g_assert (node != NULL); /* The tag summaries said some node had
3014 g_assert (node->level == 0);
3016 return node->children.line;
3020 /* Looking for any tag at all (tag == NULL).
3021 Unfortunately this can't be done in a simple and efficient way
3022 right now; so I'm just going to return the
3023 first line in the btree. FIXME */
3024 return _gtk_text_btree_get_line (tree, 0, NULL);
3029 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3032 GtkTextBTreeNode *node;
3033 GtkTextBTreeNode *last_node;
3035 GtkTextTagInfo *info;
3037 g_return_val_if_fail (tree != NULL, NULL);
3041 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3043 if (info->tag_root == NULL)
3046 node = info->tag_root;
3047 /* We know the tag root has instances of the given
3050 while (node->level > 0)
3052 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3054 node = node->children.node;
3055 while (node != NULL)
3057 if (gtk_text_btree_node_has_tag (node, tag))
3065 g_assert (node != NULL); /* The tag summaries said some node had
3068 g_assert (node->level == 0);
3070 /* Find the last line in this node */
3071 line = node->children.line;
3072 while (line->next != NULL)
3079 /* This search can't be done efficiently at the moment,
3080 at least not without complexity.
3081 So, we just return the last line.
3083 return _gtk_text_btree_get_end_iter_line (tree);
3093 _gtk_text_line_get_number (GtkTextLine *line)
3096 GtkTextBTreeNode *node, *parent, *node2;
3100 * First count how many lines precede this one in its level-0
3104 node = line->parent;
3106 for (line2 = node->children.line; line2 != line;
3107 line2 = line2->next)
3111 g_error ("gtk_text_btree_line_number couldn't find line");
3117 * Now work up through the levels of the tree one at a time,
3118 * counting how many lines are in GtkTextBTreeNodes preceding the current
3122 for (parent = node->parent ; parent != NULL;
3123 node = parent, parent = parent->parent)
3125 for (node2 = parent->children.node; node2 != node;
3126 node2 = node2->next)
3130 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3132 index += node2->num_lines;
3138 static GtkTextLineSegment*
3139 find_toggle_segment_before_char (GtkTextLine *line,
3143 GtkTextLineSegment *seg;
3144 GtkTextLineSegment *toggle_seg;
3149 seg = line->segments;
3150 while ( (index + seg->char_count) <= char_in_line )
3152 if (((seg->type == >k_text_toggle_on_type)
3153 || (seg->type == >k_text_toggle_off_type))
3154 && (seg->body.toggle.info->tag == tag))
3157 index += seg->char_count;
3164 static GtkTextLineSegment*
3165 find_toggle_segment_before_byte (GtkTextLine *line,
3169 GtkTextLineSegment *seg;
3170 GtkTextLineSegment *toggle_seg;
3175 seg = line->segments;
3176 while ( (index + seg->byte_count) <= byte_in_line )
3178 if (((seg->type == >k_text_toggle_on_type)
3179 || (seg->type == >k_text_toggle_off_type))
3180 && (seg->body.toggle.info->tag == tag))
3183 index += seg->byte_count;
3191 find_toggle_outside_current_line (GtkTextLine *line,
3195 GtkTextBTreeNode *node;
3196 GtkTextLine *sibling_line;
3197 GtkTextLineSegment *seg;
3198 GtkTextLineSegment *toggle_seg;
3200 GtkTextTagInfo *info = NULL;
3203 * No toggle in this line. Look for toggles for the tag in lines
3204 * that are predecessors of line but under the same
3205 * level-0 GtkTextBTreeNode.
3208 sibling_line = line->parent->children.line;
3209 while (sibling_line != line)
3211 seg = sibling_line->segments;
3214 if (((seg->type == >k_text_toggle_on_type)
3215 || (seg->type == >k_text_toggle_off_type))
3216 && (seg->body.toggle.info->tag == tag))
3222 sibling_line = sibling_line->next;
3225 if (toggle_seg != NULL)
3226 return (toggle_seg->type == >k_text_toggle_on_type);
3229 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3230 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3231 * siblings that precede that GtkTextBTreeNode.
3234 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3240 node = line->parent;
3241 while (node->parent != NULL)
3243 GtkTextBTreeNode *sibling_node;
3245 sibling_node = node->parent->children.node;
3246 while (sibling_node != node)
3250 summary = sibling_node->summary;
3251 while (summary != NULL)
3253 if (summary->info == info)
3254 toggles += summary->toggle_count;
3256 summary = summary->next;
3259 sibling_node = sibling_node->next;
3262 if (node == info->tag_root)
3265 node = node->parent;
3269 * An odd number of toggles means that the tag is present at the
3273 return (toggles & 1) != 0;
3276 /* FIXME this function is far too slow, for no good reason. */
3278 _gtk_text_line_char_has_tag (GtkTextLine *line,
3283 GtkTextLineSegment *toggle_seg;
3285 g_return_val_if_fail (line != NULL, FALSE);
3288 * Check for toggles for the tag in the line but before
3289 * the char. If there is one, its type indicates whether or
3290 * not the character is tagged.
3293 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3295 if (toggle_seg != NULL)
3296 return (toggle_seg->type == >k_text_toggle_on_type);
3298 return find_toggle_outside_current_line (line, tree, tag);
3302 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3307 GtkTextLineSegment *toggle_seg;
3309 g_return_val_if_fail (line != NULL, FALSE);
3312 * Check for toggles for the tag in the line but before
3313 * the char. If there is one, its type indicates whether or
3314 * not the character is tagged.
3317 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3319 if (toggle_seg != NULL)
3320 return (toggle_seg->type == >k_text_toggle_on_type);
3322 return find_toggle_outside_current_line (line, tree, tag);
3326 _gtk_text_line_is_last (GtkTextLine *line,
3329 return line == get_last_line (tree);
3333 ensure_end_iter_line (GtkTextBTree *tree)
3335 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3340 /* n_lines is without the magic line at the end */
3341 n_lines = _gtk_text_btree_line_count (tree);
3343 g_assert (n_lines >= 1);
3345 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3347 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3352 ensure_end_iter_segment (GtkTextBTree *tree)
3354 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3356 GtkTextLineSegment *seg;
3357 GtkTextLineSegment *last_with_chars;
3359 ensure_end_iter_line (tree);
3361 last_with_chars = NULL;
3363 seg = tree->end_iter_line->segments;
3366 if (seg->char_count > 0)
3367 last_with_chars = seg;
3371 tree->end_iter_segment = last_with_chars;
3373 /* We know the last char in the last line is '\n' */
3374 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3375 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3377 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3379 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3380 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3385 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3388 ensure_end_iter_line (tree);
3390 return line == tree->end_iter_line;
3394 _gtk_text_btree_is_end (GtkTextBTree *tree,
3396 GtkTextLineSegment *seg,
3400 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3402 /* Do this first to avoid walking segments in most cases */
3403 if (!_gtk_text_line_contains_end_iter (line, tree))
3406 ensure_end_iter_segment (tree);
3408 if (seg != tree->end_iter_segment)
3411 if (byte_index >= 0)
3412 return byte_index == tree->end_iter_segment_byte_index;
3414 return char_offset == tree->end_iter_segment_char_offset;
3418 _gtk_text_line_next (GtkTextLine *line)
3420 GtkTextBTreeNode *node;
3422 if (line->next != NULL)
3427 * This was the last line associated with the particular parent
3428 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3429 * then search down from that GtkTextBTreeNode to find the first
3433 node = line->parent;
3434 while (node != NULL && node->next == NULL)
3435 node = node->parent;
3441 while (node->level > 0)
3443 node = node->children.node;
3446 g_assert (node->children.line != line);
3448 return node->children.line;
3453 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3457 next = _gtk_text_line_next (line);
3459 /* If we were on the end iter line, we can't go to
3462 if (next && next->next == NULL && /* these checks are optimization only */
3463 _gtk_text_line_next (next) == NULL)
3470 _gtk_text_line_previous (GtkTextLine *line)
3472 GtkTextBTreeNode *node;
3473 GtkTextBTreeNode *node2;
3477 * Find the line under this GtkTextBTreeNode just before the starting line.
3479 prev = line->parent->children.line; /* First line at leaf */
3480 while (prev != line)
3482 if (prev->next == line)
3488 g_error ("gtk_text_btree_previous_line ran out of lines");
3492 * This was the first line associated with the particular parent
3493 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3494 * then search down from that GtkTextBTreeNode to find its last line.
3496 for (node = line->parent; ; node = node->parent)
3498 if (node == NULL || node->parent == NULL)
3500 else if (node != node->parent->children.node)
3504 for (node2 = node->parent->children.node; ;
3505 node2 = node2->children.node)
3507 while (node2->next != node)
3508 node2 = node2->next;
3510 if (node2->level == 0)
3516 for (prev = node2->children.line ; ; prev = prev->next)
3518 if (prev->next == NULL)
3522 g_assert_not_reached ();
3528 _gtk_text_line_data_new (GtkTextLayout *layout,
3531 GtkTextLineData *line_data;
3533 line_data = g_new (GtkTextLineData, 1);
3535 line_data->view_id = layout;
3536 line_data->next = NULL;
3537 line_data->width = 0;
3538 line_data->height = 0;
3539 line_data->valid = FALSE;
3545 _gtk_text_line_add_data (GtkTextLine *line,
3546 GtkTextLineData *data)
3548 g_return_if_fail (line != NULL);
3549 g_return_if_fail (data != NULL);
3550 g_return_if_fail (data->view_id != NULL);
3554 data->next = line->views;
3564 _gtk_text_line_remove_data (GtkTextLine *line,
3567 GtkTextLineData *prev;
3568 GtkTextLineData *iter;
3570 g_return_val_if_fail (line != NULL, NULL);
3571 g_return_val_if_fail (view_id != NULL, NULL);
3575 while (iter != NULL)
3577 if (iter->view_id == view_id)
3586 prev->next = iter->next;
3588 line->views = iter->next;
3597 _gtk_text_line_get_data (GtkTextLine *line,
3600 GtkTextLineData *iter;
3602 g_return_val_if_fail (line != NULL, NULL);
3603 g_return_val_if_fail (view_id != NULL, NULL);
3606 while (iter != NULL)
3608 if (iter->view_id == view_id)
3617 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3618 GtkTextLineData *ld)
3620 /* For now this is totally unoptimized. FIXME?
3622 We could probably optimize the case where the width removed
3623 is less than the max width for the parent node,
3624 and the case where the height is unchanged when we re-wrap.
3627 g_return_if_fail (ld != NULL);
3630 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3634 _gtk_text_line_char_count (GtkTextLine *line)
3636 GtkTextLineSegment *seg;
3640 seg = line->segments;
3643 size += seg->char_count;
3650 _gtk_text_line_byte_count (GtkTextLine *line)
3652 GtkTextLineSegment *seg;
3656 seg = line->segments;
3659 size += seg->byte_count;
3667 _gtk_text_line_char_index (GtkTextLine *target_line)
3669 GSList *node_stack = NULL;
3670 GtkTextBTreeNode *iter;
3674 /* Push all our parent nodes onto a stack */
3675 iter = target_line->parent;
3677 g_assert (iter != NULL);
3679 while (iter != NULL)
3681 node_stack = g_slist_prepend (node_stack, iter);
3683 iter = iter->parent;
3686 /* Check that we have the root node on top of the stack. */
3687 g_assert (node_stack != NULL &&
3688 node_stack->data != NULL &&
3689 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3691 /* Add up chars in all nodes before the nodes in our stack.
3695 iter = node_stack->data;
3696 while (iter != NULL)
3698 GtkTextBTreeNode *child_iter;
3699 GtkTextBTreeNode *next_node;
3701 next_node = node_stack->next ?
3702 node_stack->next->data : NULL;
3703 node_stack = g_slist_remove (node_stack, node_stack->data);
3705 if (iter->level == 0)
3707 /* stack should be empty when we're on the last node */
3708 g_assert (node_stack == NULL);
3709 break; /* Our children are now lines */
3712 g_assert (next_node != NULL);
3713 g_assert (iter != NULL);
3714 g_assert (next_node->parent == iter);
3716 /* Add up chars before us in the tree */
3717 child_iter = iter->children.node;
3718 while (child_iter != next_node)
3720 g_assert (child_iter != NULL);
3722 num_chars += child_iter->num_chars;
3724 child_iter = child_iter->next;
3730 g_assert (iter != NULL);
3731 g_assert (iter == target_line->parent);
3733 /* Since we don't store char counts in lines, only in segments, we
3734 have to iterate over the lines adding up segment char counts
3735 until we find our line. */
3736 line = iter->children.line;
3737 while (line != target_line)
3739 g_assert (line != NULL);
3741 num_chars += _gtk_text_line_char_count (line);
3746 g_assert (line == target_line);
3752 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3756 GtkTextLineSegment *seg;
3759 g_return_val_if_fail (line != NULL, NULL);
3761 offset = byte_offset;
3762 seg = line->segments;
3764 while (offset >= seg->byte_count)
3766 g_assert (seg != NULL); /* means an invalid byte index */
3767 offset -= seg->byte_count;
3772 *seg_offset = offset;
3778 _gtk_text_line_char_to_segment (GtkTextLine *line,
3782 GtkTextLineSegment *seg;
3785 g_return_val_if_fail (line != NULL, NULL);
3787 offset = char_offset;
3788 seg = line->segments;
3790 while (offset >= seg->char_count)
3792 g_assert (seg != NULL); /* means an invalid char index */
3793 offset -= seg->char_count;
3798 *seg_offset = offset;
3804 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3808 GtkTextLineSegment *seg;
3811 g_return_val_if_fail (line != NULL, NULL);
3813 offset = byte_offset;
3814 seg = line->segments;
3816 while (offset > 0 && offset >= seg->byte_count)
3818 g_assert (seg != NULL); /* means an invalid byte index */
3819 offset -= seg->byte_count;
3824 *seg_offset = offset;
3830 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3834 GtkTextLineSegment *seg;
3837 g_return_val_if_fail (line != NULL, NULL);
3839 offset = char_offset;
3840 seg = line->segments;
3842 while (offset > 0 && offset >= seg->char_count)
3844 g_assert (seg != NULL); /* means an invalid byte index */
3845 offset -= seg->char_count;
3850 *seg_offset = offset;
3856 _gtk_text_line_byte_to_char (GtkTextLine *line,
3860 GtkTextLineSegment *seg;
3862 g_return_val_if_fail (line != NULL, 0);
3863 g_return_val_if_fail (byte_offset >= 0, 0);
3866 seg = line->segments;
3867 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3868 the next segment) */
3870 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3872 byte_offset -= seg->byte_count;
3873 char_offset += seg->char_count;
3878 g_assert (seg != NULL);
3880 /* Now byte_offset is the offset into the current segment,
3881 and char_offset is the start of the current segment.
3882 Optimize the case where no chars use > 1 byte */
3883 if (seg->byte_count == seg->char_count)
3884 return char_offset + byte_offset;
3887 if (seg->type == >k_text_char_type)
3888 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3891 g_assert (seg->char_count == 1);
3892 g_assert (byte_offset == 0);
3900 _gtk_text_line_char_to_byte (GtkTextLine *line,
3903 g_warning ("FIXME not implemented");
3908 /* FIXME sync with char_locate (or figure out a clean
3909 way to merge the two functions) */
3911 _gtk_text_line_byte_locate (GtkTextLine *line,
3913 GtkTextLineSegment **segment,
3914 GtkTextLineSegment **any_segment,
3915 gint *seg_byte_offset,
3916 gint *line_byte_offset)
3918 GtkTextLineSegment *seg;
3919 GtkTextLineSegment *after_prev_indexable;
3920 GtkTextLineSegment *after_last_indexable;
3921 GtkTextLineSegment *last_indexable;
3925 g_return_val_if_fail (line != NULL, FALSE);
3926 g_return_val_if_fail (byte_offset >= 0, FALSE);
3929 *any_segment = NULL;
3932 offset = byte_offset;
3934 last_indexable = NULL;
3935 after_last_indexable = line->segments;
3936 after_prev_indexable = line->segments;
3937 seg = line->segments;
3939 /* The loop ends when we're inside a segment;
3940 last_indexable refers to the last segment
3941 we passed entirely. */
3942 while (seg && offset >= seg->byte_count)
3944 if (seg->char_count > 0)
3946 offset -= seg->byte_count;
3947 bytes_in_line += seg->byte_count;
3948 last_indexable = seg;
3949 after_prev_indexable = after_last_indexable;
3950 after_last_indexable = last_indexable->next;
3958 /* We went off the end of the line */
3960 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3967 if (after_last_indexable != NULL)
3968 *any_segment = after_last_indexable;
3970 *any_segment = *segment;
3973 /* Override any_segment if we're in the middle of a segment. */
3975 *any_segment = *segment;
3977 *seg_byte_offset = offset;
3979 g_assert (*segment != NULL);
3980 g_assert (*any_segment != NULL);
3981 g_assert (*seg_byte_offset < (*segment)->byte_count);
3983 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3988 /* FIXME sync with byte_locate (or figure out a clean
3989 way to merge the two functions) */
3991 _gtk_text_line_char_locate (GtkTextLine *line,
3993 GtkTextLineSegment **segment,
3994 GtkTextLineSegment **any_segment,
3995 gint *seg_char_offset,
3996 gint *line_char_offset)
3998 GtkTextLineSegment *seg;
3999 GtkTextLineSegment *after_prev_indexable;
4000 GtkTextLineSegment *after_last_indexable;
4001 GtkTextLineSegment *last_indexable;
4005 g_return_val_if_fail (line != NULL, FALSE);
4006 g_return_val_if_fail (char_offset >= 0, FALSE);
4009 *any_segment = NULL;
4012 offset = char_offset;
4014 last_indexable = NULL;
4015 after_last_indexable = line->segments;
4016 after_prev_indexable = line->segments;
4017 seg = line->segments;
4019 /* The loop ends when we're inside a segment;
4020 last_indexable refers to the last segment
4021 we passed entirely. */
4022 while (seg && offset >= seg->char_count)
4024 if (seg->char_count > 0)
4026 offset -= seg->char_count;
4027 chars_in_line += seg->char_count;
4028 last_indexable = seg;
4029 after_prev_indexable = after_last_indexable;
4030 after_last_indexable = last_indexable->next;
4038 /* end of the line */
4040 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4047 if (after_last_indexable != NULL)
4048 *any_segment = after_last_indexable;
4050 *any_segment = *segment;
4053 /* Override any_segment if we're in the middle of a segment. */
4055 *any_segment = *segment;
4057 *seg_char_offset = offset;
4059 g_assert (*segment != NULL);
4060 g_assert (*any_segment != NULL);
4061 g_assert (*seg_char_offset < (*segment)->char_count);
4063 *line_char_offset = chars_in_line + *seg_char_offset;
4069 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4071 gint *line_char_offset,
4072 gint *seg_char_offset)
4074 GtkTextLineSegment *seg;
4077 g_return_if_fail (line != NULL);
4078 g_return_if_fail (byte_offset >= 0);
4080 *line_char_offset = 0;
4082 offset = byte_offset;
4083 seg = line->segments;
4085 while (offset >= seg->byte_count)
4087 offset -= seg->byte_count;
4088 *line_char_offset += seg->char_count;
4090 g_assert (seg != NULL); /* means an invalid char offset */
4093 g_assert (seg->char_count > 0); /* indexable. */
4095 /* offset is now the number of bytes into the current segment we
4096 * want to go. Count chars into the current segment.
4099 if (seg->type == >k_text_char_type)
4101 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4103 g_assert (*seg_char_offset < seg->char_count);
4105 *line_char_offset += *seg_char_offset;
4109 g_assert (offset == 0);
4110 *seg_char_offset = 0;
4115 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4117 gint *line_byte_offset,
4118 gint *seg_byte_offset)
4120 GtkTextLineSegment *seg;
4123 g_return_if_fail (line != NULL);
4124 g_return_if_fail (char_offset >= 0);
4126 *line_byte_offset = 0;
4128 offset = char_offset;
4129 seg = line->segments;
4131 while (offset >= seg->char_count)
4133 offset -= seg->char_count;
4134 *line_byte_offset += seg->byte_count;
4136 g_assert (seg != NULL); /* means an invalid char offset */
4139 g_assert (seg->char_count > 0); /* indexable. */
4141 /* offset is now the number of chars into the current segment we
4142 want to go. Count bytes into the current segment. */
4144 if (seg->type == >k_text_char_type)
4146 *seg_byte_offset = 0;
4150 const char * start = seg->body.chars + *seg_byte_offset;
4152 bytes = g_utf8_next_char (start) - start;
4153 *seg_byte_offset += bytes;
4157 g_assert (*seg_byte_offset < seg->byte_count);
4159 *line_byte_offset += *seg_byte_offset;
4163 g_assert (offset == 0);
4164 *seg_byte_offset = 0;
4169 node_compare (GtkTextBTreeNode *lhs,
4170 GtkTextBTreeNode *rhs)
4172 GtkTextBTreeNode *iter;
4173 GtkTextBTreeNode *node;
4174 GtkTextBTreeNode *common_parent;
4175 GtkTextBTreeNode *parent_of_lower;
4176 GtkTextBTreeNode *parent_of_higher;
4177 gboolean lhs_is_lower;
4178 GtkTextBTreeNode *lower;
4179 GtkTextBTreeNode *higher;
4181 /* This function assumes that lhs and rhs are not underneath each
4188 if (lhs->level < rhs->level)
4190 lhs_is_lower = TRUE;
4196 lhs_is_lower = FALSE;
4201 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4202 * of the common parent we used to reach the common parent; the
4203 * ordering of these child nodes in the child list is the ordering
4207 /* Get on the same level (may be on same level already) */
4209 while (node->level < higher->level)
4210 node = node->parent;
4212 g_assert (node->level == higher->level);
4214 g_assert (node != higher); /* Happens if lower is underneath higher */
4216 /* Go up until we have two children with a common parent.
4218 parent_of_lower = node;
4219 parent_of_higher = higher;
4221 while (parent_of_lower->parent != parent_of_higher->parent)
4223 parent_of_lower = parent_of_lower->parent;
4224 parent_of_higher = parent_of_higher->parent;
4227 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4229 common_parent = parent_of_lower->parent;
4231 g_assert (common_parent != NULL);
4233 /* See which is first in the list of common_parent's children */
4234 iter = common_parent->children.node;
4235 while (iter != NULL)
4237 if (iter == parent_of_higher)
4239 /* higher is less than lower */
4242 return 1; /* lhs > rhs */
4246 else if (iter == parent_of_lower)
4248 /* lower is less than higher */
4251 return -1; /* lhs < rhs */
4259 g_assert_not_reached ();
4263 /* remember that tag == NULL means "any tag" */
4265 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4269 GtkTextBTreeNode *node;
4270 GtkTextTagInfo *info;
4271 gboolean below_tag_root;
4273 g_return_val_if_fail (line != NULL, NULL);
4275 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4276 _gtk_text_btree_check (tree);
4280 /* Right now we can only offer linear-search if the user wants
4281 * to know about any tag toggle at all.
4283 return _gtk_text_line_next_excluding_last (line);
4286 /* Our tag summaries only have node precision, not line
4287 * precision. This means that if any line under a node could contain a
4288 * tag, then any of the others could also contain a tag.
4290 * In the future we could have some mechanism to keep track of how
4291 * many toggles we've found under a node so far, since we have a
4292 * count of toggles under the node. But for now I'm going with KISS.
4295 /* return same-node line, if any. */
4299 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4303 if (info->tag_root == NULL)
4306 if (info->tag_root == line->parent)
4307 return NULL; /* we were at the last line under the tag root */
4309 /* We need to go up out of this node, and on to the next one with
4310 toggles for the target tag. If we're below the tag root, we need to
4311 find the next node below the tag root that has tag summaries. If
4312 we're not below the tag root, we need to see if the tag root is
4313 after us in the tree, and if so, return the first line underneath
4316 node = line->parent;
4317 below_tag_root = FALSE;
4318 while (node != NULL)
4320 if (node == info->tag_root)
4322 below_tag_root = TRUE;
4326 node = node->parent;
4331 node = line->parent;
4332 while (node != info->tag_root)
4334 if (node->next == NULL)
4335 node = node->parent;
4340 if (gtk_text_btree_node_has_tag (node, tag))
4350 ordering = node_compare (line->parent, info->tag_root);
4354 /* Tag root is ahead of us, so search there. */
4355 node = info->tag_root;
4360 /* Tag root is after us, so no more lines that
4361 * could contain the tag.
4366 g_assert_not_reached ();
4371 g_assert (node != NULL);
4373 /* We have to find the first sub-node of this node that contains
4377 while (node->level > 0)
4379 g_assert (node != NULL); /* If this fails, it likely means an
4380 incorrect tag summary led us on a
4381 wild goose chase down this branch of
4383 node = node->children.node;
4384 while (node != NULL)
4386 if (gtk_text_btree_node_has_tag (node, tag))
4392 g_assert (node != NULL);
4393 g_assert (node->level == 0);
4395 return node->children.line;
4399 prev_line_under_node (GtkTextBTreeNode *node,
4404 prev = node->children.line;
4410 while (prev->next != line)
4420 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4424 GtkTextBTreeNode *node;
4425 GtkTextBTreeNode *found_node = NULL;
4426 GtkTextTagInfo *info;
4427 gboolean below_tag_root;
4429 GtkTextBTreeNode *line_ancestor;
4430 GtkTextBTreeNode *line_ancestor_parent;
4432 /* See next_could_contain_tag () for more extensive comments
4433 * on what's going on here.
4436 g_return_val_if_fail (line != NULL, NULL);
4438 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4439 _gtk_text_btree_check (tree);
4443 /* Right now we can only offer linear-search if the user wants
4444 * to know about any tag toggle at all.
4446 return _gtk_text_line_previous (line);
4449 /* Return same-node line, if any. */
4450 prev = prev_line_under_node (line->parent, line);
4454 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4458 if (info->tag_root == NULL)
4461 if (info->tag_root == line->parent)
4462 return NULL; /* we were at the first line under the tag root */
4464 /* Are we below the tag root */
4465 node = line->parent;
4466 below_tag_root = FALSE;
4467 while (node != NULL)
4469 if (node == info->tag_root)
4471 below_tag_root = TRUE;
4475 node = node->parent;
4480 /* Look for a previous node under this tag root that has our
4484 /* this assertion holds because line->parent is not the
4485 * tag root, we are below the tag root, and the tag
4488 g_assert (line->parent->parent != NULL);
4490 line_ancestor = line->parent;
4491 line_ancestor_parent = line->parent->parent;
4493 while (line_ancestor != info->tag_root)
4495 GSList *child_nodes = NULL;
4498 /* Create reverse-order list of nodes before
4501 if (line_ancestor_parent != NULL)
4502 node = line_ancestor_parent->children.node;
4504 node = line_ancestor;
4506 while (node != line_ancestor && node != NULL)
4508 child_nodes = g_slist_prepend (child_nodes, node);
4513 /* Try to find a node with our tag on it in the list */
4517 GtkTextBTreeNode *this_node = tmp->data;
4519 g_assert (this_node != line_ancestor);
4521 if (gtk_text_btree_node_has_tag (this_node, tag))
4523 found_node = this_node;
4524 g_slist_free (child_nodes);
4528 tmp = g_slist_next (tmp);
4531 g_slist_free (child_nodes);
4533 /* Didn't find anything on this level; go up one level. */
4534 line_ancestor = line_ancestor_parent;
4535 line_ancestor_parent = line_ancestor->parent;
4545 ordering = node_compare (line->parent, info->tag_root);
4549 /* Tag root is ahead of us, so no more lines
4556 /* Tag root is after us, so grab last tagged
4557 * line underneath the tag root.
4559 found_node = info->tag_root;
4563 g_assert_not_reached ();
4568 g_assert (found_node != NULL);
4570 /* We have to find the last sub-node of this node that contains
4575 while (node->level > 0)
4577 GSList *child_nodes = NULL;
4579 g_assert (node != NULL); /* If this fails, it likely means an
4580 incorrect tag summary led us on a
4581 wild goose chase down this branch of
4584 node = node->children.node;
4585 while (node != NULL)
4587 child_nodes = g_slist_prepend (child_nodes, node);
4591 node = NULL; /* detect failure to find a child node. */
4594 while (iter != NULL)
4596 if (gtk_text_btree_node_has_tag (iter->data, tag))
4598 /* recurse into this node. */
4603 iter = g_slist_next (iter);
4606 g_slist_free (child_nodes);
4608 g_assert (node != NULL);
4611 g_assert (node != NULL);
4612 g_assert (node->level == 0);
4614 /* this assertion is correct, but slow. */
4615 /* g_assert (node_compare (node, line->parent) < 0); */
4617 /* Return last line in this node. */
4619 prev = node->children.line;
4627 * Non-public function implementations
4631 summary_list_destroy (Summary *summary)
4634 while (summary != NULL)
4636 next = summary->next;
4637 summary_destroy (summary);
4643 get_last_line (GtkTextBTree *tree)
4645 if (tree->last_line_stamp != tree->chars_changed_stamp)
4651 n_lines = _gtk_text_btree_line_count (tree);
4653 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4655 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4657 tree->last_line_stamp = tree->chars_changed_stamp;
4658 tree->last_line = line;
4661 return tree->last_line;
4669 gtk_text_line_new (void)
4673 line = g_new0(GtkTextLine, 1);
4674 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4675 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4676 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4682 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4684 GtkTextLineData *ld;
4685 GtkTextLineData *next;
4687 g_return_if_fail (line != NULL);
4694 view = gtk_text_btree_get_view (tree, ld->view_id);
4696 g_assert (view != NULL);
4699 gtk_text_layout_free_line_data (view->layout, line, ld);
4708 gtk_text_line_set_parent (GtkTextLine *line,
4709 GtkTextBTreeNode *node)
4711 if (line->parent == node)
4713 line->parent = node;
4714 gtk_text_btree_node_invalidate_upward (node, NULL);
4718 cleanup_line (GtkTextLine *line)
4720 GtkTextLineSegment *seg, **prev_p;
4724 * Make a pass over all of the segments in the line, giving each
4725 * a chance to clean itself up. This could potentially change
4726 * the structure of the line, e.g. by merging two segments
4727 * together or having two segments cancel themselves; if so,
4728 * then repeat the whole process again, since the first structure
4729 * change might make other structure changes possible. Repeat
4730 * until eventually there are no changes.
4737 for (prev_p = &line->segments, seg = *prev_p;
4739 prev_p = &(*prev_p)->next, seg = *prev_p)
4741 if (seg->type->cleanupFunc != NULL)
4743 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4756 node_data_new (gpointer view_id)
4760 nd = g_new (NodeData, 1);
4762 nd->view_id = view_id;
4772 node_data_destroy (NodeData *nd)
4778 node_data_list_destroy (NodeData *nd)
4784 while (iter != NULL)
4787 node_data_destroy (iter);
4793 node_data_find (NodeData *nd, gpointer view_id)
4797 if (nd->view_id == view_id)
4805 summary_destroy (Summary *summary)
4807 /* Fill with error-triggering garbage */
4808 summary->info = (void*)0x1;
4809 summary->toggle_count = 567;
4810 summary->next = (void*)0x1;
4814 static GtkTextBTreeNode*
4815 gtk_text_btree_node_new (void)
4817 GtkTextBTreeNode *node;
4819 node = g_new (GtkTextBTreeNode, 1);
4821 node->node_data = NULL;
4827 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4828 GtkTextTagInfo *info,
4833 summary = node->summary;
4834 while (summary != NULL)
4836 if (summary->info == info)
4838 summary->toggle_count += adjust;
4842 summary = summary->next;
4845 if (summary == NULL)
4847 /* didn't find a summary for our tag. */
4848 g_return_if_fail (adjust > 0);
4849 summary = g_new (Summary, 1);
4850 summary->info = info;
4851 summary->toggle_count = adjust;
4852 summary->next = node->summary;
4853 node->summary = summary;
4857 /* Note that the tag root and above do not have summaries
4858 for the tag; only nodes below the tag root have
4861 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4865 summary = node->summary;
4866 while (summary != NULL)
4869 summary->info->tag == tag)
4872 summary = summary->next;
4878 /* Add node and all children to the damage region. */
4880 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4884 nd = node->node_data;
4891 if (node->level == 0)
4895 line = node->children.line;
4896 while (line != NULL)
4898 GtkTextLineData *ld;
4912 GtkTextBTreeNode *child;
4914 child = node->children.node;
4916 while (child != NULL)
4918 gtk_text_btree_node_invalidate_downward (child);
4920 child = child->next;
4926 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4928 GtkTextBTreeNode *iter;
4931 while (iter != NULL)
4937 nd = node_data_find (iter->node_data, view_id);
4939 if (nd == NULL || !nd->valid)
4940 break; /* Once a node is invalid, we know its parents are as well. */
4946 gboolean should_continue = FALSE;
4948 nd = iter->node_data;
4953 should_continue = TRUE;
4960 if (!should_continue)
4961 break; /* This node was totally invalidated, so are its
4965 iter = iter->parent;
4971 * _gtk_text_btree_is_valid:
4972 * @tree: a #GtkTextBTree
4973 * @view_id: ID for the view
4975 * Check to see if the entire #GtkTextBTree is valid or not for
4978 * Return value: %TRUE if the entire #GtkTextBTree is valid
4981 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4985 g_return_val_if_fail (tree != NULL, FALSE);
4987 nd = node_data_find (tree->root_node->node_data, view_id);
4988 return (nd && nd->valid);
4991 typedef struct _ValidateState ValidateState;
4993 struct _ValidateState
4995 gint remaining_pixels;
4996 gboolean in_validation;
5003 gtk_text_btree_node_validate (BTreeView *view,
5004 GtkTextBTreeNode *node,
5006 ValidateState *state)
5008 gint node_valid = TRUE;
5009 gint node_width = 0;
5010 gint node_height = 0;
5012 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5013 g_return_if_fail (!nd->valid);
5015 if (node->level == 0)
5017 GtkTextLine *line = node->children.line;
5018 GtkTextLineData *ld;
5020 /* Iterate over leading valid lines */
5021 while (line != NULL)
5023 ld = _gtk_text_line_get_data (line, view_id);
5025 if (!ld || !ld->valid)
5027 else if (state->in_validation)
5029 state->in_validation = FALSE;
5034 state->y += ld->height;
5035 node_width = MAX (ld->width, node_width);
5036 node_height += ld->height;
5042 state->in_validation = TRUE;
5044 /* Iterate over invalid lines */
5045 while (line != NULL)
5047 ld = _gtk_text_line_get_data (line, view_id);
5049 if (ld && ld->valid)
5054 state->old_height += ld->height;
5055 ld = gtk_text_layout_wrap (view->layout, line, ld);
5056 state->new_height += ld->height;
5058 node_width = MAX (ld->width, node_width);
5059 node_height += ld->height;
5061 state->remaining_pixels -= ld->height;
5062 if (state->remaining_pixels <= 0)
5072 /* Iterate over the remaining lines */
5073 while (line != NULL)
5075 ld = _gtk_text_line_get_data (line, view_id);
5076 state->in_validation = FALSE;
5078 if (!ld || !ld->valid)
5083 node_width = MAX (ld->width, node_width);
5084 node_height += ld->height;
5092 GtkTextBTreeNode *child;
5095 child = node->children.node;
5097 /* Iterate over leading valid nodes */
5100 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5102 if (!child_nd->valid)
5104 else if (state->in_validation)
5106 state->in_validation = FALSE;
5111 state->y += child_nd->height;
5112 node_width = MAX (node_width, child_nd->width);
5113 node_height += child_nd->height;
5116 child = child->next;
5119 /* Iterate over invalid nodes */
5122 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5124 if (child_nd->valid)
5128 gtk_text_btree_node_validate (view, child, view_id, state);
5130 if (!child_nd->valid)
5132 node_width = MAX (node_width, child_nd->width);
5133 node_height += child_nd->height;
5135 if (!state->in_validation || state->remaining_pixels <= 0)
5137 child = child->next;
5142 child = child->next;
5145 /* Iterate over the remaining lines */
5148 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5149 state->in_validation = FALSE;
5151 if (!child_nd->valid)
5154 node_width = MAX (child_nd->width, node_width);
5155 node_height += child_nd->height;
5157 child = child->next;
5161 nd->width = node_width;
5162 nd->height = node_height;
5163 nd->valid = node_valid;
5167 * _gtk_text_btree_validate:
5168 * @tree: a #GtkTextBTree
5170 * @max_pixels: the maximum number of pixels to validate. (No more
5171 * than one paragraph beyond this limit will be validated)
5172 * @y: location to store starting y coordinate of validated region
5173 * @old_height: location to store old height of validated region
5174 * @new_height: location to store new height of validated region
5176 * Validate a single contiguous invalid region of a #GtkTextBTree for
5179 * Return value: %TRUE if a region has been validated, %FALSE if the
5180 * entire tree was already valid.
5183 _gtk_text_btree_validate (GtkTextBTree *tree,
5192 g_return_val_if_fail (tree != NULL, FALSE);
5194 view = gtk_text_btree_get_view (tree, view_id);
5195 g_return_val_if_fail (view != NULL, FALSE);
5197 if (!_gtk_text_btree_is_valid (tree, view_id))
5199 ValidateState state;
5201 state.remaining_pixels = max_pixels;
5202 state.in_validation = FALSE;
5204 state.old_height = 0;
5205 state.new_height = 0;
5207 gtk_text_btree_node_validate (view,
5214 *old_height = state.old_height;
5216 *new_height = state.new_height;
5218 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5219 _gtk_text_btree_check (tree);
5228 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5232 gboolean *valid_out)
5236 gboolean valid = TRUE;
5238 if (node->level == 0)
5240 GtkTextLine *line = node->children.line;
5242 while (line != NULL)
5244 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5246 if (!ld || !ld->valid)
5251 width = MAX (ld->width, width);
5252 height += ld->height;
5260 GtkTextBTreeNode *child = node->children.node;
5264 NodeData *child_nd = node_data_find (child->node_data, view_id);
5266 if (!child_nd || !child_nd->valid)
5271 width = MAX (child_nd->width, width);
5272 height += child_nd->height;
5275 child = child->next;
5280 *height_out = height;
5285 /* Recompute the validity and size of the view data for a given
5286 * view at this node from the immediate children of the node
5289 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5292 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5297 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5298 &width, &height, &valid);
5300 nd->height = height;
5307 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5312 gtk_text_btree_node_check_valid (node, view_id);
5313 node = node->parent;
5318 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5321 if (node->level == 0)
5323 return gtk_text_btree_node_check_valid (node, view_id);
5327 GtkTextBTreeNode *child = node->children.node;
5329 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5337 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5339 if (!child_nd->valid)
5341 nd->width = MAX (child_nd->width, nd->width);
5342 nd->height += child_nd->height;
5344 child = child->next;
5353 * _gtk_text_btree_validate_line:
5354 * @tree: a #GtkTextBTree
5355 * @line: line to validate
5356 * @view_id: view ID for the view to validate
5358 * Revalidate a single line of the btree for the given view, propagate
5359 * results up through the entire tree.
5362 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5366 GtkTextLineData *ld;
5369 g_return_if_fail (tree != NULL);
5370 g_return_if_fail (line != NULL);
5372 view = gtk_text_btree_get_view (tree, view_id);
5373 g_return_if_fail (view != NULL);
5375 ld = _gtk_text_line_get_data (line, view_id);
5376 if (!ld || !ld->valid)
5378 ld = gtk_text_layout_wrap (view->layout, line, ld);
5380 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5385 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5387 if (node->level == 0)
5391 line = node->children.line;
5392 while (line != NULL)
5394 GtkTextLineData *ld;
5396 ld = _gtk_text_line_remove_data (line, view_id);
5399 gtk_text_layout_free_line_data (view->layout, line, ld);
5406 GtkTextBTreeNode *child;
5408 child = node->children.node;
5410 while (child != NULL)
5413 gtk_text_btree_node_remove_view (view, child, view_id);
5415 child = child->next;
5419 gtk_text_btree_node_remove_data (node, view_id);
5423 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5425 if (node->level == 0)
5428 GtkTextLineSegment *seg;
5430 while (node->children.line != NULL)
5432 line = node->children.line;
5433 node->children.line = line->next;
5434 while (line->segments != NULL)
5436 seg = line->segments;
5437 line->segments = seg->next;
5439 (*seg->type->deleteFunc) (seg, line, TRUE);
5441 gtk_text_line_destroy (tree, line);
5446 GtkTextBTreeNode *childPtr;
5448 while (node->children.node != NULL)
5450 childPtr = node->children.node;
5451 node->children.node = childPtr->next;
5452 gtk_text_btree_node_destroy (tree, childPtr);
5456 gtk_text_btree_node_free_empty (tree, node);
5460 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5461 GtkTextBTreeNode *node)
5463 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5464 (node->level == 0 && node->children.line == NULL));
5466 summary_list_destroy (node->summary);
5467 node_data_list_destroy (node->node_data);
5472 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5476 nd = node->node_data;
5479 if (nd->view_id == view_id)
5487 nd = node_data_new (view_id);
5489 if (node->node_data)
5490 nd->next = node->node_data;
5492 node->node_data = nd;
5499 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5505 nd = node->node_data;
5508 if (nd->view_id == view_id)
5519 prev->next = nd->next;
5521 if (node->node_data == nd)
5522 node->node_data = nd->next;
5526 node_data_destroy (nd);
5530 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5531 gint *width, gint *height)
5535 g_return_if_fail (width != NULL);
5536 g_return_if_fail (height != NULL);
5538 nd = gtk_text_btree_node_ensure_data (node, view_id);
5543 *height = nd->height;
5546 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5547 * here isn't quite right, since for a lot of operations we want to
5548 * know which children of the common parent correspond to the two nodes
5549 * (e.g., when computing the order of two iters)
5551 static GtkTextBTreeNode *
5552 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5553 GtkTextBTreeNode *node2)
5555 while (node1->level < node2->level)
5556 node1 = node1->parent;
5557 while (node2->level < node1->level)
5558 node2 = node2->parent;
5559 while (node1 != node2)
5561 node1 = node1->parent;
5562 node2 = node2->parent;
5573 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5578 while (view != NULL)
5580 if (view->view_id == view_id)
5589 get_tree_bounds (GtkTextBTree *tree,
5593 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5594 _gtk_text_btree_get_end_iter (tree, end);
5598 tag_changed_cb (GtkTextTagTable *table,
5600 gboolean size_changed,
5605 /* We need to queue a relayout on all regions that are tagged with
5612 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5614 /* Must be a last toggle if there was a first one. */
5615 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5616 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5617 _gtk_text_btree_invalidate_region (tree,
5624 /* We only need to queue a redraw, not a relayout */
5629 while (view != NULL)
5633 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5634 gtk_text_layout_changed (view->layout, 0, height, height);
5642 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5645 /* Remove the tag from the tree */
5650 get_tree_bounds (tree, &start, &end);
5652 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5653 gtk_text_btree_remove_tag_info (tree, tag);
5657 /* Rebalance the out-of-whack node "node" */
5659 gtk_text_btree_rebalance (GtkTextBTree *tree,
5660 GtkTextBTreeNode *node)
5663 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5664 * up through the tree one GtkTextBTreeNode at a time until the root
5665 * GtkTextBTreeNode has been processed.
5668 while (node != NULL)
5670 GtkTextBTreeNode *new_node, *child;
5675 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5676 * then split off all but the first MIN_CHILDREN into a separate
5677 * GtkTextBTreeNode following the original one. Then repeat until the
5678 * GtkTextBTreeNode has a decent size.
5681 if (node->num_children > MAX_CHILDREN)
5686 * If the GtkTextBTreeNode being split is the root
5687 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5691 if (node->parent == NULL)
5693 new_node = gtk_text_btree_node_new ();
5694 new_node->parent = NULL;
5695 new_node->next = NULL;
5696 new_node->summary = NULL;
5697 new_node->level = node->level + 1;
5698 new_node->children.node = node;
5699 recompute_node_counts (tree, new_node);
5700 tree->root_node = new_node;
5702 new_node = gtk_text_btree_node_new ();
5703 new_node->parent = node->parent;
5704 new_node->next = node->next;
5705 node->next = new_node;
5706 new_node->summary = NULL;
5707 new_node->level = node->level;
5708 new_node->num_children = node->num_children - MIN_CHILDREN;
5709 if (node->level == 0)
5711 for (i = MIN_CHILDREN-1,
5712 line = node->children.line;
5713 i > 0; i--, line = line->next)
5715 /* Empty loop body. */
5717 new_node->children.line = line->next;
5722 for (i = MIN_CHILDREN-1,
5723 child = node->children.node;
5724 i > 0; i--, child = child->next)
5726 /* Empty loop body. */
5728 new_node->children.node = child->next;
5731 recompute_node_counts (tree, node);
5732 node->parent->num_children++;
5734 if (node->num_children <= MAX_CHILDREN)
5736 recompute_node_counts (tree, node);
5742 while (node->num_children < MIN_CHILDREN)
5744 GtkTextBTreeNode *other;
5745 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5746 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5747 int total_children, first_children, i;
5750 * Too few children for this GtkTextBTreeNode. If this is the root then,
5751 * it's OK for it to have less than MIN_CHILDREN children
5752 * as long as it's got at least two. If it has only one
5753 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5754 * the tree and use its child as the new root.
5757 if (node->parent == NULL)
5759 if ((node->num_children == 1) && (node->level > 0))
5761 tree->root_node = node->children.node;
5762 tree->root_node->parent = NULL;
5764 node->children.node = NULL;
5765 gtk_text_btree_node_free_empty (tree, node);
5771 * Not the root. Make sure that there are siblings to
5775 if (node->parent->num_children < 2)
5777 gtk_text_btree_rebalance (tree, node->parent);
5782 * Find a sibling neighbor to borrow from, and arrange for
5783 * node to be the earlier of the pair.
5786 if (node->next == NULL)
5788 for (other = node->parent->children.node;
5789 other->next != node;
5790 other = other->next)
5792 /* Empty loop body. */
5799 * We're going to either merge the two siblings together
5800 * into one GtkTextBTreeNode or redivide the children among them to
5801 * balance their loads. As preparation, join their two
5802 * child lists into a single list and remember the half-way
5803 * point in the list.
5806 total_children = node->num_children + other->num_children;
5807 first_children = total_children/2;
5808 if (node->children.node == NULL)
5810 node->children = other->children;
5811 other->children.node = NULL;
5812 other->children.line = NULL;
5814 if (node->level == 0)
5818 for (line = node->children.line, i = 1;
5820 line = line->next, i++)
5822 if (i == first_children)
5827 line->next = other->children.line;
5828 while (i <= first_children)
5837 GtkTextBTreeNode *child;
5839 for (child = node->children.node, i = 1;
5840 child->next != NULL;
5841 child = child->next, i++)
5843 if (i <= first_children)
5845 if (i == first_children)
5847 halfwaynode = child;
5851 child->next = other->children.node;
5852 while (i <= first_children)
5854 halfwaynode = child;
5855 child = child->next;
5861 * If the two siblings can simply be merged together, do it.
5864 if (total_children <= MAX_CHILDREN)
5866 recompute_node_counts (tree, node);
5867 node->next = other->next;
5868 node->parent->num_children--;
5870 other->children.node = NULL;
5871 other->children.line = NULL;
5872 gtk_text_btree_node_free_empty (tree, other);
5877 * The siblings can't be merged, so just divide their
5878 * children evenly between them.
5881 if (node->level == 0)
5883 other->children.line = halfwayline->next;
5884 halfwayline->next = NULL;
5888 other->children.node = halfwaynode->next;
5889 halfwaynode->next = NULL;
5892 recompute_node_counts (tree, node);
5893 recompute_node_counts (tree, other);
5896 node = node->parent;
5901 post_insert_fixup (GtkTextBTree *tree,
5903 gint line_count_delta,
5904 gint char_count_delta)
5907 GtkTextBTreeNode *node;
5910 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5911 * point, then rebalance the tree if necessary.
5914 for (node = line->parent ; node != NULL;
5915 node = node->parent)
5917 node->num_lines += line_count_delta;
5918 node->num_chars += char_count_delta;
5920 node = line->parent;
5921 node->num_children += line_count_delta;
5923 if (node->num_children > MAX_CHILDREN)
5925 gtk_text_btree_rebalance (tree, node);
5928 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5929 _gtk_text_btree_check (tree);
5932 static GtkTextTagInfo*
5933 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5936 GtkTextTagInfo *info;
5940 list = tree->tag_infos;
5941 while (list != NULL)
5944 if (info->tag == tag)
5947 list = g_slist_next (list);
5953 static GtkTextTagInfo*
5954 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5957 GtkTextTagInfo *info;
5959 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5963 /* didn't find it, create. */
5965 info = g_new (GtkTextTagInfo, 1);
5969 info->tag_root = NULL;
5970 info->toggle_count = 0;
5972 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5975 g_print ("Created tag info %p for tag %s(%p)\n",
5976 info, info->tag->name ? info->tag->name : "anon",
5985 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5988 GtkTextTagInfo *info;
5993 list = tree->tag_infos;
5994 while (list != NULL)
5997 if (info->tag == tag)
6000 g_print ("Removing tag info %p for tag %s(%p)\n",
6001 info, info->tag->name ? info->tag->name : "anon",
6007 prev->next = list->next;
6011 tree->tag_infos = list->next;
6014 g_slist_free (list);
6016 g_object_unref (info->tag);
6023 list = g_slist_next (list);
6028 recompute_level_zero_counts (GtkTextBTreeNode *node)
6031 GtkTextLineSegment *seg;
6033 g_assert (node->level == 0);
6035 line = node->children.line;
6036 while (line != NULL)
6038 node->num_children++;
6041 if (line->parent != node)
6042 gtk_text_line_set_parent (line, node);
6044 seg = line->segments;
6048 node->num_chars += seg->char_count;
6050 if (((seg->type != >k_text_toggle_on_type)
6051 && (seg->type != >k_text_toggle_off_type))
6052 || !(seg->body.toggle.inNodeCounts))
6058 GtkTextTagInfo *info;
6060 info = seg->body.toggle.info;
6062 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6073 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6076 GtkTextBTreeNode *child;
6078 g_assert (node->level > 0);
6080 child = node->children.node;
6081 while (child != NULL)
6083 node->num_children += 1;
6084 node->num_lines += child->num_lines;
6085 node->num_chars += child->num_chars;
6087 if (child->parent != node)
6089 child->parent = node;
6090 gtk_text_btree_node_invalidate_upward (node, NULL);
6093 summary = child->summary;
6094 while (summary != NULL)
6096 gtk_text_btree_node_adjust_toggle_count (node,
6098 summary->toggle_count);
6100 summary = summary->next;
6103 child = child->next;
6108 *----------------------------------------------------------------------
6110 * recompute_node_counts --
6112 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6113 * (tags, child information, etc.) by scanning the information in
6114 * its descendants. This procedure is called during rebalancing
6115 * when a GtkTextBTreeNode's child structure has changed.
6121 * The tag counts for node are modified to reflect its current
6122 * child structure, as are its num_children, num_lines, num_chars fields.
6123 * Also, all of the childrens' parent fields are made to point
6126 *----------------------------------------------------------------------
6130 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6133 Summary *summary, *summary2;
6136 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6137 * the existing Summary records (most of them will probably be reused).
6140 summary = node->summary;
6141 while (summary != NULL)
6143 summary->toggle_count = 0;
6144 summary = summary->next;
6147 node->num_children = 0;
6148 node->num_lines = 0;
6149 node->num_chars = 0;
6152 * Scan through the children, adding the childrens' tag counts into
6153 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6157 if (node->level == 0)
6158 recompute_level_zero_counts (node);
6160 recompute_level_nonzero_counts (node);
6165 gtk_text_btree_node_check_valid (node, view->view_id);
6170 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6171 * records that still have a zero count, or that have all the toggles.
6172 * The GtkTextBTreeNode with the children that account for all the tags toggles
6173 * have no summary information, and they become the tag_root for the tag.
6177 for (summary = node->summary; summary != NULL; )
6179 if (summary->toggle_count > 0 &&
6180 summary->toggle_count < summary->info->toggle_count)
6182 if (node->level == summary->info->tag_root->level)
6185 * The tag's root GtkTextBTreeNode split and some toggles left.
6186 * The tag root must move up a level.
6188 summary->info->tag_root = node->parent;
6191 summary = summary->next;
6194 if (summary->toggle_count == summary->info->toggle_count)
6197 * A GtkTextBTreeNode merge has collected all the toggles under
6198 * one GtkTextBTreeNode. Push the root down to this level.
6200 summary->info->tag_root = node;
6202 if (summary2 != NULL)
6204 summary2->next = summary->next;
6205 summary_destroy (summary);
6206 summary = summary2->next;
6210 node->summary = summary->next;
6211 summary_destroy (summary);
6212 summary = node->summary;
6218 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6219 GtkTextTagInfo *info,
6220 gint delta) /* may be negative */
6222 Summary *summary, *prevPtr;
6223 GtkTextBTreeNode *node2Ptr;
6224 int rootLevel; /* Level of original tag root */
6226 info->toggle_count += delta;
6228 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6230 info->tag_root = node;
6235 * Note the level of the existing root for the tag so we can detect
6236 * if it needs to be moved because of the toggle count change.
6239 rootLevel = info->tag_root->level;
6242 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6243 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6247 for ( ; node != info->tag_root; node = node->parent)
6250 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6251 * perhaps all we have to do is adjust its count.
6254 for (prevPtr = NULL, summary = node->summary;
6256 prevPtr = summary, summary = summary->next)
6258 if (summary->info == info)
6263 if (summary != NULL)
6265 summary->toggle_count += delta;
6266 if (summary->toggle_count > 0 &&
6267 summary->toggle_count < info->toggle_count)
6271 if (summary->toggle_count != 0)
6274 * Should never find a GtkTextBTreeNode with max toggle count at this
6275 * point (there shouldn't have been a summary entry in the
6279 g_error ("%s: bad toggle count (%d) max (%d)",
6280 G_STRLOC, summary->toggle_count, info->toggle_count);
6284 * Zero toggle count; must remove this tag from the list.
6287 if (prevPtr == NULL)
6289 node->summary = summary->next;
6293 prevPtr->next = summary->next;
6295 summary_destroy (summary);
6300 * This tag isn't currently in the summary information list.
6303 if (rootLevel == node->level)
6307 * The old tag root is at the same level in the tree as this
6308 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6309 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6310 * as well as the old root (if not, we'll move it up again
6311 * the next time through the loop). To push it up one level
6312 * we copy the original toggle count into the summary
6313 * information at the old root and change the root to its
6314 * parent GtkTextBTreeNode.
6317 GtkTextBTreeNode *rootnode = info->tag_root;
6318 summary = (Summary *) g_malloc (sizeof (Summary));
6319 summary->info = info;
6320 summary->toggle_count = info->toggle_count - delta;
6321 summary->next = rootnode->summary;
6322 rootnode->summary = summary;
6323 rootnode = rootnode->parent;
6324 rootLevel = rootnode->level;
6325 info->tag_root = rootnode;
6327 summary = (Summary *) g_malloc (sizeof (Summary));
6328 summary->info = info;
6329 summary->toggle_count = delta;
6330 summary->next = node->summary;
6331 node->summary = summary;
6336 * If we've decremented the toggle count, then it may be necessary
6337 * to push the tag root down one or more levels.
6344 if (info->toggle_count == 0)
6346 info->tag_root = (GtkTextBTreeNode *) NULL;
6349 node = info->tag_root;
6350 while (node->level > 0)
6353 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6354 * toggles. If so, push the root down one level.
6357 for (node2Ptr = node->children.node;
6358 node2Ptr != (GtkTextBTreeNode *)NULL ;
6359 node2Ptr = node2Ptr->next)
6361 for (prevPtr = NULL, summary = node2Ptr->summary;
6363 prevPtr = summary, summary = summary->next)
6365 if (summary->info == info)
6370 if (summary == NULL)
6374 if (summary->toggle_count != info->toggle_count)
6377 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6384 * This GtkTextBTreeNode has all the toggles, so push down the root.
6387 if (prevPtr == NULL)
6389 node2Ptr->summary = summary->next;
6393 prevPtr->next = summary->next;
6395 summary_destroy (summary);
6396 info->tag_root = node2Ptr;
6399 node = info->tag_root;
6404 *----------------------------------------------------------------------
6408 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6409 * increments the count for a particular tag, adding a new
6410 * entry for that tag if there wasn't one previously.
6416 * The information at *tagInfoPtr may be modified, and the arrays
6417 * may be reallocated to make them larger.
6419 *----------------------------------------------------------------------
6423 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6428 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6429 count > 0; tag_p++, count--)
6433 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6439 * There isn't currently an entry for this tag, so we have to
6440 * make a new one. If the arrays are full, then enlarge the
6444 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6446 GtkTextTag **newTags;
6447 int *newCounts, newSize;
6449 newSize = 2*tagInfoPtr->arraySize;
6450 newTags = (GtkTextTag **) g_malloc ((unsigned)
6451 (newSize*sizeof (GtkTextTag *)));
6452 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6453 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6454 g_free ((char *) tagInfoPtr->tags);
6455 tagInfoPtr->tags = newTags;
6456 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6457 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6458 tagInfoPtr->arraySize *sizeof (int));
6459 g_free ((char *) tagInfoPtr->counts);
6460 tagInfoPtr->counts = newCounts;
6461 tagInfoPtr->arraySize = newSize;
6464 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6465 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6466 tagInfoPtr->numTags++;
6470 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6471 const GtkTextIter *iter)
6473 GtkTextLineSegment *prev;
6477 line = _gtk_text_iter_get_text_line (iter);
6478 tree = _gtk_text_iter_get_btree (iter);
6480 prev = gtk_text_line_segment_split (iter);
6483 seg->next = line->segments;
6484 line->segments = seg;
6488 seg->next = prev->next;
6491 cleanup_line (line);
6492 segments_changed (tree);
6494 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6495 _gtk_text_btree_check (tree);
6499 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6500 GtkTextLineSegment *seg,
6503 GtkTextLineSegment *prev;
6505 if (line->segments == seg)
6507 line->segments = seg->next;
6511 for (prev = line->segments; prev->next != seg;
6514 /* Empty loop body. */
6516 prev->next = seg->next;
6518 cleanup_line (line);
6519 segments_changed (tree);
6523 * This is here because it requires BTree internals, it logically
6524 * belongs in gtktextsegment.c
6529 *--------------------------------------------------------------
6531 * _gtk_toggle_segment_check_func --
6533 * This procedure is invoked to perform consistency checks
6534 * on toggle segments.
6540 * If a consistency problem is found the procedure g_errors.
6542 *--------------------------------------------------------------
6546 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6552 if (segPtr->byte_count != 0)
6554 g_error ("toggle_segment_check_func: segment had non-zero size");
6556 if (!segPtr->body.toggle.inNodeCounts)
6558 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6560 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6561 for (summary = line->parent->summary; ;
6562 summary = summary->next)
6564 if (summary == NULL)
6568 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6575 if (summary->info == segPtr->body.toggle.info)
6579 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6591 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6592 GtkTextBTreeNode *node,
6602 while (view != NULL)
6604 if (view->view_id == nd->view_id)
6611 g_error ("Node has data for a view %p no longer attached to the tree",
6614 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6615 &width, &height, &valid);
6617 /* valid aggregate not checked the same as width/height, because on
6618 * btree rebalance we can have invalid nodes where all lines below
6619 * them are actually valid, due to moving lines around between
6622 * The guarantee is that if there are invalid lines the node is
6623 * invalid - we don't guarantee that if the node is invalid there
6624 * are invalid lines.
6627 if (nd->width != width ||
6628 nd->height != height ||
6629 (nd->valid && !valid))
6631 g_error ("Node aggregates for view %p are invalid:\n"
6632 "Are (%d,%d,%s), should be (%d,%d,%s)",
6634 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6635 width, height, valid ? "TRUE" : "FALSE");
6640 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6641 GtkTextBTreeNode *node)
6643 GtkTextBTreeNode *childnode;
6644 Summary *summary, *summary2;
6646 GtkTextLineSegment *segPtr;
6647 int num_children, num_lines, num_chars, toggle_count, min_children;
6648 GtkTextLineData *ld;
6651 if (node->parent != NULL)
6653 min_children = MIN_CHILDREN;
6655 else if (node->level > 0)
6662 if ((node->num_children < min_children)
6663 || (node->num_children > MAX_CHILDREN))
6665 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6666 node->num_children);
6669 nd = node->node_data;
6672 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6679 if (node->level == 0)
6681 for (line = node->children.line; line != NULL;
6684 if (line->parent != node)
6686 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6688 if (line->segments == NULL)
6690 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6696 /* Just ensuring we don't segv while doing this loop */
6701 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6703 if (segPtr->type->checkFunc != NULL)
6705 (*segPtr->type->checkFunc)(segPtr, line);
6707 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6708 && (segPtr->next != NULL)
6709 && (segPtr->next->byte_count == 0)
6710 && (segPtr->next->type->leftGravity))
6712 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6714 if ((segPtr->next == NULL)
6715 && (segPtr->type != >k_text_char_type))
6717 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6720 num_chars += segPtr->char_count;
6729 for (childnode = node->children.node; childnode != NULL;
6730 childnode = childnode->next)
6732 if (childnode->parent != node)
6734 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6736 if (childnode->level != (node->level-1))
6738 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6739 node->level, childnode->level);
6741 gtk_text_btree_node_check_consistency (tree, childnode);
6742 for (summary = childnode->summary; summary != NULL;
6743 summary = summary->next)
6745 for (summary2 = node->summary; ;
6746 summary2 = summary2->next)
6748 if (summary2 == NULL)
6750 if (summary->info->tag_root == node)
6754 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6755 summary->info->tag->name,
6756 "present in parent summaries");
6758 if (summary->info == summary2->info)
6765 num_lines += childnode->num_lines;
6766 num_chars += childnode->num_chars;
6769 if (num_children != node->num_children)
6771 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6772 num_children, node->num_children);
6774 if (num_lines != node->num_lines)
6776 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6777 num_lines, node->num_lines);
6779 if (num_chars != node->num_chars)
6781 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6782 num_chars, node->num_chars);
6785 for (summary = node->summary; summary != NULL;
6786 summary = summary->next)
6788 if (summary->info->toggle_count == summary->toggle_count)
6790 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6791 summary->info->tag->name);
6794 if (node->level == 0)
6796 for (line = node->children.line; line != NULL;
6799 for (segPtr = line->segments; segPtr != NULL;
6800 segPtr = segPtr->next)
6802 if ((segPtr->type != >k_text_toggle_on_type)
6803 && (segPtr->type != >k_text_toggle_off_type))
6807 if (segPtr->body.toggle.info == summary->info)
6809 if (!segPtr->body.toggle.inNodeCounts)
6810 g_error ("Toggle segment not in the node counts");
6819 for (childnode = node->children.node;
6821 childnode = childnode->next)
6823 for (summary2 = childnode->summary;
6825 summary2 = summary2->next)
6827 if (summary2->info == summary->info)
6829 toggle_count += summary2->toggle_count;
6834 if (toggle_count != summary->toggle_count)
6836 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6837 toggle_count, summary->toggle_count);
6839 for (summary2 = summary->next; summary2 != NULL;
6840 summary2 = summary2->next)
6842 if (summary2->info == summary->info)
6844 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6845 summary->info->tag->name);
6852 listify_foreach (GtkTextTag *tag, gpointer user_data)
6854 GSList** listp = user_data;
6856 *listp = g_slist_prepend (*listp, tag);
6860 list_of_tags (GtkTextTagTable *table)
6862 GSList *list = NULL;
6864 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6870 _gtk_text_btree_check (GtkTextBTree *tree)
6873 GtkTextBTreeNode *node;
6875 GtkTextLineSegment *seg;
6877 GSList *all_tags, *taglist = NULL;
6879 GtkTextTagInfo *info;
6882 * Make sure that the tag toggle counts and the tag root pointers are OK.
6884 all_tags = list_of_tags (tree->table);
6885 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6887 tag = taglist->data;
6888 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6891 node = info->tag_root;
6894 if (info->toggle_count != 0)
6896 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6897 tag->name, info->toggle_count);
6899 continue; /* no ranges for the tag */
6901 else if (info->toggle_count == 0)
6903 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6906 else if (info->toggle_count & 1)
6908 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6909 tag->name, info->toggle_count);
6911 for (summary = node->summary; summary != NULL;
6912 summary = summary->next)
6914 if (summary->info->tag == tag)
6916 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6920 if (node->level > 0)
6922 for (node = node->children.node ; node != NULL ;
6925 for (summary = node->summary; summary != NULL;
6926 summary = summary->next)
6928 if (summary->info->tag == tag)
6930 count += summary->toggle_count;
6937 GtkTextLineSegmentClass * last = NULL;
6939 for (line = node->children.line ; line != NULL ;
6942 for (seg = line->segments; seg != NULL;
6945 if ((seg->type == >k_text_toggle_on_type ||
6946 seg->type == >k_text_toggle_off_type) &&
6947 seg->body.toggle.info->tag == tag)
6949 if (last == seg->type)
6950 g_error ("Two consecutive toggles on or off weren't merged");
6951 if (!seg->body.toggle.inNodeCounts)
6952 g_error ("Toggle segment not in the node counts");
6961 if (count != info->toggle_count)
6963 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6964 info->toggle_count, tag->name, count);
6969 g_slist_free (all_tags);
6972 * Call a recursive procedure to do the main body of checks.
6975 node = tree->root_node;
6976 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6979 * Make sure that there are at least two lines in the text and
6980 * that the last line has no characters except a newline.
6983 if (node->num_lines < 2)
6985 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6987 if (node->num_chars < 2)
6989 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6991 while (node->level > 0)
6993 node = node->children.node;
6994 while (node->next != NULL)
6999 line = node->children.line;
7000 while (line->next != NULL)
7004 seg = line->segments;
7005 while ((seg->type == >k_text_toggle_off_type)
7006 || (seg->type == >k_text_right_mark_type)
7007 || (seg->type == >k_text_left_mark_type))
7010 * It's OK to toggle a tag off in the last line, but
7011 * not to start a new range. It's also OK to have marks
7017 if (seg->type != >k_text_char_type)
7019 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7021 if (seg->next != NULL)
7023 g_error ("_gtk_text_btree_check: last line has too many segments");
7025 if (seg->byte_count != 1)
7027 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7030 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7032 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7037 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7038 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7039 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7040 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7043 _gtk_text_btree_spew (GtkTextBTree *tree)
7048 printf ("%d lines in tree %p\n",
7049 _gtk_text_btree_line_count (tree), tree);
7051 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7053 while (line != NULL)
7055 _gtk_text_btree_spew_line (tree, line);
7056 line = _gtk_text_line_next (line);
7059 printf ("=================== Tag information\n");
7064 list = tree->tag_infos;
7066 while (list != NULL)
7068 GtkTextTagInfo *info;
7072 printf (" tag `%s': root at %p, toggle count %d\n",
7073 info->tag->name, info->tag_root, info->toggle_count);
7075 list = g_slist_next (list);
7078 if (tree->tag_infos == NULL)
7080 printf (" (no tags in the tree)\n");
7084 printf ("=================== Tree nodes\n");
7087 _gtk_text_btree_spew_node (tree->root_node, 0);
7092 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7095 GtkTextLineSegment *seg;
7097 spaces = g_strnfill (indent, ' ');
7099 printf ("%sline %p chars %d bytes %d\n",
7101 _gtk_text_line_char_count (line),
7102 _gtk_text_line_byte_count (line));
7104 seg = line->segments;
7107 if (seg->type == >k_text_char_type)
7109 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7114 if (*s == '\n' || *s == '\r')
7118 printf ("%s chars `%s'...\n", spaces, str);
7121 else if (seg->type == >k_text_right_mark_type)
7123 printf ("%s right mark `%s' visible: %d\n",
7125 seg->body.mark.name,
7126 seg->body.mark.visible);
7128 else if (seg->type == >k_text_left_mark_type)
7130 printf ("%s left mark `%s' visible: %d\n",
7132 seg->body.mark.name,
7133 seg->body.mark.visible);
7135 else if (seg->type == >k_text_toggle_on_type ||
7136 seg->type == >k_text_toggle_off_type)
7138 printf ("%s tag `%s' %s\n",
7139 spaces, seg->body.toggle.info->tag->name,
7140 seg->type == >k_text_toggle_off_type ? "off" : "on");
7150 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7153 GtkTextBTreeNode *iter;
7156 spaces = g_strnfill (indent, ' ');
7158 printf ("%snode %p level %d children %d lines %d chars %d\n",
7159 spaces, node, node->level,
7160 node->num_children, node->num_lines, node->num_chars);
7165 printf ("%s %d toggles of `%s' below this node\n",
7166 spaces, s->toggle_count, s->info->tag->name);
7172 if (node->level > 0)
7174 iter = node->children.node;
7175 while (iter != NULL)
7177 _gtk_text_btree_spew_node (iter, indent + 2);
7184 GtkTextLine *line = node->children.line;
7185 while (line != NULL)
7187 _gtk_text_btree_spew_line_short (line, indent + 2);
7195 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7197 GtkTextLineSegment * seg;
7199 printf ("%4d| line: %p parent: %p next: %p\n",
7200 _gtk_text_line_get_number (line), line, line->parent, line->next);
7202 seg = line->segments;
7206 _gtk_text_btree_spew_segment (tree, seg);
7212 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7214 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7215 seg, seg->type->name, seg->byte_count, seg->char_count);
7217 if (seg->type == >k_text_char_type)
7219 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7220 printf (" `%s'\n", str);
7223 else if (seg->type == >k_text_right_mark_type)
7225 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7226 seg->body.mark.name,
7227 seg->body.mark.visible,
7228 seg->body.mark.not_deleteable);
7230 else if (seg->type == >k_text_left_mark_type)
7232 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7233 seg->body.mark.name,
7234 seg->body.mark.visible,
7235 seg->body.mark.not_deleteable);
7237 else if (seg->type == >k_text_toggle_on_type ||
7238 seg->type == >k_text_toggle_off_type)
7240 printf (" tag `%s' priority %d\n",
7241 seg->body.toggle.info->tag->name,
7242 seg->body.toggle.info->tag->priority);
7246 #define __GTK_TEXT_BTREE_C__
7247 #include "gtkaliasdef.c"