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 lines below this node are in need of validation.
106 * However, width/height should always represent the current total width and
107 * max height for lines below this node; the valid flag indicates whether the
108 * width/height on the lines needs recomputing, not whether the totals
116 * The data structure below keeps summary information about one tag as part
117 * of the tag information in a node.
120 typedef struct Summary {
121 GtkTextTagInfo *info; /* Handle for tag. */
122 int toggle_count; /* Number of transitions into or
123 * out of this tag that occur in
124 * the subtree rooted at this node. */
125 struct Summary *next; /* Next in list of all tags for same
126 * node, or NULL if at end of list. */
130 * The data structure below defines a node in the B-tree.
133 struct _GtkTextBTreeNode {
134 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
135 * this is the root. */
136 GtkTextBTreeNode *next; /* Next in list of siblings with the
137 * same parent node, or NULL for end
139 Summary *summary; /* First in malloc-ed list of info
140 * about tags in this subtree (NULL if
141 * no tag info in the subtree). */
142 int level; /* Level of this node in the B-tree.
143 * 0 refers to the bottom of the tree
144 * (children are lines, not nodes). */
145 union { /* First in linked list of children. */
146 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
147 GtkTextLine *line; /* Used if level == 0. */
149 int num_children; /* Number of children of this node. */
150 int num_lines; /* Total number of lines (leaves) in
151 * the subtree rooted here. */
152 int num_chars; /* Number of chars below here */
159 * Used to store the list of views in our btree
162 typedef struct _BTreeView BTreeView;
166 GtkTextLayout *layout;
172 * And the tree itself
175 struct _GtkTextBTree {
176 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
177 GtkTextTagTable *table;
178 GHashTable *mark_table;
180 GtkTextMark *insert_mark;
181 GtkTextMark *selection_bound_mark;
182 GtkTextBuffer *buffer;
185 guint tag_changed_handler;
186 guint tag_removed_handler;
187 /* Incremented when a segment with a byte size > 0
188 * is added to or removed from the tree (i.e. the
189 * length of a line may have changed, and lines may
190 * have been added or removed). This invalidates
191 * all outstanding iterators.
193 guint chars_changed_stamp;
194 /* Incremented when any segments are added or deleted;
195 * this makes outstanding iterators recalculate their
196 * pointed-to segment and segment offset.
198 guint segments_changed_stamp;
200 /* Cache the last line in the buffer */
201 GtkTextLine *last_line;
202 guint last_line_stamp;
204 /* Cache the next-to-last line in the buffer,
205 * containing the end iterator
207 GtkTextLine *end_iter_line;
208 GtkTextLineSegment *end_iter_segment;
209 int end_iter_segment_byte_index;
210 int end_iter_segment_char_offset;
211 guint end_iter_line_stamp;
212 guint end_iter_segment_stamp;
214 GHashTable *child_anchor_table;
219 * Upper and lower bounds on how many children a node may have:
220 * rebalance when either of these limits is exceeded. MAX_CHILDREN
221 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
224 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
225 shallow. It appears to be faster to locate a particular line number
226 if the tree is narrow and deep, since it is more finely sorted. I
227 guess this may increase memory use though, and make it slower to
228 walk the tree in order, or locate a particular byte index (which
229 is done by walking the tree in order).
231 There's basically a tradeoff here. However I'm thinking we want to
232 add pixels, byte counts, and char counts to the tree nodes,
233 at that point narrow and deep should speed up all operations,
234 not just the line number searches.
238 #define MAX_CHILDREN 12
239 #define MIN_CHILDREN 6
241 #define MAX_CHILDREN 6
242 #define MIN_CHILDREN 3
249 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
251 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
252 GtkTextBTreeNode *node);
253 static GtkTextLine * get_last_line (GtkTextBTree *tree);
254 static void post_insert_fixup (GtkTextBTree *tree,
255 GtkTextLine *insert_line,
256 gint char_count_delta,
257 gint line_count_delta);
258 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
259 GtkTextTagInfo *info,
261 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
264 static void segments_changed (GtkTextBTree *tree);
265 static void chars_changed (GtkTextBTree *tree);
266 static void summary_list_destroy (Summary *summary);
267 static GtkTextLine *gtk_text_line_new (void);
268 static void gtk_text_line_destroy (GtkTextBTree *tree,
270 static void gtk_text_line_set_parent (GtkTextLine *line,
271 GtkTextBTreeNode *node);
272 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
276 static NodeData *node_data_new (gpointer view_id);
277 static void node_data_destroy (NodeData *nd);
278 static void node_data_list_destroy (NodeData *nd);
279 static NodeData *node_data_find (NodeData *nd,
282 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
283 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
284 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
286 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
288 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
290 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
293 static void gtk_text_btree_node_remove_view (BTreeView *view,
294 GtkTextBTreeNode *node,
296 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
297 GtkTextBTreeNode *node);
298 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
299 GtkTextBTreeNode *node);
300 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
302 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
304 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
308 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
309 GtkTextBTreeNode *node2);
310 static void get_tree_bounds (GtkTextBTree *tree,
313 static void tag_changed_cb (GtkTextTagTable *table,
315 gboolean size_changed,
317 static void tag_removed_cb (GtkTextTagTable *table,
320 static void cleanup_line (GtkTextLine *line);
321 static void recompute_node_counts (GtkTextBTree *tree,
322 GtkTextBTreeNode *node);
323 static void inc_count (GtkTextTag *tag,
325 TagInfo *tagInfoPtr);
327 static void summary_destroy (Summary *summary);
329 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
330 const GtkTextIter *iter);
331 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
332 GtkTextLineSegment *seg,
336 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
338 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
340 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
343 static void redisplay_region (GtkTextBTree *tree,
344 const GtkTextIter *start,
345 const GtkTextIter *end);
347 /* Inline thingies */
350 segments_changed (GtkTextBTree *tree)
352 tree->segments_changed_stamp += 1;
356 chars_changed (GtkTextBTree *tree)
358 tree->chars_changed_stamp += 1;
366 _gtk_text_btree_new (GtkTextTagTable *table,
367 GtkTextBuffer *buffer)
370 GtkTextBTreeNode *root_node;
371 GtkTextLine *line, *line2;
373 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
374 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
377 * The tree will initially have two empty lines. The second line
378 * isn't actually part of the tree's contents, but its presence
379 * makes several operations easier. The tree will have one GtkTextBTreeNode,
380 * which is also the root of the tree.
383 /* Create the root node. */
385 root_node = gtk_text_btree_node_new ();
387 line = gtk_text_line_new ();
388 line2 = gtk_text_line_new ();
390 root_node->parent = NULL;
391 root_node->next = NULL;
392 root_node->summary = NULL;
393 root_node->level = 0;
394 root_node->children.line = line;
395 root_node->num_children = 2;
396 root_node->num_lines = 2;
397 root_node->num_chars = 2;
399 line->parent = root_node;
402 line->segments = _gtk_char_segment_new ("\n", 1);
404 line2->parent = root_node;
406 line2->segments = _gtk_char_segment_new ("\n", 1);
408 /* Create the tree itself */
410 tree = g_new0(GtkTextBTree, 1);
411 tree->root_node = root_node;
415 /* Set these to values that are unlikely to be found
416 * in random memory garbage, and also avoid
417 * duplicates between tree instances.
419 tree->chars_changed_stamp = g_random_int ();
420 tree->segments_changed_stamp = g_random_int ();
422 tree->last_line_stamp = tree->chars_changed_stamp - 1;
423 tree->last_line = NULL;
425 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
426 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
427 tree->end_iter_line = NULL;
428 tree->end_iter_segment_byte_index = 0;
429 tree->end_iter_segment_char_offset = 0;
431 g_object_ref (G_OBJECT (tree->table));
433 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
435 G_CALLBACK (tag_changed_cb),
438 tree->tag_removed_handler = g_signal_connect (G_OBJECT (tree->table),
440 G_CALLBACK (tag_removed_cb),
443 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
444 tree->child_anchor_table = NULL;
446 /* We don't ref the buffer, since the buffer owns us;
447 * we'd have some circularity issues. The buffer always
448 * lasts longer than the BTree
450 tree->buffer = buffer;
454 GtkTextLineSegment *seg;
456 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
459 tree->insert_mark = _gtk_text_btree_set_mark (tree,
466 seg = tree->insert_mark->segment;
468 seg->body.mark.not_deleteable = TRUE;
469 seg->body.mark.visible = TRUE;
471 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
478 seg = tree->selection_bound_mark->segment;
480 seg->body.mark.not_deleteable = TRUE;
482 g_object_ref (G_OBJECT (tree->insert_mark));
483 g_object_ref (G_OBJECT (tree->selection_bound_mark));
492 _gtk_text_btree_ref (GtkTextBTree *tree)
494 g_return_if_fail (tree != NULL);
495 g_return_if_fail (tree->refcount > 0);
501 _gtk_text_btree_unref (GtkTextBTree *tree)
503 g_return_if_fail (tree != NULL);
504 g_return_if_fail (tree->refcount > 0);
508 if (tree->refcount == 0)
510 gtk_text_btree_node_destroy (tree, tree->root_node);
512 g_assert (g_hash_table_size (tree->mark_table) == 0);
513 g_hash_table_destroy (tree->mark_table);
515 g_object_unref (G_OBJECT (tree->insert_mark));
516 g_object_unref (G_OBJECT (tree->selection_bound_mark));
518 g_signal_handler_disconnect (G_OBJECT (tree->table),
519 tree->tag_changed_handler);
521 g_signal_handler_disconnect (G_OBJECT (tree->table),
522 tree->tag_removed_handler);
524 g_object_unref (G_OBJECT (tree->table));
531 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
537 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
539 return tree->chars_changed_stamp;
543 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
545 return tree->segments_changed_stamp;
549 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
551 g_return_if_fail (tree != NULL);
552 segments_changed (tree);
556 * Indexable segment mutation
560 _gtk_text_btree_delete (GtkTextIter *start,
563 GtkTextLineSegment *prev_seg; /* The segment just before the start
564 * of the deletion range. */
565 GtkTextLineSegment *last_seg; /* The segment just after the end
566 * of the deletion range. */
567 GtkTextLineSegment *seg, *next;
568 GtkTextLine *curline;
569 GtkTextBTreeNode *curnode, *node;
571 GtkTextLine *start_line;
572 GtkTextLine *end_line;
573 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
574 gint start_byte_offset;
576 g_return_if_fail (start != NULL);
577 g_return_if_fail (end != NULL);
578 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
579 _gtk_text_iter_get_btree (end));
581 gtk_text_iter_order (start, end);
583 tree = _gtk_text_iter_get_btree (start);
585 if (gtk_debug_flags & GTK_DEBUG_TEXT)
586 _gtk_text_btree_check (tree);
589 /* FIXME this code should no longer be required */
591 * The code below is ugly, but it's needed to make sure there
592 * is always a dummy empty line at the end of the text. If the
593 * final newline of the file (just before the dummy line) is being
594 * deleted, then back up index to just before the newline. If
595 * there is a newline just before the first character being deleted,
596 * then back up the first index too, so that an even number of lines
597 * gets deleted. Furthermore, remove any tags that are present on
598 * the newline that isn't going to be deleted after all (this simulates
599 * deleting the newline and then adding a "clean" one back again).
605 line1 = gtk_text_iter_get_line (start);
606 line2 = gtk_text_iter_get_line (end);
608 if (line2 == _gtk_text_btree_line_count (tree))
612 GtkTextIter orig_end;
615 gtk_text_iter_backward_char (end);
619 if (gtk_text_iter_get_line_offset (start) == 0 &&
622 gtk_text_iter_backward_char (start);
626 tags = _gtk_text_btree_get_tags (end,
634 while (i < array_size)
636 _gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
646 /* Broadcast the need for redisplay before we break the iterators */
647 _gtk_text_btree_invalidate_region (tree, start, end);
649 /* Save the byte offset so we can reset the iterators */
650 start_byte_offset = gtk_text_iter_get_line_index (start);
652 start_line = _gtk_text_iter_get_text_line (start);
653 end_line = _gtk_text_iter_get_text_line (end);
656 * Split the start and end segments, so we have a place
657 * to insert our new text.
659 * Tricky point: split at end first; otherwise the split
660 * at end may invalidate seg and/or prev_seg. This allows
661 * us to avoid invalidating segments for start.
664 last_seg = gtk_text_line_segment_split (end);
665 if (last_seg != NULL)
666 last_seg = last_seg->next;
668 last_seg = end_line->segments;
670 prev_seg = gtk_text_line_segment_split (start);
671 if (prev_seg != NULL)
673 seg = prev_seg->next;
674 prev_seg->next = last_seg;
678 seg = start_line->segments;
679 start_line->segments = last_seg;
682 /* notify iterators that their segments need recomputation,
683 just for robustness. */
684 segments_changed (tree);
687 * Delete all of the segments between prev_seg and last_seg.
690 curline = start_line;
691 curnode = curline->parent;
692 while (seg != last_seg)
698 GtkTextLine *nextline;
701 * We just ran off the end of a line. First find the
702 * next line, then go back to the old line and delete it
703 * (unless it's the starting line for the range).
706 nextline = _gtk_text_line_next (curline);
707 if (curline != start_line)
709 if (curnode == start_line->parent)
710 start_line->next = curline->next;
712 curnode->children.line = curline->next;
714 for (node = curnode; node != NULL;
717 /* Don't update node->num_chars, because
718 * that was done when we deleted the segments.
720 node->num_lines -= 1;
723 curnode->num_children -= 1;
724 curline->next = deleted_lines;
725 deleted_lines = curline;
729 seg = curline->segments;
732 * If the GtkTextBTreeNode is empty then delete it and its parents,
733 * recursively upwards until a non-empty GtkTextBTreeNode is found.
736 while (curnode->num_children == 0)
738 GtkTextBTreeNode *parent;
740 parent = curnode->parent;
741 if (parent->children.node == curnode)
743 parent->children.node = curnode->next;
747 GtkTextBTreeNode *prevnode = parent->children.node;
748 while (prevnode->next != curnode)
750 prevnode = prevnode->next;
752 prevnode->next = curnode->next;
754 parent->num_children--;
755 gtk_text_btree_node_free_empty (tree, curnode);
758 curnode = curline->parent;
763 char_count = seg->char_count;
765 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
768 * This segment refuses to die. Move it to prev_seg and
769 * advance prev_seg if the segment has left gravity.
772 if (prev_seg == NULL)
774 seg->next = start_line->segments;
775 start_line->segments = seg;
779 seg->next = prev_seg->next;
780 prev_seg->next = seg;
782 if (seg->type->leftGravity)
789 /* Segment is gone. Decrement the char count of the node and
791 for (node = curnode; node != NULL;
794 node->num_chars -= char_count;
802 * If the beginning and end of the deletion range are in different
803 * lines, join the two lines together and discard the ending line.
806 if (start_line != end_line)
809 GtkTextBTreeNode *ancestor_node;
810 GtkTextLine *prevline;
813 /* last_seg was appended to start_line up at the top of this function */
815 for (seg = last_seg; seg != NULL;
818 chars_moved += seg->char_count;
819 if (seg->type->lineChangeFunc != NULL)
821 (*seg->type->lineChangeFunc)(seg, end_line);
825 for (node = start_line->parent; node != NULL;
828 node->num_chars += chars_moved;
831 curnode = end_line->parent;
832 for (node = curnode; node != NULL;
835 node->num_chars -= chars_moved;
838 curnode->num_children--;
839 prevline = curnode->children.line;
840 if (prevline == end_line)
842 curnode->children.line = end_line->next;
846 while (prevline->next != end_line)
848 prevline = prevline->next;
850 prevline->next = end_line->next;
852 end_line->next = deleted_lines;
853 deleted_lines = end_line;
855 /* We now fix up the per-view aggregates. We add all the height and
856 * width for the deleted lines to the start line, so that when revalidation
857 * occurs, the correct change in size is seen.
859 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
866 gint deleted_width = 0;
867 gint deleted_height = 0;
869 line = deleted_lines;
872 GtkTextLine *next_line = line->next;
873 ld = _gtk_text_line_get_data (line, view->view_id);
877 deleted_width = MAX (deleted_width, ld->width);
878 deleted_height += ld->height;
882 gtk_text_line_destroy (tree, line);
887 if (deleted_width > 0 || deleted_height > 0)
889 ld = _gtk_text_line_get_data (start_line, view->view_id);
891 /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
892 * no way to add ld without also validating the node, which would
893 * be improper at this point.
895 /* This assertion does actually fail sometimes, must
896 fix before stable release -hp */
899 ld->width = MAX (deleted_width, ld->width);
900 ld->height += deleted_height;
904 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
905 if (ancestor_node->parent)
906 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
911 /* avoid dangling pointer */
912 deleted_lines = NULL;
914 gtk_text_btree_rebalance (tree, curnode);
918 * Cleanup the segments in the new line.
921 cleanup_line (start_line);
924 * Lastly, rebalance the first GtkTextBTreeNode of the range.
927 gtk_text_btree_rebalance (tree, start_line->parent);
929 /* Notify outstanding iterators that they
931 chars_changed (tree);
932 segments_changed (tree);
934 if (gtk_debug_flags & GTK_DEBUG_TEXT)
935 _gtk_text_btree_check (tree);
937 /* Re-initialize our iterators */
938 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
943 _gtk_text_btree_insert (GtkTextIter *iter,
947 GtkTextLineSegment *prev_seg; /* The segment just before the first
948 * new segment (NULL means new segment
949 * is at beginning of line). */
950 GtkTextLineSegment *cur_seg; /* Current segment; new characters
951 * are inserted just after this one.
952 * NULL means insert at beginning of
954 GtkTextLine *line; /* Current line (new segments are
955 * added to this line). */
956 GtkTextLineSegment *seg;
957 GtkTextLine *newline;
958 int chunk_len; /* # characters in current chunk. */
959 gint sol; /* start of line */
960 gint eol; /* Pointer to character just after last
961 * one in current chunk.
963 gint delim; /* index of paragraph delimiter */
964 int line_count_delta; /* Counts change to total number of
968 int char_count_delta; /* change to number of chars */
970 gint start_byte_index;
971 GtkTextLine *start_line;
973 g_return_if_fail (text != NULL);
974 g_return_if_fail (iter != NULL);
979 /* extract iterator info */
980 tree = _gtk_text_iter_get_btree (iter);
981 line = _gtk_text_iter_get_text_line (iter);
983 start_byte_index = gtk_text_iter_get_line_index (iter);
985 /* Get our insertion segment split */
986 prev_seg = gtk_text_line_segment_split (iter);
989 /* Invalidate all iterators */
990 chars_changed (tree);
991 segments_changed (tree);
994 * Chop the text up into lines and create a new segment for
995 * each line, plus a new line for the leftovers from the
1001 line_count_delta = 0;
1002 char_count_delta = 0;
1007 pango_find_paragraph_boundary (text + sol,
1012 /* make these relative to the start of the text */
1016 g_assert (eol >= sol);
1017 g_assert (delim >= sol);
1018 g_assert (eol >= delim);
1019 g_assert (sol >= 0);
1020 g_assert (eol <= len);
1022 chunk_len = eol - sol;
1024 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1025 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1027 char_count_delta += seg->char_count;
1029 if (cur_seg == NULL)
1031 seg->next = line->segments;
1032 line->segments = seg;
1036 seg->next = cur_seg->next;
1037 cur_seg->next = seg;
1042 /* chunk didn't end with a paragraph separator */
1043 g_assert (eol == len);
1048 * The chunk ended with a newline, so create a new GtkTextLine
1049 * and move the remainder of the old line to it.
1052 newline = gtk_text_line_new ();
1053 gtk_text_line_set_parent (newline, line->parent);
1054 newline->next = line->next;
1055 line->next = newline;
1056 newline->segments = seg->next;
1064 * Cleanup the starting line for the insertion, plus the ending
1065 * line if it's different.
1068 cleanup_line (start_line);
1069 if (line != start_line)
1071 cleanup_line (line);
1074 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1076 /* Invalidate our region, and reset the iterator the user
1077 passed in to point to the end of the inserted text. */
1083 _gtk_text_btree_get_iter_at_line (tree,
1089 /* We could almost certainly be more efficient here
1090 by saving the information from the insertion loop
1092 gtk_text_iter_forward_chars (&end, char_count_delta);
1094 _gtk_text_btree_invalidate_region (tree,
1098 /* Convenience for the user */
1104 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1105 GtkTextLineSegment *seg)
1109 GtkTextLineSegment *prevPtr;
1112 gint start_byte_offset;
1114 line = _gtk_text_iter_get_text_line (iter);
1115 tree = _gtk_text_iter_get_btree (iter);
1116 start_byte_offset = gtk_text_iter_get_line_index (iter);
1118 prevPtr = gtk_text_line_segment_split (iter);
1119 if (prevPtr == NULL)
1121 seg->next = line->segments;
1122 line->segments = seg;
1126 seg->next = prevPtr->next;
1127 prevPtr->next = seg;
1130 post_insert_fixup (tree, line, 0, seg->char_count);
1132 chars_changed (tree);
1133 segments_changed (tree);
1135 /* reset *iter for the user, and invalidate tree nodes */
1137 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1140 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1142 _gtk_text_btree_invalidate_region (tree, &start, iter);
1146 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1149 GtkTextLineSegment *seg;
1151 seg = _gtk_pixbuf_segment_new (pixbuf);
1153 insert_pixbuf_or_widget_segment (iter, seg);
1157 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1158 GtkTextChildAnchor *anchor)
1160 GtkTextLineSegment *seg;
1163 if (anchor->segment != NULL)
1165 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1169 seg = _gtk_widget_segment_new (anchor);
1171 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1173 insert_pixbuf_or_widget_segment (iter, seg);
1175 if (tree->child_anchor_table == NULL)
1176 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1178 g_hash_table_insert (tree->child_anchor_table,
1179 seg->body.child.obj,
1180 seg->body.child.obj);
1184 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1186 GtkTextLineSegment *seg;
1188 seg = anchor->segment;
1190 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1199 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1200 GtkTextBTreeNode *node, gint y, gint *line_top,
1201 GtkTextLine *last_line)
1205 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1206 _gtk_text_btree_check (tree);
1208 if (node->level == 0)
1212 line = node->children.line;
1214 while (line != NULL && line != last_line)
1216 GtkTextLineData *ld;
1218 ld = _gtk_text_line_get_data (line, view->view_id);
1222 if (y < (current_y + (ld ? ld->height : 0)))
1225 current_y += ld->height;
1226 *line_top += ld->height;
1235 GtkTextBTreeNode *child;
1237 child = node->children.node;
1239 while (child != NULL)
1244 gtk_text_btree_node_get_size (child, view->view_id,
1247 if (y < (current_y + height))
1248 return find_line_by_y (tree, view, child,
1249 y - current_y, line_top,
1252 current_y += height;
1253 *line_top += height;
1255 child = child->next;
1263 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1270 GtkTextLine *last_line;
1273 view = gtk_text_btree_get_view (tree, view_id);
1274 g_return_val_if_fail (view != NULL, NULL);
1276 last_line = get_last_line (tree);
1278 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1282 *line_top_out = line_top;
1288 find_line_top_in_line_list (GtkTextBTree *tree,
1291 GtkTextLine *target_line,
1294 while (line != NULL)
1296 GtkTextLineData *ld;
1298 if (line == target_line)
1301 ld = _gtk_text_line_get_data (line, view->view_id);
1308 g_assert_not_reached (); /* If we get here, our
1309 target line didn't exist
1310 under its parent node */
1315 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1316 GtkTextLine *target_line,
1323 GtkTextBTreeNode *node;
1325 view = gtk_text_btree_get_view (tree, view_id);
1327 g_return_val_if_fail (view != NULL, 0);
1330 node = target_line->parent;
1331 while (node != NULL)
1333 nodes = g_slist_prepend (nodes, node);
1334 node = node->parent;
1338 while (iter != NULL)
1342 if (node->level == 0)
1344 g_slist_free (nodes);
1345 return find_line_top_in_line_list (tree, view,
1346 node->children.line,
1351 GtkTextBTreeNode *child;
1352 GtkTextBTreeNode *target_node;
1354 g_assert (iter->next != NULL); /* not at level 0 */
1355 target_node = iter->next->data;
1357 child = node->children.node;
1359 while (child != NULL)
1364 if (child == target_node)
1368 gtk_text_btree_node_get_size (child, view->view_id,
1372 child = child->next;
1374 g_assert (child != NULL); /* should have broken out before we
1378 iter = g_slist_next (iter);
1381 g_assert_not_reached (); /* we return when we find the target line */
1386 _gtk_text_btree_add_view (GtkTextBTree *tree,
1387 GtkTextLayout *layout)
1390 GtkTextLine *last_line;
1391 GtkTextLineData *line_data;
1393 g_return_if_fail (tree != NULL);
1395 view = g_new (BTreeView, 1);
1397 view->view_id = layout;
1398 view->layout = layout;
1400 view->next = tree->views;
1405 g_assert (tree->views->prev == NULL);
1406 tree->views->prev = view;
1411 /* The last line in the buffer has identity values for the per-view
1412 * data so that we can avoid special case checks for it in a large
1415 last_line = get_last_line (tree);
1417 line_data = g_new (GtkTextLineData, 1);
1418 line_data->view_id = layout;
1419 line_data->next = NULL;
1420 line_data->width = 0;
1421 line_data->height = 0;
1422 line_data->valid = TRUE;
1424 _gtk_text_line_add_data (last_line, line_data);
1428 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1432 GtkTextLine *last_line;
1433 GtkTextLineData *line_data;
1435 g_return_if_fail (tree != NULL);
1439 while (view != NULL)
1441 if (view->view_id == view_id)
1447 g_return_if_fail (view != NULL);
1450 view->next->prev = view->prev;
1453 view->prev->next = view->next;
1455 if (view == tree->views)
1456 tree->views = view->next;
1458 /* Remove the line data for the last line which we added ourselves.
1459 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1461 last_line = get_last_line (tree);
1462 line_data = _gtk_text_line_remove_data (last_line, view_id);
1465 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1467 view->layout = (gpointer) 0xdeadbeef;
1468 view->view_id = (gpointer) 0xdeadbeef;
1474 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1475 const GtkTextIter *start,
1476 const GtkTextIter *end)
1482 while (view != NULL)
1484 gtk_text_layout_invalidate (view->layout, start, end);
1491 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1496 g_return_if_fail (tree != NULL);
1497 g_return_if_fail (view_id != NULL);
1499 gtk_text_btree_node_get_size (tree->root_node, view_id,
1514 iter_stack_new (void)
1517 stack = g_new (IterStack, 1);
1518 stack->iters = NULL;
1525 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1528 if (stack->count > stack->alloced)
1530 stack->alloced = stack->count*2;
1531 stack->iters = g_realloc (stack->iters,
1532 stack->alloced*sizeof (GtkTextIter));
1534 stack->iters[stack->count-1] = *iter;
1538 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1540 if (stack->count == 0)
1545 *iter = stack->iters[stack->count];
1551 iter_stack_free (IterStack *stack)
1553 g_free (stack->iters);
1558 iter_stack_invert (IterStack *stack)
1560 if (stack->count > 0)
1563 guint j = stack->count - 1;
1568 tmp = stack->iters[i];
1569 stack->iters[i] = stack->iters[j];
1570 stack->iters[j] = tmp;
1579 queue_tag_redisplay (GtkTextBTree *tree,
1581 const GtkTextIter *start,
1582 const GtkTextIter *end)
1584 if (_gtk_text_tag_affects_size (tag))
1586 _gtk_text_btree_invalidate_region (tree, start, end);
1588 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1590 /* We only need to queue a redraw, not a relayout */
1591 redisplay_region (tree, start, end);
1594 /* We don't need to do anything if the tag doesn't affect display */
1598 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1599 const GtkTextIter *end_orig,
1603 GtkTextLineSegment *seg, *prev;
1604 GtkTextLine *cleanupline;
1605 gboolean toggled_on;
1606 GtkTextLine *start_line;
1607 GtkTextLine *end_line;
1609 GtkTextIter start, end;
1612 GtkTextTagInfo *info;
1614 g_return_if_fail (start_orig != NULL);
1615 g_return_if_fail (end_orig != NULL);
1616 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1617 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1618 _gtk_text_iter_get_btree (end_orig));
1619 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1622 printf ("%s tag %s from %d to %d\n",
1623 add ? "Adding" : "Removing",
1625 gtk_text_buffer_get_offset (start_orig),
1626 gtk_text_buffer_get_offset (end_orig));
1629 if (gtk_text_iter_equal (start_orig, end_orig))
1632 start = *start_orig;
1635 gtk_text_iter_order (&start, &end);
1637 tree = _gtk_text_iter_get_btree (&start);
1639 queue_tag_redisplay (tree, tag, &start, &end);
1641 info = gtk_text_btree_get_tag_info (tree, tag);
1643 start_line = _gtk_text_iter_get_text_line (&start);
1644 end_line = _gtk_text_iter_get_text_line (&end);
1646 /* Find all tag toggles in the region; we are going to delete them.
1647 We need to find them in advance, because
1648 forward_find_tag_toggle () won't work once we start playing around
1650 stack = iter_stack_new ();
1653 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1654 * which is deliberate - we don't want to delete a toggle at the
1657 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1659 if (gtk_text_iter_compare (&iter, &end) >= 0)
1662 iter_stack_push (stack, &iter);
1665 /* We need to traverse the toggles in order. */
1666 iter_stack_invert (stack);
1669 * See whether the tag is present at the start of the range. If
1670 * the state doesn't already match what we want then add a toggle
1674 toggled_on = gtk_text_iter_has_tag (&start, tag);
1675 if ( (add && !toggled_on) ||
1676 (!add && toggled_on) )
1678 /* This could create a second toggle at the start position;
1679 cleanup_line () will remove it if so. */
1680 seg = _gtk_toggle_segment_new (info, add);
1682 prev = gtk_text_line_segment_split (&start);
1685 seg->next = start_line->segments;
1686 start_line->segments = seg;
1690 seg->next = prev->next;
1694 /* cleanup_line adds the new toggle to the node counts. */
1696 printf ("added toggle at start\n");
1698 /* we should probably call segments_changed, but in theory
1699 any still-cached segments in the iters we are about to
1700 use are still valid, since they're in front
1706 * Scan the range of characters and delete any internal tag
1707 * transitions. Keep track of what the old state was at the end
1708 * of the range, and add a toggle there if it's needed.
1712 cleanupline = start_line;
1713 while (iter_stack_pop (stack, &iter))
1715 GtkTextLineSegment *indexable_seg;
1718 line = _gtk_text_iter_get_text_line (&iter);
1719 seg = _gtk_text_iter_get_any_segment (&iter);
1720 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1722 g_assert (seg != NULL);
1723 g_assert (indexable_seg != NULL);
1724 g_assert (seg != indexable_seg);
1726 prev = line->segments;
1728 /* Find the segment that actually toggles this tag. */
1729 while (seg != indexable_seg)
1731 g_assert (seg != NULL);
1732 g_assert (indexable_seg != NULL);
1733 g_assert (seg != indexable_seg);
1735 if ( (seg->type == >k_text_toggle_on_type ||
1736 seg->type == >k_text_toggle_off_type) &&
1737 (seg->body.toggle.info == info) )
1743 g_assert (seg != NULL);
1744 g_assert (indexable_seg != NULL);
1746 g_assert (seg != indexable_seg); /* If this happens, then
1747 forward_to_tag_toggle was
1749 g_assert (seg->body.toggle.info->tag == tag);
1751 /* If this happens, when previously tagging we didn't merge
1752 overlapping tags. */
1753 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1754 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1756 toggled_on = !toggled_on;
1759 printf ("deleting %s toggle\n",
1760 seg->type == >k_text_toggle_on_type ? "on" : "off");
1762 /* Remove toggle segment from the list. */
1765 line->segments = seg->next;
1769 while (prev->next != seg)
1773 prev->next = seg->next;
1776 /* Inform iterators we've hosed them. This actually reflects a
1777 bit of inefficiency; if you have the same tag toggled on and
1778 off a lot in a single line, we keep having the rescan from
1779 the front of the line. Of course we have to do that to get
1780 "prev" anyway, but here we are doing it an additional
1782 segments_changed (tree);
1784 /* Update node counts */
1785 if (seg->body.toggle.inNodeCounts)
1787 _gtk_change_node_toggle_count (line->parent,
1789 seg->body.toggle.inNodeCounts = FALSE;
1794 /* We only clean up lines when we're done with them, saves some
1795 gratuitous line-segment-traversals */
1797 if (cleanupline != line)
1799 cleanup_line (cleanupline);
1804 iter_stack_free (stack);
1806 /* toggled_on now reflects the toggle state _just before_ the
1807 end iterator. The end iterator could already have a toggle
1808 on or a toggle off. */
1809 if ( (add && !toggled_on) ||
1810 (!add && toggled_on) )
1812 /* This could create a second toggle at the start position;
1813 cleanup_line () will remove it if so. */
1815 seg = _gtk_toggle_segment_new (info, !add);
1817 prev = gtk_text_line_segment_split (&end);
1820 seg->next = end_line->segments;
1821 end_line->segments = seg;
1825 seg->next = prev->next;
1828 /* cleanup_line adds the new toggle to the node counts. */
1829 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1831 printf ("added toggle at end\n");
1836 * Cleanup cleanupline and the last line of the range, if
1837 * these are different.
1840 cleanup_line (cleanupline);
1841 if (cleanupline != end_line)
1843 cleanup_line (end_line);
1846 segments_changed (tree);
1848 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1849 _gtk_text_btree_check (tree);
1858 _gtk_text_btree_get_line (GtkTextBTree *tree,
1860 gint *real_line_number)
1862 GtkTextBTreeNode *node;
1867 line_count = _gtk_text_btree_line_count (tree);
1869 if (line_number < 0)
1871 line_number = line_count;
1873 else if (line_number > line_count)
1875 line_number = line_count;
1878 if (real_line_number)
1879 *real_line_number = line_number;
1881 node = tree->root_node;
1882 lines_left = line_number;
1885 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1889 while (node->level != 0)
1891 for (node = node->children.node;
1892 node->num_lines <= lines_left;
1898 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1901 lines_left -= node->num_lines;
1906 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1909 for (line = node->children.line; lines_left > 0;
1915 g_error ("gtk_text_btree_find_line ran out of lines");
1924 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1927 _gtk_text_btree_get_line (tree,
1928 _gtk_text_btree_line_count (tree) - 1,
1933 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1935 gint *line_start_index,
1936 gint *real_char_index)
1938 GtkTextBTreeNode *node;
1940 GtkTextLineSegment *seg;
1945 node = tree->root_node;
1947 /* Clamp to valid indexes (-1 is magic for "highest index"),
1948 * node->num_chars includes the two newlines that aren't really
1951 if (char_index < 0 || char_index >= (node->num_chars - 1))
1953 char_index = node->num_chars - 2;
1956 *real_char_index = char_index;
1959 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1963 chars_left = char_index;
1964 while (node->level != 0)
1966 for (node = node->children.node;
1967 chars_left >= node->num_chars;
1970 chars_left -= node->num_chars;
1972 g_assert (chars_left >= 0);
1976 if (chars_left == 0)
1978 /* Start of a line */
1980 *line_start_index = char_index;
1981 return node->children.line;
1985 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1991 for (line = node->children.line; line != NULL; line = line->next)
1993 seg = line->segments;
1996 if (chars_in_line + seg->char_count > chars_left)
1997 goto found; /* found our line/segment */
1999 chars_in_line += seg->char_count;
2004 chars_left -= chars_in_line;
2012 g_assert (line != NULL); /* hosage, ran out of lines */
2013 g_assert (seg != NULL);
2015 *line_start_index = char_index - chars_left;
2020 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2023 GtkTextBTreeNode *node;
2024 GtkTextLine *siblingline;
2025 GtkTextLineSegment *seg;
2026 int src, dst, index;
2032 #define NUM_TAG_INFOS 10
2034 line = _gtk_text_iter_get_text_line (iter);
2035 tree = _gtk_text_iter_get_btree (iter);
2036 byte_index = gtk_text_iter_get_line_index (iter);
2038 tagInfo.numTags = 0;
2039 tagInfo.arraySize = NUM_TAG_INFOS;
2040 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2041 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2044 * Record tag toggles within the line of indexPtr but preceding
2045 * indexPtr. Note that if this loop segfaults, your
2046 * byte_index probably points past the sum of all
2047 * seg->byte_count */
2049 for (index = 0, seg = line->segments;
2050 (index + seg->byte_count) <= byte_index;
2051 index += seg->byte_count, seg = seg->next)
2053 if ((seg->type == >k_text_toggle_on_type)
2054 || (seg->type == >k_text_toggle_off_type))
2056 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2061 * Record toggles for tags in lines that are predecessors of
2062 * line but under the same level-0 GtkTextBTreeNode.
2065 for (siblingline = line->parent->children.line;
2066 siblingline != line;
2067 siblingline = siblingline->next)
2069 for (seg = siblingline->segments; seg != NULL;
2072 if ((seg->type == >k_text_toggle_on_type)
2073 || (seg->type == >k_text_toggle_off_type))
2075 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2081 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2082 * toggles for all siblings that precede that GtkTextBTreeNode.
2085 for (node = line->parent; node->parent != NULL;
2086 node = node->parent)
2088 GtkTextBTreeNode *siblingPtr;
2091 for (siblingPtr = node->parent->children.node;
2092 siblingPtr != node; siblingPtr = siblingPtr->next)
2094 for (summary = siblingPtr->summary; summary != NULL;
2095 summary = summary->next)
2097 if (summary->toggle_count & 1)
2099 inc_count (summary->info->tag, summary->toggle_count,
2107 * Go through the tag information and squash out all of the tags
2108 * that have even toggle counts (these tags exist before the point
2109 * of interest, but not at the desired character itself).
2112 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2114 if (tagInfo.counts[src] & 1)
2116 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2117 tagInfo.tags[dst] = tagInfo.tags[src];
2123 g_free (tagInfo.counts);
2126 g_free (tagInfo.tags);
2129 return tagInfo.tags;
2133 copy_segment (GString *string,
2134 gboolean include_hidden,
2135 gboolean include_nonchars,
2136 const GtkTextIter *start,
2137 const GtkTextIter *end)
2139 GtkTextLineSegment *end_seg;
2140 GtkTextLineSegment *seg;
2142 if (gtk_text_iter_equal (start, end))
2145 seg = _gtk_text_iter_get_indexable_segment (start);
2146 end_seg = _gtk_text_iter_get_indexable_segment (end);
2148 if (seg->type == >k_text_char_type)
2150 gboolean copy = TRUE;
2151 gint copy_bytes = 0;
2152 gint copy_start = 0;
2154 /* Don't copy if we're invisible; segments are invisible/not
2155 as a whole, no need to check each char */
2156 if (!include_hidden &&
2157 _gtk_text_btree_char_is_invisible (start))
2160 /* printf (" <invisible>\n"); */
2163 copy_start = _gtk_text_iter_get_segment_byte (start);
2167 /* End is in the same segment; need to copy fewer bytes. */
2168 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2170 copy_bytes = end_byte - copy_start;
2173 copy_bytes = seg->byte_count - copy_start;
2175 g_assert (copy_bytes != 0); /* Due to iter equality check at
2176 front of this function. */
2180 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2182 g_string_append_len (string,
2183 seg->body.chars + copy_start,
2187 /* printf (" :%s\n", string->str); */
2189 else if (seg->type == >k_text_pixbuf_type ||
2190 seg->type == >k_text_child_type)
2192 gboolean copy = TRUE;
2194 if (!include_nonchars)
2198 else if (!include_hidden &&
2199 _gtk_text_btree_char_is_invisible (start))
2206 g_string_append_len (string,
2207 gtk_text_unknown_char_utf8,
2215 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2216 const GtkTextIter *end_orig,
2217 gboolean include_hidden,
2218 gboolean include_nonchars)
2220 GtkTextLineSegment *seg;
2221 GtkTextLineSegment *end_seg;
2229 g_return_val_if_fail (start_orig != NULL, NULL);
2230 g_return_val_if_fail (end_orig != NULL, NULL);
2231 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2232 _gtk_text_iter_get_btree (end_orig), NULL);
2234 start = *start_orig;
2237 gtk_text_iter_order (&start, &end);
2239 retval = g_string_new ("");
2241 tree = _gtk_text_iter_get_btree (&start);
2243 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2245 seg = _gtk_text_iter_get_indexable_segment (&iter);
2246 while (seg != end_seg)
2248 copy_segment (retval, include_hidden, include_nonchars,
2251 _gtk_text_iter_forward_indexable_segment (&iter);
2253 seg = _gtk_text_iter_get_indexable_segment (&iter);
2256 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2259 g_string_free (retval, FALSE);
2264 _gtk_text_btree_line_count (GtkTextBTree *tree)
2266 /* Subtract bogus line at the end; we return a count
2268 return tree->root_node->num_lines - 1;
2272 _gtk_text_btree_char_count (GtkTextBTree *tree)
2274 /* Exclude newline in bogus last line and the
2275 * one in the last line that is after the end iterator
2277 return tree->root_node->num_chars - 2;
2280 #define LOTSA_TAGS 1000
2282 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2284 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2286 int deftagCnts[LOTSA_TAGS];
2287 int *tagCnts = deftagCnts;
2288 GtkTextTag *deftags[LOTSA_TAGS];
2289 GtkTextTag **tags = deftags;
2291 GtkTextBTreeNode *node;
2292 GtkTextLine *siblingline;
2293 GtkTextLineSegment *seg;
2300 line = _gtk_text_iter_get_text_line (iter);
2301 tree = _gtk_text_iter_get_btree (iter);
2302 byte_index = gtk_text_iter_get_line_index (iter);
2304 numTags = gtk_text_tag_table_get_size (tree->table);
2306 /* almost always avoid malloc, so stay out of system calls */
2307 if (LOTSA_TAGS < numTags)
2309 tagCnts = g_new (int, numTags);
2310 tags = g_new (GtkTextTag*, numTags);
2313 for (i=0; i<numTags; i++)
2319 * Record tag toggles within the line of indexPtr but preceding
2323 for (index = 0, seg = line->segments;
2324 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2325 index += seg->byte_count, seg = seg->next)
2327 if ((seg->type == >k_text_toggle_on_type)
2328 || (seg->type == >k_text_toggle_off_type))
2330 tag = seg->body.toggle.info->tag;
2331 if (tag->invisible_set && tag->values->invisible)
2333 tags[tag->priority] = tag;
2334 tagCnts[tag->priority]++;
2340 * Record toggles for tags in lines that are predecessors of
2341 * line but under the same level-0 GtkTextBTreeNode.
2344 for (siblingline = line->parent->children.line;
2345 siblingline != line;
2346 siblingline = siblingline->next)
2348 for (seg = siblingline->segments; seg != NULL;
2351 if ((seg->type == >k_text_toggle_on_type)
2352 || (seg->type == >k_text_toggle_off_type))
2354 tag = seg->body.toggle.info->tag;
2355 if (tag->invisible_set && tag->values->invisible)
2357 tags[tag->priority] = tag;
2358 tagCnts[tag->priority]++;
2365 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2366 * for all siblings that precede that GtkTextBTreeNode.
2369 for (node = line->parent; node->parent != NULL;
2370 node = node->parent)
2372 GtkTextBTreeNode *siblingPtr;
2375 for (siblingPtr = node->parent->children.node;
2376 siblingPtr != node; siblingPtr = siblingPtr->next)
2378 for (summary = siblingPtr->summary; summary != NULL;
2379 summary = summary->next)
2381 if (summary->toggle_count & 1)
2383 tag = summary->info->tag;
2384 if (tag->invisible_set && tag->values->invisible)
2386 tags[tag->priority] = tag;
2387 tagCnts[tag->priority] += summary->toggle_count;
2395 * Now traverse from highest priority to lowest,
2396 * take invisible value from first odd count (= on)
2399 for (i = numTags-1; i >=0; i--)
2403 /* FIXME not sure this should be if 0 */
2405 #ifndef ALWAYS_SHOW_SELECTION
2406 /* who would make the selection invisible? */
2407 if ((tag == tkxt->seltag)
2408 && !(tkxt->flags & GOT_FOCUS))
2414 invisible = tags[i]->values->invisible;
2419 if (LOTSA_TAGS < numTags)
2434 redisplay_region (GtkTextBTree *tree,
2435 const GtkTextIter *start,
2436 const GtkTextIter *end)
2439 GtkTextLine *start_line, *end_line;
2441 if (gtk_text_iter_compare (start, end) > 0)
2443 const GtkTextIter *tmp = start;
2448 start_line = _gtk_text_iter_get_text_line (start);
2449 end_line = _gtk_text_iter_get_text_line (end);
2452 while (view != NULL)
2454 gint start_y, end_y;
2455 GtkTextLineData *ld;
2457 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2459 if (end_line == start_line)
2462 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2464 ld = _gtk_text_line_get_data (end_line, view->view_id);
2466 end_y += ld->height;
2468 gtk_text_layout_changed (view->layout, start_y,
2477 redisplay_mark (GtkTextLineSegment *mark)
2482 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2484 mark->body.mark.obj);
2487 gtk_text_iter_forward_char (&end);
2489 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2494 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2496 if (!mark->body.mark.visible)
2499 redisplay_mark (mark);
2503 ensure_not_off_end (GtkTextBTree *tree,
2504 GtkTextLineSegment *mark,
2507 if (gtk_text_iter_get_line (iter) ==
2508 _gtk_text_btree_line_count (tree))
2509 gtk_text_iter_backward_char (iter);
2512 static GtkTextLineSegment*
2513 real_set_mark (GtkTextBTree *tree,
2514 GtkTextMark *existing_mark,
2516 gboolean left_gravity,
2517 const GtkTextIter *where,
2518 gboolean should_exist,
2519 gboolean redraw_selections)
2521 GtkTextLineSegment *mark;
2524 g_return_val_if_fail (tree != NULL, NULL);
2525 g_return_val_if_fail (where != NULL, NULL);
2526 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2529 mark = existing_mark->segment;
2530 else if (name != NULL)
2531 mark = g_hash_table_lookup (tree->mark_table,
2536 if (should_exist && mark == NULL)
2538 g_warning ("No mark `%s' exists!", name);
2542 /* OK if !should_exist and it does already exist, in that case
2548 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2549 _gtk_text_iter_check (&iter);
2553 if (redraw_selections &&
2554 (mark == tree->insert_mark->segment ||
2555 mark == tree->selection_bound_mark->segment))
2557 GtkTextIter old_pos;
2559 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2560 mark->body.mark.obj);
2561 redisplay_region (tree, &old_pos, where);
2565 * don't let visible marks be after the final newline of the
2569 if (mark->body.mark.visible)
2571 ensure_not_off_end (tree, mark, &iter);
2574 /* Redraw the mark's old location. */
2575 redisplay_mark_if_visible (mark);
2577 /* Unlink mark from its current location.
2578 This could hose our iterator... */
2579 gtk_text_btree_unlink_segment (tree, mark,
2580 mark->body.mark.line);
2581 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2582 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2584 segments_changed (tree); /* make sure the iterator recomputes its
2589 mark = _gtk_mark_segment_new (tree,
2593 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2595 if (mark->body.mark.name)
2596 g_hash_table_insert (tree->mark_table,
2597 mark->body.mark.name,
2601 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2602 _gtk_text_iter_check (&iter);
2604 /* Link mark into new location */
2605 gtk_text_btree_link_segment (mark, &iter);
2607 /* Invalidate some iterators. */
2608 segments_changed (tree);
2611 * update the screen at the mark's new location.
2614 redisplay_mark_if_visible (mark);
2616 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2617 _gtk_text_iter_check (&iter);
2619 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2620 _gtk_text_btree_check (tree);
2627 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2628 GtkTextMark *existing_mark,
2630 gboolean left_gravity,
2631 const GtkTextIter *iter,
2632 gboolean should_exist)
2634 GtkTextLineSegment *seg;
2636 seg = real_set_mark (tree, existing_mark,
2637 name, left_gravity, iter, should_exist,
2640 return seg ? seg->body.mark.obj : NULL;
2644 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2648 GtkTextIter tmp_start, tmp_end;
2650 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2652 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2653 tree->selection_bound_mark);
2655 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2667 gtk_text_iter_order (&tmp_start, &tmp_end);
2680 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2681 const GtkTextIter *iter)
2683 GtkTextIter start, end;
2685 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2686 redisplay_region (tree, &start, &end);
2688 /* Move insert AND selection_bound before we redisplay */
2689 real_set_mark (tree, tree->insert_mark,
2690 "insert", FALSE, iter, TRUE, FALSE);
2691 real_set_mark (tree, tree->selection_bound_mark,
2692 "selection_bound", FALSE, iter, TRUE, FALSE);
2696 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2701 g_return_if_fail (tree != NULL);
2702 g_return_if_fail (name != NULL);
2704 mark = g_hash_table_lookup (tree->mark_table,
2707 _gtk_text_btree_remove_mark (tree, mark);
2711 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2712 GtkTextLineSegment *segment)
2715 if (segment->body.mark.name)
2716 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2718 segment->body.mark.tree = NULL;
2719 segment->body.mark.line = NULL;
2721 /* Remove the ref on the mark, which frees segment as a side effect
2722 * if this is the last reference.
2724 g_object_unref (G_OBJECT (segment->body.mark.obj));
2728 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2731 GtkTextLineSegment *segment;
2733 g_return_if_fail (mark != NULL);
2734 g_return_if_fail (tree != NULL);
2736 segment = mark->segment;
2738 if (segment->body.mark.not_deleteable)
2740 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2744 /* This calls cleanup_line and segments_changed */
2745 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2747 _gtk_text_btree_release_mark_segment (tree, segment);
2751 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2752 GtkTextMark *segment)
2754 return segment == tree->insert_mark;
2758 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2759 GtkTextMark *segment)
2761 return segment == tree->selection_bound_mark;
2765 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2768 GtkTextLineSegment *seg;
2770 g_return_val_if_fail (tree != NULL, NULL);
2771 g_return_val_if_fail (name != NULL, NULL);
2773 seg = g_hash_table_lookup (tree->mark_table, name);
2775 return seg ? seg->body.mark.obj : NULL;
2779 * gtk_text_mark_set_visible:
2780 * @mark: a #GtkTextMark
2781 * @setting: visibility of mark
2783 * Sets the visibility of @mark; the insertion point is normally
2784 * visible, i.e. you can see it as a vertical bar. Also, the text
2785 * widget uses a visible mark to indicate where a drop will occur when
2786 * dragging-and-dropping text. Most other marks are not visible.
2787 * Marks are not visible by default.
2791 gtk_text_mark_set_visible (GtkTextMark *mark,
2794 GtkTextLineSegment *seg;
2796 g_return_if_fail (mark != NULL);
2798 seg = mark->segment;
2800 if (seg->body.mark.visible == setting)
2804 seg->body.mark.visible = setting;
2806 redisplay_mark (seg);
2811 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2814 GtkTextBTreeNode *node;
2815 GtkTextTagInfo *info;
2817 g_return_val_if_fail (tree != NULL, NULL);
2821 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2826 if (info->tag_root == NULL)
2829 node = info->tag_root;
2831 /* We know the tag root has instances of the given
2834 continue_outer_loop:
2835 g_assert (node != NULL);
2836 while (node->level > 0)
2838 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2839 node = node->children.node;
2840 while (node != NULL)
2842 if (gtk_text_btree_node_has_tag (node, tag))
2843 goto continue_outer_loop;
2847 g_assert (node != NULL);
2850 g_assert (node != NULL); /* The tag summaries said some node had
2853 g_assert (node->level == 0);
2855 return node->children.line;
2859 /* Looking for any tag at all (tag == NULL).
2860 Unfortunately this can't be done in a simple and efficient way
2861 right now; so I'm just going to return the
2862 first line in the btree. FIXME */
2863 return _gtk_text_btree_get_line (tree, 0, NULL);
2868 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2871 GtkTextBTreeNode *node;
2872 GtkTextBTreeNode *last_node;
2874 GtkTextTagInfo *info;
2876 g_return_val_if_fail (tree != NULL, NULL);
2880 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2882 if (info->tag_root == NULL)
2885 node = info->tag_root;
2886 /* We know the tag root has instances of the given
2889 while (node->level > 0)
2891 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2893 node = node->children.node;
2894 while (node != NULL)
2896 if (gtk_text_btree_node_has_tag (node, tag))
2904 g_assert (node != NULL); /* The tag summaries said some node had
2907 g_assert (node->level == 0);
2909 /* Find the last line in this node */
2910 line = node->children.line;
2911 while (line->next != NULL)
2918 /* This search can't be done efficiently at the moment,
2919 at least not without complexity.
2920 So, we just return the last line.
2922 return _gtk_text_btree_get_line (tree, -1, NULL);
2932 _gtk_text_line_get_number (GtkTextLine *line)
2935 GtkTextBTreeNode *node, *parent, *node2;
2939 * First count how many lines precede this one in its level-0
2943 node = line->parent;
2945 for (line2 = node->children.line; line2 != line;
2946 line2 = line2->next)
2950 g_error ("gtk_text_btree_line_number couldn't find line");
2956 * Now work up through the levels of the tree one at a time,
2957 * counting how many lines are in GtkTextBTreeNodes preceding the current
2961 for (parent = node->parent ; parent != NULL;
2962 node = parent, parent = parent->parent)
2964 for (node2 = parent->children.node; node2 != node;
2965 node2 = node2->next)
2969 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2971 index += node2->num_lines;
2977 static GtkTextLineSegment*
2978 find_toggle_segment_before_char (GtkTextLine *line,
2982 GtkTextLineSegment *seg;
2983 GtkTextLineSegment *toggle_seg;
2988 seg = line->segments;
2989 while ( (index + seg->char_count) <= char_in_line )
2991 if (((seg->type == >k_text_toggle_on_type)
2992 || (seg->type == >k_text_toggle_off_type))
2993 && (seg->body.toggle.info->tag == tag))
2996 index += seg->char_count;
3003 static GtkTextLineSegment*
3004 find_toggle_segment_before_byte (GtkTextLine *line,
3008 GtkTextLineSegment *seg;
3009 GtkTextLineSegment *toggle_seg;
3014 seg = line->segments;
3015 while ( (index + seg->byte_count) <= byte_in_line )
3017 if (((seg->type == >k_text_toggle_on_type)
3018 || (seg->type == >k_text_toggle_off_type))
3019 && (seg->body.toggle.info->tag == tag))
3022 index += seg->byte_count;
3030 find_toggle_outside_current_line (GtkTextLine *line,
3034 GtkTextBTreeNode *node;
3035 GtkTextLine *sibling_line;
3036 GtkTextLineSegment *seg;
3037 GtkTextLineSegment *toggle_seg;
3039 GtkTextTagInfo *info = NULL;
3042 * No toggle in this line. Look for toggles for the tag in lines
3043 * that are predecessors of line but under the same
3044 * level-0 GtkTextBTreeNode.
3047 sibling_line = line->parent->children.line;
3048 while (sibling_line != line)
3050 seg = sibling_line->segments;
3053 if (((seg->type == >k_text_toggle_on_type)
3054 || (seg->type == >k_text_toggle_off_type))
3055 && (seg->body.toggle.info->tag == tag))
3061 sibling_line = sibling_line->next;
3064 if (toggle_seg != NULL)
3065 return (toggle_seg->type == >k_text_toggle_on_type);
3068 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3069 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3070 * siblings that precede that GtkTextBTreeNode.
3073 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3079 node = line->parent;
3080 while (node->parent != NULL)
3082 GtkTextBTreeNode *sibling_node;
3084 sibling_node = node->parent->children.node;
3085 while (sibling_node != node)
3089 summary = sibling_node->summary;
3090 while (summary != NULL)
3092 if (summary->info == info)
3093 toggles += summary->toggle_count;
3095 summary = summary->next;
3098 sibling_node = sibling_node->next;
3101 if (node == info->tag_root)
3104 node = node->parent;
3108 * An odd number of toggles means that the tag is present at the
3112 return (toggles & 1) != 0;
3115 /* FIXME this function is far too slow, for no good reason. */
3117 _gtk_text_line_char_has_tag (GtkTextLine *line,
3122 GtkTextLineSegment *toggle_seg;
3124 g_return_val_if_fail (line != NULL, FALSE);
3127 * Check for toggles for the tag in the line but before
3128 * the char. If there is one, its type indicates whether or
3129 * not the character is tagged.
3132 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3134 if (toggle_seg != NULL)
3135 return (toggle_seg->type == >k_text_toggle_on_type);
3137 return find_toggle_outside_current_line (line, tree, tag);
3141 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3146 GtkTextLineSegment *toggle_seg;
3148 g_return_val_if_fail (line != NULL, FALSE);
3151 * Check for toggles for the tag in the line but before
3152 * the char. If there is one, its type indicates whether or
3153 * not the character is tagged.
3156 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3158 if (toggle_seg != NULL)
3159 return (toggle_seg->type == >k_text_toggle_on_type);
3161 return find_toggle_outside_current_line (line, tree, tag);
3165 _gtk_text_line_is_last (GtkTextLine *line,
3168 return line == get_last_line (tree);
3172 ensure_end_iter_line (GtkTextBTree *tree)
3174 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3179 /* n_lines is without the magic line at the end */
3180 n_lines = _gtk_text_btree_line_count (tree);
3182 g_assert (n_lines >= 1);
3184 tree->end_iter_line = _gtk_text_btree_get_line (tree, n_lines - 1, &real_line);
3186 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3191 ensure_end_iter_segment (GtkTextBTree *tree)
3193 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3195 GtkTextLineSegment *seg;
3196 GtkTextLineSegment *last_with_chars;
3198 ensure_end_iter_line (tree);
3200 last_with_chars = NULL;
3202 seg = tree->end_iter_line->segments;
3205 if (seg->char_count > 0)
3206 last_with_chars = seg;
3210 tree->end_iter_segment = last_with_chars;
3212 /* We know the last char in the last line is '\n' */
3213 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3214 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3216 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3221 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3224 ensure_end_iter_line (tree);
3226 return line == tree->end_iter_line;
3230 _gtk_text_btree_is_end (GtkTextBTree *tree,
3232 GtkTextLineSegment *seg,
3236 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3238 /* Do this first to avoid walking segments in most cases */
3239 if (!_gtk_text_line_contains_end_iter (line, tree))
3242 ensure_end_iter_segment (tree);
3244 if (seg != tree->end_iter_segment)
3247 if (byte_index >= 0)
3248 return byte_index == tree->end_iter_segment_byte_index;
3250 return char_offset == tree->end_iter_segment_char_offset;
3254 _gtk_text_line_next (GtkTextLine *line)
3256 GtkTextBTreeNode *node;
3258 if (line->next != NULL)
3263 * This was the last line associated with the particular parent
3264 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3265 * then search down from that GtkTextBTreeNode to find the first
3269 node = line->parent;
3270 while (node != NULL && node->next == NULL)
3271 node = node->parent;
3277 while (node->level > 0)
3279 node = node->children.node;
3282 g_assert (node->children.line != line);
3284 return node->children.line;
3289 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3293 next = _gtk_text_line_next (line);
3295 /* If we were on the end iter line, we can't go to
3298 if (next && next->next == NULL && /* these checks are optimization only */
3299 _gtk_text_line_next (next) == NULL)
3306 _gtk_text_line_previous (GtkTextLine *line)
3308 GtkTextBTreeNode *node;
3309 GtkTextBTreeNode *node2;
3313 * Find the line under this GtkTextBTreeNode just before the starting line.
3315 prev = line->parent->children.line; /* First line at leaf */
3316 while (prev != line)
3318 if (prev->next == line)
3324 g_error ("gtk_text_btree_previous_line ran out of lines");
3328 * This was the first line associated with the particular parent
3329 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3330 * then search down from that GtkTextBTreeNode to find its last line.
3332 for (node = line->parent; ; node = node->parent)
3334 if (node == NULL || node->parent == NULL)
3336 else if (node != node->parent->children.node)
3340 for (node2 = node->parent->children.node; ;
3341 node2 = node2->children.node)
3343 while (node2->next != node)
3344 node2 = node2->next;
3346 if (node2->level == 0)
3352 for (prev = node2->children.line ; ; prev = prev->next)
3354 if (prev->next == NULL)
3358 g_assert_not_reached ();
3363 _gtk_text_line_add_data (GtkTextLine *line,
3364 GtkTextLineData *data)
3366 g_return_if_fail (line != NULL);
3367 g_return_if_fail (data != NULL);
3368 g_return_if_fail (data->view_id != NULL);
3372 data->next = line->views;
3382 _gtk_text_line_remove_data (GtkTextLine *line,
3385 GtkTextLineData *prev;
3386 GtkTextLineData *iter;
3388 g_return_val_if_fail (line != NULL, NULL);
3389 g_return_val_if_fail (view_id != NULL, NULL);
3393 while (iter != NULL)
3395 if (iter->view_id == view_id)
3404 prev->next = iter->next;
3406 line->views = iter->next;
3415 _gtk_text_line_get_data (GtkTextLine *line,
3418 GtkTextLineData *iter;
3420 g_return_val_if_fail (line != NULL, NULL);
3421 g_return_val_if_fail (view_id != NULL, NULL);
3424 while (iter != NULL)
3426 if (iter->view_id == view_id)
3435 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3436 GtkTextLineData *ld)
3438 /* For now this is totally unoptimized. FIXME?
3440 We could probably optimize the case where the width removed
3441 is less than the max width for the parent node,
3442 and the case where the height is unchanged when we re-wrap.
3445 g_return_if_fail (ld != NULL);
3448 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3452 _gtk_text_line_char_count (GtkTextLine *line)
3454 GtkTextLineSegment *seg;
3458 seg = line->segments;
3461 size += seg->char_count;
3468 _gtk_text_line_byte_count (GtkTextLine *line)
3470 GtkTextLineSegment *seg;
3474 seg = line->segments;
3477 size += seg->byte_count;
3485 _gtk_text_line_char_index (GtkTextLine *target_line)
3487 GSList *node_stack = NULL;
3488 GtkTextBTreeNode *iter;
3492 /* Push all our parent nodes onto a stack */
3493 iter = target_line->parent;
3495 g_assert (iter != NULL);
3497 while (iter != NULL)
3499 node_stack = g_slist_prepend (node_stack, iter);
3501 iter = iter->parent;
3504 /* Check that we have the root node on top of the stack. */
3505 g_assert (node_stack != NULL &&
3506 node_stack->data != NULL &&
3507 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3509 /* Add up chars in all nodes before the nodes in our stack.
3513 iter = node_stack->data;
3514 while (iter != NULL)
3516 GtkTextBTreeNode *child_iter;
3517 GtkTextBTreeNode *next_node;
3519 next_node = node_stack->next ?
3520 node_stack->next->data : NULL;
3521 node_stack = g_slist_remove (node_stack, node_stack->data);
3523 if (iter->level == 0)
3525 /* stack should be empty when we're on the last node */
3526 g_assert (node_stack == NULL);
3527 break; /* Our children are now lines */
3530 g_assert (next_node != NULL);
3531 g_assert (iter != NULL);
3532 g_assert (next_node->parent == iter);
3534 /* Add up chars before us in the tree */
3535 child_iter = iter->children.node;
3536 while (child_iter != next_node)
3538 g_assert (child_iter != NULL);
3540 num_chars += child_iter->num_chars;
3542 child_iter = child_iter->next;
3548 g_assert (iter != NULL);
3549 g_assert (iter == target_line->parent);
3551 /* Since we don't store char counts in lines, only in segments, we
3552 have to iterate over the lines adding up segment char counts
3553 until we find our line. */
3554 line = iter->children.line;
3555 while (line != target_line)
3557 g_assert (line != NULL);
3559 num_chars += _gtk_text_line_char_count (line);
3564 g_assert (line == target_line);
3570 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3574 GtkTextLineSegment *seg;
3577 g_return_val_if_fail (line != NULL, NULL);
3579 offset = byte_offset;
3580 seg = line->segments;
3582 while (offset >= seg->byte_count)
3584 g_assert (seg != NULL); /* means an invalid byte index */
3585 offset -= seg->byte_count;
3590 *seg_offset = offset;
3596 _gtk_text_line_char_to_segment (GtkTextLine *line,
3600 GtkTextLineSegment *seg;
3603 g_return_val_if_fail (line != NULL, NULL);
3605 offset = char_offset;
3606 seg = line->segments;
3608 while (offset >= seg->char_count)
3610 g_assert (seg != NULL); /* means an invalid char index */
3611 offset -= seg->char_count;
3616 *seg_offset = offset;
3622 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3626 GtkTextLineSegment *seg;
3629 g_return_val_if_fail (line != NULL, NULL);
3631 offset = byte_offset;
3632 seg = line->segments;
3634 while (offset > 0 && offset >= seg->byte_count)
3636 g_assert (seg != NULL); /* means an invalid byte index */
3637 offset -= seg->byte_count;
3642 *seg_offset = offset;
3648 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3652 GtkTextLineSegment *seg;
3655 g_return_val_if_fail (line != NULL, NULL);
3657 offset = char_offset;
3658 seg = line->segments;
3660 while (offset > 0 && offset >= seg->char_count)
3662 g_assert (seg != NULL); /* means an invalid byte index */
3663 offset -= seg->char_count;
3668 *seg_offset = offset;
3674 _gtk_text_line_byte_to_char (GtkTextLine *line,
3678 GtkTextLineSegment *seg;
3680 g_return_val_if_fail (line != NULL, 0);
3681 g_return_val_if_fail (byte_offset >= 0, 0);
3684 seg = line->segments;
3685 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3686 the next segment) */
3688 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3690 byte_offset -= seg->byte_count;
3691 char_offset += seg->char_count;
3696 g_assert (seg != NULL);
3698 /* Now byte_offset is the offset into the current segment,
3699 and char_offset is the start of the current segment.
3700 Optimize the case where no chars use > 1 byte */
3701 if (seg->byte_count == seg->char_count)
3702 return char_offset + byte_offset;
3705 if (seg->type == >k_text_char_type)
3706 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3709 g_assert (seg->char_count == 1);
3710 g_assert (byte_offset == 0);
3718 _gtk_text_line_char_to_byte (GtkTextLine *line,
3721 g_warning ("FIXME not implemented");
3726 /* FIXME sync with char_locate (or figure out a clean
3727 way to merge the two functions) */
3729 _gtk_text_line_byte_locate (GtkTextLine *line,
3731 GtkTextLineSegment **segment,
3732 GtkTextLineSegment **any_segment,
3733 gint *seg_byte_offset,
3734 gint *line_byte_offset)
3736 GtkTextLineSegment *seg;
3737 GtkTextLineSegment *after_prev_indexable;
3738 GtkTextLineSegment *after_last_indexable;
3739 GtkTextLineSegment *last_indexable;
3743 g_return_val_if_fail (line != NULL, FALSE);
3744 g_return_val_if_fail (byte_offset >= 0, FALSE);
3747 *any_segment = NULL;
3750 offset = byte_offset;
3752 last_indexable = NULL;
3753 after_last_indexable = line->segments;
3754 after_prev_indexable = line->segments;
3755 seg = line->segments;
3757 /* The loop ends when we're inside a segment;
3758 last_indexable refers to the last segment
3759 we passed entirely. */
3760 while (seg && offset >= seg->byte_count)
3762 if (seg->char_count > 0)
3764 offset -= seg->byte_count;
3765 bytes_in_line += seg->byte_count;
3766 last_indexable = seg;
3767 after_prev_indexable = after_last_indexable;
3768 after_last_indexable = last_indexable->next;
3776 /* We went off the end of the line */
3778 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3785 if (after_last_indexable != NULL)
3786 *any_segment = after_last_indexable;
3788 *any_segment = *segment;
3791 /* Override any_segment if we're in the middle of a segment. */
3793 *any_segment = *segment;
3795 *seg_byte_offset = offset;
3797 g_assert (*segment != NULL);
3798 g_assert (*any_segment != NULL);
3799 g_assert (*seg_byte_offset < (*segment)->byte_count);
3801 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3806 /* FIXME sync with byte_locate (or figure out a clean
3807 way to merge the two functions) */
3809 _gtk_text_line_char_locate (GtkTextLine *line,
3811 GtkTextLineSegment **segment,
3812 GtkTextLineSegment **any_segment,
3813 gint *seg_char_offset,
3814 gint *line_char_offset)
3816 GtkTextLineSegment *seg;
3817 GtkTextLineSegment *after_prev_indexable;
3818 GtkTextLineSegment *after_last_indexable;
3819 GtkTextLineSegment *last_indexable;
3823 g_return_val_if_fail (line != NULL, FALSE);
3824 g_return_val_if_fail (char_offset >= 0, FALSE);
3827 *any_segment = NULL;
3830 offset = char_offset;
3832 last_indexable = NULL;
3833 after_last_indexable = line->segments;
3834 after_prev_indexable = line->segments;
3835 seg = line->segments;
3837 /* The loop ends when we're inside a segment;
3838 last_indexable refers to the last segment
3839 we passed entirely. */
3840 while (seg && offset >= seg->char_count)
3842 if (seg->char_count > 0)
3844 offset -= seg->char_count;
3845 chars_in_line += seg->char_count;
3846 last_indexable = seg;
3847 after_prev_indexable = after_last_indexable;
3848 after_last_indexable = last_indexable->next;
3856 /* end of the line */
3858 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3865 if (after_last_indexable != NULL)
3866 *any_segment = after_last_indexable;
3868 *any_segment = *segment;
3871 /* Override any_segment if we're in the middle of a segment. */
3873 *any_segment = *segment;
3875 *seg_char_offset = offset;
3877 g_assert (*segment != NULL);
3878 g_assert (*any_segment != NULL);
3879 g_assert (*seg_char_offset < (*segment)->char_count);
3881 *line_char_offset = chars_in_line + *seg_char_offset;
3887 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3889 gint *line_char_offset,
3890 gint *seg_char_offset)
3892 GtkTextLineSegment *seg;
3895 g_return_if_fail (line != NULL);
3896 g_return_if_fail (byte_offset >= 0);
3898 *line_char_offset = 0;
3900 offset = byte_offset;
3901 seg = line->segments;
3903 while (offset >= seg->byte_count)
3905 offset -= seg->byte_count;
3906 *line_char_offset += seg->char_count;
3908 g_assert (seg != NULL); /* means an invalid char offset */
3911 g_assert (seg->char_count > 0); /* indexable. */
3913 /* offset is now the number of bytes into the current segment we
3914 * want to go. Count chars into the current segment.
3917 if (seg->type == >k_text_char_type)
3919 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3921 g_assert (*seg_char_offset < seg->char_count);
3923 *line_char_offset += *seg_char_offset;
3927 g_assert (offset == 0);
3928 *seg_char_offset = 0;
3933 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3935 gint *line_byte_offset,
3936 gint *seg_byte_offset)
3938 GtkTextLineSegment *seg;
3941 g_return_if_fail (line != NULL);
3942 g_return_if_fail (char_offset >= 0);
3944 *line_byte_offset = 0;
3946 offset = char_offset;
3947 seg = line->segments;
3949 while (offset >= seg->char_count)
3951 offset -= seg->char_count;
3952 *line_byte_offset += seg->byte_count;
3954 g_assert (seg != NULL); /* means an invalid char offset */
3957 g_assert (seg->char_count > 0); /* indexable. */
3959 /* offset is now the number of chars into the current segment we
3960 want to go. Count bytes into the current segment. */
3962 if (seg->type == >k_text_char_type)
3964 *seg_byte_offset = 0;
3968 const char * start = seg->body.chars + *seg_byte_offset;
3970 bytes = g_utf8_next_char (start) - start;
3971 *seg_byte_offset += bytes;
3975 g_assert (*seg_byte_offset < seg->byte_count);
3977 *line_byte_offset += *seg_byte_offset;
3981 g_assert (offset == 0);
3982 *seg_byte_offset = 0;
3987 node_compare (GtkTextBTreeNode *lhs,
3988 GtkTextBTreeNode *rhs)
3990 GtkTextBTreeNode *iter;
3991 GtkTextBTreeNode *node;
3992 GtkTextBTreeNode *common_parent;
3993 GtkTextBTreeNode *parent_of_lower;
3994 GtkTextBTreeNode *parent_of_higher;
3995 gboolean lhs_is_lower;
3996 GtkTextBTreeNode *lower;
3997 GtkTextBTreeNode *higher;
3999 /* This function assumes that lhs and rhs are not underneath each
4006 if (lhs->level < rhs->level)
4008 lhs_is_lower = TRUE;
4014 lhs_is_lower = FALSE;
4019 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4020 * of the common parent we used to reach the common parent; the
4021 * ordering of these child nodes in the child list is the ordering
4025 /* Get on the same level (may be on same level already) */
4027 while (node->level < higher->level)
4028 node = node->parent;
4030 g_assert (node->level == higher->level);
4032 g_assert (node != higher); /* Happens if lower is underneath higher */
4034 /* Go up until we have two children with a common parent.
4036 parent_of_lower = node;
4037 parent_of_higher = higher;
4039 while (parent_of_lower->parent != parent_of_higher->parent)
4041 parent_of_lower = parent_of_lower->parent;
4042 parent_of_higher = parent_of_higher->parent;
4045 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4047 common_parent = parent_of_lower->parent;
4049 g_assert (common_parent != NULL);
4051 /* See which is first in the list of common_parent's children */
4052 iter = common_parent->children.node;
4053 while (iter != NULL)
4055 if (iter == parent_of_higher)
4057 /* higher is less than lower */
4060 return 1; /* lhs > rhs */
4064 else if (iter == parent_of_lower)
4066 /* lower is less than higher */
4069 return -1; /* lhs < rhs */
4077 g_assert_not_reached ();
4081 /* remember that tag == NULL means "any tag" */
4083 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4087 GtkTextBTreeNode *node;
4088 GtkTextTagInfo *info;
4089 gboolean below_tag_root;
4091 g_return_val_if_fail (line != NULL, NULL);
4093 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4094 _gtk_text_btree_check (tree);
4098 /* Right now we can only offer linear-search if the user wants
4099 * to know about any tag toggle at all.
4101 return _gtk_text_line_next_excluding_last (line);
4104 /* Our tag summaries only have node precision, not line
4105 * precision. This means that if any line under a node could contain a
4106 * tag, then any of the others could also contain a tag.
4108 * In the future we could have some mechanism to keep track of how
4109 * many toggles we've found under a node so far, since we have a
4110 * count of toggles under the node. But for now I'm going with KISS.
4113 /* return same-node line, if any. */
4117 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4121 if (info->tag_root == NULL)
4124 if (info->tag_root == line->parent)
4125 return NULL; /* we were at the last line under the tag root */
4127 /* We need to go up out of this node, and on to the next one with
4128 toggles for the target tag. If we're below the tag root, we need to
4129 find the next node below the tag root that has tag summaries. If
4130 we're not below the tag root, we need to see if the tag root is
4131 after us in the tree, and if so, return the first line underneath
4134 node = line->parent;
4135 below_tag_root = FALSE;
4136 while (node != NULL)
4138 if (node == info->tag_root)
4140 below_tag_root = TRUE;
4144 node = node->parent;
4149 node = line->parent;
4150 while (node != info->tag_root)
4152 if (node->next == NULL)
4153 node = node->parent;
4158 if (gtk_text_btree_node_has_tag (node, tag))
4168 ordering = node_compare (line->parent, info->tag_root);
4172 /* Tag root is ahead of us, so search there. */
4173 node = info->tag_root;
4178 /* Tag root is after us, so no more lines that
4179 * could contain the tag.
4184 g_assert_not_reached ();
4189 g_assert (node != NULL);
4191 /* We have to find the first sub-node of this node that contains
4195 while (node->level > 0)
4197 g_assert (node != NULL); /* If this fails, it likely means an
4198 incorrect tag summary led us on a
4199 wild goose chase down this branch of
4201 node = node->children.node;
4202 while (node != NULL)
4204 if (gtk_text_btree_node_has_tag (node, tag))
4210 g_assert (node != NULL);
4211 g_assert (node->level == 0);
4213 return node->children.line;
4217 prev_line_under_node (GtkTextBTreeNode *node,
4222 prev = node->children.line;
4228 while (prev->next != line)
4238 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4242 GtkTextBTreeNode *node;
4243 GtkTextBTreeNode *found_node = NULL;
4244 GtkTextTagInfo *info;
4245 gboolean below_tag_root;
4247 GtkTextBTreeNode *line_ancestor;
4248 GtkTextBTreeNode *line_ancestor_parent;
4250 /* See next_could_contain_tag () for more extensive comments
4251 * on what's going on here.
4254 g_return_val_if_fail (line != NULL, NULL);
4256 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4257 _gtk_text_btree_check (tree);
4261 /* Right now we can only offer linear-search if the user wants
4262 * to know about any tag toggle at all.
4264 return _gtk_text_line_previous (line);
4267 /* Return same-node line, if any. */
4268 prev = prev_line_under_node (line->parent, line);
4272 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4276 if (info->tag_root == NULL)
4279 if (info->tag_root == line->parent)
4280 return NULL; /* we were at the first line under the tag root */
4282 /* Are we below the tag root */
4283 node = line->parent;
4284 below_tag_root = FALSE;
4285 while (node != NULL)
4287 if (node == info->tag_root)
4289 below_tag_root = TRUE;
4293 node = node->parent;
4298 /* Look for a previous node under this tag root that has our
4302 /* this assertion holds because line->parent is not the
4303 * tag root, we are below the tag root, and the tag
4306 g_assert (line->parent->parent != NULL);
4308 line_ancestor = line->parent;
4309 line_ancestor_parent = line->parent->parent;
4311 node = line_ancestor_parent->children.node;
4312 while (node != line_ancestor &&
4313 line_ancestor != info->tag_root)
4315 GSList *child_nodes = NULL;
4318 /* Create reverse-order list of nodes before
4321 while (node != line_ancestor
4324 child_nodes = g_slist_prepend (child_nodes, node);
4329 /* Try to find a node with our tag on it in the list */
4333 GtkTextBTreeNode *this_node = tmp->data;
4335 g_assert (this_node != line_ancestor);
4337 if (gtk_text_btree_node_has_tag (this_node, tag))
4339 found_node = this_node;
4340 g_slist_free (child_nodes);
4344 tmp = g_slist_next (tmp);
4347 g_slist_free (child_nodes);
4349 /* Didn't find anything on this level; go up one level. */
4350 line_ancestor = line_ancestor_parent;
4351 line_ancestor_parent = line_ancestor->parent;
4353 node = line_ancestor_parent->children.node;
4363 ordering = node_compare (line->parent, info->tag_root);
4367 /* Tag root is ahead of us, so no more lines
4374 /* Tag root is after us, so grab last tagged
4375 * line underneath the tag root.
4377 found_node = info->tag_root;
4381 g_assert_not_reached ();
4386 g_assert (found_node != NULL);
4388 /* We have to find the last sub-node of this node that contains
4393 while (node->level > 0)
4395 GSList *child_nodes = NULL;
4397 g_assert (node != NULL); /* If this fails, it likely means an
4398 incorrect tag summary led us on a
4399 wild goose chase down this branch of
4402 node = node->children.node;
4403 while (node != NULL)
4405 child_nodes = g_slist_prepend (child_nodes, node);
4409 node = NULL; /* detect failure to find a child node. */
4412 while (iter != NULL)
4414 if (gtk_text_btree_node_has_tag (iter->data, tag))
4416 /* recurse into this node. */
4421 iter = g_slist_next (iter);
4424 g_slist_free (child_nodes);
4426 g_assert (node != NULL);
4429 g_assert (node != NULL);
4430 g_assert (node->level == 0);
4432 /* this assertion is correct, but slow. */
4433 /* g_assert (node_compare (node, line->parent) < 0); */
4435 /* Return last line in this node. */
4437 prev = node->children.line;
4445 * Non-public function implementations
4449 summary_list_destroy (Summary *summary)
4452 while (summary != NULL)
4454 next = summary->next;
4455 summary_destroy (summary);
4461 get_last_line (GtkTextBTree *tree)
4463 if (tree->last_line_stamp != tree->chars_changed_stamp)
4469 n_lines = _gtk_text_btree_line_count (tree);
4471 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4473 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4475 tree->last_line_stamp = tree->chars_changed_stamp;
4476 tree->last_line = line;
4479 return tree->last_line;
4487 gtk_text_line_new (void)
4491 line = g_new0(GtkTextLine, 1);
4497 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4499 GtkTextLineData *ld;
4500 GtkTextLineData *next;
4502 g_return_if_fail (line != NULL);
4509 view = gtk_text_btree_get_view (tree, ld->view_id);
4511 g_assert (view != NULL);
4514 gtk_text_layout_free_line_data (view->layout, line, ld);
4523 gtk_text_line_set_parent (GtkTextLine *line,
4524 GtkTextBTreeNode *node)
4526 if (line->parent == node)
4528 line->parent = node;
4529 gtk_text_btree_node_invalidate_upward (node, NULL);
4533 cleanup_line (GtkTextLine *line)
4535 GtkTextLineSegment *seg, **prev_p;
4539 * Make a pass over all of the segments in the line, giving each
4540 * a chance to clean itself up. This could potentially change
4541 * the structure of the line, e.g. by merging two segments
4542 * together or having two segments cancel themselves; if so,
4543 * then repeat the whole process again, since the first structure
4544 * change might make other structure changes possible. Repeat
4545 * until eventually there are no changes.
4552 for (prev_p = &line->segments, seg = *prev_p;
4554 prev_p = &(*prev_p)->next, seg = *prev_p)
4556 if (seg->type->cleanupFunc != NULL)
4558 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4571 node_data_new (gpointer view_id)
4575 nd = g_new (NodeData, 1);
4577 nd->view_id = view_id;
4587 node_data_destroy (NodeData *nd)
4593 node_data_list_destroy (NodeData *nd)
4599 while (iter != NULL)
4602 node_data_destroy (iter);
4608 node_data_find (NodeData *nd, gpointer view_id)
4612 if (nd->view_id == view_id)
4620 summary_destroy (Summary *summary)
4622 /* Fill with error-triggering garbage */
4623 summary->info = (void*)0x1;
4624 summary->toggle_count = 567;
4625 summary->next = (void*)0x1;
4629 static GtkTextBTreeNode*
4630 gtk_text_btree_node_new (void)
4632 GtkTextBTreeNode *node;
4634 node = g_new (GtkTextBTreeNode, 1);
4636 node->node_data = NULL;
4642 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4643 GtkTextTagInfo *info,
4648 summary = node->summary;
4649 while (summary != NULL)
4651 if (summary->info == info)
4653 summary->toggle_count += adjust;
4657 summary = summary->next;
4660 if (summary == NULL)
4662 /* didn't find a summary for our tag. */
4663 g_return_if_fail (adjust > 0);
4664 summary = g_new (Summary, 1);
4665 summary->info = info;
4666 summary->toggle_count = adjust;
4667 summary->next = node->summary;
4668 node->summary = summary;
4672 /* Note that the tag root and above do not have summaries
4673 for the tag; only nodes below the tag root have
4676 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4680 summary = node->summary;
4681 while (summary != NULL)
4684 summary->info->tag == tag)
4687 summary = summary->next;
4693 /* Add node and all children to the damage region. */
4695 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4699 nd = node->node_data;
4706 if (node->level == 0)
4710 line = node->children.line;
4711 while (line != NULL)
4713 GtkTextLineData *ld;
4727 GtkTextBTreeNode *child;
4729 child = node->children.node;
4731 while (child != NULL)
4733 gtk_text_btree_node_invalidate_downward (child);
4735 child = child->next;
4741 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4743 GtkTextBTreeNode *iter;
4746 while (iter != NULL)
4752 nd = node_data_find (iter->node_data, view_id);
4754 if (nd == NULL || !nd->valid)
4755 break; /* Once a node is invalid, we know its parents are as well. */
4761 gboolean should_continue = FALSE;
4763 nd = iter->node_data;
4768 should_continue = TRUE;
4775 if (!should_continue)
4776 break; /* This node was totally invalidated, so are its
4780 iter = iter->parent;
4786 * _gtk_text_btree_is_valid:
4787 * @tree: a #GtkTextBTree
4788 * @view_id: ID for the view
4790 * Check to see if the entire #GtkTextBTree is valid or not for
4793 * Return value: %TRUE if the entire #GtkTextBTree is valid
4796 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4800 g_return_val_if_fail (tree != NULL, FALSE);
4802 nd = node_data_find (tree->root_node->node_data, view_id);
4803 return (nd && nd->valid);
4806 typedef struct _ValidateState ValidateState;
4808 struct _ValidateState
4810 gint remaining_pixels;
4811 gboolean in_validation;
4818 gtk_text_btree_node_validate (BTreeView *view,
4819 GtkTextBTreeNode *node,
4821 ValidateState *state)
4823 gint node_valid = TRUE;
4824 gint node_width = 0;
4825 gint node_height = 0;
4827 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4828 g_return_if_fail (!nd->valid);
4830 if (node->level == 0)
4832 GtkTextLine *line = node->children.line;
4833 GtkTextLineData *ld;
4835 /* Iterate over leading valid lines */
4836 while (line != NULL)
4838 ld = _gtk_text_line_get_data (line, view_id);
4840 if (!ld || !ld->valid)
4842 else if (state->in_validation)
4844 state->in_validation = FALSE;
4849 state->y += ld->height;
4850 node_width = MAX (ld->width, node_width);
4851 node_height += ld->height;
4857 state->in_validation = TRUE;
4859 /* Iterate over invalid lines */
4860 while (line != NULL)
4862 ld = _gtk_text_line_get_data (line, view_id);
4864 if (ld && ld->valid)
4869 state->old_height += ld->height;
4870 ld = gtk_text_layout_wrap (view->layout, line, ld);
4871 state->new_height += ld->height;
4873 node_width = MAX (ld->width, node_width);
4874 node_height += ld->height;
4876 state->remaining_pixels -= ld->height;
4877 if (state->remaining_pixels <= 0)
4887 /* Iterate over the remaining lines */
4888 while (line != NULL)
4890 ld = _gtk_text_line_get_data (line, view_id);
4891 state->in_validation = FALSE;
4893 if (!ld || !ld->valid)
4898 node_width = MAX (ld->width, node_width);
4899 node_height += ld->height;
4907 GtkTextBTreeNode *child;
4910 child = node->children.node;
4912 /* Iterate over leading valid nodes */
4915 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4917 if (!child_nd->valid)
4919 else if (state->in_validation)
4921 state->in_validation = FALSE;
4926 state->y += child_nd->height;
4927 node_width = MAX (node_width, child_nd->width);
4928 node_height += child_nd->height;
4931 child = child->next;
4934 /* Iterate over invalid nodes */
4937 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4939 if (child_nd->valid)
4943 gtk_text_btree_node_validate (view, child, view_id, state);
4945 if (!child_nd->valid)
4947 node_width = MAX (node_width, child_nd->width);
4948 node_height += child_nd->height;
4950 if (!state->in_validation || state->remaining_pixels <= 0)
4952 child = child->next;
4957 child = child->next;
4960 /* Iterate over the remaining lines */
4963 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4964 state->in_validation = FALSE;
4966 if (!child_nd->valid)
4969 node_width = MAX (child_nd->width, node_width);
4970 node_height += child_nd->height;
4972 child = child->next;
4976 nd->width = node_width;
4977 nd->height = node_height;
4978 nd->valid = node_valid;
4982 * _gtk_text_btree_validate:
4983 * @tree: a #GtkTextBTree
4985 * @max_pixels: the maximum number of pixels to validate. (No more
4986 * than one paragraph beyond this limit will be validated)
4987 * @y: location to store starting y coordinate of validated region
4988 * @old_height: location to store old height of validated region
4989 * @new_height: location to store new height of validated region
4991 * Validate a single contiguous invalid region of a #GtkTextBTree for
4994 * Return value: %TRUE if a region has been validated, %FALSE if the
4995 * entire tree was already valid.
4998 _gtk_text_btree_validate (GtkTextBTree *tree,
5007 g_return_val_if_fail (tree != NULL, FALSE);
5009 view = gtk_text_btree_get_view (tree, view_id);
5010 g_return_val_if_fail (view != NULL, FALSE);
5012 if (!_gtk_text_btree_is_valid (tree, view_id))
5014 ValidateState state;
5016 state.remaining_pixels = max_pixels;
5017 state.in_validation = FALSE;
5019 state.old_height = 0;
5020 state.new_height = 0;
5022 gtk_text_btree_node_validate (view,
5029 *old_height = state.old_height;
5031 *new_height = state.new_height;
5033 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5034 _gtk_text_btree_check (tree);
5043 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5047 gboolean *valid_out)
5051 gboolean valid = TRUE;
5053 if (node->level == 0)
5055 GtkTextLine *line = node->children.line;
5057 while (line != NULL)
5059 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5061 if (!ld || !ld->valid)
5066 width = MAX (ld->width, width);
5067 height += ld->height;
5075 GtkTextBTreeNode *child = node->children.node;
5079 NodeData *child_nd = node_data_find (child->node_data, view_id);
5081 if (!child_nd || !child_nd->valid)
5086 width = MAX (child_nd->width, width);
5087 height += child_nd->height;
5090 child = child->next;
5095 *height_out = height;
5100 /* Recompute the validity and size of the view data for a given
5101 * view at this node from the immediate children of the node
5104 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5107 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5112 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5113 &width, &height, &valid);
5115 nd->height = height;
5122 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5127 gtk_text_btree_node_check_valid (node, view_id);
5128 node = node->parent;
5133 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5136 if (node->level == 0)
5138 return gtk_text_btree_node_check_valid (node, view_id);
5142 GtkTextBTreeNode *child = node->children.node;
5144 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5152 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5154 if (!child_nd->valid)
5156 nd->width = MAX (child_nd->width, nd->width);
5157 nd->height += child_nd->height;
5159 child = child->next;
5168 * _gtk_text_btree_validate_line:
5169 * @tree: a #GtkTextBTree
5170 * @line: line to validate
5171 * @view_id: view ID for the view to validate
5173 * Revalidate a single line of the btree for the given view, propagate
5174 * results up through the entire tree.
5177 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5181 GtkTextLineData *ld;
5184 g_return_if_fail (tree != NULL);
5185 g_return_if_fail (line != NULL);
5187 view = gtk_text_btree_get_view (tree, view_id);
5188 g_return_if_fail (view != NULL);
5190 ld = _gtk_text_line_get_data (line, view_id);
5191 if (!ld || !ld->valid)
5193 ld = gtk_text_layout_wrap (view->layout, line, ld);
5195 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5200 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5202 if (node->level == 0)
5206 line = node->children.line;
5207 while (line != NULL)
5209 GtkTextLineData *ld;
5211 ld = _gtk_text_line_remove_data (line, view_id);
5214 gtk_text_layout_free_line_data (view->layout, line, ld);
5221 GtkTextBTreeNode *child;
5223 child = node->children.node;
5225 while (child != NULL)
5228 gtk_text_btree_node_remove_view (view, child, view_id);
5230 child = child->next;
5234 gtk_text_btree_node_remove_data (node, view_id);
5238 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5240 if (node->level == 0)
5243 GtkTextLineSegment *seg;
5245 while (node->children.line != NULL)
5247 line = node->children.line;
5248 node->children.line = line->next;
5249 while (line->segments != NULL)
5251 seg = line->segments;
5252 line->segments = seg->next;
5254 (*seg->type->deleteFunc) (seg, line, TRUE);
5256 gtk_text_line_destroy (tree, line);
5261 GtkTextBTreeNode *childPtr;
5263 while (node->children.node != NULL)
5265 childPtr = node->children.node;
5266 node->children.node = childPtr->next;
5267 gtk_text_btree_node_destroy (tree, childPtr);
5271 gtk_text_btree_node_free_empty (tree, node);
5275 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5276 GtkTextBTreeNode *node)
5278 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5279 (node->level == 0 && node->children.line == NULL));
5281 summary_list_destroy (node->summary);
5282 node_data_list_destroy (node->node_data);
5287 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5291 nd = node->node_data;
5294 if (nd->view_id == view_id)
5302 nd = node_data_new (view_id);
5304 if (node->node_data)
5305 nd->next = node->node_data;
5307 node->node_data = nd;
5314 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5320 nd = node->node_data;
5323 if (nd->view_id == view_id)
5334 prev->next = nd->next;
5336 if (node->node_data == nd)
5337 node->node_data = nd->next;
5341 node_data_destroy (nd);
5345 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5346 gint *width, gint *height)
5350 g_return_if_fail (width != NULL);
5351 g_return_if_fail (height != NULL);
5353 nd = gtk_text_btree_node_ensure_data (node, view_id);
5358 *height = nd->height;
5361 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5362 * here isn't quite right, since for a lot of operations we want to
5363 * know which children of the common parent correspond to the two nodes
5364 * (e.g., when computing the order of two iters)
5366 static GtkTextBTreeNode *
5367 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5368 GtkTextBTreeNode *node2)
5370 while (node1->level < node2->level)
5371 node1 = node1->parent;
5372 while (node2->level < node1->level)
5373 node2 = node2->parent;
5374 while (node1 != node2)
5376 node1 = node1->parent;
5377 node2 = node2->parent;
5388 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5393 while (view != NULL)
5395 if (view->view_id == view_id)
5404 get_tree_bounds (GtkTextBTree *tree,
5408 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5409 _gtk_text_btree_get_end_iter (tree, end);
5413 tag_changed_cb (GtkTextTagTable *table,
5415 gboolean size_changed,
5420 /* We need to queue a relayout on all regions that are tagged with
5427 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5429 /* Must be a last toggle if there was a first one. */
5430 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5431 _gtk_text_btree_invalidate_region (tree,
5438 /* We only need to queue a redraw, not a relayout */
5443 while (view != NULL)
5447 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5448 gtk_text_layout_changed (view->layout, 0, height, height);
5456 tag_removed_cb (GtkTextTagTable *table,
5460 /* Remove the tag from the tree */
5465 get_tree_bounds (tree, &start, &end);
5467 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5468 gtk_text_btree_remove_tag_info (tree, tag);
5472 /* Rebalance the out-of-whack node "node" */
5474 gtk_text_btree_rebalance (GtkTextBTree *tree,
5475 GtkTextBTreeNode *node)
5478 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5479 * up through the tree one GtkTextBTreeNode at a time until the root
5480 * GtkTextBTreeNode has been processed.
5483 while (node != NULL)
5485 GtkTextBTreeNode *new_node, *child;
5490 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5491 * then split off all but the first MIN_CHILDREN into a separate
5492 * GtkTextBTreeNode following the original one. Then repeat until the
5493 * GtkTextBTreeNode has a decent size.
5496 if (node->num_children > MAX_CHILDREN)
5501 * If the GtkTextBTreeNode being split is the root
5502 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5506 if (node->parent == NULL)
5508 new_node = gtk_text_btree_node_new ();
5509 new_node->parent = NULL;
5510 new_node->next = NULL;
5511 new_node->summary = NULL;
5512 new_node->level = node->level + 1;
5513 new_node->children.node = node;
5514 recompute_node_counts (tree, new_node);
5515 tree->root_node = new_node;
5517 new_node = gtk_text_btree_node_new ();
5518 new_node->parent = node->parent;
5519 new_node->next = node->next;
5520 node->next = new_node;
5521 new_node->summary = NULL;
5522 new_node->level = node->level;
5523 new_node->num_children = node->num_children - MIN_CHILDREN;
5524 if (node->level == 0)
5526 for (i = MIN_CHILDREN-1,
5527 line = node->children.line;
5528 i > 0; i--, line = line->next)
5530 /* Empty loop body. */
5532 new_node->children.line = line->next;
5537 for (i = MIN_CHILDREN-1,
5538 child = node->children.node;
5539 i > 0; i--, child = child->next)
5541 /* Empty loop body. */
5543 new_node->children.node = child->next;
5546 recompute_node_counts (tree, node);
5547 node->parent->num_children++;
5549 if (node->num_children <= MAX_CHILDREN)
5551 recompute_node_counts (tree, node);
5557 while (node->num_children < MIN_CHILDREN)
5559 GtkTextBTreeNode *other;
5560 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5561 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5562 int total_children, first_children, i;
5565 * Too few children for this GtkTextBTreeNode. If this is the root then,
5566 * it's OK for it to have less than MIN_CHILDREN children
5567 * as long as it's got at least two. If it has only one
5568 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5569 * the tree and use its child as the new root.
5572 if (node->parent == NULL)
5574 if ((node->num_children == 1) && (node->level > 0))
5576 tree->root_node = node->children.node;
5577 tree->root_node->parent = NULL;
5579 node->children.node = NULL;
5580 gtk_text_btree_node_free_empty (tree, node);
5586 * Not the root. Make sure that there are siblings to
5590 if (node->parent->num_children < 2)
5592 gtk_text_btree_rebalance (tree, node->parent);
5597 * Find a sibling neighbor to borrow from, and arrange for
5598 * node to be the earlier of the pair.
5601 if (node->next == NULL)
5603 for (other = node->parent->children.node;
5604 other->next != node;
5605 other = other->next)
5607 /* Empty loop body. */
5614 * We're going to either merge the two siblings together
5615 * into one GtkTextBTreeNode or redivide the children among them to
5616 * balance their loads. As preparation, join their two
5617 * child lists into a single list and remember the half-way
5618 * point in the list.
5621 total_children = node->num_children + other->num_children;
5622 first_children = total_children/2;
5623 if (node->children.node == NULL)
5625 node->children = other->children;
5626 other->children.node = NULL;
5627 other->children.line = NULL;
5629 if (node->level == 0)
5633 for (line = node->children.line, i = 1;
5635 line = line->next, i++)
5637 if (i == first_children)
5642 line->next = other->children.line;
5643 while (i <= first_children)
5652 GtkTextBTreeNode *child;
5654 for (child = node->children.node, i = 1;
5655 child->next != NULL;
5656 child = child->next, i++)
5658 if (i <= first_children)
5660 if (i == first_children)
5662 halfwaynode = child;
5666 child->next = other->children.node;
5667 while (i <= first_children)
5669 halfwaynode = child;
5670 child = child->next;
5676 * If the two siblings can simply be merged together, do it.
5679 if (total_children <= MAX_CHILDREN)
5681 recompute_node_counts (tree, node);
5682 node->next = other->next;
5683 node->parent->num_children--;
5685 other->children.node = NULL;
5686 other->children.line = NULL;
5687 gtk_text_btree_node_free_empty (tree, other);
5692 * The siblings can't be merged, so just divide their
5693 * children evenly between them.
5696 if (node->level == 0)
5698 other->children.line = halfwayline->next;
5699 halfwayline->next = NULL;
5703 other->children.node = halfwaynode->next;
5704 halfwaynode->next = NULL;
5707 recompute_node_counts (tree, node);
5708 recompute_node_counts (tree, other);
5711 node = node->parent;
5716 post_insert_fixup (GtkTextBTree *tree,
5718 gint line_count_delta,
5719 gint char_count_delta)
5722 GtkTextBTreeNode *node;
5725 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5726 * point, then rebalance the tree if necessary.
5729 for (node = line->parent ; node != NULL;
5730 node = node->parent)
5732 node->num_lines += line_count_delta;
5733 node->num_chars += char_count_delta;
5735 node = line->parent;
5736 node->num_children += line_count_delta;
5738 if (node->num_children > MAX_CHILDREN)
5740 gtk_text_btree_rebalance (tree, node);
5743 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5744 _gtk_text_btree_check (tree);
5747 static GtkTextTagInfo*
5748 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5751 GtkTextTagInfo *info;
5755 list = tree->tag_infos;
5756 while (list != NULL)
5759 if (info->tag == tag)
5762 list = g_slist_next (list);
5768 static GtkTextTagInfo*
5769 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5772 GtkTextTagInfo *info;
5774 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5778 /* didn't find it, create. */
5780 info = g_new (GtkTextTagInfo, 1);
5783 g_object_ref (G_OBJECT (tag));
5784 info->tag_root = NULL;
5785 info->toggle_count = 0;
5787 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5794 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5797 GtkTextTagInfo *info;
5802 list = tree->tag_infos;
5803 while (list != NULL)
5806 if (info->tag == tag)
5810 prev->next = list->next;
5814 tree->tag_infos = list->next;
5817 g_slist_free (list);
5819 g_object_unref (G_OBJECT (info->tag));
5825 list = g_slist_next (list);
5828 g_assert_not_reached ();
5833 recompute_level_zero_counts (GtkTextBTreeNode *node)
5836 GtkTextLineSegment *seg;
5838 g_assert (node->level == 0);
5840 line = node->children.line;
5841 while (line != NULL)
5843 node->num_children++;
5846 if (line->parent != node)
5847 gtk_text_line_set_parent (line, node);
5849 seg = line->segments;
5853 node->num_chars += seg->char_count;
5855 if (((seg->type != >k_text_toggle_on_type)
5856 && (seg->type != >k_text_toggle_off_type))
5857 || !(seg->body.toggle.inNodeCounts))
5863 GtkTextTagInfo *info;
5865 info = seg->body.toggle.info;
5867 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5878 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5881 GtkTextBTreeNode *child;
5883 g_assert (node->level > 0);
5885 child = node->children.node;
5886 while (child != NULL)
5888 node->num_children += 1;
5889 node->num_lines += child->num_lines;
5890 node->num_chars += child->num_chars;
5892 if (child->parent != node)
5894 child->parent = node;
5895 gtk_text_btree_node_invalidate_upward (node, NULL);
5898 summary = child->summary;
5899 while (summary != NULL)
5901 gtk_text_btree_node_adjust_toggle_count (node,
5903 summary->toggle_count);
5905 summary = summary->next;
5908 child = child->next;
5913 *----------------------------------------------------------------------
5915 * recompute_node_counts --
5917 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5918 * (tags, child information, etc.) by scanning the information in
5919 * its descendants. This procedure is called during rebalancing
5920 * when a GtkTextBTreeNode's child structure has changed.
5926 * The tag counts for node are modified to reflect its current
5927 * child structure, as are its num_children, num_lines, num_chars fields.
5928 * Also, all of the childrens' parent fields are made to point
5931 *----------------------------------------------------------------------
5935 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5938 Summary *summary, *summary2;
5941 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5942 * the existing Summary records (most of them will probably be reused).
5945 summary = node->summary;
5946 while (summary != NULL)
5948 summary->toggle_count = 0;
5949 summary = summary->next;
5952 node->num_children = 0;
5953 node->num_lines = 0;
5954 node->num_chars = 0;
5957 * Scan through the children, adding the childrens' tag counts into
5958 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5962 if (node->level == 0)
5963 recompute_level_zero_counts (node);
5965 recompute_level_nonzero_counts (node);
5970 gtk_text_btree_node_check_valid (node, view->view_id);
5975 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5976 * records that still have a zero count, or that have all the toggles.
5977 * The GtkTextBTreeNode with the children that account for all the tags toggles
5978 * have no summary information, and they become the tag_root for the tag.
5982 for (summary = node->summary; summary != NULL; )
5984 if (summary->toggle_count > 0 &&
5985 summary->toggle_count < summary->info->toggle_count)
5987 if (node->level == summary->info->tag_root->level)
5990 * The tag's root GtkTextBTreeNode split and some toggles left.
5991 * The tag root must move up a level.
5993 summary->info->tag_root = node->parent;
5996 summary = summary->next;
5999 if (summary->toggle_count == summary->info->toggle_count)
6002 * A GtkTextBTreeNode merge has collected all the toggles under
6003 * one GtkTextBTreeNode. Push the root down to this level.
6005 summary->info->tag_root = node;
6007 if (summary2 != NULL)
6009 summary2->next = summary->next;
6010 summary_destroy (summary);
6011 summary = summary2->next;
6015 node->summary = summary->next;
6016 summary_destroy (summary);
6017 summary = node->summary;
6023 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6024 GtkTextTagInfo *info,
6025 gint delta) /* may be negative */
6027 Summary *summary, *prevPtr;
6028 GtkTextBTreeNode *node2Ptr;
6029 int rootLevel; /* Level of original tag root */
6031 info->toggle_count += delta;
6033 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6035 info->tag_root = node;
6040 * Note the level of the existing root for the tag so we can detect
6041 * if it needs to be moved because of the toggle count change.
6044 rootLevel = info->tag_root->level;
6047 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6048 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6052 for ( ; node != info->tag_root; node = node->parent)
6055 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6056 * perhaps all we have to do is adjust its count.
6059 for (prevPtr = NULL, summary = node->summary;
6061 prevPtr = summary, summary = summary->next)
6063 if (summary->info == info)
6068 if (summary != NULL)
6070 summary->toggle_count += delta;
6071 if (summary->toggle_count > 0 &&
6072 summary->toggle_count < info->toggle_count)
6076 if (summary->toggle_count != 0)
6079 * Should never find a GtkTextBTreeNode with max toggle count at this
6080 * point (there shouldn't have been a summary entry in the
6084 g_error ("%s: bad toggle count (%d) max (%d)",
6085 G_STRLOC, summary->toggle_count, info->toggle_count);
6089 * Zero toggle count; must remove this tag from the list.
6092 if (prevPtr == NULL)
6094 node->summary = summary->next;
6098 prevPtr->next = summary->next;
6100 summary_destroy (summary);
6105 * This tag isn't currently in the summary information list.
6108 if (rootLevel == node->level)
6112 * The old tag root is at the same level in the tree as this
6113 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6114 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6115 * as well as the old root (if not, we'll move it up again
6116 * the next time through the loop). To push it up one level
6117 * we copy the original toggle count into the summary
6118 * information at the old root and change the root to its
6119 * parent GtkTextBTreeNode.
6122 GtkTextBTreeNode *rootnode = info->tag_root;
6123 summary = (Summary *) g_malloc (sizeof (Summary));
6124 summary->info = info;
6125 summary->toggle_count = info->toggle_count - delta;
6126 summary->next = rootnode->summary;
6127 rootnode->summary = summary;
6128 rootnode = rootnode->parent;
6129 rootLevel = rootnode->level;
6130 info->tag_root = rootnode;
6132 summary = (Summary *) g_malloc (sizeof (Summary));
6133 summary->info = info;
6134 summary->toggle_count = delta;
6135 summary->next = node->summary;
6136 node->summary = summary;
6141 * If we've decremented the toggle count, then it may be necessary
6142 * to push the tag root down one or more levels.
6149 if (info->toggle_count == 0)
6151 info->tag_root = (GtkTextBTreeNode *) NULL;
6154 node = info->tag_root;
6155 while (node->level > 0)
6158 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6159 * toggles. If so, push the root down one level.
6162 for (node2Ptr = node->children.node;
6163 node2Ptr != (GtkTextBTreeNode *)NULL ;
6164 node2Ptr = node2Ptr->next)
6166 for (prevPtr = NULL, summary = node2Ptr->summary;
6168 prevPtr = summary, summary = summary->next)
6170 if (summary->info == info)
6175 if (summary == NULL)
6179 if (summary->toggle_count != info->toggle_count)
6182 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6189 * This GtkTextBTreeNode has all the toggles, so push down the root.
6192 if (prevPtr == NULL)
6194 node2Ptr->summary = summary->next;
6198 prevPtr->next = summary->next;
6200 summary_destroy (summary);
6201 info->tag_root = node2Ptr;
6204 node = info->tag_root;
6209 *----------------------------------------------------------------------
6213 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6214 * increments the count for a particular tag, adding a new
6215 * entry for that tag if there wasn't one previously.
6221 * The information at *tagInfoPtr may be modified, and the arrays
6222 * may be reallocated to make them larger.
6224 *----------------------------------------------------------------------
6228 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6233 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6234 count > 0; tag_p++, count--)
6238 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6244 * There isn't currently an entry for this tag, so we have to
6245 * make a new one. If the arrays are full, then enlarge the
6249 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6251 GtkTextTag **newTags;
6252 int *newCounts, newSize;
6254 newSize = 2*tagInfoPtr->arraySize;
6255 newTags = (GtkTextTag **) g_malloc ((unsigned)
6256 (newSize*sizeof (GtkTextTag *)));
6257 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6258 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6259 g_free ((char *) tagInfoPtr->tags);
6260 tagInfoPtr->tags = newTags;
6261 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6262 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6263 tagInfoPtr->arraySize *sizeof (int));
6264 g_free ((char *) tagInfoPtr->counts);
6265 tagInfoPtr->counts = newCounts;
6266 tagInfoPtr->arraySize = newSize;
6269 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6270 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6271 tagInfoPtr->numTags++;
6275 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6276 const GtkTextIter *iter)
6278 GtkTextLineSegment *prev;
6282 line = _gtk_text_iter_get_text_line (iter);
6283 tree = _gtk_text_iter_get_btree (iter);
6285 prev = gtk_text_line_segment_split (iter);
6288 seg->next = line->segments;
6289 line->segments = seg;
6293 seg->next = prev->next;
6296 cleanup_line (line);
6297 segments_changed (tree);
6299 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6300 _gtk_text_btree_check (tree);
6304 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6305 GtkTextLineSegment *seg,
6308 GtkTextLineSegment *prev;
6310 if (line->segments == seg)
6312 line->segments = seg->next;
6316 for (prev = line->segments; prev->next != seg;
6319 /* Empty loop body. */
6321 prev->next = seg->next;
6323 cleanup_line (line);
6324 segments_changed (tree);
6328 * This is here because it requires BTree internals, it logically
6329 * belongs in gtktextsegment.c
6334 *--------------------------------------------------------------
6336 * _gtk_toggle_segment_check_func --
6338 * This procedure is invoked to perform consistency checks
6339 * on toggle segments.
6345 * If a consistency problem is found the procedure g_errors.
6347 *--------------------------------------------------------------
6351 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6357 if (segPtr->byte_count != 0)
6359 g_error ("toggle_segment_check_func: segment had non-zero size");
6361 if (!segPtr->body.toggle.inNodeCounts)
6363 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6365 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6366 for (summary = line->parent->summary; ;
6367 summary = summary->next)
6369 if (summary == NULL)
6373 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6380 if (summary->info == segPtr->body.toggle.info)
6384 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6396 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6397 GtkTextBTreeNode *node,
6407 while (view != NULL)
6409 if (view->view_id == nd->view_id)
6416 g_error ("Node has data for a view %p no longer attached to the tree",
6419 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6420 &width, &height, &valid);
6421 if (nd->width != width ||
6422 nd->height != height ||
6423 !nd->valid != !valid)
6425 g_error ("Node aggregates for view %p are invalid:\n"
6426 "Are (%d,%d,%s), should be (%d,%d,%s)",
6428 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6429 width, height, valid ? "TRUE" : "FALSE");
6434 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6435 GtkTextBTreeNode *node)
6437 GtkTextBTreeNode *childnode;
6438 Summary *summary, *summary2;
6440 GtkTextLineSegment *segPtr;
6441 int num_children, num_lines, num_chars, toggle_count, min_children;
6442 GtkTextLineData *ld;
6445 if (node->parent != NULL)
6447 min_children = MIN_CHILDREN;
6449 else if (node->level > 0)
6456 if ((node->num_children < min_children)
6457 || (node->num_children > MAX_CHILDREN))
6459 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6460 node->num_children);
6463 nd = node->node_data;
6466 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6473 if (node->level == 0)
6475 for (line = node->children.line; line != NULL;
6478 if (line->parent != node)
6480 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6482 if (line->segments == NULL)
6484 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6490 /* Just ensuring we don't segv while doing this loop */
6495 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6497 if (segPtr->type->checkFunc != NULL)
6499 (*segPtr->type->checkFunc)(segPtr, line);
6501 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6502 && (segPtr->next != NULL)
6503 && (segPtr->next->byte_count == 0)
6504 && (segPtr->next->type->leftGravity))
6506 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6508 if ((segPtr->next == NULL)
6509 && (segPtr->type != >k_text_char_type))
6511 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6514 num_chars += segPtr->char_count;
6523 for (childnode = node->children.node; childnode != NULL;
6524 childnode = childnode->next)
6526 if (childnode->parent != node)
6528 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6530 if (childnode->level != (node->level-1))
6532 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6533 node->level, childnode->level);
6535 gtk_text_btree_node_check_consistency (tree, childnode);
6536 for (summary = childnode->summary; summary != NULL;
6537 summary = summary->next)
6539 for (summary2 = node->summary; ;
6540 summary2 = summary2->next)
6542 if (summary2 == NULL)
6544 if (summary->info->tag_root == node)
6548 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6549 summary->info->tag->name,
6550 "present in parent summaries");
6552 if (summary->info == summary2->info)
6559 num_lines += childnode->num_lines;
6560 num_chars += childnode->num_chars;
6563 if (num_children != node->num_children)
6565 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6566 num_children, node->num_children);
6568 if (num_lines != node->num_lines)
6570 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6571 num_lines, node->num_lines);
6573 if (num_chars != node->num_chars)
6575 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6576 num_chars, node->num_chars);
6579 for (summary = node->summary; summary != NULL;
6580 summary = summary->next)
6582 if (summary->info->toggle_count == summary->toggle_count)
6584 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6585 summary->info->tag->name);
6588 if (node->level == 0)
6590 for (line = node->children.line; line != NULL;
6593 for (segPtr = line->segments; segPtr != NULL;
6594 segPtr = segPtr->next)
6596 if ((segPtr->type != >k_text_toggle_on_type)
6597 && (segPtr->type != >k_text_toggle_off_type))
6601 if (segPtr->body.toggle.info == summary->info)
6603 if (!segPtr->body.toggle.inNodeCounts)
6604 g_error ("Toggle segment not in the node counts");
6613 for (childnode = node->children.node;
6615 childnode = childnode->next)
6617 for (summary2 = childnode->summary;
6619 summary2 = summary2->next)
6621 if (summary2->info == summary->info)
6623 toggle_count += summary2->toggle_count;
6628 if (toggle_count != summary->toggle_count)
6630 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6631 toggle_count, summary->toggle_count);
6633 for (summary2 = summary->next; summary2 != NULL;
6634 summary2 = summary2->next)
6636 if (summary2->info == summary->info)
6638 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6639 summary->info->tag->name);
6646 listify_foreach (GtkTextTag *tag, gpointer user_data)
6648 GSList** listp = user_data;
6650 *listp = g_slist_prepend (*listp, tag);
6654 list_of_tags (GtkTextTagTable *table)
6656 GSList *list = NULL;
6658 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6664 _gtk_text_btree_check (GtkTextBTree *tree)
6667 GtkTextBTreeNode *node;
6669 GtkTextLineSegment *seg;
6671 GSList *taglist = NULL;
6673 GtkTextTagInfo *info;
6676 * Make sure that the tag toggle counts and the tag root pointers are OK.
6678 for (taglist = list_of_tags (tree->table);
6679 taglist != NULL ; taglist = taglist->next)
6681 tag = taglist->data;
6682 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6685 node = info->tag_root;
6688 if (info->toggle_count != 0)
6690 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6691 tag->name, info->toggle_count);
6693 continue; /* no ranges for the tag */
6695 else if (info->toggle_count == 0)
6697 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6700 else if (info->toggle_count & 1)
6702 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6703 tag->name, info->toggle_count);
6705 for (summary = node->summary; summary != NULL;
6706 summary = summary->next)
6708 if (summary->info->tag == tag)
6710 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6714 if (node->level > 0)
6716 for (node = node->children.node ; node != NULL ;
6719 for (summary = node->summary; summary != NULL;
6720 summary = summary->next)
6722 if (summary->info->tag == tag)
6724 count += summary->toggle_count;
6731 GtkTextLineSegmentClass * last = NULL;
6733 for (line = node->children.line ; line != NULL ;
6736 for (seg = line->segments; seg != NULL;
6739 if ((seg->type == >k_text_toggle_on_type ||
6740 seg->type == >k_text_toggle_off_type) &&
6741 seg->body.toggle.info->tag == tag)
6743 if (last == seg->type)
6744 g_error ("Two consecutive toggles on or off weren't merged");
6745 if (!seg->body.toggle.inNodeCounts)
6746 g_error ("Toggle segment not in the node counts");
6755 if (count != info->toggle_count)
6757 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6758 info->toggle_count, tag->name, count);
6763 g_slist_free (taglist);
6767 * Call a recursive procedure to do the main body of checks.
6770 node = tree->root_node;
6771 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6774 * Make sure that there are at least two lines in the text and
6775 * that the last line has no characters except a newline.
6778 if (node->num_lines < 2)
6780 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6782 if (node->num_chars < 2)
6784 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6786 while (node->level > 0)
6788 node = node->children.node;
6789 while (node->next != NULL)
6794 line = node->children.line;
6795 while (line->next != NULL)
6799 seg = line->segments;
6800 while ((seg->type == >k_text_toggle_off_type)
6801 || (seg->type == >k_text_right_mark_type)
6802 || (seg->type == >k_text_left_mark_type))
6805 * It's OK to toggle a tag off in the last line, but
6806 * not to start a new range. It's also OK to have marks
6812 if (seg->type != >k_text_char_type)
6814 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6816 if (seg->next != NULL)
6818 g_error ("_gtk_text_btree_check: last line has too many segments");
6820 if (seg->byte_count != 1)
6822 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6825 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6827 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6832 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6833 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6834 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6835 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6838 _gtk_text_btree_spew (GtkTextBTree *tree)
6843 printf ("%d lines in tree %p\n",
6844 _gtk_text_btree_line_count (tree), tree);
6846 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6848 while (line != NULL)
6850 _gtk_text_btree_spew_line (tree, line);
6851 line = _gtk_text_line_next (line);
6854 printf ("=================== Tag information\n");
6859 list = tree->tag_infos;
6861 while (list != NULL)
6863 GtkTextTagInfo *info;
6867 printf (" tag `%s': root at %p, toggle count %d\n",
6868 info->tag->name, info->tag_root, info->toggle_count);
6870 list = g_slist_next (list);
6873 if (tree->tag_infos == NULL)
6875 printf (" (no tags in the tree)\n");
6879 printf ("=================== Tree nodes\n");
6882 _gtk_text_btree_spew_node (tree->root_node, 0);
6887 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6890 GtkTextLineSegment *seg;
6892 spaces = g_strnfill (indent, ' ');
6894 printf ("%sline %p chars %d bytes %d\n",
6896 _gtk_text_line_char_count (line),
6897 _gtk_text_line_byte_count (line));
6899 seg = line->segments;
6902 if (seg->type == >k_text_char_type)
6904 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6909 if (*s == '\n' || *s == '\r')
6913 printf ("%s chars `%s'...\n", spaces, str);
6916 else if (seg->type == >k_text_right_mark_type)
6918 printf ("%s right mark `%s' visible: %d\n",
6920 seg->body.mark.name,
6921 seg->body.mark.visible);
6923 else if (seg->type == >k_text_left_mark_type)
6925 printf ("%s left mark `%s' visible: %d\n",
6927 seg->body.mark.name,
6928 seg->body.mark.visible);
6930 else if (seg->type == >k_text_toggle_on_type ||
6931 seg->type == >k_text_toggle_off_type)
6933 printf ("%s tag `%s' %s\n",
6934 spaces, seg->body.toggle.info->tag->name,
6935 seg->type == >k_text_toggle_off_type ? "off" : "on");
6945 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6948 GtkTextBTreeNode *iter;
6951 spaces = g_strnfill (indent, ' ');
6953 printf ("%snode %p level %d children %d lines %d chars %d\n",
6954 spaces, node, node->level,
6955 node->num_children, node->num_lines, node->num_chars);
6960 printf ("%s %d toggles of `%s' below this node\n",
6961 spaces, s->toggle_count, s->info->tag->name);
6967 if (node->level > 0)
6969 iter = node->children.node;
6970 while (iter != NULL)
6972 _gtk_text_btree_spew_node (iter, indent + 2);
6979 GtkTextLine *line = node->children.line;
6980 while (line != NULL)
6982 _gtk_text_btree_spew_line_short (line, indent + 2);
6990 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
6992 GtkTextLineSegment * seg;
6994 printf ("%4d| line: %p parent: %p next: %p\n",
6995 _gtk_text_line_get_number (line), line, line->parent, line->next);
6997 seg = line->segments;
7001 _gtk_text_btree_spew_segment (tree, seg);
7007 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7009 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7010 seg, seg->type->name, seg->byte_count, seg->char_count);
7012 if (seg->type == >k_text_char_type)
7014 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7015 printf (" `%s'\n", str);
7018 else if (seg->type == >k_text_right_mark_type)
7020 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7021 seg->body.mark.name,
7022 seg->body.mark.visible,
7023 seg->body.mark.not_deleteable);
7025 else if (seg->type == >k_text_left_mark_type)
7027 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7028 seg->body.mark.name,
7029 seg->body.mark.visible,
7030 seg->body.mark.not_deleteable);
7032 else if (seg->type == >k_text_toggle_on_type ||
7033 seg->type == >k_text_toggle_off_type)
7035 printf (" tag `%s' priority %d\n",
7036 seg->body.toggle.info->tag->name,
7037 seg->body.toggle.info->tag->priority);