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 #include "gtktextbtree.h"
60 #include "gtksignal.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
74 * The structure below is used to pass information between
75 * _gtk_text_btree_get_tags and inc_count:
78 typedef struct TagInfo {
79 int numTags; /* Number of tags for which there
80 * is currently information in
82 int arraySize; /* Number of entries allocated for
84 GtkTextTag **tags; /* Array of tags seen so far.
86 int *counts; /* Toggle count (so far) for each
87 * entry in tags. Malloc-ed. */
92 * This is used to store per-view width/height info at the tree nodes.
95 typedef struct _NodeData NodeData;
101 /* Height and width of this node */
105 /* boolean indicating whether the height/width need to be recomputed */
111 * The data structure below keeps summary information about one tag as part
112 * of the tag information in a node.
115 typedef struct Summary {
116 GtkTextTagInfo *info; /* Handle for tag. */
117 int toggle_count; /* Number of transitions into or
118 * out of this tag that occur in
119 * the subtree rooted at this node. */
120 struct Summary *next; /* Next in list of all tags for same
121 * node, or NULL if at end of list. */
125 * The data structure below defines a node in the B-tree.
128 struct _GtkTextBTreeNode {
129 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
130 * this is the root. */
131 GtkTextBTreeNode *next; /* Next in list of siblings with the
132 * same parent node, or NULL for end
134 Summary *summary; /* First in malloc-ed list of info
135 * about tags in this subtree (NULL if
136 * no tag info in the subtree). */
137 int level; /* Level of this node in the B-tree.
138 * 0 refers to the bottom of the tree
139 * (children are lines, not nodes). */
140 union { /* First in linked list of children. */
141 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
142 GtkTextLine *line; /* Used if level == 0. */
144 int num_children; /* Number of children of this node. */
145 int num_lines; /* Total number of lines (leaves) in
146 * the subtree rooted here. */
147 int num_chars; /* Number of chars below here */
154 * Used to store the list of views in our btree
157 typedef struct _BTreeView BTreeView;
161 GtkTextLayout *layout;
167 * And the tree itself
170 struct _GtkTextBTree {
171 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
172 GtkTextTagTable *table;
173 GHashTable *mark_table;
175 GtkTextMark *insert_mark;
176 GtkTextMark *selection_bound_mark;
177 GtkTextBuffer *buffer;
180 guint tag_changed_handler;
181 guint tag_removed_handler;
182 /* Incremented when a segment with a byte size > 0
183 is added to or removed from the tree (i.e. the
184 length of a line may have changed, and lines may
185 have been added or removed). This invalidates
186 all outstanding iterators.
188 guint chars_changed_stamp;
189 /* Incremented when any segments are added or deleted;
190 this makes outstanding iterators recalculate their
191 pointed-to segment and segment offset.
193 guint segments_changed_stamp;
195 GtkTextLine *end_iter_line;
197 guint end_iter_line_stamp;
199 GHashTable *child_anchor_table;
204 * Upper and lower bounds on how many children a node may have:
205 * rebalance when either of these limits is exceeded. MAX_CHILDREN
206 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
209 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
210 shallow. It appears to be faster to locate a particular line number
211 if the tree is narrow and deep, since it is more finely sorted. I
212 guess this may increase memory use though, and make it slower to
213 walk the tree in order, or locate a particular byte index (which
214 is done by walking the tree in order).
216 There's basically a tradeoff here. However I'm thinking we want to
217 add pixels, byte counts, and char counts to the tree nodes,
218 at that point narrow and deep should speed up all operations,
219 not just the line number searches.
223 #define MAX_CHILDREN 12
224 #define MIN_CHILDREN 6
226 #define MAX_CHILDREN 6
227 #define MIN_CHILDREN 3
234 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
236 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
237 GtkTextBTreeNode *node);
238 static GtkTextLine * get_last_line (GtkTextBTree *tree);
239 static void post_insert_fixup (GtkTextBTree *tree,
240 GtkTextLine *insert_line,
241 gint char_count_delta,
242 gint line_count_delta);
243 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
244 GtkTextTagInfo *info,
246 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
249 static void segments_changed (GtkTextBTree *tree);
250 static void chars_changed (GtkTextBTree *tree);
251 static void summary_list_destroy (Summary *summary);
252 static GtkTextLine *gtk_text_line_new (void);
253 static void gtk_text_line_destroy (GtkTextBTree *tree,
255 static void gtk_text_line_set_parent (GtkTextLine *line,
256 GtkTextBTreeNode *node);
257 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
261 static NodeData *node_data_new (gpointer view_id);
262 static void node_data_destroy (NodeData *nd);
263 static void node_data_list_destroy (NodeData *nd);
264 static NodeData *node_data_find (NodeData *nd,
267 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
268 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
269 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
271 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
273 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
275 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
278 static void gtk_text_btree_node_remove_view (BTreeView *view,
279 GtkTextBTreeNode *node,
281 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
282 GtkTextBTreeNode *node);
283 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
285 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
287 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
291 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
292 GtkTextBTreeNode *node2);
293 static void get_tree_bounds (GtkTextBTree *tree,
296 static void tag_changed_cb (GtkTextTagTable *table,
298 gboolean size_changed,
300 static void tag_removed_cb (GtkTextTagTable *table,
303 static void cleanup_line (GtkTextLine *line);
304 static void recompute_node_counts (GtkTextBTree *tree,
305 GtkTextBTreeNode *node);
306 static void inc_count (GtkTextTag *tag,
308 TagInfo *tagInfoPtr);
310 static void summary_destroy (Summary *summary);
312 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
313 const GtkTextIter *iter);
314 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
315 GtkTextLineSegment *seg,
319 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
321 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
323 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
326 static void redisplay_region (GtkTextBTree *tree,
327 const GtkTextIter *start,
328 const GtkTextIter *end);
330 /* Inline thingies */
333 segments_changed (GtkTextBTree *tree)
335 tree->segments_changed_stamp += 1;
339 chars_changed (GtkTextBTree *tree)
341 tree->chars_changed_stamp += 1;
349 _gtk_text_btree_new (GtkTextTagTable *table,
350 GtkTextBuffer *buffer)
353 GtkTextBTreeNode *root_node;
354 GtkTextLine *line, *line2;
356 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
357 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
360 * The tree will initially have two empty lines. The second line
361 * isn't actually part of the tree's contents, but its presence
362 * makes several operations easier. The tree will have one GtkTextBTreeNode,
363 * which is also the root of the tree.
366 /* Create the root node. */
368 root_node = gtk_text_btree_node_new ();
370 line = gtk_text_line_new ();
371 line2 = gtk_text_line_new ();
373 root_node->parent = NULL;
374 root_node->next = NULL;
375 root_node->summary = NULL;
376 root_node->level = 0;
377 root_node->children.line = line;
378 root_node->num_children = 2;
379 root_node->num_lines = 2;
380 root_node->num_chars = 2;
382 line->parent = root_node;
385 line->segments = _gtk_char_segment_new ("\n", 1);
387 line2->parent = root_node;
389 line2->segments = _gtk_char_segment_new ("\n", 1);
391 /* Create the tree itself */
393 tree = g_new0(GtkTextBTree, 1);
394 tree->root_node = root_node;
398 /* Set these to values that are unlikely to be found
399 * in random memory garbage, and also avoid
400 * duplicates between tree instances.
402 tree->chars_changed_stamp = g_random_int ();
403 tree->segments_changed_stamp = g_random_int ();
405 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
406 tree->end_iter_line = NULL;
408 g_object_ref (G_OBJECT (tree->table));
410 tree->tag_changed_handler = g_signal_connect_data (G_OBJECT (tree->table),
416 tree->tag_removed_handler = g_signal_connect_data (G_OBJECT (tree->table),
422 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
423 tree->child_anchor_table = NULL;
425 /* We don't ref the buffer, since the buffer owns us;
426 * we'd have some circularity issues. The buffer always
427 * lasts longer than the BTree
429 tree->buffer = buffer;
433 GtkTextLineSegment *seg;
435 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
438 tree->insert_mark = _gtk_text_btree_set_mark (tree,
445 seg = tree->insert_mark->segment;
447 seg->body.mark.not_deleteable = TRUE;
448 seg->body.mark.visible = TRUE;
450 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
457 seg = tree->selection_bound_mark->segment;
459 seg->body.mark.not_deleteable = TRUE;
461 g_object_ref (G_OBJECT (tree->insert_mark));
462 g_object_ref (G_OBJECT (tree->selection_bound_mark));
471 _gtk_text_btree_ref (GtkTextBTree *tree)
473 g_return_if_fail (tree != NULL);
474 g_return_if_fail (tree->refcount > 0);
480 mark_destroy_foreach (gpointer key, gpointer value, gpointer user_data)
482 GtkTextLineSegment *seg = value;
484 g_return_if_fail (seg->body.mark.tree == NULL);
486 g_object_unref (G_OBJECT (seg->body.mark.obj));
490 _gtk_text_btree_unref (GtkTextBTree *tree)
492 g_return_if_fail (tree != NULL);
493 g_return_if_fail (tree->refcount > 0);
497 if (tree->refcount == 0)
499 gtk_text_btree_node_destroy (tree, tree->root_node);
501 g_hash_table_foreach (tree->mark_table,
502 mark_destroy_foreach,
504 g_hash_table_destroy (tree->mark_table);
506 g_object_unref (G_OBJECT (tree->insert_mark));
507 g_object_unref (G_OBJECT (tree->selection_bound_mark));
509 g_signal_handler_disconnect (G_OBJECT (tree->table),
510 tree->tag_changed_handler);
512 g_signal_handler_disconnect (G_OBJECT (tree->table),
513 tree->tag_removed_handler);
515 g_object_unref (G_OBJECT (tree->table));
522 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
528 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
530 return tree->chars_changed_stamp;
534 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
536 return tree->segments_changed_stamp;
540 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
542 g_return_if_fail (tree != NULL);
543 segments_changed (tree);
547 * Indexable segment mutation
551 _gtk_text_btree_delete (GtkTextIter *start,
554 GtkTextLineSegment *prev_seg; /* The segment just before the start
555 * of the deletion range. */
556 GtkTextLineSegment *last_seg; /* The segment just after the end
557 * of the deletion range. */
558 GtkTextLineSegment *seg, *next;
559 GtkTextLine *curline;
560 GtkTextBTreeNode *curnode, *node;
562 GtkTextLine *start_line;
563 GtkTextLine *end_line;
564 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
565 gint start_byte_offset;
567 g_return_if_fail (start != NULL);
568 g_return_if_fail (end != NULL);
569 g_return_if_fail (gtk_text_iter_get_btree (start) ==
570 gtk_text_iter_get_btree (end));
572 gtk_text_iter_reorder (start, end);
574 tree = gtk_text_iter_get_btree (start);
578 * The code below is ugly, but it's needed to make sure there
579 * is always a dummy empty line at the end of the text. If the
580 * final newline of the file (just before the dummy line) is being
581 * deleted, then back up index to just before the newline. If
582 * there is a newline just before the first character being deleted,
583 * then back up the first index too, so that an even number of lines
584 * gets deleted. Furthermore, remove any tags that are present on
585 * the newline that isn't going to be deleted after all (this simulates
586 * deleting the newline and then adding a "clean" one back again).
592 line1 = gtk_text_iter_get_line (start);
593 line2 = gtk_text_iter_get_line (end);
595 if (line2 == _gtk_text_btree_line_count (tree))
599 GtkTextIter orig_end;
602 gtk_text_iter_backward_char (end);
606 if (gtk_text_iter_get_line_offset (start) == 0 &&
609 gtk_text_iter_backward_char (start);
613 tags = _gtk_text_btree_get_tags (end,
621 while (i < array_size)
623 _gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
633 /* Broadcast the need for redisplay before we break the iterators */
634 _gtk_text_btree_invalidate_region (tree, start, end);
636 /* Save the byte offset so we can reset the iterators */
637 start_byte_offset = gtk_text_iter_get_line_index (start);
639 start_line = gtk_text_iter_get_text_line (start);
640 end_line = gtk_text_iter_get_text_line (end);
643 * Split the start and end segments, so we have a place
644 * to insert our new text.
646 * Tricky point: split at end first; otherwise the split
647 * at end may invalidate seg and/or prev_seg. This allows
648 * us to avoid invalidating segments for start.
651 last_seg = gtk_text_line_segment_split (end);
652 if (last_seg != NULL)
653 last_seg = last_seg->next;
655 last_seg = end_line->segments;
657 prev_seg = gtk_text_line_segment_split (start);
658 if (prev_seg != NULL)
660 seg = prev_seg->next;
661 prev_seg->next = last_seg;
665 seg = start_line->segments;
666 start_line->segments = last_seg;
669 /* notify iterators that their segments need recomputation,
670 just for robustness. */
671 segments_changed (tree);
674 * Delete all of the segments between prev_seg and last_seg.
677 curline = start_line;
678 curnode = curline->parent;
679 while (seg != last_seg)
685 GtkTextLine *nextline;
688 * We just ran off the end of a line. First find the
689 * next line, then go back to the old line and delete it
690 * (unless it's the starting line for the range).
693 nextline = _gtk_text_line_next (curline);
694 if (curline != start_line)
696 if (curnode == start_line->parent)
697 start_line->next = curline->next;
699 curnode->children.line = curline->next;
701 for (node = curnode; node != NULL;
704 node->num_lines -= 1;
707 curnode->num_children -= 1;
708 curline->next = deleted_lines;
709 deleted_lines = curline;
713 seg = curline->segments;
716 * If the GtkTextBTreeNode is empty then delete it and its parents,
717 * recursively upwards until a non-empty GtkTextBTreeNode is found.
720 while (curnode->num_children == 0)
722 GtkTextBTreeNode *parent;
724 parent = curnode->parent;
725 if (parent->children.node == curnode)
727 parent->children.node = curnode->next;
731 GtkTextBTreeNode *prevnode = parent->children.node;
732 while (prevnode->next != curnode)
734 prevnode = prevnode->next;
736 prevnode->next = curnode->next;
738 parent->num_children--;
742 curnode = curline->parent;
747 char_count = seg->char_count;
749 if ((*seg->type->deleteFunc)(seg, curline, 0) != 0)
752 * This segment refuses to die. Move it to prev_seg and
753 * advance prev_seg if the segment has left gravity.
756 if (prev_seg == NULL)
758 seg->next = start_line->segments;
759 start_line->segments = seg;
763 seg->next = prev_seg->next;
764 prev_seg->next = seg;
766 if (seg->type->leftGravity)
773 /* Segment is gone. Decrement the char count of the node and
775 for (node = curnode; node != NULL;
778 node->num_chars -= char_count;
786 * If the beginning and end of the deletion range are in different
787 * lines, join the two lines together and discard the ending line.
790 if (start_line != end_line)
793 GtkTextBTreeNode *ancestor_node;
795 GtkTextLine *prevline;
797 for (seg = last_seg; seg != NULL;
800 if (seg->type->lineChangeFunc != NULL)
802 (*seg->type->lineChangeFunc)(seg, end_line);
805 curnode = end_line->parent;
806 for (node = curnode; node != NULL;
811 curnode->num_children--;
812 prevline = curnode->children.line;
813 if (prevline == end_line)
815 curnode->children.line = end_line->next;
819 while (prevline->next != end_line)
821 prevline = prevline->next;
823 prevline->next = end_line->next;
825 end_line->next = deleted_lines;
826 deleted_lines = end_line;
828 /* We now fix up the per-view aggregates. We add all the height and
829 * width for the deleted lines to the start line, so that when revalidation
830 * occurs, the correct change in size is seen.
832 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
839 gint deleted_width = 0;
840 gint deleted_height = 0;
842 line = deleted_lines;
845 GtkTextLine *next_line = line->next;
846 ld = _gtk_text_line_get_data (line, view->view_id);
850 deleted_width = MAX (deleted_width, ld->width);
851 deleted_height += ld->height;
855 gtk_text_line_destroy (tree, line);
860 if (deleted_width > 0 || deleted_height > 0)
862 ld = _gtk_text_line_get_data (start_line, view->view_id);
864 /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
865 * no way to add ld without also validating the node, which would
866 * be improper at this point.
868 /* This assertion does actually fail sometimes, must
869 fix before stable release -hp */
872 ld->width = MAX (deleted_width, ld->width);
873 ld->height += deleted_height;
877 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
878 if (ancestor_node->parent)
879 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
884 gtk_text_btree_rebalance (tree, curnode);
888 * Cleanup the segments in the new line.
891 cleanup_line (start_line);
894 * Lastly, rebalance the first GtkTextBTreeNode of the range.
897 gtk_text_btree_rebalance (tree, start_line->parent);
899 /* Notify outstanding iterators that they
901 chars_changed (tree);
902 segments_changed (tree);
904 if (gtk_debug_flags & GTK_DEBUG_TEXT)
905 _gtk_text_btree_check (tree);
907 /* Re-initialize our iterators */
908 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
913 _gtk_text_btree_insert (GtkTextIter *iter,
917 GtkTextLineSegment *prev_seg; /* The segment just before the first
918 * new segment (NULL means new segment
919 * is at beginning of line). */
920 GtkTextLineSegment *cur_seg; /* Current segment; new characters
921 * are inserted just after this one.
922 * NULL means insert at beginning of
924 GtkTextLine *line; /* Current line (new segments are
925 * added to this line). */
926 GtkTextLineSegment *seg;
927 GtkTextLine *newline;
928 int chunk_len; /* # characters in current chunk. */
929 gint sol; /* start of line */
930 gint eol; /* Pointer to character just after last
931 * one in current chunk.
933 gint delim; /* index of paragraph delimiter */
934 int line_count_delta; /* Counts change to total number of
938 int char_count_delta; /* change to number of chars */
940 gint start_byte_index;
941 GtkTextLine *start_line;
943 g_return_if_fail (text != NULL);
944 g_return_if_fail (iter != NULL);
949 /* extract iterator info */
950 tree = gtk_text_iter_get_btree (iter);
951 line = gtk_text_iter_get_text_line (iter);
953 start_byte_index = gtk_text_iter_get_line_index (iter);
955 /* Get our insertion segment split */
956 prev_seg = gtk_text_line_segment_split (iter);
959 /* Invalidate all iterators */
960 chars_changed (tree);
961 segments_changed (tree);
963 g_assert (g_utf8_validate (text, len, NULL));
966 * Chop the text up into lines and create a new segment for
967 * each line, plus a new line for the leftovers from the
973 line_count_delta = 0;
974 char_count_delta = 0;
979 pango_find_paragraph_boundary (text + sol,
984 /* make these relative to the start of the text */
988 g_assert (eol >= sol);
989 g_assert (delim >= sol);
990 g_assert (eol >= delim);
992 g_assert (eol <= len);
994 chunk_len = eol - sol;
996 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
997 seg = _gtk_char_segment_new (&text[sol], chunk_len);
999 char_count_delta += seg->char_count;
1001 if (cur_seg == NULL)
1003 seg->next = line->segments;
1004 line->segments = seg;
1008 seg->next = cur_seg->next;
1009 cur_seg->next = seg;
1014 /* chunk didn't end with a paragraph separator */
1015 g_assert (eol == len);
1020 * The chunk ended with a newline, so create a new GtkTextLine
1021 * and move the remainder of the old line to it.
1024 newline = gtk_text_line_new ();
1025 gtk_text_line_set_parent (newline, line->parent);
1026 newline->next = line->next;
1027 line->next = newline;
1028 newline->segments = seg->next;
1036 * Cleanup the starting line for the insertion, plus the ending
1037 * line if it's different.
1040 cleanup_line (start_line);
1041 if (line != start_line)
1043 cleanup_line (line);
1046 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1048 /* Invalidate our region, and reset the iterator the user
1049 passed in to point to the end of the inserted text. */
1055 _gtk_text_btree_get_iter_at_line (tree,
1061 /* We could almost certainly be more efficient here
1062 by saving the information from the insertion loop
1064 gtk_text_iter_forward_chars (&end, char_count_delta);
1066 _gtk_text_btree_invalidate_region (tree,
1070 /* Convenience for the user */
1076 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1077 GtkTextLineSegment *seg)
1081 GtkTextLineSegment *prevPtr;
1084 gint start_byte_offset;
1086 line = gtk_text_iter_get_text_line (iter);
1087 tree = gtk_text_iter_get_btree (iter);
1088 start_byte_offset = gtk_text_iter_get_line_index (iter);
1090 prevPtr = gtk_text_line_segment_split (iter);
1091 if (prevPtr == NULL)
1093 seg->next = line->segments;
1094 line->segments = seg;
1098 seg->next = prevPtr->next;
1099 prevPtr->next = seg;
1102 post_insert_fixup (tree, line, 0, seg->char_count);
1104 chars_changed (tree);
1105 segments_changed (tree);
1107 /* reset *iter for the user, and invalidate tree nodes */
1109 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1112 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1114 _gtk_text_btree_invalidate_region (tree, &start, iter);
1118 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1121 GtkTextLineSegment *seg;
1123 seg = _gtk_pixbuf_segment_new (pixbuf);
1125 insert_pixbuf_or_widget_segment (iter, seg);
1129 _gtk_text_btree_create_child_anchor (GtkTextIter *iter)
1131 GtkTextLineSegment *seg;
1134 seg = _gtk_widget_segment_new ();
1136 tree = seg->body.child.tree = gtk_text_iter_get_btree (iter);
1138 insert_pixbuf_or_widget_segment (iter, seg);
1140 if (tree->child_anchor_table == NULL)
1141 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1143 g_hash_table_insert (tree->child_anchor_table,
1144 seg->body.child.obj,
1145 seg->body.child.obj);
1147 return seg->body.child.obj;
1151 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1153 GtkTextLineSegment *seg;
1155 seg = anchor->segment;
1157 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1166 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1167 GtkTextBTreeNode *node, gint y, gint *line_top,
1168 GtkTextLine *last_line)
1172 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1173 _gtk_text_btree_check (tree);
1175 if (node->level == 0)
1179 line = node->children.line;
1181 while (line != NULL && line != last_line)
1183 GtkTextLineData *ld;
1185 ld = _gtk_text_line_get_data (line, view->view_id);
1189 if (y < (current_y + (ld ? ld->height : 0)))
1192 current_y += ld->height;
1193 *line_top += ld->height;
1202 GtkTextBTreeNode *child;
1204 child = node->children.node;
1206 while (child != NULL)
1211 gtk_text_btree_node_get_size (child, view->view_id,
1214 if (y < (current_y + height))
1215 return find_line_by_y (tree, view, child,
1216 y - current_y, line_top,
1219 current_y += height;
1220 *line_top += height;
1222 child = child->next;
1230 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1237 GtkTextLine *last_line;
1240 view = gtk_text_btree_get_view (tree, view_id);
1241 g_return_val_if_fail (view != NULL, NULL);
1243 last_line = get_last_line (tree);
1245 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1249 *line_top_out = line_top;
1255 find_line_top_in_line_list (GtkTextBTree *tree,
1258 GtkTextLine *target_line,
1261 while (line != NULL)
1263 GtkTextLineData *ld;
1265 if (line == target_line)
1268 ld = _gtk_text_line_get_data (line, view->view_id);
1275 g_assert_not_reached (); /* If we get here, our
1276 target line didn't exist
1277 under its parent node */
1282 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1283 GtkTextLine *target_line,
1290 GtkTextBTreeNode *node;
1292 view = gtk_text_btree_get_view (tree, view_id);
1294 g_return_val_if_fail (view != NULL, 0);
1297 node = target_line->parent;
1298 while (node != NULL)
1300 nodes = g_slist_prepend (nodes, node);
1301 node = node->parent;
1305 while (iter != NULL)
1309 if (node->level == 0)
1311 g_slist_free (nodes);
1312 return find_line_top_in_line_list (tree, view,
1313 node->children.line,
1318 GtkTextBTreeNode *child;
1319 GtkTextBTreeNode *target_node;
1321 g_assert (iter->next != NULL); /* not at level 0 */
1322 target_node = iter->next->data;
1324 child = node->children.node;
1326 while (child != NULL)
1331 if (child == target_node)
1335 gtk_text_btree_node_get_size (child, view->view_id,
1339 child = child->next;
1341 g_assert (child != NULL); /* should have broken out before we
1345 iter = g_slist_next (iter);
1348 g_assert_not_reached (); /* we return when we find the target line */
1353 _gtk_text_btree_add_view (GtkTextBTree *tree,
1354 GtkTextLayout *layout)
1357 GtkTextLine *last_line;
1358 GtkTextLineData *line_data;
1360 g_return_if_fail (tree != NULL);
1362 view = g_new (BTreeView, 1);
1364 view->view_id = layout;
1365 view->layout = layout;
1367 view->next = tree->views;
1372 /* The last line in the buffer has identity values for the per-view
1373 * data so that we can avoid special case checks for it in a large
1376 last_line = get_last_line (tree);
1378 line_data = g_new (GtkTextLineData, 1);
1379 line_data->view_id = layout;
1380 line_data->next = NULL;
1381 line_data->width = 0;
1382 line_data->height = 0;
1383 line_data->valid = TRUE;
1385 _gtk_text_line_add_data (last_line, line_data);
1389 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1393 GtkTextLine *last_line;
1394 GtkTextLineData *line_data;
1396 g_return_if_fail (tree != NULL);
1400 while (view != NULL)
1402 if (view->view_id == view_id)
1408 g_return_if_fail (view != NULL);
1411 view->next->prev = view->prev;
1414 view->prev->next = view->next;
1416 if (view == tree->views)
1417 tree->views = view->next;
1419 /* Remove the line data for the last line which we added ourselves.
1420 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1422 last_line = get_last_line (tree);
1423 line_data = _gtk_text_line_remove_data (last_line, view_id);
1426 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1432 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1433 const GtkTextIter *start,
1434 const GtkTextIter *end)
1440 while (view != NULL)
1442 gtk_text_layout_invalidate (view->layout, start, end);
1449 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1454 g_return_if_fail (tree != NULL);
1455 g_return_if_fail (view_id != NULL);
1457 gtk_text_btree_node_get_size (tree->root_node, view_id,
1472 iter_stack_new (void)
1475 stack = g_new (IterStack, 1);
1476 stack->iters = NULL;
1483 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1486 if (stack->count > stack->alloced)
1488 stack->alloced = stack->count*2;
1489 stack->iters = g_realloc (stack->iters,
1490 stack->alloced*sizeof (GtkTextIter));
1492 stack->iters[stack->count-1] = *iter;
1496 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1498 if (stack->count == 0)
1503 *iter = stack->iters[stack->count];
1509 iter_stack_free (IterStack *stack)
1511 g_free (stack->iters);
1516 iter_stack_invert (IterStack *stack)
1518 if (stack->count > 0)
1521 guint j = stack->count - 1;
1526 tmp = stack->iters[i];
1527 stack->iters[i] = stack->iters[j];
1528 stack->iters[j] = tmp;
1537 queue_tag_redisplay (GtkTextBTree *tree,
1539 const GtkTextIter *start,
1540 const GtkTextIter *end)
1542 if (_gtk_text_tag_affects_size (tag))
1544 _gtk_text_btree_invalidate_region (tree, start, end);
1546 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1548 /* We only need to queue a redraw, not a relayout */
1549 redisplay_region (tree, start, end);
1552 /* We don't need to do anything if the tag doesn't affect display */
1556 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1557 const GtkTextIter *end_orig,
1561 GtkTextLineSegment *seg, *prev;
1562 GtkTextLine *cleanupline;
1563 gboolean toggled_on;
1564 GtkTextLine *start_line;
1565 GtkTextLine *end_line;
1567 GtkTextIter start, end;
1570 GtkTextTagInfo *info;
1572 g_return_if_fail (start_orig != NULL);
1573 g_return_if_fail (end_orig != NULL);
1574 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1575 g_return_if_fail (gtk_text_iter_get_btree (start_orig) ==
1576 gtk_text_iter_get_btree (end_orig));
1579 printf ("%s tag %s from %d to %d\n",
1580 add ? "Adding" : "Removing",
1582 gtk_text_buffer_get_offset (start_orig),
1583 gtk_text_buffer_get_offset (end_orig));
1586 if (gtk_text_iter_equal (start_orig, end_orig))
1589 start = *start_orig;
1592 gtk_text_iter_reorder (&start, &end);
1594 tree = gtk_text_iter_get_btree (&start);
1596 queue_tag_redisplay (tree, tag, &start, &end);
1598 info = gtk_text_btree_get_tag_info (tree, tag);
1600 start_line = gtk_text_iter_get_text_line (&start);
1601 end_line = gtk_text_iter_get_text_line (&end);
1603 /* Find all tag toggles in the region; we are going to delete them.
1604 We need to find them in advance, because
1605 forward_find_tag_toggle () won't work once we start playing around
1607 stack = iter_stack_new ();
1609 /* We don't want to delete a toggle that's at the start iterator. */
1610 gtk_text_iter_forward_char (&iter);
1611 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1613 if (gtk_text_iter_compare (&iter, &end) >= 0)
1616 iter_stack_push (stack, &iter);
1619 /* We need to traverse the toggles in order. */
1620 iter_stack_invert (stack);
1623 * See whether the tag is present at the start of the range. If
1624 * the state doesn't already match what we want then add a toggle
1628 toggled_on = gtk_text_iter_has_tag (&start, tag);
1629 if ( (add && !toggled_on) ||
1630 (!add && toggled_on) )
1632 /* This could create a second toggle at the start position;
1633 cleanup_line () will remove it if so. */
1634 seg = _gtk_toggle_segment_new (info, add);
1636 prev = gtk_text_line_segment_split (&start);
1639 seg->next = start_line->segments;
1640 start_line->segments = seg;
1644 seg->next = prev->next;
1648 /* cleanup_line adds the new toggle to the node counts. */
1650 printf ("added toggle at start\n");
1652 /* we should probably call segments_changed, but in theory
1653 any still-cached segments in the iters we are about to
1654 use are still valid, since they're in front
1660 * Scan the range of characters and delete any internal tag
1661 * transitions. Keep track of what the old state was at the end
1662 * of the range, and add a toggle there if it's needed.
1666 cleanupline = start_line;
1667 while (iter_stack_pop (stack, &iter))
1669 GtkTextLineSegment *indexable_seg;
1672 line = gtk_text_iter_get_text_line (&iter);
1673 seg = gtk_text_iter_get_any_segment (&iter);
1674 indexable_seg = gtk_text_iter_get_indexable_segment (&iter);
1676 g_assert (seg != NULL);
1677 g_assert (indexable_seg != NULL);
1678 g_assert (seg != indexable_seg);
1680 prev = line->segments;
1682 /* Find the segment that actually toggles this tag. */
1683 while (seg != indexable_seg)
1685 g_assert (seg != NULL);
1686 g_assert (indexable_seg != NULL);
1687 g_assert (seg != indexable_seg);
1689 if ( (seg->type == >k_text_toggle_on_type ||
1690 seg->type == >k_text_toggle_off_type) &&
1691 (seg->body.toggle.info == info) )
1697 g_assert (seg != NULL);
1698 g_assert (indexable_seg != NULL);
1700 g_assert (seg != indexable_seg); /* If this happens, then
1701 forward_to_tag_toggle was
1703 g_assert (seg->body.toggle.info->tag == tag);
1705 /* If this happens, when previously tagging we didn't merge
1706 overlapping tags. */
1707 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1708 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1710 toggled_on = !toggled_on;
1713 printf ("deleting %s toggle\n",
1714 seg->type == >k_text_toggle_on_type ? "on" : "off");
1716 /* Remove toggle segment from the list. */
1719 line->segments = seg->next;
1723 while (prev->next != seg)
1727 prev->next = seg->next;
1730 /* Inform iterators we've hosed them. This actually reflects a
1731 bit of inefficiency; if you have the same tag toggled on and
1732 off a lot in a single line, we keep having the rescan from
1733 the front of the line. Of course we have to do that to get
1734 "prev" anyway, but here we are doing it an additional
1736 segments_changed (tree);
1738 /* Update node counts */
1739 if (seg->body.toggle.inNodeCounts)
1741 _gtk_change_node_toggle_count (line->parent,
1743 seg->body.toggle.inNodeCounts = FALSE;
1748 /* We only clean up lines when we're done with them, saves some
1749 gratuitous line-segment-traversals */
1751 if (cleanupline != line)
1753 cleanup_line (cleanupline);
1758 iter_stack_free (stack);
1760 /* toggled_on now reflects the toggle state _just before_ the
1761 end iterator. The end iterator could already have a toggle
1762 on or a toggle off. */
1763 if ( (add && !toggled_on) ||
1764 (!add && toggled_on) )
1766 /* This could create a second toggle at the start position;
1767 cleanup_line () will remove it if so. */
1769 seg = _gtk_toggle_segment_new (info, !add);
1771 prev = gtk_text_line_segment_split (&end);
1774 seg->next = end_line->segments;
1775 end_line->segments = seg;
1779 seg->next = prev->next;
1782 /* cleanup_line adds the new toggle to the node counts. */
1783 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1785 printf ("added toggle at end\n");
1790 * Cleanup cleanupline and the last line of the range, if
1791 * these are different.
1794 cleanup_line (cleanupline);
1795 if (cleanupline != end_line)
1797 cleanup_line (end_line);
1800 segments_changed (tree);
1802 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1803 _gtk_text_btree_check (tree);
1812 _gtk_text_btree_get_line (GtkTextBTree *tree,
1814 gint *real_line_number)
1816 GtkTextBTreeNode *node;
1821 line_count = _gtk_text_btree_line_count (tree);
1823 if (line_number < 0)
1825 line_number = line_count;
1827 else if (line_number > line_count)
1829 line_number = line_count;
1832 if (real_line_number)
1833 *real_line_number = line_number;
1835 node = tree->root_node;
1836 lines_left = line_number;
1839 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1843 while (node->level != 0)
1845 for (node = node->children.node;
1846 node->num_lines <= lines_left;
1852 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1855 lines_left -= node->num_lines;
1860 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1863 for (line = node->children.line; lines_left > 0;
1869 g_error ("gtk_text_btree_find_line ran out of lines");
1878 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1880 gint *line_start_index,
1881 gint *real_char_index)
1883 GtkTextBTreeNode *node;
1885 GtkTextLineSegment *seg;
1890 node = tree->root_node;
1892 /* Clamp to valid indexes (-1 is magic for "highest index") */
1893 if (char_index < 0 || char_index >= node->num_chars)
1895 char_index = node->num_chars - 1;
1898 *real_char_index = char_index;
1901 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1905 chars_left = char_index;
1906 while (node->level != 0)
1908 for (node = node->children.node;
1909 chars_left >= node->num_chars;
1912 chars_left -= node->num_chars;
1914 g_assert (chars_left >= 0);
1918 if (chars_left == 0)
1920 /* Start of a line */
1922 *line_start_index = char_index;
1923 return node->children.line;
1927 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1933 for (line = node->children.line; line != NULL; line = line->next)
1935 seg = line->segments;
1938 if (chars_in_line + seg->char_count > chars_left)
1939 goto found; /* found our line/segment */
1941 chars_in_line += seg->char_count;
1946 chars_left -= chars_in_line;
1954 g_assert (line != NULL); /* hosage, ran out of lines */
1955 g_assert (seg != NULL);
1957 *line_start_index = char_index - chars_left;
1962 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1965 GtkTextBTreeNode *node;
1966 GtkTextLine *siblingline;
1967 GtkTextLineSegment *seg;
1968 int src, dst, index;
1974 #define NUM_TAG_INFOS 10
1976 line = gtk_text_iter_get_text_line (iter);
1977 tree = gtk_text_iter_get_btree (iter);
1978 byte_index = gtk_text_iter_get_line_index (iter);
1980 tagInfo.numTags = 0;
1981 tagInfo.arraySize = NUM_TAG_INFOS;
1982 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
1983 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
1986 * Record tag toggles within the line of indexPtr but preceding
1987 * indexPtr. Note that if this loop segfaults, your
1988 * byte_index probably points past the sum of all
1989 * seg->byte_count */
1991 for (index = 0, seg = line->segments;
1992 (index + seg->byte_count) <= byte_index;
1993 index += seg->byte_count, seg = seg->next)
1995 if ((seg->type == >k_text_toggle_on_type)
1996 || (seg->type == >k_text_toggle_off_type))
1998 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2003 * Record toggles for tags in lines that are predecessors of
2004 * line but under the same level-0 GtkTextBTreeNode.
2007 for (siblingline = line->parent->children.line;
2008 siblingline != line;
2009 siblingline = siblingline->next)
2011 for (seg = siblingline->segments; seg != NULL;
2014 if ((seg->type == >k_text_toggle_on_type)
2015 || (seg->type == >k_text_toggle_off_type))
2017 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2023 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2024 * toggles for all siblings that precede that GtkTextBTreeNode.
2027 for (node = line->parent; node->parent != NULL;
2028 node = node->parent)
2030 GtkTextBTreeNode *siblingPtr;
2033 for (siblingPtr = node->parent->children.node;
2034 siblingPtr != node; siblingPtr = siblingPtr->next)
2036 for (summary = siblingPtr->summary; summary != NULL;
2037 summary = summary->next)
2039 if (summary->toggle_count & 1)
2041 inc_count (summary->info->tag, summary->toggle_count,
2049 * Go through the tag information and squash out all of the tags
2050 * that have even toggle counts (these tags exist before the point
2051 * of interest, but not at the desired character itself).
2054 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2056 if (tagInfo.counts[src] & 1)
2058 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2059 tagInfo.tags[dst] = tagInfo.tags[src];
2065 g_free (tagInfo.counts);
2068 g_free (tagInfo.tags);
2071 return tagInfo.tags;
2075 copy_segment (GString *string,
2076 gboolean include_hidden,
2077 gboolean include_nonchars,
2078 const GtkTextIter *start,
2079 const GtkTextIter *end)
2081 GtkTextLineSegment *end_seg;
2082 GtkTextLineSegment *seg;
2084 if (gtk_text_iter_equal (start, end))
2087 seg = gtk_text_iter_get_indexable_segment (start);
2088 end_seg = gtk_text_iter_get_indexable_segment (end);
2090 if (seg->type == >k_text_char_type)
2092 gboolean copy = TRUE;
2093 gint copy_bytes = 0;
2094 gint copy_start = 0;
2096 /* Don't copy if we're invisible; segments are invisible/not
2097 as a whole, no need to check each char */
2098 if (!include_hidden &&
2099 _gtk_text_btree_char_is_invisible (start))
2102 /* printf (" <invisible>\n"); */
2105 copy_start = gtk_text_iter_get_segment_byte (start);
2109 /* End is in the same segment; need to copy fewer bytes. */
2110 gint end_byte = gtk_text_iter_get_segment_byte (end);
2112 copy_bytes = end_byte - copy_start;
2115 copy_bytes = seg->byte_count - copy_start;
2117 g_assert (copy_bytes != 0); /* Due to iter equality check at
2118 front of this function. */
2122 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2124 g_string_append_len (string,
2125 seg->body.chars + copy_start,
2129 /* printf (" :%s\n", string->str); */
2131 else if (seg->type == >k_text_pixbuf_type ||
2132 seg->type == >k_text_child_type)
2134 gboolean copy = TRUE;
2136 if (!include_nonchars)
2140 else if (!include_hidden &&
2141 _gtk_text_btree_char_is_invisible (start))
2148 g_string_append_len (string,
2149 gtk_text_unknown_char_utf8,
2157 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2158 const GtkTextIter *end_orig,
2159 gboolean include_hidden,
2160 gboolean include_nonchars)
2162 GtkTextLineSegment *seg;
2163 GtkTextLineSegment *end_seg;
2171 g_return_val_if_fail (start_orig != NULL, NULL);
2172 g_return_val_if_fail (end_orig != NULL, NULL);
2173 g_return_val_if_fail (gtk_text_iter_get_btree (start_orig) ==
2174 gtk_text_iter_get_btree (end_orig), NULL);
2176 start = *start_orig;
2179 gtk_text_iter_reorder (&start, &end);
2181 retval = g_string_new ("");
2183 tree = gtk_text_iter_get_btree (&start);
2185 end_seg = gtk_text_iter_get_indexable_segment (&end);
2187 seg = gtk_text_iter_get_indexable_segment (&iter);
2188 while (seg != end_seg)
2190 copy_segment (retval, include_hidden, include_nonchars,
2193 gtk_text_iter_forward_indexable_segment (&iter);
2195 seg = gtk_text_iter_get_indexable_segment (&iter);
2198 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2201 g_string_free (retval, FALSE);
2206 _gtk_text_btree_line_count (GtkTextBTree *tree)
2208 /* Subtract bogus line at the end; we return a count
2210 return tree->root_node->num_lines - 1;
2214 _gtk_text_btree_char_count (GtkTextBTree *tree)
2216 /* Exclude newline in bogus last line */
2217 return tree->root_node->num_chars - 1;
2220 #define LOTSA_TAGS 1000
2222 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2224 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2226 int deftagCnts[LOTSA_TAGS];
2227 int *tagCnts = deftagCnts;
2228 GtkTextTag *deftags[LOTSA_TAGS];
2229 GtkTextTag **tags = deftags;
2231 GtkTextBTreeNode *node;
2232 GtkTextLine *siblingline;
2233 GtkTextLineSegment *seg;
2240 line = gtk_text_iter_get_text_line (iter);
2241 tree = gtk_text_iter_get_btree (iter);
2242 byte_index = gtk_text_iter_get_line_index (iter);
2244 numTags = gtk_text_tag_table_size (tree->table);
2246 /* almost always avoid malloc, so stay out of system calls */
2247 if (LOTSA_TAGS < numTags)
2249 tagCnts = g_new (int, numTags);
2250 tags = g_new (GtkTextTag*, numTags);
2253 for (i=0; i<numTags; i++)
2259 * Record tag toggles within the line of indexPtr but preceding
2263 for (index = 0, seg = line->segments;
2264 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2265 index += seg->byte_count, seg = seg->next)
2267 if ((seg->type == >k_text_toggle_on_type)
2268 || (seg->type == >k_text_toggle_off_type))
2270 tag = seg->body.toggle.info->tag;
2271 if (tag->invisible_set && tag->values->invisible)
2273 tags[tag->priority] = tag;
2274 tagCnts[tag->priority]++;
2280 * Record toggles for tags in lines that are predecessors of
2281 * line but under the same level-0 GtkTextBTreeNode.
2284 for (siblingline = line->parent->children.line;
2285 siblingline != line;
2286 siblingline = siblingline->next)
2288 for (seg = siblingline->segments; seg != NULL;
2291 if ((seg->type == >k_text_toggle_on_type)
2292 || (seg->type == >k_text_toggle_off_type))
2294 tag = seg->body.toggle.info->tag;
2295 if (tag->invisible_set && tag->values->invisible)
2297 tags[tag->priority] = tag;
2298 tagCnts[tag->priority]++;
2305 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2306 * for all siblings that precede that GtkTextBTreeNode.
2309 for (node = line->parent; node->parent != NULL;
2310 node = node->parent)
2312 GtkTextBTreeNode *siblingPtr;
2315 for (siblingPtr = node->parent->children.node;
2316 siblingPtr != node; siblingPtr = siblingPtr->next)
2318 for (summary = siblingPtr->summary; summary != NULL;
2319 summary = summary->next)
2321 if (summary->toggle_count & 1)
2323 tag = summary->info->tag;
2324 if (tag->invisible_set && tag->values->invisible)
2326 tags[tag->priority] = tag;
2327 tagCnts[tag->priority] += summary->toggle_count;
2335 * Now traverse from highest priority to lowest,
2336 * take invisible value from first odd count (= on)
2339 for (i = numTags-1; i >=0; i--)
2343 /* FIXME not sure this should be if 0 */
2345 #ifndef ALWAYS_SHOW_SELECTION
2346 /* who would make the selection invisible? */
2347 if ((tag == tkxt->seltag)
2348 && !(tkxt->flags & GOT_FOCUS))
2354 invisible = tags[i]->values->invisible;
2359 if (LOTSA_TAGS < numTags)
2374 redisplay_region (GtkTextBTree *tree,
2375 const GtkTextIter *start,
2376 const GtkTextIter *end)
2379 GtkTextLine *start_line, *end_line;
2381 if (gtk_text_iter_compare (start, end) > 0)
2383 const GtkTextIter *tmp = start;
2388 start_line = gtk_text_iter_get_text_line (start);
2389 end_line = gtk_text_iter_get_text_line (end);
2392 while (view != NULL)
2394 gint start_y, end_y;
2395 GtkTextLineData *ld;
2397 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2399 if (end_line == start_line)
2402 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2404 ld = _gtk_text_line_get_data (end_line, view->view_id);
2406 end_y += ld->height;
2408 gtk_text_layout_changed (view->layout, start_y,
2417 redisplay_mark (GtkTextLineSegment *mark)
2422 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2424 mark->body.mark.obj);
2427 gtk_text_iter_forward_char (&end);
2429 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2434 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2436 if (!mark->body.mark.visible)
2439 redisplay_mark (mark);
2443 ensure_not_off_end (GtkTextBTree *tree,
2444 GtkTextLineSegment *mark,
2447 if (gtk_text_iter_get_line (iter) ==
2448 _gtk_text_btree_line_count (tree))
2449 gtk_text_iter_backward_char (iter);
2452 static GtkTextLineSegment*
2453 real_set_mark (GtkTextBTree *tree,
2454 GtkTextMark *existing_mark,
2456 gboolean left_gravity,
2457 const GtkTextIter *where,
2458 gboolean should_exist,
2459 gboolean redraw_selections)
2461 GtkTextLineSegment *mark;
2464 g_return_val_if_fail (tree != NULL, NULL);
2465 g_return_val_if_fail (where != NULL, NULL);
2466 g_return_val_if_fail (gtk_text_iter_get_btree (where) == tree, NULL);
2469 mark = existing_mark->segment;
2470 else if (name != NULL)
2471 mark = g_hash_table_lookup (tree->mark_table,
2476 if (should_exist && mark == NULL)
2478 g_warning ("No mark `%s' exists!", name);
2482 /* OK if !should_exist and it does already exist, in that case
2488 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2489 gtk_text_iter_check (&iter);
2493 if (redraw_selections &&
2494 (mark == tree->insert_mark->segment ||
2495 mark == tree->selection_bound_mark->segment))
2497 GtkTextIter old_pos;
2499 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2500 mark->body.mark.obj);
2501 redisplay_region (tree, &old_pos, where);
2505 * don't let visible marks be after the final newline of the
2509 if (mark->body.mark.visible)
2511 ensure_not_off_end (tree, mark, &iter);
2514 /* Redraw the mark's old location. */
2515 redisplay_mark_if_visible (mark);
2517 /* Unlink mark from its current location.
2518 This could hose our iterator... */
2519 gtk_text_btree_unlink_segment (tree, mark,
2520 mark->body.mark.line);
2521 mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
2522 g_assert (mark->body.mark.line == gtk_text_iter_get_text_line (&iter));
2524 segments_changed (tree); /* make sure the iterator recomputes its
2529 mark = _gtk_mark_segment_new (tree,
2533 mark->body.mark.line = gtk_text_iter_get_text_line (&iter);
2535 if (mark->body.mark.name)
2536 g_hash_table_insert (tree->mark_table,
2537 mark->body.mark.name,
2541 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2542 gtk_text_iter_check (&iter);
2544 /* Link mark into new location */
2545 gtk_text_btree_link_segment (mark, &iter);
2547 /* Invalidate some iterators. */
2548 segments_changed (tree);
2551 * update the screen at the mark's new location.
2554 redisplay_mark_if_visible (mark);
2556 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2557 gtk_text_iter_check (&iter);
2559 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2560 _gtk_text_btree_check (tree);
2567 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2568 GtkTextMark *existing_mark,
2570 gboolean left_gravity,
2571 const GtkTextIter *iter,
2572 gboolean should_exist)
2574 GtkTextLineSegment *seg;
2576 seg = real_set_mark (tree, existing_mark,
2577 name, left_gravity, iter, should_exist,
2580 return seg ? seg->body.mark.obj : NULL;
2584 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2588 GtkTextIter tmp_start, tmp_end;
2590 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2592 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2593 tree->selection_bound_mark);
2595 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2607 gtk_text_iter_reorder (&tmp_start, &tmp_end);
2620 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2621 const GtkTextIter *iter)
2623 GtkTextIter start, end;
2625 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2626 redisplay_region (tree, &start, &end);
2628 /* Move insert AND selection_bound before we redisplay */
2629 real_set_mark (tree, tree->insert_mark,
2630 "insert", FALSE, iter, TRUE, FALSE);
2631 real_set_mark (tree, tree->selection_bound_mark,
2632 "selection_bound", FALSE, iter, TRUE, FALSE);
2636 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2641 g_return_if_fail (tree != NULL);
2642 g_return_if_fail (name != NULL);
2644 mark = g_hash_table_lookup (tree->mark_table,
2647 _gtk_text_btree_remove_mark (tree, mark);
2651 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2654 GtkTextLineSegment *segment;
2656 g_return_if_fail (mark != NULL);
2657 g_return_if_fail (tree != NULL);
2659 segment = mark->segment;
2661 if (segment->body.mark.not_deleteable)
2663 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2667 /* This calls cleanup_line and segments_changed */
2668 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2670 if (segment->body.mark.name)
2671 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2673 /* Remove the ref on the mark that belonged to the segment. */
2674 g_object_unref (G_OBJECT (mark));
2676 segment->body.mark.tree = NULL;
2677 segment->body.mark.line = NULL;
2681 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2682 GtkTextMark *segment)
2684 return segment == tree->insert_mark;
2688 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2689 GtkTextMark *segment)
2691 return segment == tree->selection_bound_mark;
2695 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2698 GtkTextLineSegment *seg;
2700 g_return_val_if_fail (tree != NULL, NULL);
2701 g_return_val_if_fail (name != NULL, NULL);
2703 seg = g_hash_table_lookup (tree->mark_table, name);
2705 return seg ? seg->body.mark.obj : NULL;
2709 gtk_text_mark_set_visible (GtkTextMark *mark,
2712 GtkTextLineSegment *seg;
2714 g_return_if_fail (mark != NULL);
2716 seg = mark->segment;
2718 if (seg->body.mark.visible == setting)
2722 seg->body.mark.visible = setting;
2724 redisplay_mark (seg);
2729 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2732 GtkTextBTreeNode *node;
2733 GtkTextTagInfo *info;
2735 g_return_val_if_fail (tree != NULL, NULL);
2739 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2744 if (info->tag_root == NULL)
2747 node = info->tag_root;
2749 /* We know the tag root has instances of the given
2752 continue_outer_loop:
2753 g_assert (node != NULL);
2754 while (node->level > 0)
2756 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2757 node = node->children.node;
2758 while (node != NULL)
2760 if (gtk_text_btree_node_has_tag (node, tag))
2761 goto continue_outer_loop;
2765 g_assert (node != NULL);
2768 g_assert (node != NULL); /* The tag summaries said some node had
2771 g_assert (node->level == 0);
2773 return node->children.line;
2777 /* Looking for any tag at all (tag == NULL).
2778 Unfortunately this can't be done in a simple and efficient way
2779 right now; so I'm just going to return the
2780 first line in the btree. FIXME */
2781 return _gtk_text_btree_get_line (tree, 0, NULL);
2786 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2789 GtkTextBTreeNode *node;
2790 GtkTextBTreeNode *last_node;
2792 GtkTextTagInfo *info;
2794 g_return_val_if_fail (tree != NULL, NULL);
2798 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2800 if (info->tag_root == NULL)
2803 node = info->tag_root;
2804 /* We know the tag root has instances of the given
2807 while (node->level > 0)
2809 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2811 node = node->children.node;
2812 while (node != NULL)
2814 if (gtk_text_btree_node_has_tag (node, tag))
2822 g_assert (node != NULL); /* The tag summaries said some node had
2825 g_assert (node->level == 0);
2827 /* Find the last line in this node */
2828 line = node->children.line;
2829 while (line->next != NULL)
2836 /* This search can't be done efficiently at the moment,
2837 at least not without complexity.
2838 So, we just return the last line.
2840 return _gtk_text_btree_get_line (tree, -1, NULL);
2850 _gtk_text_line_get_number (GtkTextLine *line)
2853 GtkTextBTreeNode *node, *parent, *node2;
2857 * First count how many lines precede this one in its level-0
2861 node = line->parent;
2863 for (line2 = node->children.line; line2 != line;
2864 line2 = line2->next)
2868 g_error ("gtk_text_btree_line_number couldn't find line");
2874 * Now work up through the levels of the tree one at a time,
2875 * counting how many lines are in GtkTextBTreeNodes preceding the current
2879 for (parent = node->parent ; parent != NULL;
2880 node = parent, parent = parent->parent)
2882 for (node2 = parent->children.node; node2 != node;
2883 node2 = node2->next)
2887 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2889 index += node2->num_lines;
2895 static GtkTextLineSegment*
2896 find_toggle_segment_before_char (GtkTextLine *line,
2900 GtkTextLineSegment *seg;
2901 GtkTextLineSegment *toggle_seg;
2906 seg = line->segments;
2907 while ( (index + seg->char_count) <= char_in_line )
2909 if (((seg->type == >k_text_toggle_on_type)
2910 || (seg->type == >k_text_toggle_off_type))
2911 && (seg->body.toggle.info->tag == tag))
2914 index += seg->char_count;
2921 static GtkTextLineSegment*
2922 find_toggle_segment_before_byte (GtkTextLine *line,
2926 GtkTextLineSegment *seg;
2927 GtkTextLineSegment *toggle_seg;
2932 seg = line->segments;
2933 while ( (index + seg->byte_count) <= byte_in_line )
2935 if (((seg->type == >k_text_toggle_on_type)
2936 || (seg->type == >k_text_toggle_off_type))
2937 && (seg->body.toggle.info->tag == tag))
2940 index += seg->byte_count;
2948 find_toggle_outside_current_line (GtkTextLine *line,
2952 GtkTextBTreeNode *node;
2953 GtkTextLine *sibling_line;
2954 GtkTextLineSegment *seg;
2955 GtkTextLineSegment *toggle_seg;
2957 GtkTextTagInfo *info = NULL;
2960 * No toggle in this line. Look for toggles for the tag in lines
2961 * that are predecessors of line but under the same
2962 * level-0 GtkTextBTreeNode.
2965 sibling_line = line->parent->children.line;
2966 while (sibling_line != line)
2968 seg = sibling_line->segments;
2971 if (((seg->type == >k_text_toggle_on_type)
2972 || (seg->type == >k_text_toggle_off_type))
2973 && (seg->body.toggle.info->tag == tag))
2979 sibling_line = sibling_line->next;
2982 if (toggle_seg != NULL)
2983 return (toggle_seg->type == >k_text_toggle_on_type);
2986 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
2987 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
2988 * siblings that precede that GtkTextBTreeNode.
2991 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2997 node = line->parent;
2998 while (node->parent != NULL)
3000 GtkTextBTreeNode *sibling_node;
3002 sibling_node = node->parent->children.node;
3003 while (sibling_node != node)
3007 summary = sibling_node->summary;
3008 while (summary != NULL)
3010 if (summary->info == info)
3011 toggles += summary->toggle_count;
3013 summary = summary->next;
3016 sibling_node = sibling_node->next;
3019 if (node == info->tag_root)
3022 node = node->parent;
3026 * An odd number of toggles means that the tag is present at the
3030 return (toggles & 1) != 0;
3033 /* FIXME this function is far too slow, for no good reason. */
3035 _gtk_text_line_char_has_tag (GtkTextLine *line,
3040 GtkTextLineSegment *toggle_seg;
3042 g_return_val_if_fail (line != NULL, FALSE);
3045 * Check for toggles for the tag in the line but before
3046 * the char. If there is one, its type indicates whether or
3047 * not the character is tagged.
3050 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3052 if (toggle_seg != NULL)
3053 return (toggle_seg->type == >k_text_toggle_on_type);
3055 return find_toggle_outside_current_line (line, tree, tag);
3059 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3064 GtkTextLineSegment *toggle_seg;
3066 g_return_val_if_fail (line != NULL, FALSE);
3069 * Check for toggles for the tag in the line but before
3070 * the char. If there is one, its type indicates whether or
3071 * not the character is tagged.
3074 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3076 if (toggle_seg != NULL)
3077 return (toggle_seg->type == >k_text_toggle_on_type);
3079 return find_toggle_outside_current_line (line, tree, tag);
3083 _gtk_text_line_is_last (GtkTextLine *line,
3086 return line == get_last_line (tree);
3090 _gtk_text_line_next (GtkTextLine *line)
3092 GtkTextBTreeNode *node;
3094 if (line->next != NULL)
3099 * This was the last line associated with the particular parent
3100 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3101 * then search down from that GtkTextBTreeNode to find the first
3105 node = line->parent;
3106 while (node != NULL && node->next == NULL)
3107 node = node->parent;
3113 while (node->level > 0)
3115 node = node->children.node;
3118 g_assert (node->children.line != line);
3120 return node->children.line;
3125 _gtk_text_line_previous (GtkTextLine *line)
3127 GtkTextBTreeNode *node;
3128 GtkTextBTreeNode *node2;
3132 * Find the line under this GtkTextBTreeNode just before the starting line.
3134 prev = line->parent->children.line; /* First line at leaf */
3135 while (prev != line)
3137 if (prev->next == line)
3143 g_error ("gtk_text_btree_previous_line ran out of lines");
3147 * This was the first line associated with the particular parent
3148 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3149 * then search down from that GtkTextBTreeNode to find its last line.
3151 for (node = line->parent; ; node = node->parent)
3153 if (node == NULL || node->parent == NULL)
3155 else if (node != node->parent->children.node)
3159 for (node2 = node->parent->children.node; ;
3160 node2 = node2->children.node)
3162 while (node2->next != node)
3163 node2 = node2->next;
3165 if (node2->level == 0)
3171 for (prev = node2->children.line ; ; prev = prev->next)
3173 if (prev->next == NULL)
3177 g_assert_not_reached ();
3182 _gtk_text_line_add_data (GtkTextLine *line,
3183 GtkTextLineData *data)
3185 g_return_if_fail (line != NULL);
3186 g_return_if_fail (data != NULL);
3187 g_return_if_fail (data->view_id != NULL);
3191 data->next = line->views;
3201 _gtk_text_line_remove_data (GtkTextLine *line,
3204 GtkTextLineData *prev;
3205 GtkTextLineData *iter;
3207 g_return_val_if_fail (line != NULL, NULL);
3208 g_return_val_if_fail (view_id != NULL, NULL);
3212 while (iter != NULL)
3214 if (iter->view_id == view_id)
3223 prev->next = iter->next;
3225 line->views = iter->next;
3234 _gtk_text_line_get_data (GtkTextLine *line,
3237 GtkTextLineData *iter;
3239 g_return_val_if_fail (line != NULL, NULL);
3240 g_return_val_if_fail (view_id != NULL, NULL);
3243 while (iter != NULL)
3245 if (iter->view_id == view_id)
3254 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3255 GtkTextLineData *ld)
3257 /* For now this is totally unoptimized. FIXME?
3259 We could probably optimize the case where the width removed
3260 is less than the max width for the parent node,
3261 and the case where the height is unchanged when we re-wrap.
3264 g_return_if_fail (ld != NULL);
3267 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3271 _gtk_text_line_char_count (GtkTextLine *line)
3273 GtkTextLineSegment *seg;
3277 seg = line->segments;
3280 size += seg->char_count;
3287 _gtk_text_line_byte_count (GtkTextLine *line)
3289 GtkTextLineSegment *seg;
3293 seg = line->segments;
3296 size += seg->byte_count;
3304 _gtk_text_line_char_index (GtkTextLine *target_line)
3306 GSList *node_stack = NULL;
3307 GtkTextBTreeNode *iter;
3311 /* Push all our parent nodes onto a stack */
3312 iter = target_line->parent;
3314 g_assert (iter != NULL);
3316 while (iter != NULL)
3318 node_stack = g_slist_prepend (node_stack, iter);
3320 iter = iter->parent;
3323 /* Check that we have the root node on top of the stack. */
3324 g_assert (node_stack != NULL &&
3325 node_stack->data != NULL &&
3326 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3328 /* Add up chars in all nodes before the nodes in our stack.
3332 iter = node_stack->data;
3333 while (iter != NULL)
3335 GtkTextBTreeNode *child_iter;
3336 GtkTextBTreeNode *next_node;
3338 next_node = node_stack->next ?
3339 node_stack->next->data : NULL;
3340 node_stack = g_slist_remove (node_stack, node_stack->data);
3342 if (iter->level == 0)
3344 /* stack should be empty when we're on the last node */
3345 g_assert (node_stack == NULL);
3346 break; /* Our children are now lines */
3349 g_assert (next_node != NULL);
3350 g_assert (iter != NULL);
3351 g_assert (next_node->parent == iter);
3353 /* Add up chars before us in the tree */
3354 child_iter = iter->children.node;
3355 while (child_iter != next_node)
3357 g_assert (child_iter != NULL);
3359 num_chars += child_iter->num_chars;
3361 child_iter = child_iter->next;
3367 g_assert (iter != NULL);
3368 g_assert (iter == target_line->parent);
3370 /* Since we don't store char counts in lines, only in segments, we
3371 have to iterate over the lines adding up segment char counts
3372 until we find our line. */
3373 line = iter->children.line;
3374 while (line != target_line)
3376 g_assert (line != NULL);
3378 num_chars += _gtk_text_line_char_count (line);
3383 g_assert (line == target_line);
3389 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3393 GtkTextLineSegment *seg;
3396 g_return_val_if_fail (line != NULL, NULL);
3398 offset = byte_offset;
3399 seg = line->segments;
3401 while (offset >= seg->byte_count)
3403 g_assert (seg != NULL); /* means an invalid byte index */
3404 offset -= seg->byte_count;
3409 *seg_offset = offset;
3415 _gtk_text_line_char_to_segment (GtkTextLine *line,
3419 GtkTextLineSegment *seg;
3422 g_return_val_if_fail (line != NULL, NULL);
3424 offset = char_offset;
3425 seg = line->segments;
3427 while (offset >= seg->char_count)
3429 g_assert (seg != NULL); /* means an invalid char index */
3430 offset -= seg->char_count;
3435 *seg_offset = offset;
3441 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3445 GtkTextLineSegment *seg;
3448 g_return_val_if_fail (line != NULL, NULL);
3450 offset = byte_offset;
3451 seg = line->segments;
3453 while (offset > 0 && offset >= seg->byte_count)
3455 g_assert (seg != NULL); /* means an invalid byte index */
3456 offset -= seg->byte_count;
3461 *seg_offset = offset;
3467 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3471 GtkTextLineSegment *seg;
3474 g_return_val_if_fail (line != NULL, NULL);
3476 offset = char_offset;
3477 seg = line->segments;
3479 while (offset > 0 && offset >= seg->char_count)
3481 g_assert (seg != NULL); /* means an invalid byte index */
3482 offset -= seg->char_count;
3487 *seg_offset = offset;
3493 _gtk_text_line_byte_to_char (GtkTextLine *line,
3497 GtkTextLineSegment *seg;
3499 g_return_val_if_fail (line != NULL, 0);
3500 g_return_val_if_fail (byte_offset >= 0, 0);
3503 seg = line->segments;
3504 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3505 the next segment) */
3507 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3509 byte_offset -= seg->byte_count;
3510 char_offset += seg->char_count;
3515 g_assert (seg != NULL);
3517 /* Now byte_offset is the offset into the current segment,
3518 and char_offset is the start of the current segment.
3519 Optimize the case where no chars use > 1 byte */
3520 if (seg->byte_count == seg->char_count)
3521 return char_offset + byte_offset;
3524 if (seg->type == >k_text_char_type)
3525 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3528 g_assert (seg->char_count == 1);
3529 g_assert (byte_offset == 0);
3537 _gtk_text_line_char_to_byte (GtkTextLine *line,
3540 g_warning ("FIXME not implemented");
3545 /* FIXME sync with char_locate (or figure out a clean
3546 way to merge the two functions) */
3548 _gtk_text_line_byte_locate (GtkTextLine *line,
3550 GtkTextLineSegment **segment,
3551 GtkTextLineSegment **any_segment,
3552 gint *seg_byte_offset,
3553 gint *line_byte_offset)
3555 GtkTextLineSegment *seg;
3556 GtkTextLineSegment *after_prev_indexable;
3557 GtkTextLineSegment *after_last_indexable;
3558 GtkTextLineSegment *last_indexable;
3562 g_return_val_if_fail (line != NULL, FALSE);
3563 g_return_val_if_fail (byte_offset >= 0, FALSE);
3566 *any_segment = NULL;
3569 offset = byte_offset;
3571 last_indexable = NULL;
3572 after_last_indexable = line->segments;
3573 after_prev_indexable = line->segments;
3574 seg = line->segments;
3576 /* The loop ends when we're inside a segment;
3577 last_indexable refers to the last segment
3578 we passed entirely. */
3579 while (seg && offset >= seg->byte_count)
3581 if (seg->char_count > 0)
3583 offset -= seg->byte_count;
3584 bytes_in_line += seg->byte_count;
3585 last_indexable = seg;
3586 after_prev_indexable = after_last_indexable;
3587 after_last_indexable = last_indexable->next;
3595 /* We went off the end of the line */
3597 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3604 if (after_last_indexable != NULL)
3605 *any_segment = after_last_indexable;
3607 *any_segment = *segment;
3610 /* Override any_segment if we're in the middle of a segment. */
3612 *any_segment = *segment;
3614 *seg_byte_offset = offset;
3616 g_assert (*segment != NULL);
3617 g_assert (*any_segment != NULL);
3618 g_assert (*seg_byte_offset < (*segment)->byte_count);
3620 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3625 /* FIXME sync with byte_locate (or figure out a clean
3626 way to merge the two functions) */
3628 _gtk_text_line_char_locate (GtkTextLine *line,
3630 GtkTextLineSegment **segment,
3631 GtkTextLineSegment **any_segment,
3632 gint *seg_char_offset,
3633 gint *line_char_offset)
3635 GtkTextLineSegment *seg;
3636 GtkTextLineSegment *after_prev_indexable;
3637 GtkTextLineSegment *after_last_indexable;
3638 GtkTextLineSegment *last_indexable;
3642 g_return_val_if_fail (line != NULL, FALSE);
3643 g_return_val_if_fail (char_offset >= 0, FALSE);
3646 *any_segment = NULL;
3649 offset = char_offset;
3651 last_indexable = NULL;
3652 after_last_indexable = line->segments;
3653 after_prev_indexable = line->segments;
3654 seg = line->segments;
3656 /* The loop ends when we're inside a segment;
3657 last_indexable refers to the last segment
3658 we passed entirely. */
3659 while (seg && offset >= seg->char_count)
3661 if (seg->char_count > 0)
3663 offset -= seg->char_count;
3664 chars_in_line += seg->char_count;
3665 last_indexable = seg;
3666 after_prev_indexable = after_last_indexable;
3667 after_last_indexable = last_indexable->next;
3675 /* end of the line */
3677 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3684 if (after_last_indexable != NULL)
3685 *any_segment = after_last_indexable;
3687 *any_segment = *segment;
3690 /* Override any_segment if we're in the middle of a segment. */
3692 *any_segment = *segment;
3694 *seg_char_offset = offset;
3696 g_assert (*segment != NULL);
3697 g_assert (*any_segment != NULL);
3698 g_assert (*seg_char_offset < (*segment)->char_count);
3700 *line_char_offset = chars_in_line + *seg_char_offset;
3706 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3708 gint *line_char_offset,
3709 gint *seg_char_offset)
3711 GtkTextLineSegment *seg;
3714 g_return_if_fail (line != NULL);
3715 g_return_if_fail (byte_offset >= 0);
3717 *line_char_offset = 0;
3719 offset = byte_offset;
3720 seg = line->segments;
3722 while (offset >= seg->byte_count)
3724 offset -= seg->byte_count;
3725 *line_char_offset += seg->char_count;
3727 g_assert (seg != NULL); /* means an invalid char offset */
3730 g_assert (seg->char_count > 0); /* indexable. */
3732 /* offset is now the number of bytes into the current segment we
3733 * want to go. Count chars into the current segment.
3736 if (seg->type == >k_text_char_type)
3738 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3740 g_assert (*seg_char_offset < seg->char_count);
3742 *line_char_offset += *seg_char_offset;
3746 g_assert (offset == 0);
3747 *seg_char_offset = 0;
3752 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3754 gint *line_byte_offset,
3755 gint *seg_byte_offset)
3757 GtkTextLineSegment *seg;
3760 g_return_if_fail (line != NULL);
3761 g_return_if_fail (char_offset >= 0);
3763 *line_byte_offset = 0;
3765 offset = char_offset;
3766 seg = line->segments;
3768 while (offset >= seg->char_count)
3770 offset -= seg->char_count;
3771 *line_byte_offset += seg->byte_count;
3773 g_assert (seg != NULL); /* means an invalid char offset */
3776 g_assert (seg->char_count > 0); /* indexable. */
3778 /* offset is now the number of chars into the current segment we
3779 want to go. Count bytes into the current segment. */
3781 if (seg->type == >k_text_char_type)
3783 *seg_byte_offset = 0;
3787 const char * start = seg->body.chars + *seg_byte_offset;
3789 bytes = g_utf8_next_char (start) - start;
3790 *seg_byte_offset += bytes;
3794 g_assert (*seg_byte_offset < seg->byte_count);
3796 *line_byte_offset += *seg_byte_offset;
3800 g_assert (offset == 0);
3801 *seg_byte_offset = 0;
3806 node_compare (GtkTextBTreeNode *lhs,
3807 GtkTextBTreeNode *rhs)
3809 GtkTextBTreeNode *iter;
3810 GtkTextBTreeNode *node;
3811 GtkTextBTreeNode *common_parent;
3812 GtkTextBTreeNode *parent_of_lower;
3813 GtkTextBTreeNode *parent_of_higher;
3814 gboolean lhs_is_lower;
3815 GtkTextBTreeNode *lower;
3816 GtkTextBTreeNode *higher;
3818 /* This function assumes that lhs and rhs are not underneath each
3825 if (lhs->level < rhs->level)
3827 lhs_is_lower = TRUE;
3833 lhs_is_lower = FALSE;
3838 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
3839 * of the common parent we used to reach the common parent; the
3840 * ordering of these child nodes in the child list is the ordering
3844 /* Get on the same level (may be on same level already) */
3846 while (node->level < higher->level)
3847 node = node->parent;
3849 g_assert (node->level == higher->level);
3851 g_assert (node != higher); /* Happens if lower is underneath higher */
3853 /* Go up until we have two children with a common parent.
3855 parent_of_lower = node;
3856 parent_of_higher = higher;
3858 while (parent_of_lower->parent != parent_of_higher->parent)
3860 parent_of_lower = parent_of_lower->parent;
3861 parent_of_higher = parent_of_higher->parent;
3864 g_assert (parent_of_lower->parent == parent_of_higher->parent);
3866 common_parent = parent_of_lower->parent;
3868 g_assert (common_parent != NULL);
3870 /* See which is first in the list of common_parent's children */
3871 iter = common_parent->children.node;
3872 while (iter != NULL)
3874 if (iter == parent_of_higher)
3876 /* higher is less than lower */
3879 return 1; /* lhs > rhs */
3883 else if (iter == parent_of_lower)
3885 /* lower is less than higher */
3888 return -1; /* lhs < rhs */
3896 g_assert_not_reached ();
3900 /* remember that tag == NULL means "any tag" */
3902 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
3906 GtkTextBTreeNode *node;
3907 GtkTextTagInfo *info;
3908 gboolean below_tag_root;
3910 g_return_val_if_fail (line != NULL, NULL);
3912 if (gtk_debug_flags & GTK_DEBUG_TEXT)
3913 _gtk_text_btree_check (tree);
3917 /* Right now we can only offer linear-search if the user wants
3918 * to know about any tag toggle at all.
3920 return _gtk_text_line_next (line);
3923 /* Our tag summaries only have node precision, not line
3924 precision. This means that if any line under a node could contain a
3925 tag, then any of the others could also contain a tag.
3927 In the future we could have some mechanism to keep track of how
3928 many toggles we've found under a node so far, since we have a
3929 count of toggles under the node. But for now I'm going with KISS.
3932 /* return same-node line, if any. */
3936 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3940 if (info->tag_root == NULL)
3943 if (info->tag_root == line->parent)
3944 return NULL; /* we were at the last line under the tag root */
3946 /* We need to go up out of this node, and on to the next one with
3947 toggles for the target tag. If we're below the tag root, we need to
3948 find the next node below the tag root that has tag summaries. If
3949 we're not below the tag root, we need to see if the tag root is
3950 after us in the tree, and if so, return the first line underneath
3953 node = line->parent;
3954 below_tag_root = FALSE;
3955 while (node != NULL)
3957 if (node == info->tag_root)
3959 below_tag_root = TRUE;
3963 node = node->parent;
3968 node = line->parent;
3969 while (node != info->tag_root)
3971 if (node->next == NULL)
3972 node = node->parent;
3977 if (gtk_text_btree_node_has_tag (node, tag))
3987 ordering = node_compare (line->parent, info->tag_root);
3991 /* Tag root is ahead of us, so search there. */
3992 node = info->tag_root;
3997 /* Tag root is after us, so no more lines that
3998 * could contain the tag.
4003 g_assert_not_reached ();
4008 g_assert (node != NULL);
4010 /* We have to find the first sub-node of this node that contains
4014 while (node->level > 0)
4016 g_assert (node != NULL); /* If this fails, it likely means an
4017 incorrect tag summary led us on a
4018 wild goose chase down this branch of
4020 node = node->children.node;
4021 while (node != NULL)
4023 if (gtk_text_btree_node_has_tag (node, tag))
4029 g_assert (node != NULL);
4030 g_assert (node->level == 0);
4032 return node->children.line;
4036 prev_line_under_node (GtkTextBTreeNode *node,
4041 prev = node->children.line;
4047 while (prev->next != line)
4057 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4061 GtkTextBTreeNode *node;
4062 GtkTextBTreeNode *found_node = NULL;
4063 GtkTextTagInfo *info;
4064 gboolean below_tag_root;
4066 GtkTextBTreeNode *line_ancestor;
4067 GtkTextBTreeNode *line_ancestor_parent;
4069 /* See next_could_contain_tag () for more extensive comments
4070 * on what's going on here.
4073 g_return_val_if_fail (line != NULL, NULL);
4075 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4076 _gtk_text_btree_check (tree);
4080 /* Right now we can only offer linear-search if the user wants
4081 * to know about any tag toggle at all.
4083 return _gtk_text_line_previous (line);
4086 /* Return same-node line, if any. */
4087 prev = prev_line_under_node (line->parent, line);
4091 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4095 if (info->tag_root == NULL)
4098 if (info->tag_root == line->parent)
4099 return NULL; /* we were at the first line under the tag root */
4101 /* Are we below the tag root */
4102 node = line->parent;
4103 below_tag_root = FALSE;
4104 while (node != NULL)
4106 if (node == info->tag_root)
4108 below_tag_root = TRUE;
4112 node = node->parent;
4117 /* Look for a previous node under this tag root that has our
4121 /* this assertion holds because line->parent is not the
4122 * tag root, we are below the tag root, and the tag
4125 g_assert (line->parent->parent != NULL);
4127 line_ancestor = line->parent;
4128 line_ancestor_parent = line->parent->parent;
4130 node = line_ancestor_parent->children.node;
4131 while (node != line_ancestor &&
4132 line_ancestor != info->tag_root)
4134 GSList *child_nodes = NULL;
4137 /* Create reverse-order list of nodes before
4140 while (node != line_ancestor
4143 child_nodes = g_slist_prepend (child_nodes, node);
4148 /* Try to find a node with our tag on it in the list */
4152 GtkTextBTreeNode *this_node = tmp->data;
4154 g_assert (this_node != line_ancestor);
4156 if (gtk_text_btree_node_has_tag (this_node, tag))
4158 found_node = this_node;
4159 g_slist_free (child_nodes);
4163 tmp = g_slist_next (tmp);
4166 g_slist_free (child_nodes);
4168 /* Didn't find anything on this level; go up one level. */
4169 line_ancestor = line_ancestor_parent;
4170 line_ancestor_parent = line_ancestor->parent;
4172 node = line_ancestor_parent->children.node;
4182 ordering = node_compare (line->parent, info->tag_root);
4186 /* Tag root is ahead of us, so no more lines
4193 /* Tag root is after us, so grab last tagged
4194 * line underneath the tag root.
4196 found_node = info->tag_root;
4200 g_assert_not_reached ();
4205 g_assert (found_node != NULL);
4207 /* We have to find the last sub-node of this node that contains
4212 while (node->level > 0)
4214 GSList *child_nodes = NULL;
4216 g_assert (node != NULL); /* If this fails, it likely means an
4217 incorrect tag summary led us on a
4218 wild goose chase down this branch of
4221 node = node->children.node;
4222 while (node != NULL)
4224 child_nodes = g_slist_prepend (child_nodes, node);
4228 node = NULL; /* detect failure to find a child node. */
4231 while (iter != NULL)
4233 if (gtk_text_btree_node_has_tag (iter->data, tag))
4235 /* recurse into this node. */
4240 iter = g_slist_next (iter);
4243 g_slist_free (child_nodes);
4245 g_assert (node != NULL);
4248 g_assert (node != NULL);
4249 g_assert (node->level == 0);
4251 /* this assertion is correct, but slow. */
4252 /* g_assert (node_compare (node, line->parent) < 0); */
4254 /* Return last line in this node. */
4256 prev = node->children.line;
4264 * Non-public function implementations
4268 summary_list_destroy (Summary *summary)
4271 while (summary != NULL)
4273 next = summary->next;
4274 summary_destroy (summary);
4280 get_last_line (GtkTextBTree *tree)
4282 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
4288 n_lines = _gtk_text_btree_line_count (tree);
4290 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4292 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4294 tree->end_iter_line_stamp = tree->chars_changed_stamp;
4295 tree->end_iter_line = line;
4298 return tree->end_iter_line;
4306 gtk_text_line_new (void)
4310 line = g_new0(GtkTextLine, 1);
4316 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4318 GtkTextLineData *ld;
4319 GtkTextLineData *next;
4321 g_return_if_fail (line != NULL);
4328 view = gtk_text_btree_get_view (tree, ld->view_id);
4330 g_assert (view != NULL);
4333 gtk_text_layout_free_line_data (view->layout, line, ld);
4342 gtk_text_line_set_parent (GtkTextLine *line,
4343 GtkTextBTreeNode *node)
4345 if (line->parent == node)
4347 line->parent = node;
4348 gtk_text_btree_node_invalidate_upward (node, NULL);
4352 cleanup_line (GtkTextLine *line)
4354 GtkTextLineSegment *seg, **prev_p;
4358 * Make a pass over all of the segments in the line, giving each
4359 * a chance to clean itself up. This could potentially change
4360 * the structure of the line, e.g. by merging two segments
4361 * together or having two segments cancel themselves; if so,
4362 * then repeat the whole process again, since the first structure
4363 * change might make other structure changes possible. Repeat
4364 * until eventually there are no changes.
4371 for (prev_p = &line->segments, seg = *prev_p;
4373 prev_p = &(*prev_p)->next, seg = *prev_p)
4375 if (seg->type->cleanupFunc != NULL)
4377 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4390 node_data_new (gpointer view_id)
4394 nd = g_new (NodeData, 1);
4396 nd->view_id = view_id;
4406 node_data_destroy (NodeData *nd)
4413 node_data_list_destroy (NodeData *nd)
4419 while (iter != NULL)
4422 node_data_destroy (iter);
4428 node_data_find (NodeData *nd, gpointer view_id)
4432 if (nd->view_id == view_id)
4440 summary_destroy (Summary *summary)
4442 /* Fill with error-triggering garbage */
4443 summary->info = (void*)0x1;
4444 summary->toggle_count = 567;
4445 summary->next = (void*)0x1;
4449 static GtkTextBTreeNode*
4450 gtk_text_btree_node_new (void)
4452 GtkTextBTreeNode *node;
4454 node = g_new (GtkTextBTreeNode, 1);
4456 node->node_data = NULL;
4462 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4463 GtkTextTagInfo *info,
4468 summary = node->summary;
4469 while (summary != NULL)
4471 if (summary->info == info)
4473 summary->toggle_count += adjust;
4477 summary = summary->next;
4480 if (summary == NULL)
4482 /* didn't find a summary for our tag. */
4483 g_return_if_fail (adjust > 0);
4484 summary = g_new (Summary, 1);
4485 summary->info = info;
4486 summary->toggle_count = adjust;
4487 summary->next = node->summary;
4488 node->summary = summary;
4492 /* Note that the tag root and above do not have summaries
4493 for the tag; only nodes below the tag root have
4496 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4500 summary = node->summary;
4501 while (summary != NULL)
4504 summary->info->tag == tag)
4507 summary = summary->next;
4513 /* Add node and all children to the damage region. */
4515 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4519 nd = node->node_data;
4526 if (node->level == 0)
4530 line = node->children.line;
4531 while (line != NULL)
4533 GtkTextLineData *ld;
4547 GtkTextBTreeNode *child;
4549 child = node->children.node;
4551 while (child != NULL)
4553 gtk_text_btree_node_invalidate_downward (child);
4555 child = child->next;
4561 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4563 GtkTextBTreeNode *iter;
4566 while (iter != NULL)
4572 nd = node_data_find (iter->node_data, view_id);
4574 if (nd == NULL || !nd->valid)
4575 break; /* Once a node is invalid, we know its parents are as well. */
4581 gboolean should_continue = FALSE;
4583 nd = iter->node_data;
4588 should_continue = TRUE;
4595 if (!should_continue)
4596 break; /* This node was totally invalidated, so are its
4600 iter = iter->parent;
4606 * _gtk_text_btree_is_valid:
4607 * @tree: a #GtkTextBTree
4608 * @view_id: ID for the view
4610 * Check to see if the entire #GtkTextBTree is valid or not for
4613 * Return value: %TRUE if the entire #GtkTextBTree is valid
4616 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4620 g_return_val_if_fail (tree != NULL, FALSE);
4622 nd = node_data_find (tree->root_node->node_data, view_id);
4623 return (nd && nd->valid);
4626 typedef struct _ValidateState ValidateState;
4628 struct _ValidateState
4630 gint remaining_pixels;
4631 gboolean in_validation;
4638 gtk_text_btree_node_validate (BTreeView *view,
4639 GtkTextBTreeNode *node,
4641 ValidateState *state)
4643 gint node_valid = TRUE;
4644 gint node_width = 0;
4645 gint node_height = 0;
4647 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4648 g_return_if_fail (!nd->valid);
4650 if (node->level == 0)
4652 GtkTextLine *line = node->children.line;
4653 GtkTextLineData *ld;
4655 /* Iterate over leading valid lines */
4656 while (line != NULL)
4658 ld = _gtk_text_line_get_data (line, view_id);
4660 if (!ld || !ld->valid)
4662 else if (state->in_validation)
4664 state->in_validation = FALSE;
4669 state->y += ld->height;
4670 node_width = MAX (ld->width, node_width);
4671 node_height += ld->height;
4677 state->in_validation = TRUE;
4679 /* Iterate over invalid lines */
4680 while (line != NULL)
4682 ld = _gtk_text_line_get_data (line, view_id);
4684 if (ld && ld->valid)
4689 state->old_height += ld->height;
4690 ld = gtk_text_layout_wrap (view->layout, line, ld);
4691 state->new_height += ld->height;
4693 node_width = MAX (ld->width, node_width);
4694 node_height += ld->height;
4696 state->remaining_pixels -= ld->height;
4697 if (state->remaining_pixels <= 0)
4707 /* Iterate over the remaining lines */
4708 while (line != NULL)
4710 ld = _gtk_text_line_get_data (line, view_id);
4711 state->in_validation = FALSE;
4713 if (!ld || !ld->valid)
4718 node_width = MAX (ld->width, node_width);
4719 node_height += ld->height;
4727 GtkTextBTreeNode *child;
4730 child = node->children.node;
4732 /* Iterate over leading valid nodes */
4735 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4737 if (!child_nd->valid)
4739 else if (state->in_validation)
4741 state->in_validation = FALSE;
4746 state->y += child_nd->height;
4747 node_width = MAX (node_width, child_nd->width);
4748 node_height += child_nd->height;
4751 child = child->next;
4754 /* Iterate over invalid nodes */
4757 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4759 if (child_nd->valid)
4763 gtk_text_btree_node_validate (view, child, view_id, state);
4765 if (!child_nd->valid)
4767 node_width = MAX (node_width, child_nd->width);
4768 node_height += child_nd->height;
4770 if (!state->in_validation || state->remaining_pixels <= 0)
4772 child = child->next;
4777 child = child->next;
4780 /* Iterate over the remaining lines */
4783 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4784 state->in_validation = FALSE;
4786 if (!child_nd->valid)
4789 node_width = MAX (child_nd->width, node_width);
4790 node_height += child_nd->height;
4792 child = child->next;
4796 nd->width = node_width;
4797 nd->height = node_height;
4798 nd->valid = node_valid;
4802 * _gtk_text_btree_validate:
4803 * @tree: a #GtkTextBTree
4805 * @max_pixels: the maximum number of pixels to validate. (No more
4806 * than one paragraph beyond this limit will be validated)
4807 * @y: location to store starting y coordinate of validated region
4808 * @old_height: location to store old height of validated region
4809 * @new_height: location to store new height of validated region
4811 * Validate a single contiguous invalid region of a #GtkTextBTree for
4814 * Return value: %TRUE if a region has been validated, %FALSE if the
4815 * entire tree was already valid.
4818 _gtk_text_btree_validate (GtkTextBTree *tree,
4827 g_return_val_if_fail (tree != NULL, FALSE);
4829 view = gtk_text_btree_get_view (tree, view_id);
4830 g_return_val_if_fail (view != NULL, FALSE);
4832 if (!_gtk_text_btree_is_valid (tree, view_id))
4834 ValidateState state;
4836 state.remaining_pixels = max_pixels;
4837 state.in_validation = FALSE;
4839 state.old_height = 0;
4840 state.new_height = 0;
4842 gtk_text_btree_node_validate (view,
4849 *old_height = state.old_height;
4851 *new_height = state.new_height;
4853 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4854 _gtk_text_btree_check (tree);
4863 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
4867 gboolean *valid_out)
4871 gboolean valid = TRUE;
4873 if (node->level == 0)
4875 GtkTextLine *line = node->children.line;
4877 while (line != NULL)
4879 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
4881 if (!ld || !ld->valid)
4886 width = MAX (ld->width, width);
4887 height += ld->height;
4895 GtkTextBTreeNode *child = node->children.node;
4899 NodeData *child_nd = node_data_find (child->node_data, view_id);
4901 if (!child_nd || !child_nd->valid)
4906 width = MAX (child_nd->width, width);
4907 height += child_nd->height;
4910 child = child->next;
4915 *height_out = height;
4920 /* Recompute the validity and size of the view data for a given
4921 * view at this node from the immediate children of the node
4924 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
4927 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4932 gtk_text_btree_node_compute_view_aggregates (node, view_id,
4933 &width, &height, &valid);
4935 nd->height = height;
4942 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
4947 gtk_text_btree_node_check_valid (node, view_id);
4948 node = node->parent;
4953 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
4956 if (node->level == 0)
4958 return gtk_text_btree_node_check_valid (node, view_id);
4962 GtkTextBTreeNode *child = node->children.node;
4964 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4972 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
4974 if (!child_nd->valid)
4976 nd->width = MAX (child_nd->width, nd->width);
4977 nd->height += child_nd->height;
4979 child = child->next;
4988 * _gtk_text_btree_validate_line:
4989 * @tree: a #GtkTextBTree
4990 * @line: line to validate
4991 * @view_id: view ID for the view to validate
4993 * Revalidate a single line of the btree for the given view, propagate
4994 * results up through the entire tree.
4997 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5001 GtkTextLineData *ld;
5004 g_return_if_fail (tree != NULL);
5005 g_return_if_fail (line != NULL);
5007 view = gtk_text_btree_get_view (tree, view_id);
5008 g_return_if_fail (view != NULL);
5010 ld = _gtk_text_line_get_data (line, view_id);
5011 if (!ld || !ld->valid)
5013 ld = gtk_text_layout_wrap (view->layout, line, ld);
5015 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5020 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5022 if (node->level == 0)
5026 line = node->children.line;
5027 while (line != NULL)
5029 GtkTextLineData *ld;
5031 ld = _gtk_text_line_remove_data (line, view_id);
5034 gtk_text_layout_free_line_data (view->layout, line, ld);
5041 GtkTextBTreeNode *child;
5043 child = node->children.node;
5045 while (child != NULL)
5048 gtk_text_btree_node_remove_view (view, child, view_id);
5050 child = child->next;
5054 gtk_text_btree_node_remove_data (node, view_id);
5058 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5060 if (node->level == 0)
5063 GtkTextLineSegment *seg;
5065 while (node->children.line != NULL)
5067 line = node->children.line;
5068 node->children.line = line->next;
5069 while (line->segments != NULL)
5071 seg = line->segments;
5072 line->segments = seg->next;
5074 if (GTK_IS_TEXT_MARK_SEGMENT (seg))
5076 /* Set the mark as deleted */
5077 seg->body.mark.tree = NULL;
5078 seg->body.mark.line = NULL;
5081 (*seg->type->deleteFunc) (seg, line, 1);
5083 gtk_text_line_destroy (tree, line);
5088 GtkTextBTreeNode *childPtr;
5090 while (node->children.node != NULL)
5092 childPtr = node->children.node;
5093 node->children.node = childPtr->next;
5094 gtk_text_btree_node_destroy (tree, childPtr);
5098 summary_list_destroy (node->summary);
5099 node_data_list_destroy (node->node_data);
5104 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5108 nd = node->node_data;
5111 if (nd->view_id == view_id)
5118 nd = node_data_new (view_id);
5120 if (node->node_data)
5121 nd->next = node->node_data;
5123 node->node_data = nd;
5130 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5136 nd = node->node_data;
5139 if (nd->view_id == view_id)
5150 prev->next = nd->next;
5152 if (node->node_data == nd)
5153 node->node_data = nd->next;
5157 node_data_destroy (nd);
5161 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5162 gint *width, gint *height)
5166 g_return_if_fail (width != NULL);
5167 g_return_if_fail (height != NULL);
5169 nd = gtk_text_btree_node_ensure_data (node, view_id);
5174 *height = nd->height;
5177 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5178 * here isn't quite right, since for a lot of operations we want to
5179 * know which children of the common parent correspond to the two nodes
5180 * (e.g., when computing the order of two iters)
5182 static GtkTextBTreeNode *
5183 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5184 GtkTextBTreeNode *node2)
5186 while (node1->level < node2->level)
5187 node1 = node1->parent;
5188 while (node2->level < node1->level)
5189 node2 = node2->parent;
5190 while (node1 != node2)
5192 node1 = node1->parent;
5193 node2 = node2->parent;
5204 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5209 while (view != NULL)
5211 if (view->view_id == view_id)
5220 get_tree_bounds (GtkTextBTree *tree,
5224 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5225 _gtk_text_btree_get_last_iter (tree, end);
5229 tag_changed_cb (GtkTextTagTable *table,
5231 gboolean size_changed,
5236 /* We need to queue a relayout on all regions that are tagged with
5243 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5245 /* Must be a last toggle if there was a first one. */
5246 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5247 _gtk_text_btree_invalidate_region (tree,
5254 /* We only need to queue a redraw, not a relayout */
5259 while (view != NULL)
5263 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5264 gtk_text_layout_changed (view->layout, 0, height, height);
5272 tag_removed_cb (GtkTextTagTable *table,
5276 /* Remove the tag from the tree */
5281 get_tree_bounds (tree, &start, &end);
5283 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5284 gtk_text_btree_remove_tag_info (tree, tag);
5288 /* Rebalance the out-of-whack node "node" */
5290 gtk_text_btree_rebalance (GtkTextBTree *tree,
5291 GtkTextBTreeNode *node)
5294 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5295 * up through the tree one GtkTextBTreeNode at a time until the root
5296 * GtkTextBTreeNode has been processed.
5299 while (node != NULL)
5301 GtkTextBTreeNode *new_node, *child;
5306 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5307 * then split off all but the first MIN_CHILDREN into a separate
5308 * GtkTextBTreeNode following the original one. Then repeat until the
5309 * GtkTextBTreeNode has a decent size.
5312 if (node->num_children > MAX_CHILDREN)
5317 * If the GtkTextBTreeNode being split is the root
5318 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5322 if (node->parent == NULL)
5324 new_node = gtk_text_btree_node_new ();
5325 new_node->parent = NULL;
5326 new_node->next = NULL;
5327 new_node->summary = NULL;
5328 new_node->level = node->level + 1;
5329 new_node->children.node = node;
5330 recompute_node_counts (tree, new_node);
5331 tree->root_node = new_node;
5333 new_node = gtk_text_btree_node_new ();
5334 new_node->parent = node->parent;
5335 new_node->next = node->next;
5336 node->next = new_node;
5337 new_node->summary = NULL;
5338 new_node->level = node->level;
5339 new_node->num_children = node->num_children - MIN_CHILDREN;
5340 if (node->level == 0)
5342 for (i = MIN_CHILDREN-1,
5343 line = node->children.line;
5344 i > 0; i--, line = line->next)
5346 /* Empty loop body. */
5348 new_node->children.line = line->next;
5353 for (i = MIN_CHILDREN-1,
5354 child = node->children.node;
5355 i > 0; i--, child = child->next)
5357 /* Empty loop body. */
5359 new_node->children.node = child->next;
5362 recompute_node_counts (tree, node);
5363 node->parent->num_children++;
5365 if (node->num_children <= MAX_CHILDREN)
5367 recompute_node_counts (tree, node);
5373 while (node->num_children < MIN_CHILDREN)
5375 GtkTextBTreeNode *other;
5376 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5377 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5378 int total_children, first_children, i;
5381 * Too few children for this GtkTextBTreeNode. If this is the root then,
5382 * it's OK for it to have less than MIN_CHILDREN children
5383 * as long as it's got at least two. If it has only one
5384 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5385 * the tree and use its child as the new root.
5388 if (node->parent == NULL)
5390 if ((node->num_children == 1) && (node->level > 0))
5392 tree->root_node = node->children.node;
5393 tree->root_node->parent = NULL;
5394 summary_list_destroy (node->summary);
5401 * Not the root. Make sure that there are siblings to
5405 if (node->parent->num_children < 2)
5407 gtk_text_btree_rebalance (tree, node->parent);
5412 * Find a sibling neighbor to borrow from, and arrange for
5413 * node to be the earlier of the pair.
5416 if (node->next == NULL)
5418 for (other = node->parent->children.node;
5419 other->next != node;
5420 other = other->next)
5422 /* Empty loop body. */
5429 * We're going to either merge the two siblings together
5430 * into one GtkTextBTreeNode or redivide the children among them to
5431 * balance their loads. As preparation, join their two
5432 * child lists into a single list and remember the half-way
5433 * point in the list.
5436 total_children = node->num_children + other->num_children;
5437 first_children = total_children/2;
5438 if (node->children.node == NULL)
5440 node->children = other->children;
5441 other->children.node = NULL;
5442 other->children.line = NULL;
5444 if (node->level == 0)
5448 for (line = node->children.line, i = 1;
5450 line = line->next, i++)
5452 if (i == first_children)
5457 line->next = other->children.line;
5458 while (i <= first_children)
5467 GtkTextBTreeNode *child;
5469 for (child = node->children.node, i = 1;
5470 child->next != NULL;
5471 child = child->next, i++)
5473 if (i <= first_children)
5475 if (i == first_children)
5477 halfwaynode = child;
5481 child->next = other->children.node;
5482 while (i <= first_children)
5484 halfwaynode = child;
5485 child = child->next;
5491 * If the two siblings can simply be merged together, do it.
5494 if (total_children <= MAX_CHILDREN)
5496 recompute_node_counts (tree, node);
5497 node->next = other->next;
5498 node->parent->num_children--;
5499 summary_list_destroy (other->summary);
5505 * The siblings can't be merged, so just divide their
5506 * children evenly between them.
5509 if (node->level == 0)
5511 other->children.line = halfwayline->next;
5512 halfwayline->next = NULL;
5516 other->children.node = halfwaynode->next;
5517 halfwaynode->next = NULL;
5520 recompute_node_counts (tree, node);
5521 recompute_node_counts (tree, other);
5524 node = node->parent;
5529 post_insert_fixup (GtkTextBTree *tree,
5531 gint line_count_delta,
5532 gint char_count_delta)
5535 GtkTextBTreeNode *node;
5538 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5539 * point, then rebalance the tree if necessary.
5542 for (node = line->parent ; node != NULL;
5543 node = node->parent)
5545 node->num_lines += line_count_delta;
5546 node->num_chars += char_count_delta;
5548 node = line->parent;
5549 node->num_children += line_count_delta;
5551 if (node->num_children > MAX_CHILDREN)
5553 gtk_text_btree_rebalance (tree, node);
5556 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5557 _gtk_text_btree_check (tree);
5560 static GtkTextTagInfo*
5561 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5564 GtkTextTagInfo *info;
5568 list = tree->tag_infos;
5569 while (list != NULL)
5572 if (info->tag == tag)
5575 list = g_slist_next (list);
5581 static GtkTextTagInfo*
5582 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5585 GtkTextTagInfo *info;
5587 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5591 /* didn't find it, create. */
5593 info = g_new (GtkTextTagInfo, 1);
5596 g_object_ref (G_OBJECT (tag));
5597 info->tag_root = NULL;
5598 info->toggle_count = 0;
5600 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5607 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5610 GtkTextTagInfo *info;
5615 list = tree->tag_infos;
5616 while (list != NULL)
5619 if (info->tag == tag)
5623 prev->next = list->next;
5627 tree->tag_infos = list->next;
5630 g_slist_free (list);
5632 g_object_unref (G_OBJECT (info->tag));
5638 list = g_slist_next (list);
5641 g_assert_not_reached ();
5646 recompute_level_zero_counts (GtkTextBTreeNode *node)
5649 GtkTextLineSegment *seg;
5651 g_assert (node->level == 0);
5653 line = node->children.line;
5654 while (line != NULL)
5656 node->num_children++;
5659 if (line->parent != node)
5660 gtk_text_line_set_parent (line, node);
5662 seg = line->segments;
5666 node->num_chars += seg->char_count;
5668 if (((seg->type != >k_text_toggle_on_type)
5669 && (seg->type != >k_text_toggle_off_type))
5670 || !(seg->body.toggle.inNodeCounts))
5676 GtkTextTagInfo *info;
5678 info = seg->body.toggle.info;
5680 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5691 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5694 GtkTextBTreeNode *child;
5696 g_assert (node->level > 0);
5698 child = node->children.node;
5699 while (child != NULL)
5701 node->num_children += 1;
5702 node->num_lines += child->num_lines;
5703 node->num_chars += child->num_chars;
5705 if (child->parent != node)
5707 child->parent = node;
5708 gtk_text_btree_node_invalidate_upward (node, NULL);
5711 summary = child->summary;
5712 while (summary != NULL)
5714 gtk_text_btree_node_adjust_toggle_count (node,
5716 summary->toggle_count);
5718 summary = summary->next;
5721 child = child->next;
5726 *----------------------------------------------------------------------
5728 * recompute_node_counts --
5730 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5731 * (tags, child information, etc.) by scanning the information in
5732 * its descendants. This procedure is called during rebalancing
5733 * when a GtkTextBTreeNode's child structure has changed.
5739 * The tag counts for node are modified to reflect its current
5740 * child structure, as are its num_children, num_lines, num_chars fields.
5741 * Also, all of the childrens' parent fields are made to point
5744 *----------------------------------------------------------------------
5748 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5751 Summary *summary, *summary2;
5754 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5755 * the existing Summary records (most of them will probably be reused).
5758 summary = node->summary;
5759 while (summary != NULL)
5761 summary->toggle_count = 0;
5762 summary = summary->next;
5765 node->num_children = 0;
5766 node->num_lines = 0;
5767 node->num_chars = 0;
5770 * Scan through the children, adding the childrens' tag counts into
5771 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5775 if (node->level == 0)
5776 recompute_level_zero_counts (node);
5778 recompute_level_nonzero_counts (node);
5783 gtk_text_btree_node_check_valid (node, view->view_id);
5788 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5789 * records that still have a zero count, or that have all the toggles.
5790 * The GtkTextBTreeNode with the children that account for all the tags toggles
5791 * have no summary information, and they become the tag_root for the tag.
5795 for (summary = node->summary; summary != NULL; )
5797 if (summary->toggle_count > 0 &&
5798 summary->toggle_count < summary->info->toggle_count)
5800 if (node->level == summary->info->tag_root->level)
5803 * The tag's root GtkTextBTreeNode split and some toggles left.
5804 * The tag root must move up a level.
5806 summary->info->tag_root = node->parent;
5809 summary = summary->next;
5812 if (summary->toggle_count == summary->info->toggle_count)
5815 * A GtkTextBTreeNode merge has collected all the toggles under
5816 * one GtkTextBTreeNode. Push the root down to this level.
5818 summary->info->tag_root = node;
5820 if (summary2 != NULL)
5822 summary2->next = summary->next;
5823 summary_destroy (summary);
5824 summary = summary2->next;
5828 node->summary = summary->next;
5829 summary_destroy (summary);
5830 summary = node->summary;
5836 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
5837 GtkTextTagInfo *info,
5838 gint delta) /* may be negative */
5840 Summary *summary, *prevPtr;
5841 GtkTextBTreeNode *node2Ptr;
5842 int rootLevel; /* Level of original tag root */
5844 info->toggle_count += delta;
5846 if (info->tag_root == (GtkTextBTreeNode *) NULL)
5848 info->tag_root = node;
5853 * Note the level of the existing root for the tag so we can detect
5854 * if it needs to be moved because of the toggle count change.
5857 rootLevel = info->tag_root->level;
5860 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
5861 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
5865 for ( ; node != info->tag_root; node = node->parent)
5868 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
5869 * perhaps all we have to do is adjust its count.
5872 for (prevPtr = NULL, summary = node->summary;
5874 prevPtr = summary, summary = summary->next)
5876 if (summary->info == info)
5881 if (summary != NULL)
5883 summary->toggle_count += delta;
5884 if (summary->toggle_count > 0 &&
5885 summary->toggle_count < info->toggle_count)
5889 if (summary->toggle_count != 0)
5892 * Should never find a GtkTextBTreeNode with max toggle count at this
5893 * point (there shouldn't have been a summary entry in the
5897 g_error ("%s: bad toggle count (%d) max (%d)",
5898 G_STRLOC, summary->toggle_count, info->toggle_count);
5902 * Zero toggle count; must remove this tag from the list.
5905 if (prevPtr == NULL)
5907 node->summary = summary->next;
5911 prevPtr->next = summary->next;
5913 summary_destroy (summary);
5918 * This tag isn't currently in the summary information list.
5921 if (rootLevel == node->level)
5925 * The old tag root is at the same level in the tree as this
5926 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
5927 * a level, in the hopes that it will now cover this GtkTextBTreeNode
5928 * as well as the old root (if not, we'll move it up again
5929 * the next time through the loop). To push it up one level
5930 * we copy the original toggle count into the summary
5931 * information at the old root and change the root to its
5932 * parent GtkTextBTreeNode.
5935 GtkTextBTreeNode *rootnode = info->tag_root;
5936 summary = (Summary *) g_malloc (sizeof (Summary));
5937 summary->info = info;
5938 summary->toggle_count = info->toggle_count - delta;
5939 summary->next = rootnode->summary;
5940 rootnode->summary = summary;
5941 rootnode = rootnode->parent;
5942 rootLevel = rootnode->level;
5943 info->tag_root = rootnode;
5945 summary = (Summary *) g_malloc (sizeof (Summary));
5946 summary->info = info;
5947 summary->toggle_count = delta;
5948 summary->next = node->summary;
5949 node->summary = summary;
5954 * If we've decremented the toggle count, then it may be necessary
5955 * to push the tag root down one or more levels.
5962 if (info->toggle_count == 0)
5964 info->tag_root = (GtkTextBTreeNode *) NULL;
5967 node = info->tag_root;
5968 while (node->level > 0)
5971 * See if a single child GtkTextBTreeNode accounts for all of the tag's
5972 * toggles. If so, push the root down one level.
5975 for (node2Ptr = node->children.node;
5976 node2Ptr != (GtkTextBTreeNode *)NULL ;
5977 node2Ptr = node2Ptr->next)
5979 for (prevPtr = NULL, summary = node2Ptr->summary;
5981 prevPtr = summary, summary = summary->next)
5983 if (summary->info == info)
5988 if (summary == NULL)
5992 if (summary->toggle_count != info->toggle_count)
5995 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6002 * This GtkTextBTreeNode has all the toggles, so push down the root.
6005 if (prevPtr == NULL)
6007 node2Ptr->summary = summary->next;
6011 prevPtr->next = summary->next;
6013 summary_destroy (summary);
6014 info->tag_root = node2Ptr;
6017 node = info->tag_root;
6022 *----------------------------------------------------------------------
6026 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6027 * increments the count for a particular tag, adding a new
6028 * entry for that tag if there wasn't one previously.
6034 * The information at *tagInfoPtr may be modified, and the arrays
6035 * may be reallocated to make them larger.
6037 *----------------------------------------------------------------------
6041 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6046 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6047 count > 0; tag_p++, count--)
6051 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6057 * There isn't currently an entry for this tag, so we have to
6058 * make a new one. If the arrays are full, then enlarge the
6062 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6064 GtkTextTag **newTags;
6065 int *newCounts, newSize;
6067 newSize = 2*tagInfoPtr->arraySize;
6068 newTags = (GtkTextTag **) g_malloc ((unsigned)
6069 (newSize*sizeof (GtkTextTag *)));
6070 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6071 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6072 g_free ((char *) tagInfoPtr->tags);
6073 tagInfoPtr->tags = newTags;
6074 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6075 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6076 tagInfoPtr->arraySize *sizeof (int));
6077 g_free ((char *) tagInfoPtr->counts);
6078 tagInfoPtr->counts = newCounts;
6079 tagInfoPtr->arraySize = newSize;
6082 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6083 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6084 tagInfoPtr->numTags++;
6088 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6089 const GtkTextIter *iter)
6091 GtkTextLineSegment *prev;
6095 line = gtk_text_iter_get_text_line (iter);
6096 tree = gtk_text_iter_get_btree (iter);
6098 prev = gtk_text_line_segment_split (iter);
6101 seg->next = line->segments;
6102 line->segments = seg;
6106 seg->next = prev->next;
6109 cleanup_line (line);
6110 segments_changed (tree);
6112 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6113 _gtk_text_btree_check (tree);
6117 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6118 GtkTextLineSegment *seg,
6121 GtkTextLineSegment *prev;
6123 if (line->segments == seg)
6125 line->segments = seg->next;
6129 for (prev = line->segments; prev->next != seg;
6132 /* Empty loop body. */
6134 prev->next = seg->next;
6136 cleanup_line (line);
6137 segments_changed (tree);
6141 * This is here because it requires BTree internals, it logically
6142 * belongs in gtktextsegment.c
6147 *--------------------------------------------------------------
6149 * _gtk_toggle_segment_check_func --
6151 * This procedure is invoked to perform consistency checks
6152 * on toggle segments.
6158 * If a consistency problem is found the procedure g_errors.
6160 *--------------------------------------------------------------
6164 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6170 if (segPtr->byte_count != 0)
6172 g_error ("toggle_segment_check_func: segment had non-zero size");
6174 if (!segPtr->body.toggle.inNodeCounts)
6176 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6178 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6179 for (summary = line->parent->summary; ;
6180 summary = summary->next)
6182 if (summary == NULL)
6186 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6193 if (summary->info == segPtr->body.toggle.info)
6197 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6209 gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
6216 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6217 &width, &height, &valid);
6218 if (nd->width != width ||
6219 nd->height != height ||
6220 !nd->valid != !valid)
6222 g_error ("Node aggregates for view %p are invalid:\n"
6223 "Are (%d,%d,%s), should be (%d,%d,%s)",
6225 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6226 width, height, valid ? "TRUE" : "FALSE");
6231 gtk_text_btree_node_check_consistency (GtkTextBTreeNode *node)
6233 GtkTextBTreeNode *childnode;
6234 Summary *summary, *summary2;
6236 GtkTextLineSegment *segPtr;
6237 int num_children, num_lines, num_chars, toggle_count, min_children;
6238 GtkTextLineData *ld;
6241 if (node->parent != NULL)
6243 min_children = MIN_CHILDREN;
6245 else if (node->level > 0)
6252 if ((node->num_children < min_children)
6253 || (node->num_children > MAX_CHILDREN))
6255 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6256 node->num_children);
6259 nd = node->node_data;
6262 gtk_text_btree_node_view_check_consistency (node, nd);
6269 if (node->level == 0)
6271 for (line = node->children.line; line != NULL;
6274 if (line->parent != node)
6276 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6278 if (line->segments == NULL)
6280 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6286 /* Just ensuring we don't segv while doing this loop */
6291 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6293 if (segPtr->type->checkFunc != NULL)
6295 (*segPtr->type->checkFunc)(segPtr, line);
6297 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6298 && (segPtr->next != NULL)
6299 && (segPtr->next->byte_count == 0)
6300 && (segPtr->next->type->leftGravity))
6302 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6304 if ((segPtr->next == NULL)
6305 && (segPtr->type != >k_text_char_type))
6307 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6310 num_chars += segPtr->char_count;
6319 for (childnode = node->children.node; childnode != NULL;
6320 childnode = childnode->next)
6322 if (childnode->parent != node)
6324 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6326 if (childnode->level != (node->level-1))
6328 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6329 node->level, childnode->level);
6331 gtk_text_btree_node_check_consistency (childnode);
6332 for (summary = childnode->summary; summary != NULL;
6333 summary = summary->next)
6335 for (summary2 = node->summary; ;
6336 summary2 = summary2->next)
6338 if (summary2 == NULL)
6340 if (summary->info->tag_root == node)
6344 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6345 summary->info->tag->name,
6346 "present in parent summaries");
6348 if (summary->info == summary2->info)
6355 num_lines += childnode->num_lines;
6356 num_chars += childnode->num_chars;
6359 if (num_children != node->num_children)
6361 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6362 num_children, node->num_children);
6364 if (num_lines != node->num_lines)
6366 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6367 num_lines, node->num_lines);
6369 if (num_chars != node->num_chars)
6371 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6372 num_chars, node->num_chars);
6375 for (summary = node->summary; summary != NULL;
6376 summary = summary->next)
6378 if (summary->info->toggle_count == summary->toggle_count)
6380 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6381 summary->info->tag->name);
6384 if (node->level == 0)
6386 for (line = node->children.line; line != NULL;
6389 for (segPtr = line->segments; segPtr != NULL;
6390 segPtr = segPtr->next)
6392 if ((segPtr->type != >k_text_toggle_on_type)
6393 && (segPtr->type != >k_text_toggle_off_type))
6397 if (segPtr->body.toggle.info == summary->info)
6399 if (!segPtr->body.toggle.inNodeCounts)
6400 g_error ("Toggle segment not in the node counts");
6409 for (childnode = node->children.node;
6411 childnode = childnode->next)
6413 for (summary2 = childnode->summary;
6415 summary2 = summary2->next)
6417 if (summary2->info == summary->info)
6419 toggle_count += summary2->toggle_count;
6424 if (toggle_count != summary->toggle_count)
6426 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6427 toggle_count, summary->toggle_count);
6429 for (summary2 = summary->next; summary2 != NULL;
6430 summary2 = summary2->next)
6432 if (summary2->info == summary->info)
6434 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6435 summary->info->tag->name);
6442 listify_foreach (GtkTextTag *tag, gpointer user_data)
6444 GSList** listp = user_data;
6446 *listp = g_slist_prepend (*listp, tag);
6450 list_of_tags (GtkTextTagTable *table)
6452 GSList *list = NULL;
6454 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6460 _gtk_text_btree_check (GtkTextBTree *tree)
6463 GtkTextBTreeNode *node;
6465 GtkTextLineSegment *seg;
6467 GSList *taglist = NULL;
6469 GtkTextTagInfo *info;
6472 * Make sure that the tag toggle counts and the tag root pointers are OK.
6474 for (taglist = list_of_tags (tree->table);
6475 taglist != NULL ; taglist = taglist->next)
6477 tag = taglist->data;
6478 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6481 node = info->tag_root;
6484 if (info->toggle_count != 0)
6486 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6487 tag->name, info->toggle_count);
6489 continue; /* no ranges for the tag */
6491 else if (info->toggle_count == 0)
6493 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6496 else if (info->toggle_count & 1)
6498 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6499 tag->name, info->toggle_count);
6501 for (summary = node->summary; summary != NULL;
6502 summary = summary->next)
6504 if (summary->info->tag == tag)
6506 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6510 if (node->level > 0)
6512 for (node = node->children.node ; node != NULL ;
6515 for (summary = node->summary; summary != NULL;
6516 summary = summary->next)
6518 if (summary->info->tag == tag)
6520 count += summary->toggle_count;
6527 GtkTextLineSegmentClass * last = NULL;
6529 for (line = node->children.line ; line != NULL ;
6532 for (seg = line->segments; seg != NULL;
6535 if ((seg->type == >k_text_toggle_on_type ||
6536 seg->type == >k_text_toggle_off_type) &&
6537 seg->body.toggle.info->tag == tag)
6539 if (last == seg->type)
6540 g_error ("Two consecutive toggles on or off weren't merged");
6541 if (!seg->body.toggle.inNodeCounts)
6542 g_error ("Toggle segment not in the node counts");
6551 if (count != info->toggle_count)
6553 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6554 info->toggle_count, tag->name, count);
6559 g_slist_free (taglist);
6563 * Call a recursive procedure to do the main body of checks.
6566 node = tree->root_node;
6567 gtk_text_btree_node_check_consistency (tree->root_node);
6570 * Make sure that there are at least two lines in the text and
6571 * that the last line has no characters except a newline.
6574 if (node->num_lines < 2)
6576 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6578 if (node->num_chars < 2)
6580 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6582 while (node->level > 0)
6584 node = node->children.node;
6585 while (node->next != NULL)
6590 line = node->children.line;
6591 while (line->next != NULL)
6595 seg = line->segments;
6596 while ((seg->type == >k_text_toggle_off_type)
6597 || (seg->type == >k_text_right_mark_type)
6598 || (seg->type == >k_text_left_mark_type))
6601 * It's OK to toggle a tag off in the last line, but
6602 * not to start a new range. It's also OK to have marks
6608 if (seg->type != >k_text_char_type)
6610 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6612 if (seg->next != NULL)
6614 g_error ("_gtk_text_btree_check: last line has too many segments");
6616 if (seg->byte_count != 1)
6618 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6621 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6623 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6628 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6629 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6630 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6631 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6634 _gtk_text_btree_spew (GtkTextBTree *tree)
6639 printf ("%d lines in tree %p\n",
6640 _gtk_text_btree_line_count (tree), tree);
6642 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6644 while (line != NULL)
6646 _gtk_text_btree_spew_line (tree, line);
6647 line = _gtk_text_line_next (line);
6650 printf ("=================== Tag information\n");
6655 list = tree->tag_infos;
6657 while (list != NULL)
6659 GtkTextTagInfo *info;
6663 printf (" tag `%s': root at %p, toggle count %d\n",
6664 info->tag->name, info->tag_root, info->toggle_count);
6666 list = g_slist_next (list);
6669 if (tree->tag_infos == NULL)
6671 printf (" (no tags in the tree)\n");
6675 printf ("=================== Tree nodes\n");
6678 _gtk_text_btree_spew_node (tree->root_node, 0);
6683 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6686 GtkTextLineSegment *seg;
6688 spaces = g_strnfill (indent, ' ');
6690 printf ("%sline %p chars %d bytes %d\n",
6692 _gtk_text_line_char_count (line),
6693 _gtk_text_line_byte_count (line));
6695 seg = line->segments;
6698 if (seg->type == >k_text_char_type)
6700 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6705 if (*s == '\n' || *s == '\r')
6709 printf ("%s chars `%s'...\n", spaces, str);
6712 else if (seg->type == >k_text_right_mark_type)
6714 printf ("%s right mark `%s' visible: %d\n",
6716 seg->body.mark.name,
6717 seg->body.mark.visible);
6719 else if (seg->type == >k_text_left_mark_type)
6721 printf ("%s left mark `%s' visible: %d\n",
6723 seg->body.mark.name,
6724 seg->body.mark.visible);
6726 else if (seg->type == >k_text_toggle_on_type ||
6727 seg->type == >k_text_toggle_off_type)
6729 printf ("%s tag `%s' %s\n",
6730 spaces, seg->body.toggle.info->tag->name,
6731 seg->type == >k_text_toggle_off_type ? "off" : "on");
6741 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6744 GtkTextBTreeNode *iter;
6747 spaces = g_strnfill (indent, ' ');
6749 printf ("%snode %p level %d children %d lines %d chars %d\n",
6750 spaces, node, node->level,
6751 node->num_children, node->num_lines, node->num_chars);
6756 printf ("%s %d toggles of `%s' below this node\n",
6757 spaces, s->toggle_count, s->info->tag->name);
6763 if (node->level > 0)
6765 iter = node->children.node;
6766 while (iter != NULL)
6768 _gtk_text_btree_spew_node (iter, indent + 2);
6775 GtkTextLine *line = node->children.line;
6776 while (line != NULL)
6778 _gtk_text_btree_spew_line_short (line, indent + 2);
6786 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
6788 GtkTextLineSegment * seg;
6790 printf ("%4d| line: %p parent: %p next: %p\n",
6791 _gtk_text_line_get_number (line), line, line->parent, line->next);
6793 seg = line->segments;
6797 _gtk_text_btree_spew_segment (tree, seg);
6803 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
6805 printf (" segment: %p type: %s bytes: %d chars: %d\n",
6806 seg, seg->type->name, seg->byte_count, seg->char_count);
6808 if (seg->type == >k_text_char_type)
6810 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
6811 printf (" `%s'\n", str);
6814 else if (seg->type == >k_text_right_mark_type)
6816 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
6817 seg->body.mark.name,
6818 seg->body.mark.visible,
6819 seg->body.mark.not_deleteable);
6821 else if (seg->type == >k_text_left_mark_type)
6823 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
6824 seg->body.mark.name,
6825 seg->body.mark.visible,
6826 seg->body.mark.not_deleteable);
6828 else if (seg->type == >k_text_toggle_on_type ||
6829 seg->type == >k_text_toggle_off_type)
6831 printf (" tag `%s' priority %d\n",
6832 seg->body.toggle.info->tag->name,
6833 seg->body.toggle.info->tag->priority);