4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
61 #include "gtksignal.h"
62 #include "gtktexttag.h"
63 #include "gtktexttagtable.h"
64 #include "gtktextlayout.h"
65 #include "gtktextiterprivate.h"
67 #include "gtktextmarkprivate.h"
75 * The structure below is used to pass information between
76 * _gtk_text_btree_get_tags and inc_count:
79 typedef struct TagInfo {
80 int numTags; /* Number of tags for which there
81 * is currently information in
83 int arraySize; /* Number of entries allocated for
85 GtkTextTag **tags; /* Array of tags seen so far.
87 int *counts; /* Toggle count (so far) for each
88 * entry in tags. Malloc-ed. */
93 * This is used to store per-view width/height info at the tree nodes.
96 typedef struct _NodeData NodeData;
102 /* Height and width of this node */
106 /* boolean indicating whether the lines below this node are in need of validation.
107 * However, width/height should always represent the current total width and
108 * max height for lines below this node; the valid flag indicates whether the
109 * width/height on the lines needs recomputing, not whether the totals
117 * The data structure below keeps summary information about one tag as part
118 * of the tag information in a node.
121 typedef struct Summary {
122 GtkTextTagInfo *info; /* Handle for tag. */
123 int toggle_count; /* Number of transitions into or
124 * out of this tag that occur in
125 * the subtree rooted at this node. */
126 struct Summary *next; /* Next in list of all tags for same
127 * node, or NULL if at end of list. */
131 * The data structure below defines a node in the B-tree.
134 struct _GtkTextBTreeNode {
135 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
136 * this is the root. */
137 GtkTextBTreeNode *next; /* Next in list of siblings with the
138 * same parent node, or NULL for end
140 Summary *summary; /* First in malloc-ed list of info
141 * about tags in this subtree (NULL if
142 * no tag info in the subtree). */
143 int level; /* Level of this node in the B-tree.
144 * 0 refers to the bottom of the tree
145 * (children are lines, not nodes). */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
150 int num_children; /* Number of children of this node. */
151 int num_lines; /* Total number of lines (leaves) in
152 * the subtree rooted here. */
153 int num_chars; /* Number of chars below here */
160 * Used to store the list of views in our btree
163 typedef struct _BTreeView BTreeView;
167 GtkTextLayout *layout;
173 * And the tree itself
176 struct _GtkTextBTree {
177 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
178 GtkTextTagTable *table;
179 GHashTable *mark_table;
181 GtkTextMark *insert_mark;
182 GtkTextMark *selection_bound_mark;
183 GtkTextBuffer *buffer;
186 guint tag_changed_handler;
187 guint tag_removed_handler;
188 /* Incremented when a segment with a byte size > 0
189 * is added to or removed from the tree (i.e. the
190 * length of a line may have changed, and lines may
191 * have been added or removed). This invalidates
192 * all outstanding iterators.
194 guint chars_changed_stamp;
195 /* Incremented when any segments are added or deleted;
196 * this makes outstanding iterators recalculate their
197 * pointed-to segment and segment offset.
199 guint segments_changed_stamp;
201 /* Cache the last line in the buffer */
202 GtkTextLine *last_line;
203 guint last_line_stamp;
205 /* Cache the next-to-last line in the buffer,
206 * containing the end iterator
208 GtkTextLine *end_iter_line;
209 GtkTextLineSegment *end_iter_segment;
210 int end_iter_segment_byte_index;
211 int end_iter_segment_char_offset;
212 guint end_iter_line_stamp;
213 guint end_iter_segment_stamp;
215 GHashTable *child_anchor_table;
220 * Upper and lower bounds on how many children a node may have:
221 * rebalance when either of these limits is exceeded. MAX_CHILDREN
222 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
225 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
226 shallow. It appears to be faster to locate a particular line number
227 if the tree is narrow and deep, since it is more finely sorted. I
228 guess this may increase memory use though, and make it slower to
229 walk the tree in order, or locate a particular byte index (which
230 is done by walking the tree in order).
232 There's basically a tradeoff here. However I'm thinking we want to
233 add pixels, byte counts, and char counts to the tree nodes,
234 at that point narrow and deep should speed up all operations,
235 not just the line number searches.
239 #define MAX_CHILDREN 12
240 #define MIN_CHILDREN 6
242 #define MAX_CHILDREN 6
243 #define MIN_CHILDREN 3
250 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
252 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
253 GtkTextBTreeNode *node);
254 static GtkTextLine * get_last_line (GtkTextBTree *tree);
255 static void post_insert_fixup (GtkTextBTree *tree,
256 GtkTextLine *insert_line,
257 gint char_count_delta,
258 gint line_count_delta);
259 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
260 GtkTextTagInfo *info,
262 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
265 static void segments_changed (GtkTextBTree *tree);
266 static void chars_changed (GtkTextBTree *tree);
267 static void summary_list_destroy (Summary *summary);
268 static GtkTextLine *gtk_text_line_new (void);
269 static void gtk_text_line_destroy (GtkTextBTree *tree,
271 static void gtk_text_line_set_parent (GtkTextLine *line,
272 GtkTextBTreeNode *node);
273 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277 static NodeData *node_data_new (gpointer view_id);
278 static void node_data_destroy (NodeData *nd);
279 static void node_data_list_destroy (NodeData *nd);
280 static NodeData *node_data_find (NodeData *nd,
283 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
284 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
285 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
289 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
291 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
294 static void gtk_text_btree_node_remove_view (BTreeView *view,
295 GtkTextBTreeNode *node,
297 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
300 GtkTextBTreeNode *node);
301 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
305 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
309 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
310 GtkTextBTreeNode *node2);
311 static void get_tree_bounds (GtkTextBTree *tree,
314 static void tag_changed_cb (GtkTextTagTable *table,
316 gboolean size_changed,
318 static void tag_removed_cb (GtkTextTagTable *table,
321 static void cleanup_line (GtkTextLine *line);
322 static void recompute_node_counts (GtkTextBTree *tree,
323 GtkTextBTreeNode *node);
324 static void inc_count (GtkTextTag *tag,
326 TagInfo *tagInfoPtr);
328 static void summary_destroy (Summary *summary);
330 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
331 const GtkTextIter *iter);
332 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
333 GtkTextLineSegment *seg,
337 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
339 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
341 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
344 static void redisplay_region (GtkTextBTree *tree,
345 const GtkTextIter *start,
346 const GtkTextIter *end);
348 /* Inline thingies */
351 segments_changed (GtkTextBTree *tree)
353 tree->segments_changed_stamp += 1;
357 chars_changed (GtkTextBTree *tree)
359 tree->chars_changed_stamp += 1;
367 _gtk_text_btree_new (GtkTextTagTable *table,
368 GtkTextBuffer *buffer)
371 GtkTextBTreeNode *root_node;
372 GtkTextLine *line, *line2;
374 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
375 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
378 * The tree will initially have two empty lines. The second line
379 * isn't actually part of the tree's contents, but its presence
380 * makes several operations easier. The tree will have one GtkTextBTreeNode,
381 * which is also the root of the tree.
384 /* Create the root node. */
386 root_node = gtk_text_btree_node_new ();
388 line = gtk_text_line_new ();
389 line2 = gtk_text_line_new ();
391 root_node->parent = NULL;
392 root_node->next = NULL;
393 root_node->summary = NULL;
394 root_node->level = 0;
395 root_node->children.line = line;
396 root_node->num_children = 2;
397 root_node->num_lines = 2;
398 root_node->num_chars = 2;
400 line->parent = root_node;
403 line->segments = _gtk_char_segment_new ("\n", 1);
405 line2->parent = root_node;
407 line2->segments = _gtk_char_segment_new ("\n", 1);
409 /* Create the tree itself */
411 tree = g_new0(GtkTextBTree, 1);
412 tree->root_node = root_node;
416 /* Set these to values that are unlikely to be found
417 * in random memory garbage, and also avoid
418 * duplicates between tree instances.
420 tree->chars_changed_stamp = g_random_int ();
421 tree->segments_changed_stamp = g_random_int ();
423 tree->last_line_stamp = tree->chars_changed_stamp - 1;
424 tree->last_line = NULL;
426 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
427 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
428 tree->end_iter_line = NULL;
429 tree->end_iter_segment_byte_index = 0;
430 tree->end_iter_segment_char_offset = 0;
432 g_object_ref (G_OBJECT (tree->table));
434 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
436 G_CALLBACK (tag_changed_cb),
439 tree->tag_removed_handler = g_signal_connect (G_OBJECT (tree->table),
441 G_CALLBACK (tag_removed_cb),
444 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
445 tree->child_anchor_table = NULL;
447 /* We don't ref the buffer, since the buffer owns us;
448 * we'd have some circularity issues. The buffer always
449 * lasts longer than the BTree
451 tree->buffer = buffer;
455 GtkTextLineSegment *seg;
457 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
460 tree->insert_mark = _gtk_text_btree_set_mark (tree,
467 seg = tree->insert_mark->segment;
469 seg->body.mark.not_deleteable = TRUE;
470 seg->body.mark.visible = TRUE;
472 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
479 seg = tree->selection_bound_mark->segment;
481 seg->body.mark.not_deleteable = TRUE;
483 g_object_ref (G_OBJECT (tree->insert_mark));
484 g_object_ref (G_OBJECT (tree->selection_bound_mark));
493 _gtk_text_btree_ref (GtkTextBTree *tree)
495 g_return_if_fail (tree != NULL);
496 g_return_if_fail (tree->refcount > 0);
502 _gtk_text_btree_unref (GtkTextBTree *tree)
504 g_return_if_fail (tree != NULL);
505 g_return_if_fail (tree->refcount > 0);
509 if (tree->refcount == 0)
511 gtk_text_btree_node_destroy (tree, tree->root_node);
513 g_assert (g_hash_table_size (tree->mark_table) == 0);
514 g_hash_table_destroy (tree->mark_table);
516 g_object_unref (G_OBJECT (tree->insert_mark));
517 g_object_unref (G_OBJECT (tree->selection_bound_mark));
519 g_signal_handler_disconnect (G_OBJECT (tree->table),
520 tree->tag_changed_handler);
522 g_signal_handler_disconnect (G_OBJECT (tree->table),
523 tree->tag_removed_handler);
525 g_object_unref (G_OBJECT (tree->table));
532 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
538 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
540 return tree->chars_changed_stamp;
544 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
546 return tree->segments_changed_stamp;
550 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
552 g_return_if_fail (tree != NULL);
553 segments_changed (tree);
557 * Indexable segment mutation
561 _gtk_text_btree_delete (GtkTextIter *start,
564 GtkTextLineSegment *prev_seg; /* The segment just before the start
565 * of the deletion range. */
566 GtkTextLineSegment *last_seg; /* The segment just after the end
567 * of the deletion range. */
568 GtkTextLineSegment *seg, *next;
569 GtkTextLine *curline;
570 GtkTextBTreeNode *curnode, *node;
572 GtkTextLine *start_line;
573 GtkTextLine *end_line;
574 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
575 gint start_byte_offset;
577 g_return_if_fail (start != NULL);
578 g_return_if_fail (end != NULL);
579 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
580 _gtk_text_iter_get_btree (end));
582 gtk_text_iter_order (start, end);
584 tree = _gtk_text_iter_get_btree (start);
586 if (gtk_debug_flags & GTK_DEBUG_TEXT)
587 _gtk_text_btree_check (tree);
590 /* FIXME this code should no longer be required */
592 * The code below is ugly, but it's needed to make sure there
593 * is always a dummy empty line at the end of the text. If the
594 * final newline of the file (just before the dummy line) is being
595 * deleted, then back up index to just before the newline. If
596 * there is a newline just before the first character being deleted,
597 * then back up the first index too, so that an even number of lines
598 * gets deleted. Furthermore, remove any tags that are present on
599 * the newline that isn't going to be deleted after all (this simulates
600 * deleting the newline and then adding a "clean" one back again).
606 line1 = gtk_text_iter_get_line (start);
607 line2 = gtk_text_iter_get_line (end);
609 if (line2 == _gtk_text_btree_line_count (tree))
613 GtkTextIter orig_end;
616 gtk_text_iter_backward_char (end);
620 if (gtk_text_iter_get_line_offset (start) == 0 &&
623 gtk_text_iter_backward_char (start);
627 tags = _gtk_text_btree_get_tags (end,
635 while (i < array_size)
637 _gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
647 /* Broadcast the need for redisplay before we break the iterators */
648 _gtk_text_btree_invalidate_region (tree, start, end);
650 /* Save the byte offset so we can reset the iterators */
651 start_byte_offset = gtk_text_iter_get_line_index (start);
653 start_line = _gtk_text_iter_get_text_line (start);
654 end_line = _gtk_text_iter_get_text_line (end);
657 * Split the start and end segments, so we have a place
658 * to insert our new text.
660 * Tricky point: split at end first; otherwise the split
661 * at end may invalidate seg and/or prev_seg. This allows
662 * us to avoid invalidating segments for start.
665 last_seg = gtk_text_line_segment_split (end);
666 if (last_seg != NULL)
667 last_seg = last_seg->next;
669 last_seg = end_line->segments;
671 prev_seg = gtk_text_line_segment_split (start);
672 if (prev_seg != NULL)
674 seg = prev_seg->next;
675 prev_seg->next = last_seg;
679 seg = start_line->segments;
680 start_line->segments = last_seg;
683 /* notify iterators that their segments need recomputation,
684 just for robustness. */
685 segments_changed (tree);
688 * Delete all of the segments between prev_seg and last_seg.
691 curline = start_line;
692 curnode = curline->parent;
693 while (seg != last_seg)
699 GtkTextLine *nextline;
702 * We just ran off the end of a line. First find the
703 * next line, then go back to the old line and delete it
704 * (unless it's the starting line for the range).
707 nextline = _gtk_text_line_next (curline);
708 if (curline != start_line)
710 if (curnode == start_line->parent)
711 start_line->next = curline->next;
713 curnode->children.line = curline->next;
715 for (node = curnode; node != NULL;
718 /* Don't update node->num_chars, because
719 * that was done when we deleted the segments.
721 node->num_lines -= 1;
724 curnode->num_children -= 1;
725 curline->next = deleted_lines;
726 deleted_lines = curline;
730 seg = curline->segments;
733 * If the GtkTextBTreeNode is empty then delete it and its parents,
734 * recursively upwards until a non-empty GtkTextBTreeNode is found.
737 while (curnode->num_children == 0)
739 GtkTextBTreeNode *parent;
741 parent = curnode->parent;
742 if (parent->children.node == curnode)
744 parent->children.node = curnode->next;
748 GtkTextBTreeNode *prevnode = parent->children.node;
749 while (prevnode->next != curnode)
751 prevnode = prevnode->next;
753 prevnode->next = curnode->next;
755 parent->num_children--;
756 gtk_text_btree_node_free_empty (tree, curnode);
759 curnode = curline->parent;
764 char_count = seg->char_count;
766 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
769 * This segment refuses to die. Move it to prev_seg and
770 * advance prev_seg if the segment has left gravity.
773 if (prev_seg == NULL)
775 seg->next = start_line->segments;
776 start_line->segments = seg;
780 seg->next = prev_seg->next;
781 prev_seg->next = seg;
783 if (seg->type->leftGravity)
790 /* Segment is gone. Decrement the char count of the node and
792 for (node = curnode; node != NULL;
795 node->num_chars -= char_count;
803 * If the beginning and end of the deletion range are in different
804 * lines, join the two lines together and discard the ending line.
807 if (start_line != end_line)
810 GtkTextBTreeNode *ancestor_node;
811 GtkTextLine *prevline;
814 /* last_seg was appended to start_line up at the top of this function */
816 for (seg = last_seg; seg != NULL;
819 chars_moved += seg->char_count;
820 if (seg->type->lineChangeFunc != NULL)
822 (*seg->type->lineChangeFunc)(seg, end_line);
826 for (node = start_line->parent; node != NULL;
829 node->num_chars += chars_moved;
832 curnode = end_line->parent;
833 for (node = curnode; node != NULL;
836 node->num_chars -= chars_moved;
839 curnode->num_children--;
840 prevline = curnode->children.line;
841 if (prevline == end_line)
843 curnode->children.line = end_line->next;
847 while (prevline->next != end_line)
849 prevline = prevline->next;
851 prevline->next = end_line->next;
853 end_line->next = deleted_lines;
854 deleted_lines = end_line;
856 /* We now fix up the per-view aggregates. We add all the height and
857 * width for the deleted lines to the start line, so that when revalidation
858 * occurs, the correct change in size is seen.
860 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
867 gint deleted_width = 0;
868 gint deleted_height = 0;
870 line = deleted_lines;
873 GtkTextLine *next_line = line->next;
874 ld = _gtk_text_line_get_data (line, view->view_id);
878 deleted_width = MAX (deleted_width, ld->width);
879 deleted_height += ld->height;
883 gtk_text_line_destroy (tree, line);
888 if (deleted_width > 0 || deleted_height > 0)
890 ld = _gtk_text_line_get_data (start_line, view->view_id);
892 /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
893 * no way to add ld without also validating the node, which would
894 * be improper at this point.
896 /* This assertion does actually fail sometimes, must
897 fix before stable release -hp */
900 ld->width = MAX (deleted_width, ld->width);
901 ld->height += deleted_height;
905 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
906 if (ancestor_node->parent)
907 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
912 /* avoid dangling pointer */
913 deleted_lines = NULL;
915 gtk_text_btree_rebalance (tree, curnode);
919 * Cleanup the segments in the new line.
922 cleanup_line (start_line);
925 * Lastly, rebalance the first GtkTextBTreeNode of the range.
928 gtk_text_btree_rebalance (tree, start_line->parent);
930 /* Notify outstanding iterators that they
932 chars_changed (tree);
933 segments_changed (tree);
935 if (gtk_debug_flags & GTK_DEBUG_TEXT)
936 _gtk_text_btree_check (tree);
938 /* Re-initialize our iterators */
939 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
944 _gtk_text_btree_insert (GtkTextIter *iter,
948 GtkTextLineSegment *prev_seg; /* The segment just before the first
949 * new segment (NULL means new segment
950 * is at beginning of line). */
951 GtkTextLineSegment *cur_seg; /* Current segment; new characters
952 * are inserted just after this one.
953 * NULL means insert at beginning of
955 GtkTextLine *line; /* Current line (new segments are
956 * added to this line). */
957 GtkTextLineSegment *seg;
958 GtkTextLine *newline;
959 int chunk_len; /* # characters in current chunk. */
960 gint sol; /* start of line */
961 gint eol; /* Pointer to character just after last
962 * one in current chunk.
964 gint delim; /* index of paragraph delimiter */
965 int line_count_delta; /* Counts change to total number of
969 int char_count_delta; /* change to number of chars */
971 gint start_byte_index;
972 GtkTextLine *start_line;
974 g_return_if_fail (text != NULL);
975 g_return_if_fail (iter != NULL);
980 /* extract iterator info */
981 tree = _gtk_text_iter_get_btree (iter);
982 line = _gtk_text_iter_get_text_line (iter);
984 start_byte_index = gtk_text_iter_get_line_index (iter);
986 /* Get our insertion segment split */
987 prev_seg = gtk_text_line_segment_split (iter);
990 /* Invalidate all iterators */
991 chars_changed (tree);
992 segments_changed (tree);
995 * Chop the text up into lines and create a new segment for
996 * each line, plus a new line for the leftovers from the
1002 line_count_delta = 0;
1003 char_count_delta = 0;
1008 pango_find_paragraph_boundary (text + sol,
1013 /* make these relative to the start of the text */
1017 g_assert (eol >= sol);
1018 g_assert (delim >= sol);
1019 g_assert (eol >= delim);
1020 g_assert (sol >= 0);
1021 g_assert (eol <= len);
1023 chunk_len = eol - sol;
1025 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1026 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1028 char_count_delta += seg->char_count;
1030 if (cur_seg == NULL)
1032 seg->next = line->segments;
1033 line->segments = seg;
1037 seg->next = cur_seg->next;
1038 cur_seg->next = seg;
1043 /* chunk didn't end with a paragraph separator */
1044 g_assert (eol == len);
1049 * The chunk ended with a newline, so create a new GtkTextLine
1050 * and move the remainder of the old line to it.
1053 newline = gtk_text_line_new ();
1054 gtk_text_line_set_parent (newline, line->parent);
1055 newline->next = line->next;
1056 line->next = newline;
1057 newline->segments = seg->next;
1065 * Cleanup the starting line for the insertion, plus the ending
1066 * line if it's different.
1069 cleanup_line (start_line);
1070 if (line != start_line)
1072 cleanup_line (line);
1075 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1077 /* Invalidate our region, and reset the iterator the user
1078 passed in to point to the end of the inserted text. */
1084 _gtk_text_btree_get_iter_at_line (tree,
1090 /* We could almost certainly be more efficient here
1091 by saving the information from the insertion loop
1093 gtk_text_iter_forward_chars (&end, char_count_delta);
1095 _gtk_text_btree_invalidate_region (tree,
1099 /* Convenience for the user */
1105 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1106 GtkTextLineSegment *seg)
1110 GtkTextLineSegment *prevPtr;
1113 gint start_byte_offset;
1115 line = _gtk_text_iter_get_text_line (iter);
1116 tree = _gtk_text_iter_get_btree (iter);
1117 start_byte_offset = gtk_text_iter_get_line_index (iter);
1119 prevPtr = gtk_text_line_segment_split (iter);
1120 if (prevPtr == NULL)
1122 seg->next = line->segments;
1123 line->segments = seg;
1127 seg->next = prevPtr->next;
1128 prevPtr->next = seg;
1131 post_insert_fixup (tree, line, 0, seg->char_count);
1133 chars_changed (tree);
1134 segments_changed (tree);
1136 /* reset *iter for the user, and invalidate tree nodes */
1138 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1141 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1143 _gtk_text_btree_invalidate_region (tree, &start, iter);
1147 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1150 GtkTextLineSegment *seg;
1152 seg = _gtk_pixbuf_segment_new (pixbuf);
1154 insert_pixbuf_or_widget_segment (iter, seg);
1158 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1159 GtkTextChildAnchor *anchor)
1161 GtkTextLineSegment *seg;
1164 if (anchor->segment != NULL)
1166 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1170 seg = _gtk_widget_segment_new (anchor);
1172 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1174 insert_pixbuf_or_widget_segment (iter, seg);
1176 if (tree->child_anchor_table == NULL)
1177 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1179 g_hash_table_insert (tree->child_anchor_table,
1180 seg->body.child.obj,
1181 seg->body.child.obj);
1185 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1187 GtkTextLineSegment *seg;
1189 seg = anchor->segment;
1191 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1200 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1201 GtkTextBTreeNode *node, gint y, gint *line_top,
1202 GtkTextLine *last_line)
1206 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1207 _gtk_text_btree_check (tree);
1209 if (node->level == 0)
1213 line = node->children.line;
1215 while (line != NULL && line != last_line)
1217 GtkTextLineData *ld;
1219 ld = _gtk_text_line_get_data (line, view->view_id);
1223 if (y < (current_y + (ld ? ld->height : 0)))
1226 current_y += ld->height;
1227 *line_top += ld->height;
1236 GtkTextBTreeNode *child;
1238 child = node->children.node;
1240 while (child != NULL)
1245 gtk_text_btree_node_get_size (child, view->view_id,
1248 if (y < (current_y + height))
1249 return find_line_by_y (tree, view, child,
1250 y - current_y, line_top,
1253 current_y += height;
1254 *line_top += height;
1256 child = child->next;
1264 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1271 GtkTextLine *last_line;
1274 view = gtk_text_btree_get_view (tree, view_id);
1275 g_return_val_if_fail (view != NULL, NULL);
1277 last_line = get_last_line (tree);
1279 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1283 *line_top_out = line_top;
1289 find_line_top_in_line_list (GtkTextBTree *tree,
1292 GtkTextLine *target_line,
1295 while (line != NULL)
1297 GtkTextLineData *ld;
1299 if (line == target_line)
1302 ld = _gtk_text_line_get_data (line, view->view_id);
1309 g_assert_not_reached (); /* If we get here, our
1310 target line didn't exist
1311 under its parent node */
1316 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1317 GtkTextLine *target_line,
1324 GtkTextBTreeNode *node;
1326 view = gtk_text_btree_get_view (tree, view_id);
1328 g_return_val_if_fail (view != NULL, 0);
1331 node = target_line->parent;
1332 while (node != NULL)
1334 nodes = g_slist_prepend (nodes, node);
1335 node = node->parent;
1339 while (iter != NULL)
1343 if (node->level == 0)
1345 g_slist_free (nodes);
1346 return find_line_top_in_line_list (tree, view,
1347 node->children.line,
1352 GtkTextBTreeNode *child;
1353 GtkTextBTreeNode *target_node;
1355 g_assert (iter->next != NULL); /* not at level 0 */
1356 target_node = iter->next->data;
1358 child = node->children.node;
1360 while (child != NULL)
1365 if (child == target_node)
1369 gtk_text_btree_node_get_size (child, view->view_id,
1373 child = child->next;
1375 g_assert (child != NULL); /* should have broken out before we
1379 iter = g_slist_next (iter);
1382 g_assert_not_reached (); /* we return when we find the target line */
1387 _gtk_text_btree_add_view (GtkTextBTree *tree,
1388 GtkTextLayout *layout)
1391 GtkTextLine *last_line;
1392 GtkTextLineData *line_data;
1394 g_return_if_fail (tree != NULL);
1396 view = g_new (BTreeView, 1);
1398 view->view_id = layout;
1399 view->layout = layout;
1401 view->next = tree->views;
1406 g_assert (tree->views->prev == NULL);
1407 tree->views->prev = view;
1412 /* The last line in the buffer has identity values for the per-view
1413 * data so that we can avoid special case checks for it in a large
1416 last_line = get_last_line (tree);
1418 line_data = g_new (GtkTextLineData, 1);
1419 line_data->view_id = layout;
1420 line_data->next = NULL;
1421 line_data->width = 0;
1422 line_data->height = 0;
1423 line_data->valid = TRUE;
1425 _gtk_text_line_add_data (last_line, line_data);
1429 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1433 GtkTextLine *last_line;
1434 GtkTextLineData *line_data;
1436 g_return_if_fail (tree != NULL);
1440 while (view != NULL)
1442 if (view->view_id == view_id)
1448 g_return_if_fail (view != NULL);
1451 view->next->prev = view->prev;
1454 view->prev->next = view->next;
1456 if (view == tree->views)
1457 tree->views = view->next;
1459 /* Remove the line data for the last line which we added ourselves.
1460 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1462 last_line = get_last_line (tree);
1463 line_data = _gtk_text_line_remove_data (last_line, view_id);
1466 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1468 view->layout = (gpointer) 0xdeadbeef;
1469 view->view_id = (gpointer) 0xdeadbeef;
1475 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1476 const GtkTextIter *start,
1477 const GtkTextIter *end)
1483 while (view != NULL)
1485 gtk_text_layout_invalidate (view->layout, start, end);
1492 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1497 g_return_if_fail (tree != NULL);
1498 g_return_if_fail (view_id != NULL);
1500 gtk_text_btree_node_get_size (tree->root_node, view_id,
1515 iter_stack_new (void)
1518 stack = g_new (IterStack, 1);
1519 stack->iters = NULL;
1526 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1529 if (stack->count > stack->alloced)
1531 stack->alloced = stack->count*2;
1532 stack->iters = g_realloc (stack->iters,
1533 stack->alloced*sizeof (GtkTextIter));
1535 stack->iters[stack->count-1] = *iter;
1539 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1541 if (stack->count == 0)
1546 *iter = stack->iters[stack->count];
1552 iter_stack_free (IterStack *stack)
1554 g_free (stack->iters);
1559 iter_stack_invert (IterStack *stack)
1561 if (stack->count > 0)
1564 guint j = stack->count - 1;
1569 tmp = stack->iters[i];
1570 stack->iters[i] = stack->iters[j];
1571 stack->iters[j] = tmp;
1580 queue_tag_redisplay (GtkTextBTree *tree,
1582 const GtkTextIter *start,
1583 const GtkTextIter *end)
1585 if (_gtk_text_tag_affects_size (tag))
1587 _gtk_text_btree_invalidate_region (tree, start, end);
1589 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1591 /* We only need to queue a redraw, not a relayout */
1592 redisplay_region (tree, start, end);
1595 /* We don't need to do anything if the tag doesn't affect display */
1599 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1600 const GtkTextIter *end_orig,
1604 GtkTextLineSegment *seg, *prev;
1605 GtkTextLine *cleanupline;
1606 gboolean toggled_on;
1607 GtkTextLine *start_line;
1608 GtkTextLine *end_line;
1610 GtkTextIter start, end;
1613 GtkTextTagInfo *info;
1615 g_return_if_fail (start_orig != NULL);
1616 g_return_if_fail (end_orig != NULL);
1617 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1618 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1619 _gtk_text_iter_get_btree (end_orig));
1620 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1623 printf ("%s tag %s from %d to %d\n",
1624 add ? "Adding" : "Removing",
1626 gtk_text_buffer_get_offset (start_orig),
1627 gtk_text_buffer_get_offset (end_orig));
1630 if (gtk_text_iter_equal (start_orig, end_orig))
1633 start = *start_orig;
1636 gtk_text_iter_order (&start, &end);
1638 tree = _gtk_text_iter_get_btree (&start);
1640 queue_tag_redisplay (tree, tag, &start, &end);
1642 info = gtk_text_btree_get_tag_info (tree, tag);
1644 start_line = _gtk_text_iter_get_text_line (&start);
1645 end_line = _gtk_text_iter_get_text_line (&end);
1647 /* Find all tag toggles in the region; we are going to delete them.
1648 We need to find them in advance, because
1649 forward_find_tag_toggle () won't work once we start playing around
1651 stack = iter_stack_new ();
1654 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1655 * which is deliberate - we don't want to delete a toggle at the
1658 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1660 if (gtk_text_iter_compare (&iter, &end) >= 0)
1663 iter_stack_push (stack, &iter);
1666 /* We need to traverse the toggles in order. */
1667 iter_stack_invert (stack);
1670 * See whether the tag is present at the start of the range. If
1671 * the state doesn't already match what we want then add a toggle
1675 toggled_on = gtk_text_iter_has_tag (&start, tag);
1676 if ( (add && !toggled_on) ||
1677 (!add && toggled_on) )
1679 /* This could create a second toggle at the start position;
1680 cleanup_line () will remove it if so. */
1681 seg = _gtk_toggle_segment_new (info, add);
1683 prev = gtk_text_line_segment_split (&start);
1686 seg->next = start_line->segments;
1687 start_line->segments = seg;
1691 seg->next = prev->next;
1695 /* cleanup_line adds the new toggle to the node counts. */
1697 printf ("added toggle at start\n");
1699 /* we should probably call segments_changed, but in theory
1700 any still-cached segments in the iters we are about to
1701 use are still valid, since they're in front
1707 * Scan the range of characters and delete any internal tag
1708 * transitions. Keep track of what the old state was at the end
1709 * of the range, and add a toggle there if it's needed.
1713 cleanupline = start_line;
1714 while (iter_stack_pop (stack, &iter))
1716 GtkTextLineSegment *indexable_seg;
1719 line = _gtk_text_iter_get_text_line (&iter);
1720 seg = _gtk_text_iter_get_any_segment (&iter);
1721 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1723 g_assert (seg != NULL);
1724 g_assert (indexable_seg != NULL);
1725 g_assert (seg != indexable_seg);
1727 prev = line->segments;
1729 /* Find the segment that actually toggles this tag. */
1730 while (seg != indexable_seg)
1732 g_assert (seg != NULL);
1733 g_assert (indexable_seg != NULL);
1734 g_assert (seg != indexable_seg);
1736 if ( (seg->type == >k_text_toggle_on_type ||
1737 seg->type == >k_text_toggle_off_type) &&
1738 (seg->body.toggle.info == info) )
1744 g_assert (seg != NULL);
1745 g_assert (indexable_seg != NULL);
1747 g_assert (seg != indexable_seg); /* If this happens, then
1748 forward_to_tag_toggle was
1750 g_assert (seg->body.toggle.info->tag == tag);
1752 /* If this happens, when previously tagging we didn't merge
1753 overlapping tags. */
1754 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1755 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1757 toggled_on = !toggled_on;
1760 printf ("deleting %s toggle\n",
1761 seg->type == >k_text_toggle_on_type ? "on" : "off");
1763 /* Remove toggle segment from the list. */
1766 line->segments = seg->next;
1770 while (prev->next != seg)
1774 prev->next = seg->next;
1777 /* Inform iterators we've hosed them. This actually reflects a
1778 bit of inefficiency; if you have the same tag toggled on and
1779 off a lot in a single line, we keep having the rescan from
1780 the front of the line. Of course we have to do that to get
1781 "prev" anyway, but here we are doing it an additional
1783 segments_changed (tree);
1785 /* Update node counts */
1786 if (seg->body.toggle.inNodeCounts)
1788 _gtk_change_node_toggle_count (line->parent,
1790 seg->body.toggle.inNodeCounts = FALSE;
1795 /* We only clean up lines when we're done with them, saves some
1796 gratuitous line-segment-traversals */
1798 if (cleanupline != line)
1800 cleanup_line (cleanupline);
1805 iter_stack_free (stack);
1807 /* toggled_on now reflects the toggle state _just before_ the
1808 end iterator. The end iterator could already have a toggle
1809 on or a toggle off. */
1810 if ( (add && !toggled_on) ||
1811 (!add && toggled_on) )
1813 /* This could create a second toggle at the start position;
1814 cleanup_line () will remove it if so. */
1816 seg = _gtk_toggle_segment_new (info, !add);
1818 prev = gtk_text_line_segment_split (&end);
1821 seg->next = end_line->segments;
1822 end_line->segments = seg;
1826 seg->next = prev->next;
1829 /* cleanup_line adds the new toggle to the node counts. */
1830 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1832 printf ("added toggle at end\n");
1837 * Cleanup cleanupline and the last line of the range, if
1838 * these are different.
1841 cleanup_line (cleanupline);
1842 if (cleanupline != end_line)
1844 cleanup_line (end_line);
1847 segments_changed (tree);
1849 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1850 _gtk_text_btree_check (tree);
1859 get_line_internal (GtkTextBTree *tree,
1861 gint *real_line_number,
1862 gboolean include_last)
1864 GtkTextBTreeNode *node;
1869 line_count = _gtk_text_btree_line_count (tree);
1873 if (line_number < 0)
1875 line_number = line_count;
1877 else if (line_number > line_count)
1879 line_number = line_count;
1882 if (real_line_number)
1883 *real_line_number = line_number;
1885 node = tree->root_node;
1886 lines_left = line_number;
1889 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1893 while (node->level != 0)
1895 for (node = node->children.node;
1896 node->num_lines <= lines_left;
1902 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1905 lines_left -= node->num_lines;
1910 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1913 for (line = node->children.line; lines_left > 0;
1919 g_error ("gtk_text_btree_find_line ran out of lines");
1928 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1931 _gtk_text_btree_get_line (tree,
1932 _gtk_text_btree_line_count (tree) - 1,
1937 _gtk_text_btree_get_line (GtkTextBTree *tree,
1939 gint *real_line_number)
1941 return get_line_internal (tree, line_number, real_line_number, TRUE);
1945 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1947 gint *real_line_number)
1949 return get_line_internal (tree, line_number, real_line_number, FALSE);
1953 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1955 gint *line_start_index,
1956 gint *real_char_index)
1958 GtkTextBTreeNode *node;
1960 GtkTextLineSegment *seg;
1965 node = tree->root_node;
1967 /* Clamp to valid indexes (-1 is magic for "highest index"),
1968 * node->num_chars includes the two newlines that aren't really
1971 if (char_index < 0 || char_index >= (node->num_chars - 1))
1973 char_index = node->num_chars - 2;
1976 *real_char_index = char_index;
1979 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1983 chars_left = char_index;
1984 while (node->level != 0)
1986 for (node = node->children.node;
1987 chars_left >= node->num_chars;
1990 chars_left -= node->num_chars;
1992 g_assert (chars_left >= 0);
1996 if (chars_left == 0)
1998 /* Start of a line */
2000 *line_start_index = char_index;
2001 return node->children.line;
2005 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2011 for (line = node->children.line; line != NULL; line = line->next)
2013 seg = line->segments;
2016 if (chars_in_line + seg->char_count > chars_left)
2017 goto found; /* found our line/segment */
2019 chars_in_line += seg->char_count;
2024 chars_left -= chars_in_line;
2032 g_assert (line != NULL); /* hosage, ran out of lines */
2033 g_assert (seg != NULL);
2035 *line_start_index = char_index - chars_left;
2040 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2043 GtkTextBTreeNode *node;
2044 GtkTextLine *siblingline;
2045 GtkTextLineSegment *seg;
2046 int src, dst, index;
2052 #define NUM_TAG_INFOS 10
2054 line = _gtk_text_iter_get_text_line (iter);
2055 tree = _gtk_text_iter_get_btree (iter);
2056 byte_index = gtk_text_iter_get_line_index (iter);
2058 tagInfo.numTags = 0;
2059 tagInfo.arraySize = NUM_TAG_INFOS;
2060 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2061 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2064 * Record tag toggles within the line of indexPtr but preceding
2065 * indexPtr. Note that if this loop segfaults, your
2066 * byte_index probably points past the sum of all
2067 * seg->byte_count */
2069 for (index = 0, seg = line->segments;
2070 (index + seg->byte_count) <= byte_index;
2071 index += seg->byte_count, seg = seg->next)
2073 if ((seg->type == >k_text_toggle_on_type)
2074 || (seg->type == >k_text_toggle_off_type))
2076 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2081 * Record toggles for tags in lines that are predecessors of
2082 * line but under the same level-0 GtkTextBTreeNode.
2085 for (siblingline = line->parent->children.line;
2086 siblingline != line;
2087 siblingline = siblingline->next)
2089 for (seg = siblingline->segments; seg != NULL;
2092 if ((seg->type == >k_text_toggle_on_type)
2093 || (seg->type == >k_text_toggle_off_type))
2095 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2101 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2102 * toggles for all siblings that precede that GtkTextBTreeNode.
2105 for (node = line->parent; node->parent != NULL;
2106 node = node->parent)
2108 GtkTextBTreeNode *siblingPtr;
2111 for (siblingPtr = node->parent->children.node;
2112 siblingPtr != node; siblingPtr = siblingPtr->next)
2114 for (summary = siblingPtr->summary; summary != NULL;
2115 summary = summary->next)
2117 if (summary->toggle_count & 1)
2119 inc_count (summary->info->tag, summary->toggle_count,
2127 * Go through the tag information and squash out all of the tags
2128 * that have even toggle counts (these tags exist before the point
2129 * of interest, but not at the desired character itself).
2132 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2134 if (tagInfo.counts[src] & 1)
2136 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2137 tagInfo.tags[dst] = tagInfo.tags[src];
2143 g_free (tagInfo.counts);
2146 g_free (tagInfo.tags);
2149 return tagInfo.tags;
2153 copy_segment (GString *string,
2154 gboolean include_hidden,
2155 gboolean include_nonchars,
2156 const GtkTextIter *start,
2157 const GtkTextIter *end)
2159 GtkTextLineSegment *end_seg;
2160 GtkTextLineSegment *seg;
2162 if (gtk_text_iter_equal (start, end))
2165 seg = _gtk_text_iter_get_indexable_segment (start);
2166 end_seg = _gtk_text_iter_get_indexable_segment (end);
2168 if (seg->type == >k_text_char_type)
2170 gboolean copy = TRUE;
2171 gint copy_bytes = 0;
2172 gint copy_start = 0;
2174 /* Don't copy if we're invisible; segments are invisible/not
2175 as a whole, no need to check each char */
2176 if (!include_hidden &&
2177 _gtk_text_btree_char_is_invisible (start))
2180 /* printf (" <invisible>\n"); */
2183 copy_start = _gtk_text_iter_get_segment_byte (start);
2187 /* End is in the same segment; need to copy fewer bytes. */
2188 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2190 copy_bytes = end_byte - copy_start;
2193 copy_bytes = seg->byte_count - copy_start;
2195 g_assert (copy_bytes != 0); /* Due to iter equality check at
2196 front of this function. */
2200 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2202 g_string_append_len (string,
2203 seg->body.chars + copy_start,
2207 /* printf (" :%s\n", string->str); */
2209 else if (seg->type == >k_text_pixbuf_type ||
2210 seg->type == >k_text_child_type)
2212 gboolean copy = TRUE;
2214 if (!include_nonchars)
2218 else if (!include_hidden &&
2219 _gtk_text_btree_char_is_invisible (start))
2226 g_string_append_len (string,
2227 gtk_text_unknown_char_utf8,
2235 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2236 const GtkTextIter *end_orig,
2237 gboolean include_hidden,
2238 gboolean include_nonchars)
2240 GtkTextLineSegment *seg;
2241 GtkTextLineSegment *end_seg;
2249 g_return_val_if_fail (start_orig != NULL, NULL);
2250 g_return_val_if_fail (end_orig != NULL, NULL);
2251 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2252 _gtk_text_iter_get_btree (end_orig), NULL);
2254 start = *start_orig;
2257 gtk_text_iter_order (&start, &end);
2259 retval = g_string_new ("");
2261 tree = _gtk_text_iter_get_btree (&start);
2263 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2265 seg = _gtk_text_iter_get_indexable_segment (&iter);
2266 while (seg != end_seg)
2268 copy_segment (retval, include_hidden, include_nonchars,
2271 _gtk_text_iter_forward_indexable_segment (&iter);
2273 seg = _gtk_text_iter_get_indexable_segment (&iter);
2276 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2279 g_string_free (retval, FALSE);
2284 _gtk_text_btree_line_count (GtkTextBTree *tree)
2286 /* Subtract bogus line at the end; we return a count
2288 return tree->root_node->num_lines - 1;
2292 _gtk_text_btree_char_count (GtkTextBTree *tree)
2294 /* Exclude newline in bogus last line and the
2295 * one in the last line that is after the end iterator
2297 return tree->root_node->num_chars - 2;
2300 #define LOTSA_TAGS 1000
2302 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2304 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2306 int deftagCnts[LOTSA_TAGS];
2307 int *tagCnts = deftagCnts;
2308 GtkTextTag *deftags[LOTSA_TAGS];
2309 GtkTextTag **tags = deftags;
2311 GtkTextBTreeNode *node;
2312 GtkTextLine *siblingline;
2313 GtkTextLineSegment *seg;
2320 line = _gtk_text_iter_get_text_line (iter);
2321 tree = _gtk_text_iter_get_btree (iter);
2322 byte_index = gtk_text_iter_get_line_index (iter);
2324 numTags = gtk_text_tag_table_get_size (tree->table);
2326 /* almost always avoid malloc, so stay out of system calls */
2327 if (LOTSA_TAGS < numTags)
2329 tagCnts = g_new (int, numTags);
2330 tags = g_new (GtkTextTag*, numTags);
2333 for (i=0; i<numTags; i++)
2339 * Record tag toggles within the line of indexPtr but preceding
2343 for (index = 0, seg = line->segments;
2344 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2345 index += seg->byte_count, seg = seg->next)
2347 if ((seg->type == >k_text_toggle_on_type)
2348 || (seg->type == >k_text_toggle_off_type))
2350 tag = seg->body.toggle.info->tag;
2351 if (tag->invisible_set && tag->values->invisible)
2353 tags[tag->priority] = tag;
2354 tagCnts[tag->priority]++;
2360 * Record toggles for tags in lines that are predecessors of
2361 * line but under the same level-0 GtkTextBTreeNode.
2364 for (siblingline = line->parent->children.line;
2365 siblingline != line;
2366 siblingline = siblingline->next)
2368 for (seg = siblingline->segments; seg != NULL;
2371 if ((seg->type == >k_text_toggle_on_type)
2372 || (seg->type == >k_text_toggle_off_type))
2374 tag = seg->body.toggle.info->tag;
2375 if (tag->invisible_set && tag->values->invisible)
2377 tags[tag->priority] = tag;
2378 tagCnts[tag->priority]++;
2385 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2386 * for all siblings that precede that GtkTextBTreeNode.
2389 for (node = line->parent; node->parent != NULL;
2390 node = node->parent)
2392 GtkTextBTreeNode *siblingPtr;
2395 for (siblingPtr = node->parent->children.node;
2396 siblingPtr != node; siblingPtr = siblingPtr->next)
2398 for (summary = siblingPtr->summary; summary != NULL;
2399 summary = summary->next)
2401 if (summary->toggle_count & 1)
2403 tag = summary->info->tag;
2404 if (tag->invisible_set && tag->values->invisible)
2406 tags[tag->priority] = tag;
2407 tagCnts[tag->priority] += summary->toggle_count;
2415 * Now traverse from highest priority to lowest,
2416 * take invisible value from first odd count (= on)
2419 for (i = numTags-1; i >=0; i--)
2423 /* FIXME not sure this should be if 0 */
2425 #ifndef ALWAYS_SHOW_SELECTION
2426 /* who would make the selection invisible? */
2427 if ((tag == tkxt->seltag)
2428 && !(tkxt->flags & GOT_FOCUS))
2434 invisible = tags[i]->values->invisible;
2439 if (LOTSA_TAGS < numTags)
2454 redisplay_region (GtkTextBTree *tree,
2455 const GtkTextIter *start,
2456 const GtkTextIter *end)
2459 GtkTextLine *start_line, *end_line;
2461 if (gtk_text_iter_compare (start, end) > 0)
2463 const GtkTextIter *tmp = start;
2468 start_line = _gtk_text_iter_get_text_line (start);
2469 end_line = _gtk_text_iter_get_text_line (end);
2472 while (view != NULL)
2474 gint start_y, end_y;
2475 GtkTextLineData *ld;
2477 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2479 if (end_line == start_line)
2482 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2484 ld = _gtk_text_line_get_data (end_line, view->view_id);
2486 end_y += ld->height;
2488 gtk_text_layout_changed (view->layout, start_y,
2497 redisplay_mark (GtkTextLineSegment *mark)
2502 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2504 mark->body.mark.obj);
2507 gtk_text_iter_forward_char (&end);
2509 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2514 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2516 if (!mark->body.mark.visible)
2519 redisplay_mark (mark);
2523 ensure_not_off_end (GtkTextBTree *tree,
2524 GtkTextLineSegment *mark,
2527 if (gtk_text_iter_get_line (iter) ==
2528 _gtk_text_btree_line_count (tree))
2529 gtk_text_iter_backward_char (iter);
2532 static GtkTextLineSegment*
2533 real_set_mark (GtkTextBTree *tree,
2534 GtkTextMark *existing_mark,
2536 gboolean left_gravity,
2537 const GtkTextIter *where,
2538 gboolean should_exist,
2539 gboolean redraw_selections)
2541 GtkTextLineSegment *mark;
2544 g_return_val_if_fail (tree != NULL, NULL);
2545 g_return_val_if_fail (where != NULL, NULL);
2546 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2549 mark = existing_mark->segment;
2550 else if (name != NULL)
2551 mark = g_hash_table_lookup (tree->mark_table,
2556 if (should_exist && mark == NULL)
2558 g_warning ("No mark `%s' exists!", name);
2562 /* OK if !should_exist and it does already exist, in that case
2568 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2569 _gtk_text_iter_check (&iter);
2573 if (redraw_selections &&
2574 (mark == tree->insert_mark->segment ||
2575 mark == tree->selection_bound_mark->segment))
2577 GtkTextIter old_pos;
2579 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2580 mark->body.mark.obj);
2581 redisplay_region (tree, &old_pos, where);
2585 * don't let visible marks be after the final newline of the
2589 if (mark->body.mark.visible)
2591 ensure_not_off_end (tree, mark, &iter);
2594 /* Redraw the mark's old location. */
2595 redisplay_mark_if_visible (mark);
2597 /* Unlink mark from its current location.
2598 This could hose our iterator... */
2599 gtk_text_btree_unlink_segment (tree, mark,
2600 mark->body.mark.line);
2601 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2602 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2604 segments_changed (tree); /* make sure the iterator recomputes its
2609 mark = _gtk_mark_segment_new (tree,
2613 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2615 if (mark->body.mark.name)
2616 g_hash_table_insert (tree->mark_table,
2617 mark->body.mark.name,
2621 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2622 _gtk_text_iter_check (&iter);
2624 /* Link mark into new location */
2625 gtk_text_btree_link_segment (mark, &iter);
2627 /* Invalidate some iterators. */
2628 segments_changed (tree);
2631 * update the screen at the mark's new location.
2634 redisplay_mark_if_visible (mark);
2636 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2637 _gtk_text_iter_check (&iter);
2639 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2640 _gtk_text_btree_check (tree);
2647 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2648 GtkTextMark *existing_mark,
2650 gboolean left_gravity,
2651 const GtkTextIter *iter,
2652 gboolean should_exist)
2654 GtkTextLineSegment *seg;
2656 seg = real_set_mark (tree, existing_mark,
2657 name, left_gravity, iter, should_exist,
2660 return seg ? seg->body.mark.obj : NULL;
2664 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2668 GtkTextIter tmp_start, tmp_end;
2670 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2672 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2673 tree->selection_bound_mark);
2675 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2687 gtk_text_iter_order (&tmp_start, &tmp_end);
2700 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2701 const GtkTextIter *iter)
2703 GtkTextIter start, end;
2705 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2706 redisplay_region (tree, &start, &end);
2708 /* Move insert AND selection_bound before we redisplay */
2709 real_set_mark (tree, tree->insert_mark,
2710 "insert", FALSE, iter, TRUE, FALSE);
2711 real_set_mark (tree, tree->selection_bound_mark,
2712 "selection_bound", FALSE, iter, TRUE, FALSE);
2716 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2721 g_return_if_fail (tree != NULL);
2722 g_return_if_fail (name != NULL);
2724 mark = g_hash_table_lookup (tree->mark_table,
2727 _gtk_text_btree_remove_mark (tree, mark);
2731 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2732 GtkTextLineSegment *segment)
2735 if (segment->body.mark.name)
2736 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2738 segment->body.mark.tree = NULL;
2739 segment->body.mark.line = NULL;
2741 /* Remove the ref on the mark, which frees segment as a side effect
2742 * if this is the last reference.
2744 g_object_unref (G_OBJECT (segment->body.mark.obj));
2748 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2751 GtkTextLineSegment *segment;
2753 g_return_if_fail (mark != NULL);
2754 g_return_if_fail (tree != NULL);
2756 segment = mark->segment;
2758 if (segment->body.mark.not_deleteable)
2760 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2764 /* This calls cleanup_line and segments_changed */
2765 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2767 _gtk_text_btree_release_mark_segment (tree, segment);
2771 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2772 GtkTextMark *segment)
2774 return segment == tree->insert_mark;
2778 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2779 GtkTextMark *segment)
2781 return segment == tree->selection_bound_mark;
2785 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2788 GtkTextLineSegment *seg;
2790 g_return_val_if_fail (tree != NULL, NULL);
2791 g_return_val_if_fail (name != NULL, NULL);
2793 seg = g_hash_table_lookup (tree->mark_table, name);
2795 return seg ? seg->body.mark.obj : NULL;
2799 * gtk_text_mark_set_visible:
2800 * @mark: a #GtkTextMark
2801 * @setting: visibility of mark
2803 * Sets the visibility of @mark; the insertion point is normally
2804 * visible, i.e. you can see it as a vertical bar. Also, the text
2805 * widget uses a visible mark to indicate where a drop will occur when
2806 * dragging-and-dropping text. Most other marks are not visible.
2807 * Marks are not visible by default.
2811 gtk_text_mark_set_visible (GtkTextMark *mark,
2814 GtkTextLineSegment *seg;
2816 g_return_if_fail (mark != NULL);
2818 seg = mark->segment;
2820 if (seg->body.mark.visible == setting)
2824 seg->body.mark.visible = setting;
2826 redisplay_mark (seg);
2831 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2834 GtkTextBTreeNode *node;
2835 GtkTextTagInfo *info;
2837 g_return_val_if_fail (tree != NULL, NULL);
2841 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2846 if (info->tag_root == NULL)
2849 node = info->tag_root;
2851 /* We know the tag root has instances of the given
2854 continue_outer_loop:
2855 g_assert (node != NULL);
2856 while (node->level > 0)
2858 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2859 node = node->children.node;
2860 while (node != NULL)
2862 if (gtk_text_btree_node_has_tag (node, tag))
2863 goto continue_outer_loop;
2867 g_assert (node != NULL);
2870 g_assert (node != NULL); /* The tag summaries said some node had
2873 g_assert (node->level == 0);
2875 return node->children.line;
2879 /* Looking for any tag at all (tag == NULL).
2880 Unfortunately this can't be done in a simple and efficient way
2881 right now; so I'm just going to return the
2882 first line in the btree. FIXME */
2883 return _gtk_text_btree_get_line (tree, 0, NULL);
2888 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2891 GtkTextBTreeNode *node;
2892 GtkTextBTreeNode *last_node;
2894 GtkTextTagInfo *info;
2896 g_return_val_if_fail (tree != NULL, NULL);
2900 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2902 if (info->tag_root == NULL)
2905 node = info->tag_root;
2906 /* We know the tag root has instances of the given
2909 while (node->level > 0)
2911 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2913 node = node->children.node;
2914 while (node != NULL)
2916 if (gtk_text_btree_node_has_tag (node, tag))
2924 g_assert (node != NULL); /* The tag summaries said some node had
2927 g_assert (node->level == 0);
2929 /* Find the last line in this node */
2930 line = node->children.line;
2931 while (line->next != NULL)
2938 /* This search can't be done efficiently at the moment,
2939 at least not without complexity.
2940 So, we just return the last line.
2942 return _gtk_text_btree_get_end_iter_line (tree);
2952 _gtk_text_line_get_number (GtkTextLine *line)
2955 GtkTextBTreeNode *node, *parent, *node2;
2959 * First count how many lines precede this one in its level-0
2963 node = line->parent;
2965 for (line2 = node->children.line; line2 != line;
2966 line2 = line2->next)
2970 g_error ("gtk_text_btree_line_number couldn't find line");
2976 * Now work up through the levels of the tree one at a time,
2977 * counting how many lines are in GtkTextBTreeNodes preceding the current
2981 for (parent = node->parent ; parent != NULL;
2982 node = parent, parent = parent->parent)
2984 for (node2 = parent->children.node; node2 != node;
2985 node2 = node2->next)
2989 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2991 index += node2->num_lines;
2997 static GtkTextLineSegment*
2998 find_toggle_segment_before_char (GtkTextLine *line,
3002 GtkTextLineSegment *seg;
3003 GtkTextLineSegment *toggle_seg;
3008 seg = line->segments;
3009 while ( (index + seg->char_count) <= char_in_line )
3011 if (((seg->type == >k_text_toggle_on_type)
3012 || (seg->type == >k_text_toggle_off_type))
3013 && (seg->body.toggle.info->tag == tag))
3016 index += seg->char_count;
3023 static GtkTextLineSegment*
3024 find_toggle_segment_before_byte (GtkTextLine *line,
3028 GtkTextLineSegment *seg;
3029 GtkTextLineSegment *toggle_seg;
3034 seg = line->segments;
3035 while ( (index + seg->byte_count) <= byte_in_line )
3037 if (((seg->type == >k_text_toggle_on_type)
3038 || (seg->type == >k_text_toggle_off_type))
3039 && (seg->body.toggle.info->tag == tag))
3042 index += seg->byte_count;
3050 find_toggle_outside_current_line (GtkTextLine *line,
3054 GtkTextBTreeNode *node;
3055 GtkTextLine *sibling_line;
3056 GtkTextLineSegment *seg;
3057 GtkTextLineSegment *toggle_seg;
3059 GtkTextTagInfo *info = NULL;
3062 * No toggle in this line. Look for toggles for the tag in lines
3063 * that are predecessors of line but under the same
3064 * level-0 GtkTextBTreeNode.
3067 sibling_line = line->parent->children.line;
3068 while (sibling_line != line)
3070 seg = sibling_line->segments;
3073 if (((seg->type == >k_text_toggle_on_type)
3074 || (seg->type == >k_text_toggle_off_type))
3075 && (seg->body.toggle.info->tag == tag))
3081 sibling_line = sibling_line->next;
3084 if (toggle_seg != NULL)
3085 return (toggle_seg->type == >k_text_toggle_on_type);
3088 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3089 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3090 * siblings that precede that GtkTextBTreeNode.
3093 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3099 node = line->parent;
3100 while (node->parent != NULL)
3102 GtkTextBTreeNode *sibling_node;
3104 sibling_node = node->parent->children.node;
3105 while (sibling_node != node)
3109 summary = sibling_node->summary;
3110 while (summary != NULL)
3112 if (summary->info == info)
3113 toggles += summary->toggle_count;
3115 summary = summary->next;
3118 sibling_node = sibling_node->next;
3121 if (node == info->tag_root)
3124 node = node->parent;
3128 * An odd number of toggles means that the tag is present at the
3132 return (toggles & 1) != 0;
3135 /* FIXME this function is far too slow, for no good reason. */
3137 _gtk_text_line_char_has_tag (GtkTextLine *line,
3142 GtkTextLineSegment *toggle_seg;
3144 g_return_val_if_fail (line != NULL, FALSE);
3147 * Check for toggles for the tag in the line but before
3148 * the char. If there is one, its type indicates whether or
3149 * not the character is tagged.
3152 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3154 if (toggle_seg != NULL)
3155 return (toggle_seg->type == >k_text_toggle_on_type);
3157 return find_toggle_outside_current_line (line, tree, tag);
3161 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3166 GtkTextLineSegment *toggle_seg;
3168 g_return_val_if_fail (line != NULL, FALSE);
3171 * Check for toggles for the tag in the line but before
3172 * the char. If there is one, its type indicates whether or
3173 * not the character is tagged.
3176 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3178 if (toggle_seg != NULL)
3179 return (toggle_seg->type == >k_text_toggle_on_type);
3181 return find_toggle_outside_current_line (line, tree, tag);
3185 _gtk_text_line_is_last (GtkTextLine *line,
3188 return line == get_last_line (tree);
3192 ensure_end_iter_line (GtkTextBTree *tree)
3194 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3199 /* n_lines is without the magic line at the end */
3200 n_lines = _gtk_text_btree_line_count (tree);
3202 g_assert (n_lines >= 1);
3204 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3206 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3211 ensure_end_iter_segment (GtkTextBTree *tree)
3213 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3215 GtkTextLineSegment *seg;
3216 GtkTextLineSegment *last_with_chars;
3218 ensure_end_iter_line (tree);
3220 last_with_chars = NULL;
3222 seg = tree->end_iter_line->segments;
3225 if (seg->char_count > 0)
3226 last_with_chars = seg;
3230 tree->end_iter_segment = last_with_chars;
3232 /* We know the last char in the last line is '\n' */
3233 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3234 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3236 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3241 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3244 ensure_end_iter_line (tree);
3246 return line == tree->end_iter_line;
3250 _gtk_text_btree_is_end (GtkTextBTree *tree,
3252 GtkTextLineSegment *seg,
3256 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3258 /* Do this first to avoid walking segments in most cases */
3259 if (!_gtk_text_line_contains_end_iter (line, tree))
3262 ensure_end_iter_segment (tree);
3264 if (seg != tree->end_iter_segment)
3267 if (byte_index >= 0)
3268 return byte_index == tree->end_iter_segment_byte_index;
3270 return char_offset == tree->end_iter_segment_char_offset;
3274 _gtk_text_line_next (GtkTextLine *line)
3276 GtkTextBTreeNode *node;
3278 if (line->next != NULL)
3283 * This was the last line associated with the particular parent
3284 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3285 * then search down from that GtkTextBTreeNode to find the first
3289 node = line->parent;
3290 while (node != NULL && node->next == NULL)
3291 node = node->parent;
3297 while (node->level > 0)
3299 node = node->children.node;
3302 g_assert (node->children.line != line);
3304 return node->children.line;
3309 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3313 next = _gtk_text_line_next (line);
3315 /* If we were on the end iter line, we can't go to
3318 if (next && next->next == NULL && /* these checks are optimization only */
3319 _gtk_text_line_next (next) == NULL)
3326 _gtk_text_line_previous (GtkTextLine *line)
3328 GtkTextBTreeNode *node;
3329 GtkTextBTreeNode *node2;
3333 * Find the line under this GtkTextBTreeNode just before the starting line.
3335 prev = line->parent->children.line; /* First line at leaf */
3336 while (prev != line)
3338 if (prev->next == line)
3344 g_error ("gtk_text_btree_previous_line ran out of lines");
3348 * This was the first line associated with the particular parent
3349 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3350 * then search down from that GtkTextBTreeNode to find its last line.
3352 for (node = line->parent; ; node = node->parent)
3354 if (node == NULL || node->parent == NULL)
3356 else if (node != node->parent->children.node)
3360 for (node2 = node->parent->children.node; ;
3361 node2 = node2->children.node)
3363 while (node2->next != node)
3364 node2 = node2->next;
3366 if (node2->level == 0)
3372 for (prev = node2->children.line ; ; prev = prev->next)
3374 if (prev->next == NULL)
3378 g_assert_not_reached ();
3383 _gtk_text_line_add_data (GtkTextLine *line,
3384 GtkTextLineData *data)
3386 g_return_if_fail (line != NULL);
3387 g_return_if_fail (data != NULL);
3388 g_return_if_fail (data->view_id != NULL);
3392 data->next = line->views;
3402 _gtk_text_line_remove_data (GtkTextLine *line,
3405 GtkTextLineData *prev;
3406 GtkTextLineData *iter;
3408 g_return_val_if_fail (line != NULL, NULL);
3409 g_return_val_if_fail (view_id != NULL, NULL);
3413 while (iter != NULL)
3415 if (iter->view_id == view_id)
3424 prev->next = iter->next;
3426 line->views = iter->next;
3435 _gtk_text_line_get_data (GtkTextLine *line,
3438 GtkTextLineData *iter;
3440 g_return_val_if_fail (line != NULL, NULL);
3441 g_return_val_if_fail (view_id != NULL, NULL);
3444 while (iter != NULL)
3446 if (iter->view_id == view_id)
3455 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3456 GtkTextLineData *ld)
3458 /* For now this is totally unoptimized. FIXME?
3460 We could probably optimize the case where the width removed
3461 is less than the max width for the parent node,
3462 and the case where the height is unchanged when we re-wrap.
3465 g_return_if_fail (ld != NULL);
3468 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3472 _gtk_text_line_char_count (GtkTextLine *line)
3474 GtkTextLineSegment *seg;
3478 seg = line->segments;
3481 size += seg->char_count;
3488 _gtk_text_line_byte_count (GtkTextLine *line)
3490 GtkTextLineSegment *seg;
3494 seg = line->segments;
3497 size += seg->byte_count;
3505 _gtk_text_line_char_index (GtkTextLine *target_line)
3507 GSList *node_stack = NULL;
3508 GtkTextBTreeNode *iter;
3512 /* Push all our parent nodes onto a stack */
3513 iter = target_line->parent;
3515 g_assert (iter != NULL);
3517 while (iter != NULL)
3519 node_stack = g_slist_prepend (node_stack, iter);
3521 iter = iter->parent;
3524 /* Check that we have the root node on top of the stack. */
3525 g_assert (node_stack != NULL &&
3526 node_stack->data != NULL &&
3527 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3529 /* Add up chars in all nodes before the nodes in our stack.
3533 iter = node_stack->data;
3534 while (iter != NULL)
3536 GtkTextBTreeNode *child_iter;
3537 GtkTextBTreeNode *next_node;
3539 next_node = node_stack->next ?
3540 node_stack->next->data : NULL;
3541 node_stack = g_slist_remove (node_stack, node_stack->data);
3543 if (iter->level == 0)
3545 /* stack should be empty when we're on the last node */
3546 g_assert (node_stack == NULL);
3547 break; /* Our children are now lines */
3550 g_assert (next_node != NULL);
3551 g_assert (iter != NULL);
3552 g_assert (next_node->parent == iter);
3554 /* Add up chars before us in the tree */
3555 child_iter = iter->children.node;
3556 while (child_iter != next_node)
3558 g_assert (child_iter != NULL);
3560 num_chars += child_iter->num_chars;
3562 child_iter = child_iter->next;
3568 g_assert (iter != NULL);
3569 g_assert (iter == target_line->parent);
3571 /* Since we don't store char counts in lines, only in segments, we
3572 have to iterate over the lines adding up segment char counts
3573 until we find our line. */
3574 line = iter->children.line;
3575 while (line != target_line)
3577 g_assert (line != NULL);
3579 num_chars += _gtk_text_line_char_count (line);
3584 g_assert (line == target_line);
3590 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3594 GtkTextLineSegment *seg;
3597 g_return_val_if_fail (line != NULL, NULL);
3599 offset = byte_offset;
3600 seg = line->segments;
3602 while (offset >= seg->byte_count)
3604 g_assert (seg != NULL); /* means an invalid byte index */
3605 offset -= seg->byte_count;
3610 *seg_offset = offset;
3616 _gtk_text_line_char_to_segment (GtkTextLine *line,
3620 GtkTextLineSegment *seg;
3623 g_return_val_if_fail (line != NULL, NULL);
3625 offset = char_offset;
3626 seg = line->segments;
3628 while (offset >= seg->char_count)
3630 g_assert (seg != NULL); /* means an invalid char index */
3631 offset -= seg->char_count;
3636 *seg_offset = offset;
3642 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3646 GtkTextLineSegment *seg;
3649 g_return_val_if_fail (line != NULL, NULL);
3651 offset = byte_offset;
3652 seg = line->segments;
3654 while (offset > 0 && offset >= seg->byte_count)
3656 g_assert (seg != NULL); /* means an invalid byte index */
3657 offset -= seg->byte_count;
3662 *seg_offset = offset;
3668 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3672 GtkTextLineSegment *seg;
3675 g_return_val_if_fail (line != NULL, NULL);
3677 offset = char_offset;
3678 seg = line->segments;
3680 while (offset > 0 && offset >= seg->char_count)
3682 g_assert (seg != NULL); /* means an invalid byte index */
3683 offset -= seg->char_count;
3688 *seg_offset = offset;
3694 _gtk_text_line_byte_to_char (GtkTextLine *line,
3698 GtkTextLineSegment *seg;
3700 g_return_val_if_fail (line != NULL, 0);
3701 g_return_val_if_fail (byte_offset >= 0, 0);
3704 seg = line->segments;
3705 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3706 the next segment) */
3708 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3710 byte_offset -= seg->byte_count;
3711 char_offset += seg->char_count;
3716 g_assert (seg != NULL);
3718 /* Now byte_offset is the offset into the current segment,
3719 and char_offset is the start of the current segment.
3720 Optimize the case where no chars use > 1 byte */
3721 if (seg->byte_count == seg->char_count)
3722 return char_offset + byte_offset;
3725 if (seg->type == >k_text_char_type)
3726 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3729 g_assert (seg->char_count == 1);
3730 g_assert (byte_offset == 0);
3738 _gtk_text_line_char_to_byte (GtkTextLine *line,
3741 g_warning ("FIXME not implemented");
3746 /* FIXME sync with char_locate (or figure out a clean
3747 way to merge the two functions) */
3749 _gtk_text_line_byte_locate (GtkTextLine *line,
3751 GtkTextLineSegment **segment,
3752 GtkTextLineSegment **any_segment,
3753 gint *seg_byte_offset,
3754 gint *line_byte_offset)
3756 GtkTextLineSegment *seg;
3757 GtkTextLineSegment *after_prev_indexable;
3758 GtkTextLineSegment *after_last_indexable;
3759 GtkTextLineSegment *last_indexable;
3763 g_return_val_if_fail (line != NULL, FALSE);
3764 g_return_val_if_fail (byte_offset >= 0, FALSE);
3767 *any_segment = NULL;
3770 offset = byte_offset;
3772 last_indexable = NULL;
3773 after_last_indexable = line->segments;
3774 after_prev_indexable = line->segments;
3775 seg = line->segments;
3777 /* The loop ends when we're inside a segment;
3778 last_indexable refers to the last segment
3779 we passed entirely. */
3780 while (seg && offset >= seg->byte_count)
3782 if (seg->char_count > 0)
3784 offset -= seg->byte_count;
3785 bytes_in_line += seg->byte_count;
3786 last_indexable = seg;
3787 after_prev_indexable = after_last_indexable;
3788 after_last_indexable = last_indexable->next;
3796 /* We went off the end of the line */
3798 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3805 if (after_last_indexable != NULL)
3806 *any_segment = after_last_indexable;
3808 *any_segment = *segment;
3811 /* Override any_segment if we're in the middle of a segment. */
3813 *any_segment = *segment;
3815 *seg_byte_offset = offset;
3817 g_assert (*segment != NULL);
3818 g_assert (*any_segment != NULL);
3819 g_assert (*seg_byte_offset < (*segment)->byte_count);
3821 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3826 /* FIXME sync with byte_locate (or figure out a clean
3827 way to merge the two functions) */
3829 _gtk_text_line_char_locate (GtkTextLine *line,
3831 GtkTextLineSegment **segment,
3832 GtkTextLineSegment **any_segment,
3833 gint *seg_char_offset,
3834 gint *line_char_offset)
3836 GtkTextLineSegment *seg;
3837 GtkTextLineSegment *after_prev_indexable;
3838 GtkTextLineSegment *after_last_indexable;
3839 GtkTextLineSegment *last_indexable;
3843 g_return_val_if_fail (line != NULL, FALSE);
3844 g_return_val_if_fail (char_offset >= 0, FALSE);
3847 *any_segment = NULL;
3850 offset = char_offset;
3852 last_indexable = NULL;
3853 after_last_indexable = line->segments;
3854 after_prev_indexable = line->segments;
3855 seg = line->segments;
3857 /* The loop ends when we're inside a segment;
3858 last_indexable refers to the last segment
3859 we passed entirely. */
3860 while (seg && offset >= seg->char_count)
3862 if (seg->char_count > 0)
3864 offset -= seg->char_count;
3865 chars_in_line += seg->char_count;
3866 last_indexable = seg;
3867 after_prev_indexable = after_last_indexable;
3868 after_last_indexable = last_indexable->next;
3876 /* end of the line */
3878 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3885 if (after_last_indexable != NULL)
3886 *any_segment = after_last_indexable;
3888 *any_segment = *segment;
3891 /* Override any_segment if we're in the middle of a segment. */
3893 *any_segment = *segment;
3895 *seg_char_offset = offset;
3897 g_assert (*segment != NULL);
3898 g_assert (*any_segment != NULL);
3899 g_assert (*seg_char_offset < (*segment)->char_count);
3901 *line_char_offset = chars_in_line + *seg_char_offset;
3907 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3909 gint *line_char_offset,
3910 gint *seg_char_offset)
3912 GtkTextLineSegment *seg;
3915 g_return_if_fail (line != NULL);
3916 g_return_if_fail (byte_offset >= 0);
3918 *line_char_offset = 0;
3920 offset = byte_offset;
3921 seg = line->segments;
3923 while (offset >= seg->byte_count)
3925 offset -= seg->byte_count;
3926 *line_char_offset += seg->char_count;
3928 g_assert (seg != NULL); /* means an invalid char offset */
3931 g_assert (seg->char_count > 0); /* indexable. */
3933 /* offset is now the number of bytes into the current segment we
3934 * want to go. Count chars into the current segment.
3937 if (seg->type == >k_text_char_type)
3939 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3941 g_assert (*seg_char_offset < seg->char_count);
3943 *line_char_offset += *seg_char_offset;
3947 g_assert (offset == 0);
3948 *seg_char_offset = 0;
3953 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3955 gint *line_byte_offset,
3956 gint *seg_byte_offset)
3958 GtkTextLineSegment *seg;
3961 g_return_if_fail (line != NULL);
3962 g_return_if_fail (char_offset >= 0);
3964 *line_byte_offset = 0;
3966 offset = char_offset;
3967 seg = line->segments;
3969 while (offset >= seg->char_count)
3971 offset -= seg->char_count;
3972 *line_byte_offset += seg->byte_count;
3974 g_assert (seg != NULL); /* means an invalid char offset */
3977 g_assert (seg->char_count > 0); /* indexable. */
3979 /* offset is now the number of chars into the current segment we
3980 want to go. Count bytes into the current segment. */
3982 if (seg->type == >k_text_char_type)
3984 *seg_byte_offset = 0;
3988 const char * start = seg->body.chars + *seg_byte_offset;
3990 bytes = g_utf8_next_char (start) - start;
3991 *seg_byte_offset += bytes;
3995 g_assert (*seg_byte_offset < seg->byte_count);
3997 *line_byte_offset += *seg_byte_offset;
4001 g_assert (offset == 0);
4002 *seg_byte_offset = 0;
4007 node_compare (GtkTextBTreeNode *lhs,
4008 GtkTextBTreeNode *rhs)
4010 GtkTextBTreeNode *iter;
4011 GtkTextBTreeNode *node;
4012 GtkTextBTreeNode *common_parent;
4013 GtkTextBTreeNode *parent_of_lower;
4014 GtkTextBTreeNode *parent_of_higher;
4015 gboolean lhs_is_lower;
4016 GtkTextBTreeNode *lower;
4017 GtkTextBTreeNode *higher;
4019 /* This function assumes that lhs and rhs are not underneath each
4026 if (lhs->level < rhs->level)
4028 lhs_is_lower = TRUE;
4034 lhs_is_lower = FALSE;
4039 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4040 * of the common parent we used to reach the common parent; the
4041 * ordering of these child nodes in the child list is the ordering
4045 /* Get on the same level (may be on same level already) */
4047 while (node->level < higher->level)
4048 node = node->parent;
4050 g_assert (node->level == higher->level);
4052 g_assert (node != higher); /* Happens if lower is underneath higher */
4054 /* Go up until we have two children with a common parent.
4056 parent_of_lower = node;
4057 parent_of_higher = higher;
4059 while (parent_of_lower->parent != parent_of_higher->parent)
4061 parent_of_lower = parent_of_lower->parent;
4062 parent_of_higher = parent_of_higher->parent;
4065 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4067 common_parent = parent_of_lower->parent;
4069 g_assert (common_parent != NULL);
4071 /* See which is first in the list of common_parent's children */
4072 iter = common_parent->children.node;
4073 while (iter != NULL)
4075 if (iter == parent_of_higher)
4077 /* higher is less than lower */
4080 return 1; /* lhs > rhs */
4084 else if (iter == parent_of_lower)
4086 /* lower is less than higher */
4089 return -1; /* lhs < rhs */
4097 g_assert_not_reached ();
4101 /* remember that tag == NULL means "any tag" */
4103 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4107 GtkTextBTreeNode *node;
4108 GtkTextTagInfo *info;
4109 gboolean below_tag_root;
4111 g_return_val_if_fail (line != NULL, NULL);
4113 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4114 _gtk_text_btree_check (tree);
4118 /* Right now we can only offer linear-search if the user wants
4119 * to know about any tag toggle at all.
4121 return _gtk_text_line_next_excluding_last (line);
4124 /* Our tag summaries only have node precision, not line
4125 * precision. This means that if any line under a node could contain a
4126 * tag, then any of the others could also contain a tag.
4128 * In the future we could have some mechanism to keep track of how
4129 * many toggles we've found under a node so far, since we have a
4130 * count of toggles under the node. But for now I'm going with KISS.
4133 /* return same-node line, if any. */
4137 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4141 if (info->tag_root == NULL)
4144 if (info->tag_root == line->parent)
4145 return NULL; /* we were at the last line under the tag root */
4147 /* We need to go up out of this node, and on to the next one with
4148 toggles for the target tag. If we're below the tag root, we need to
4149 find the next node below the tag root that has tag summaries. If
4150 we're not below the tag root, we need to see if the tag root is
4151 after us in the tree, and if so, return the first line underneath
4154 node = line->parent;
4155 below_tag_root = FALSE;
4156 while (node != NULL)
4158 if (node == info->tag_root)
4160 below_tag_root = TRUE;
4164 node = node->parent;
4169 node = line->parent;
4170 while (node != info->tag_root)
4172 if (node->next == NULL)
4173 node = node->parent;
4178 if (gtk_text_btree_node_has_tag (node, tag))
4188 ordering = node_compare (line->parent, info->tag_root);
4192 /* Tag root is ahead of us, so search there. */
4193 node = info->tag_root;
4198 /* Tag root is after us, so no more lines that
4199 * could contain the tag.
4204 g_assert_not_reached ();
4209 g_assert (node != NULL);
4211 /* We have to find the first sub-node of this node that contains
4215 while (node->level > 0)
4217 g_assert (node != NULL); /* If this fails, it likely means an
4218 incorrect tag summary led us on a
4219 wild goose chase down this branch of
4221 node = node->children.node;
4222 while (node != NULL)
4224 if (gtk_text_btree_node_has_tag (node, tag))
4230 g_assert (node != NULL);
4231 g_assert (node->level == 0);
4233 return node->children.line;
4237 prev_line_under_node (GtkTextBTreeNode *node,
4242 prev = node->children.line;
4248 while (prev->next != line)
4258 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4262 GtkTextBTreeNode *node;
4263 GtkTextBTreeNode *found_node = NULL;
4264 GtkTextTagInfo *info;
4265 gboolean below_tag_root;
4267 GtkTextBTreeNode *line_ancestor;
4268 GtkTextBTreeNode *line_ancestor_parent;
4270 /* See next_could_contain_tag () for more extensive comments
4271 * on what's going on here.
4274 g_return_val_if_fail (line != NULL, NULL);
4276 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4277 _gtk_text_btree_check (tree);
4281 /* Right now we can only offer linear-search if the user wants
4282 * to know about any tag toggle at all.
4284 return _gtk_text_line_previous (line);
4287 /* Return same-node line, if any. */
4288 prev = prev_line_under_node (line->parent, line);
4292 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4296 if (info->tag_root == NULL)
4299 if (info->tag_root == line->parent)
4300 return NULL; /* we were at the first line under the tag root */
4302 /* Are we below the tag root */
4303 node = line->parent;
4304 below_tag_root = FALSE;
4305 while (node != NULL)
4307 if (node == info->tag_root)
4309 below_tag_root = TRUE;
4313 node = node->parent;
4318 /* Look for a previous node under this tag root that has our
4322 /* this assertion holds because line->parent is not the
4323 * tag root, we are below the tag root, and the tag
4326 g_assert (line->parent->parent != NULL);
4328 line_ancestor = line->parent;
4329 line_ancestor_parent = line->parent->parent;
4331 node = line_ancestor_parent->children.node;
4332 while (node != line_ancestor &&
4333 line_ancestor != info->tag_root)
4335 GSList *child_nodes = NULL;
4338 /* Create reverse-order list of nodes before
4341 while (node != line_ancestor
4344 child_nodes = g_slist_prepend (child_nodes, node);
4349 /* Try to find a node with our tag on it in the list */
4353 GtkTextBTreeNode *this_node = tmp->data;
4355 g_assert (this_node != line_ancestor);
4357 if (gtk_text_btree_node_has_tag (this_node, tag))
4359 found_node = this_node;
4360 g_slist_free (child_nodes);
4364 tmp = g_slist_next (tmp);
4367 g_slist_free (child_nodes);
4369 /* Didn't find anything on this level; go up one level. */
4370 line_ancestor = line_ancestor_parent;
4371 line_ancestor_parent = line_ancestor->parent;
4373 node = line_ancestor_parent->children.node;
4383 ordering = node_compare (line->parent, info->tag_root);
4387 /* Tag root is ahead of us, so no more lines
4394 /* Tag root is after us, so grab last tagged
4395 * line underneath the tag root.
4397 found_node = info->tag_root;
4401 g_assert_not_reached ();
4406 g_assert (found_node != NULL);
4408 /* We have to find the last sub-node of this node that contains
4413 while (node->level > 0)
4415 GSList *child_nodes = NULL;
4417 g_assert (node != NULL); /* If this fails, it likely means an
4418 incorrect tag summary led us on a
4419 wild goose chase down this branch of
4422 node = node->children.node;
4423 while (node != NULL)
4425 child_nodes = g_slist_prepend (child_nodes, node);
4429 node = NULL; /* detect failure to find a child node. */
4432 while (iter != NULL)
4434 if (gtk_text_btree_node_has_tag (iter->data, tag))
4436 /* recurse into this node. */
4441 iter = g_slist_next (iter);
4444 g_slist_free (child_nodes);
4446 g_assert (node != NULL);
4449 g_assert (node != NULL);
4450 g_assert (node->level == 0);
4452 /* this assertion is correct, but slow. */
4453 /* g_assert (node_compare (node, line->parent) < 0); */
4455 /* Return last line in this node. */
4457 prev = node->children.line;
4465 * Non-public function implementations
4469 summary_list_destroy (Summary *summary)
4472 while (summary != NULL)
4474 next = summary->next;
4475 summary_destroy (summary);
4481 get_last_line (GtkTextBTree *tree)
4483 if (tree->last_line_stamp != tree->chars_changed_stamp)
4489 n_lines = _gtk_text_btree_line_count (tree);
4491 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4493 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4495 tree->last_line_stamp = tree->chars_changed_stamp;
4496 tree->last_line = line;
4499 return tree->last_line;
4507 gtk_text_line_new (void)
4511 line = g_new0(GtkTextLine, 1);
4517 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4519 GtkTextLineData *ld;
4520 GtkTextLineData *next;
4522 g_return_if_fail (line != NULL);
4529 view = gtk_text_btree_get_view (tree, ld->view_id);
4531 g_assert (view != NULL);
4534 gtk_text_layout_free_line_data (view->layout, line, ld);
4543 gtk_text_line_set_parent (GtkTextLine *line,
4544 GtkTextBTreeNode *node)
4546 if (line->parent == node)
4548 line->parent = node;
4549 gtk_text_btree_node_invalidate_upward (node, NULL);
4553 cleanup_line (GtkTextLine *line)
4555 GtkTextLineSegment *seg, **prev_p;
4559 * Make a pass over all of the segments in the line, giving each
4560 * a chance to clean itself up. This could potentially change
4561 * the structure of the line, e.g. by merging two segments
4562 * together or having two segments cancel themselves; if so,
4563 * then repeat the whole process again, since the first structure
4564 * change might make other structure changes possible. Repeat
4565 * until eventually there are no changes.
4572 for (prev_p = &line->segments, seg = *prev_p;
4574 prev_p = &(*prev_p)->next, seg = *prev_p)
4576 if (seg->type->cleanupFunc != NULL)
4578 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4591 node_data_new (gpointer view_id)
4595 nd = g_new (NodeData, 1);
4597 nd->view_id = view_id;
4607 node_data_destroy (NodeData *nd)
4613 node_data_list_destroy (NodeData *nd)
4619 while (iter != NULL)
4622 node_data_destroy (iter);
4628 node_data_find (NodeData *nd, gpointer view_id)
4632 if (nd->view_id == view_id)
4640 summary_destroy (Summary *summary)
4642 /* Fill with error-triggering garbage */
4643 summary->info = (void*)0x1;
4644 summary->toggle_count = 567;
4645 summary->next = (void*)0x1;
4649 static GtkTextBTreeNode*
4650 gtk_text_btree_node_new (void)
4652 GtkTextBTreeNode *node;
4654 node = g_new (GtkTextBTreeNode, 1);
4656 node->node_data = NULL;
4662 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4663 GtkTextTagInfo *info,
4668 summary = node->summary;
4669 while (summary != NULL)
4671 if (summary->info == info)
4673 summary->toggle_count += adjust;
4677 summary = summary->next;
4680 if (summary == NULL)
4682 /* didn't find a summary for our tag. */
4683 g_return_if_fail (adjust > 0);
4684 summary = g_new (Summary, 1);
4685 summary->info = info;
4686 summary->toggle_count = adjust;
4687 summary->next = node->summary;
4688 node->summary = summary;
4692 /* Note that the tag root and above do not have summaries
4693 for the tag; only nodes below the tag root have
4696 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4700 summary = node->summary;
4701 while (summary != NULL)
4704 summary->info->tag == tag)
4707 summary = summary->next;
4713 /* Add node and all children to the damage region. */
4715 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4719 nd = node->node_data;
4726 if (node->level == 0)
4730 line = node->children.line;
4731 while (line != NULL)
4733 GtkTextLineData *ld;
4747 GtkTextBTreeNode *child;
4749 child = node->children.node;
4751 while (child != NULL)
4753 gtk_text_btree_node_invalidate_downward (child);
4755 child = child->next;
4761 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4763 GtkTextBTreeNode *iter;
4766 while (iter != NULL)
4772 nd = node_data_find (iter->node_data, view_id);
4774 if (nd == NULL || !nd->valid)
4775 break; /* Once a node is invalid, we know its parents are as well. */
4781 gboolean should_continue = FALSE;
4783 nd = iter->node_data;
4788 should_continue = TRUE;
4795 if (!should_continue)
4796 break; /* This node was totally invalidated, so are its
4800 iter = iter->parent;
4806 * _gtk_text_btree_is_valid:
4807 * @tree: a #GtkTextBTree
4808 * @view_id: ID for the view
4810 * Check to see if the entire #GtkTextBTree is valid or not for
4813 * Return value: %TRUE if the entire #GtkTextBTree is valid
4816 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4820 g_return_val_if_fail (tree != NULL, FALSE);
4822 nd = node_data_find (tree->root_node->node_data, view_id);
4823 return (nd && nd->valid);
4826 typedef struct _ValidateState ValidateState;
4828 struct _ValidateState
4830 gint remaining_pixels;
4831 gboolean in_validation;
4838 gtk_text_btree_node_validate (BTreeView *view,
4839 GtkTextBTreeNode *node,
4841 ValidateState *state)
4843 gint node_valid = TRUE;
4844 gint node_width = 0;
4845 gint node_height = 0;
4847 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4848 g_return_if_fail (!nd->valid);
4850 if (node->level == 0)
4852 GtkTextLine *line = node->children.line;
4853 GtkTextLineData *ld;
4855 /* Iterate over leading valid lines */
4856 while (line != NULL)
4858 ld = _gtk_text_line_get_data (line, view_id);
4860 if (!ld || !ld->valid)
4862 else if (state->in_validation)
4864 state->in_validation = FALSE;
4869 state->y += ld->height;
4870 node_width = MAX (ld->width, node_width);
4871 node_height += ld->height;
4877 state->in_validation = TRUE;
4879 /* Iterate over invalid lines */
4880 while (line != NULL)
4882 ld = _gtk_text_line_get_data (line, view_id);
4884 if (ld && ld->valid)
4889 state->old_height += ld->height;
4890 ld = gtk_text_layout_wrap (view->layout, line, ld);
4891 state->new_height += ld->height;
4893 node_width = MAX (ld->width, node_width);
4894 node_height += ld->height;
4896 state->remaining_pixels -= ld->height;
4897 if (state->remaining_pixels <= 0)
4907 /* Iterate over the remaining lines */
4908 while (line != NULL)
4910 ld = _gtk_text_line_get_data (line, view_id);
4911 state->in_validation = FALSE;
4913 if (!ld || !ld->valid)
4918 node_width = MAX (ld->width, node_width);
4919 node_height += ld->height;
4927 GtkTextBTreeNode *child;
4930 child = node->children.node;
4932 /* Iterate over leading valid nodes */
4935 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4937 if (!child_nd->valid)
4939 else if (state->in_validation)
4941 state->in_validation = FALSE;
4946 state->y += child_nd->height;
4947 node_width = MAX (node_width, child_nd->width);
4948 node_height += child_nd->height;
4951 child = child->next;
4954 /* Iterate over invalid nodes */
4957 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4959 if (child_nd->valid)
4963 gtk_text_btree_node_validate (view, child, view_id, state);
4965 if (!child_nd->valid)
4967 node_width = MAX (node_width, child_nd->width);
4968 node_height += child_nd->height;
4970 if (!state->in_validation || state->remaining_pixels <= 0)
4972 child = child->next;
4977 child = child->next;
4980 /* Iterate over the remaining lines */
4983 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4984 state->in_validation = FALSE;
4986 if (!child_nd->valid)
4989 node_width = MAX (child_nd->width, node_width);
4990 node_height += child_nd->height;
4992 child = child->next;
4996 nd->width = node_width;
4997 nd->height = node_height;
4998 nd->valid = node_valid;
5002 * _gtk_text_btree_validate:
5003 * @tree: a #GtkTextBTree
5005 * @max_pixels: the maximum number of pixels to validate. (No more
5006 * than one paragraph beyond this limit will be validated)
5007 * @y: location to store starting y coordinate of validated region
5008 * @old_height: location to store old height of validated region
5009 * @new_height: location to store new height of validated region
5011 * Validate a single contiguous invalid region of a #GtkTextBTree for
5014 * Return value: %TRUE if a region has been validated, %FALSE if the
5015 * entire tree was already valid.
5018 _gtk_text_btree_validate (GtkTextBTree *tree,
5027 g_return_val_if_fail (tree != NULL, FALSE);
5029 view = gtk_text_btree_get_view (tree, view_id);
5030 g_return_val_if_fail (view != NULL, FALSE);
5032 if (!_gtk_text_btree_is_valid (tree, view_id))
5034 ValidateState state;
5036 state.remaining_pixels = max_pixels;
5037 state.in_validation = FALSE;
5039 state.old_height = 0;
5040 state.new_height = 0;
5042 gtk_text_btree_node_validate (view,
5049 *old_height = state.old_height;
5051 *new_height = state.new_height;
5053 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5054 _gtk_text_btree_check (tree);
5063 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5067 gboolean *valid_out)
5071 gboolean valid = TRUE;
5073 if (node->level == 0)
5075 GtkTextLine *line = node->children.line;
5077 while (line != NULL)
5079 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5081 if (!ld || !ld->valid)
5086 width = MAX (ld->width, width);
5087 height += ld->height;
5095 GtkTextBTreeNode *child = node->children.node;
5099 NodeData *child_nd = node_data_find (child->node_data, view_id);
5101 if (!child_nd || !child_nd->valid)
5106 width = MAX (child_nd->width, width);
5107 height += child_nd->height;
5110 child = child->next;
5115 *height_out = height;
5120 /* Recompute the validity and size of the view data for a given
5121 * view at this node from the immediate children of the node
5124 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5127 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5132 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5133 &width, &height, &valid);
5135 nd->height = height;
5142 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5147 gtk_text_btree_node_check_valid (node, view_id);
5148 node = node->parent;
5153 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5156 if (node->level == 0)
5158 return gtk_text_btree_node_check_valid (node, view_id);
5162 GtkTextBTreeNode *child = node->children.node;
5164 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5172 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5174 if (!child_nd->valid)
5176 nd->width = MAX (child_nd->width, nd->width);
5177 nd->height += child_nd->height;
5179 child = child->next;
5188 * _gtk_text_btree_validate_line:
5189 * @tree: a #GtkTextBTree
5190 * @line: line to validate
5191 * @view_id: view ID for the view to validate
5193 * Revalidate a single line of the btree for the given view, propagate
5194 * results up through the entire tree.
5197 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5201 GtkTextLineData *ld;
5204 g_return_if_fail (tree != NULL);
5205 g_return_if_fail (line != NULL);
5207 view = gtk_text_btree_get_view (tree, view_id);
5208 g_return_if_fail (view != NULL);
5210 ld = _gtk_text_line_get_data (line, view_id);
5211 if (!ld || !ld->valid)
5213 ld = gtk_text_layout_wrap (view->layout, line, ld);
5215 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5220 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5222 if (node->level == 0)
5226 line = node->children.line;
5227 while (line != NULL)
5229 GtkTextLineData *ld;
5231 ld = _gtk_text_line_remove_data (line, view_id);
5234 gtk_text_layout_free_line_data (view->layout, line, ld);
5241 GtkTextBTreeNode *child;
5243 child = node->children.node;
5245 while (child != NULL)
5248 gtk_text_btree_node_remove_view (view, child, view_id);
5250 child = child->next;
5254 gtk_text_btree_node_remove_data (node, view_id);
5258 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5260 if (node->level == 0)
5263 GtkTextLineSegment *seg;
5265 while (node->children.line != NULL)
5267 line = node->children.line;
5268 node->children.line = line->next;
5269 while (line->segments != NULL)
5271 seg = line->segments;
5272 line->segments = seg->next;
5274 (*seg->type->deleteFunc) (seg, line, TRUE);
5276 gtk_text_line_destroy (tree, line);
5281 GtkTextBTreeNode *childPtr;
5283 while (node->children.node != NULL)
5285 childPtr = node->children.node;
5286 node->children.node = childPtr->next;
5287 gtk_text_btree_node_destroy (tree, childPtr);
5291 gtk_text_btree_node_free_empty (tree, node);
5295 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5296 GtkTextBTreeNode *node)
5298 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5299 (node->level == 0 && node->children.line == NULL));
5301 summary_list_destroy (node->summary);
5302 node_data_list_destroy (node->node_data);
5307 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5311 nd = node->node_data;
5314 if (nd->view_id == view_id)
5322 nd = node_data_new (view_id);
5324 if (node->node_data)
5325 nd->next = node->node_data;
5327 node->node_data = nd;
5334 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5340 nd = node->node_data;
5343 if (nd->view_id == view_id)
5354 prev->next = nd->next;
5356 if (node->node_data == nd)
5357 node->node_data = nd->next;
5361 node_data_destroy (nd);
5365 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5366 gint *width, gint *height)
5370 g_return_if_fail (width != NULL);
5371 g_return_if_fail (height != NULL);
5373 nd = gtk_text_btree_node_ensure_data (node, view_id);
5378 *height = nd->height;
5381 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5382 * here isn't quite right, since for a lot of operations we want to
5383 * know which children of the common parent correspond to the two nodes
5384 * (e.g., when computing the order of two iters)
5386 static GtkTextBTreeNode *
5387 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5388 GtkTextBTreeNode *node2)
5390 while (node1->level < node2->level)
5391 node1 = node1->parent;
5392 while (node2->level < node1->level)
5393 node2 = node2->parent;
5394 while (node1 != node2)
5396 node1 = node1->parent;
5397 node2 = node2->parent;
5408 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5413 while (view != NULL)
5415 if (view->view_id == view_id)
5424 get_tree_bounds (GtkTextBTree *tree,
5428 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5429 _gtk_text_btree_get_end_iter (tree, end);
5433 tag_changed_cb (GtkTextTagTable *table,
5435 gboolean size_changed,
5440 /* We need to queue a relayout on all regions that are tagged with
5447 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5449 /* Must be a last toggle if there was a first one. */
5450 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5451 _gtk_text_btree_invalidate_region (tree,
5458 /* We only need to queue a redraw, not a relayout */
5463 while (view != NULL)
5467 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5468 gtk_text_layout_changed (view->layout, 0, height, height);
5476 tag_removed_cb (GtkTextTagTable *table,
5480 /* Remove the tag from the tree */
5485 get_tree_bounds (tree, &start, &end);
5487 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5488 gtk_text_btree_remove_tag_info (tree, tag);
5492 /* Rebalance the out-of-whack node "node" */
5494 gtk_text_btree_rebalance (GtkTextBTree *tree,
5495 GtkTextBTreeNode *node)
5498 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5499 * up through the tree one GtkTextBTreeNode at a time until the root
5500 * GtkTextBTreeNode has been processed.
5503 while (node != NULL)
5505 GtkTextBTreeNode *new_node, *child;
5510 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5511 * then split off all but the first MIN_CHILDREN into a separate
5512 * GtkTextBTreeNode following the original one. Then repeat until the
5513 * GtkTextBTreeNode has a decent size.
5516 if (node->num_children > MAX_CHILDREN)
5521 * If the GtkTextBTreeNode being split is the root
5522 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5526 if (node->parent == NULL)
5528 new_node = gtk_text_btree_node_new ();
5529 new_node->parent = NULL;
5530 new_node->next = NULL;
5531 new_node->summary = NULL;
5532 new_node->level = node->level + 1;
5533 new_node->children.node = node;
5534 recompute_node_counts (tree, new_node);
5535 tree->root_node = new_node;
5537 new_node = gtk_text_btree_node_new ();
5538 new_node->parent = node->parent;
5539 new_node->next = node->next;
5540 node->next = new_node;
5541 new_node->summary = NULL;
5542 new_node->level = node->level;
5543 new_node->num_children = node->num_children - MIN_CHILDREN;
5544 if (node->level == 0)
5546 for (i = MIN_CHILDREN-1,
5547 line = node->children.line;
5548 i > 0; i--, line = line->next)
5550 /* Empty loop body. */
5552 new_node->children.line = line->next;
5557 for (i = MIN_CHILDREN-1,
5558 child = node->children.node;
5559 i > 0; i--, child = child->next)
5561 /* Empty loop body. */
5563 new_node->children.node = child->next;
5566 recompute_node_counts (tree, node);
5567 node->parent->num_children++;
5569 if (node->num_children <= MAX_CHILDREN)
5571 recompute_node_counts (tree, node);
5577 while (node->num_children < MIN_CHILDREN)
5579 GtkTextBTreeNode *other;
5580 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5581 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5582 int total_children, first_children, i;
5585 * Too few children for this GtkTextBTreeNode. If this is the root then,
5586 * it's OK for it to have less than MIN_CHILDREN children
5587 * as long as it's got at least two. If it has only one
5588 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5589 * the tree and use its child as the new root.
5592 if (node->parent == NULL)
5594 if ((node->num_children == 1) && (node->level > 0))
5596 tree->root_node = node->children.node;
5597 tree->root_node->parent = NULL;
5599 node->children.node = NULL;
5600 gtk_text_btree_node_free_empty (tree, node);
5606 * Not the root. Make sure that there are siblings to
5610 if (node->parent->num_children < 2)
5612 gtk_text_btree_rebalance (tree, node->parent);
5617 * Find a sibling neighbor to borrow from, and arrange for
5618 * node to be the earlier of the pair.
5621 if (node->next == NULL)
5623 for (other = node->parent->children.node;
5624 other->next != node;
5625 other = other->next)
5627 /* Empty loop body. */
5634 * We're going to either merge the two siblings together
5635 * into one GtkTextBTreeNode or redivide the children among them to
5636 * balance their loads. As preparation, join their two
5637 * child lists into a single list and remember the half-way
5638 * point in the list.
5641 total_children = node->num_children + other->num_children;
5642 first_children = total_children/2;
5643 if (node->children.node == NULL)
5645 node->children = other->children;
5646 other->children.node = NULL;
5647 other->children.line = NULL;
5649 if (node->level == 0)
5653 for (line = node->children.line, i = 1;
5655 line = line->next, i++)
5657 if (i == first_children)
5662 line->next = other->children.line;
5663 while (i <= first_children)
5672 GtkTextBTreeNode *child;
5674 for (child = node->children.node, i = 1;
5675 child->next != NULL;
5676 child = child->next, i++)
5678 if (i <= first_children)
5680 if (i == first_children)
5682 halfwaynode = child;
5686 child->next = other->children.node;
5687 while (i <= first_children)
5689 halfwaynode = child;
5690 child = child->next;
5696 * If the two siblings can simply be merged together, do it.
5699 if (total_children <= MAX_CHILDREN)
5701 recompute_node_counts (tree, node);
5702 node->next = other->next;
5703 node->parent->num_children--;
5705 other->children.node = NULL;
5706 other->children.line = NULL;
5707 gtk_text_btree_node_free_empty (tree, other);
5712 * The siblings can't be merged, so just divide their
5713 * children evenly between them.
5716 if (node->level == 0)
5718 other->children.line = halfwayline->next;
5719 halfwayline->next = NULL;
5723 other->children.node = halfwaynode->next;
5724 halfwaynode->next = NULL;
5727 recompute_node_counts (tree, node);
5728 recompute_node_counts (tree, other);
5731 node = node->parent;
5736 post_insert_fixup (GtkTextBTree *tree,
5738 gint line_count_delta,
5739 gint char_count_delta)
5742 GtkTextBTreeNode *node;
5745 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5746 * point, then rebalance the tree if necessary.
5749 for (node = line->parent ; node != NULL;
5750 node = node->parent)
5752 node->num_lines += line_count_delta;
5753 node->num_chars += char_count_delta;
5755 node = line->parent;
5756 node->num_children += line_count_delta;
5758 if (node->num_children > MAX_CHILDREN)
5760 gtk_text_btree_rebalance (tree, node);
5763 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5764 _gtk_text_btree_check (tree);
5767 static GtkTextTagInfo*
5768 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5771 GtkTextTagInfo *info;
5775 list = tree->tag_infos;
5776 while (list != NULL)
5779 if (info->tag == tag)
5782 list = g_slist_next (list);
5788 static GtkTextTagInfo*
5789 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5792 GtkTextTagInfo *info;
5794 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5798 /* didn't find it, create. */
5800 info = g_new (GtkTextTagInfo, 1);
5803 g_object_ref (G_OBJECT (tag));
5804 info->tag_root = NULL;
5805 info->toggle_count = 0;
5807 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5814 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5817 GtkTextTagInfo *info;
5822 list = tree->tag_infos;
5823 while (list != NULL)
5826 if (info->tag == tag)
5830 prev->next = list->next;
5834 tree->tag_infos = list->next;
5837 g_slist_free (list);
5839 g_object_unref (G_OBJECT (info->tag));
5845 list = g_slist_next (list);
5848 g_assert_not_reached ();
5853 recompute_level_zero_counts (GtkTextBTreeNode *node)
5856 GtkTextLineSegment *seg;
5858 g_assert (node->level == 0);
5860 line = node->children.line;
5861 while (line != NULL)
5863 node->num_children++;
5866 if (line->parent != node)
5867 gtk_text_line_set_parent (line, node);
5869 seg = line->segments;
5873 node->num_chars += seg->char_count;
5875 if (((seg->type != >k_text_toggle_on_type)
5876 && (seg->type != >k_text_toggle_off_type))
5877 || !(seg->body.toggle.inNodeCounts))
5883 GtkTextTagInfo *info;
5885 info = seg->body.toggle.info;
5887 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5898 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5901 GtkTextBTreeNode *child;
5903 g_assert (node->level > 0);
5905 child = node->children.node;
5906 while (child != NULL)
5908 node->num_children += 1;
5909 node->num_lines += child->num_lines;
5910 node->num_chars += child->num_chars;
5912 if (child->parent != node)
5914 child->parent = node;
5915 gtk_text_btree_node_invalidate_upward (node, NULL);
5918 summary = child->summary;
5919 while (summary != NULL)
5921 gtk_text_btree_node_adjust_toggle_count (node,
5923 summary->toggle_count);
5925 summary = summary->next;
5928 child = child->next;
5933 *----------------------------------------------------------------------
5935 * recompute_node_counts --
5937 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5938 * (tags, child information, etc.) by scanning the information in
5939 * its descendants. This procedure is called during rebalancing
5940 * when a GtkTextBTreeNode's child structure has changed.
5946 * The tag counts for node are modified to reflect its current
5947 * child structure, as are its num_children, num_lines, num_chars fields.
5948 * Also, all of the childrens' parent fields are made to point
5951 *----------------------------------------------------------------------
5955 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5958 Summary *summary, *summary2;
5961 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5962 * the existing Summary records (most of them will probably be reused).
5965 summary = node->summary;
5966 while (summary != NULL)
5968 summary->toggle_count = 0;
5969 summary = summary->next;
5972 node->num_children = 0;
5973 node->num_lines = 0;
5974 node->num_chars = 0;
5977 * Scan through the children, adding the childrens' tag counts into
5978 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5982 if (node->level == 0)
5983 recompute_level_zero_counts (node);
5985 recompute_level_nonzero_counts (node);
5990 gtk_text_btree_node_check_valid (node, view->view_id);
5995 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5996 * records that still have a zero count, or that have all the toggles.
5997 * The GtkTextBTreeNode with the children that account for all the tags toggles
5998 * have no summary information, and they become the tag_root for the tag.
6002 for (summary = node->summary; summary != NULL; )
6004 if (summary->toggle_count > 0 &&
6005 summary->toggle_count < summary->info->toggle_count)
6007 if (node->level == summary->info->tag_root->level)
6010 * The tag's root GtkTextBTreeNode split and some toggles left.
6011 * The tag root must move up a level.
6013 summary->info->tag_root = node->parent;
6016 summary = summary->next;
6019 if (summary->toggle_count == summary->info->toggle_count)
6022 * A GtkTextBTreeNode merge has collected all the toggles under
6023 * one GtkTextBTreeNode. Push the root down to this level.
6025 summary->info->tag_root = node;
6027 if (summary2 != NULL)
6029 summary2->next = summary->next;
6030 summary_destroy (summary);
6031 summary = summary2->next;
6035 node->summary = summary->next;
6036 summary_destroy (summary);
6037 summary = node->summary;
6043 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6044 GtkTextTagInfo *info,
6045 gint delta) /* may be negative */
6047 Summary *summary, *prevPtr;
6048 GtkTextBTreeNode *node2Ptr;
6049 int rootLevel; /* Level of original tag root */
6051 info->toggle_count += delta;
6053 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6055 info->tag_root = node;
6060 * Note the level of the existing root for the tag so we can detect
6061 * if it needs to be moved because of the toggle count change.
6064 rootLevel = info->tag_root->level;
6067 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6068 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6072 for ( ; node != info->tag_root; node = node->parent)
6075 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6076 * perhaps all we have to do is adjust its count.
6079 for (prevPtr = NULL, summary = node->summary;
6081 prevPtr = summary, summary = summary->next)
6083 if (summary->info == info)
6088 if (summary != NULL)
6090 summary->toggle_count += delta;
6091 if (summary->toggle_count > 0 &&
6092 summary->toggle_count < info->toggle_count)
6096 if (summary->toggle_count != 0)
6099 * Should never find a GtkTextBTreeNode with max toggle count at this
6100 * point (there shouldn't have been a summary entry in the
6104 g_error ("%s: bad toggle count (%d) max (%d)",
6105 G_STRLOC, summary->toggle_count, info->toggle_count);
6109 * Zero toggle count; must remove this tag from the list.
6112 if (prevPtr == NULL)
6114 node->summary = summary->next;
6118 prevPtr->next = summary->next;
6120 summary_destroy (summary);
6125 * This tag isn't currently in the summary information list.
6128 if (rootLevel == node->level)
6132 * The old tag root is at the same level in the tree as this
6133 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6134 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6135 * as well as the old root (if not, we'll move it up again
6136 * the next time through the loop). To push it up one level
6137 * we copy the original toggle count into the summary
6138 * information at the old root and change the root to its
6139 * parent GtkTextBTreeNode.
6142 GtkTextBTreeNode *rootnode = info->tag_root;
6143 summary = (Summary *) g_malloc (sizeof (Summary));
6144 summary->info = info;
6145 summary->toggle_count = info->toggle_count - delta;
6146 summary->next = rootnode->summary;
6147 rootnode->summary = summary;
6148 rootnode = rootnode->parent;
6149 rootLevel = rootnode->level;
6150 info->tag_root = rootnode;
6152 summary = (Summary *) g_malloc (sizeof (Summary));
6153 summary->info = info;
6154 summary->toggle_count = delta;
6155 summary->next = node->summary;
6156 node->summary = summary;
6161 * If we've decremented the toggle count, then it may be necessary
6162 * to push the tag root down one or more levels.
6169 if (info->toggle_count == 0)
6171 info->tag_root = (GtkTextBTreeNode *) NULL;
6174 node = info->tag_root;
6175 while (node->level > 0)
6178 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6179 * toggles. If so, push the root down one level.
6182 for (node2Ptr = node->children.node;
6183 node2Ptr != (GtkTextBTreeNode *)NULL ;
6184 node2Ptr = node2Ptr->next)
6186 for (prevPtr = NULL, summary = node2Ptr->summary;
6188 prevPtr = summary, summary = summary->next)
6190 if (summary->info == info)
6195 if (summary == NULL)
6199 if (summary->toggle_count != info->toggle_count)
6202 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6209 * This GtkTextBTreeNode has all the toggles, so push down the root.
6212 if (prevPtr == NULL)
6214 node2Ptr->summary = summary->next;
6218 prevPtr->next = summary->next;
6220 summary_destroy (summary);
6221 info->tag_root = node2Ptr;
6224 node = info->tag_root;
6229 *----------------------------------------------------------------------
6233 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6234 * increments the count for a particular tag, adding a new
6235 * entry for that tag if there wasn't one previously.
6241 * The information at *tagInfoPtr may be modified, and the arrays
6242 * may be reallocated to make them larger.
6244 *----------------------------------------------------------------------
6248 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6253 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6254 count > 0; tag_p++, count--)
6258 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6264 * There isn't currently an entry for this tag, so we have to
6265 * make a new one. If the arrays are full, then enlarge the
6269 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6271 GtkTextTag **newTags;
6272 int *newCounts, newSize;
6274 newSize = 2*tagInfoPtr->arraySize;
6275 newTags = (GtkTextTag **) g_malloc ((unsigned)
6276 (newSize*sizeof (GtkTextTag *)));
6277 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6278 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6279 g_free ((char *) tagInfoPtr->tags);
6280 tagInfoPtr->tags = newTags;
6281 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6282 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6283 tagInfoPtr->arraySize *sizeof (int));
6284 g_free ((char *) tagInfoPtr->counts);
6285 tagInfoPtr->counts = newCounts;
6286 tagInfoPtr->arraySize = newSize;
6289 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6290 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6291 tagInfoPtr->numTags++;
6295 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6296 const GtkTextIter *iter)
6298 GtkTextLineSegment *prev;
6302 line = _gtk_text_iter_get_text_line (iter);
6303 tree = _gtk_text_iter_get_btree (iter);
6305 prev = gtk_text_line_segment_split (iter);
6308 seg->next = line->segments;
6309 line->segments = seg;
6313 seg->next = prev->next;
6316 cleanup_line (line);
6317 segments_changed (tree);
6319 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6320 _gtk_text_btree_check (tree);
6324 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6325 GtkTextLineSegment *seg,
6328 GtkTextLineSegment *prev;
6330 if (line->segments == seg)
6332 line->segments = seg->next;
6336 for (prev = line->segments; prev->next != seg;
6339 /* Empty loop body. */
6341 prev->next = seg->next;
6343 cleanup_line (line);
6344 segments_changed (tree);
6348 * This is here because it requires BTree internals, it logically
6349 * belongs in gtktextsegment.c
6354 *--------------------------------------------------------------
6356 * _gtk_toggle_segment_check_func --
6358 * This procedure is invoked to perform consistency checks
6359 * on toggle segments.
6365 * If a consistency problem is found the procedure g_errors.
6367 *--------------------------------------------------------------
6371 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6377 if (segPtr->byte_count != 0)
6379 g_error ("toggle_segment_check_func: segment had non-zero size");
6381 if (!segPtr->body.toggle.inNodeCounts)
6383 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6385 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6386 for (summary = line->parent->summary; ;
6387 summary = summary->next)
6389 if (summary == NULL)
6393 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6400 if (summary->info == segPtr->body.toggle.info)
6404 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6416 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6417 GtkTextBTreeNode *node,
6427 while (view != NULL)
6429 if (view->view_id == nd->view_id)
6436 g_error ("Node has data for a view %p no longer attached to the tree",
6439 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6440 &width, &height, &valid);
6441 if (nd->width != width ||
6442 nd->height != height ||
6443 !nd->valid != !valid)
6445 g_error ("Node aggregates for view %p are invalid:\n"
6446 "Are (%d,%d,%s), should be (%d,%d,%s)",
6448 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6449 width, height, valid ? "TRUE" : "FALSE");
6454 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6455 GtkTextBTreeNode *node)
6457 GtkTextBTreeNode *childnode;
6458 Summary *summary, *summary2;
6460 GtkTextLineSegment *segPtr;
6461 int num_children, num_lines, num_chars, toggle_count, min_children;
6462 GtkTextLineData *ld;
6465 if (node->parent != NULL)
6467 min_children = MIN_CHILDREN;
6469 else if (node->level > 0)
6476 if ((node->num_children < min_children)
6477 || (node->num_children > MAX_CHILDREN))
6479 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6480 node->num_children);
6483 nd = node->node_data;
6486 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6493 if (node->level == 0)
6495 for (line = node->children.line; line != NULL;
6498 if (line->parent != node)
6500 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6502 if (line->segments == NULL)
6504 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6510 /* Just ensuring we don't segv while doing this loop */
6515 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6517 if (segPtr->type->checkFunc != NULL)
6519 (*segPtr->type->checkFunc)(segPtr, line);
6521 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6522 && (segPtr->next != NULL)
6523 && (segPtr->next->byte_count == 0)
6524 && (segPtr->next->type->leftGravity))
6526 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6528 if ((segPtr->next == NULL)
6529 && (segPtr->type != >k_text_char_type))
6531 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6534 num_chars += segPtr->char_count;
6543 for (childnode = node->children.node; childnode != NULL;
6544 childnode = childnode->next)
6546 if (childnode->parent != node)
6548 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6550 if (childnode->level != (node->level-1))
6552 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6553 node->level, childnode->level);
6555 gtk_text_btree_node_check_consistency (tree, childnode);
6556 for (summary = childnode->summary; summary != NULL;
6557 summary = summary->next)
6559 for (summary2 = node->summary; ;
6560 summary2 = summary2->next)
6562 if (summary2 == NULL)
6564 if (summary->info->tag_root == node)
6568 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6569 summary->info->tag->name,
6570 "present in parent summaries");
6572 if (summary->info == summary2->info)
6579 num_lines += childnode->num_lines;
6580 num_chars += childnode->num_chars;
6583 if (num_children != node->num_children)
6585 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6586 num_children, node->num_children);
6588 if (num_lines != node->num_lines)
6590 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6591 num_lines, node->num_lines);
6593 if (num_chars != node->num_chars)
6595 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6596 num_chars, node->num_chars);
6599 for (summary = node->summary; summary != NULL;
6600 summary = summary->next)
6602 if (summary->info->toggle_count == summary->toggle_count)
6604 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6605 summary->info->tag->name);
6608 if (node->level == 0)
6610 for (line = node->children.line; line != NULL;
6613 for (segPtr = line->segments; segPtr != NULL;
6614 segPtr = segPtr->next)
6616 if ((segPtr->type != >k_text_toggle_on_type)
6617 && (segPtr->type != >k_text_toggle_off_type))
6621 if (segPtr->body.toggle.info == summary->info)
6623 if (!segPtr->body.toggle.inNodeCounts)
6624 g_error ("Toggle segment not in the node counts");
6633 for (childnode = node->children.node;
6635 childnode = childnode->next)
6637 for (summary2 = childnode->summary;
6639 summary2 = summary2->next)
6641 if (summary2->info == summary->info)
6643 toggle_count += summary2->toggle_count;
6648 if (toggle_count != summary->toggle_count)
6650 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6651 toggle_count, summary->toggle_count);
6653 for (summary2 = summary->next; summary2 != NULL;
6654 summary2 = summary2->next)
6656 if (summary2->info == summary->info)
6658 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6659 summary->info->tag->name);
6666 listify_foreach (GtkTextTag *tag, gpointer user_data)
6668 GSList** listp = user_data;
6670 *listp = g_slist_prepend (*listp, tag);
6674 list_of_tags (GtkTextTagTable *table)
6676 GSList *list = NULL;
6678 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6684 _gtk_text_btree_check (GtkTextBTree *tree)
6687 GtkTextBTreeNode *node;
6689 GtkTextLineSegment *seg;
6691 GSList *taglist = NULL;
6693 GtkTextTagInfo *info;
6696 * Make sure that the tag toggle counts and the tag root pointers are OK.
6698 for (taglist = list_of_tags (tree->table);
6699 taglist != NULL ; taglist = taglist->next)
6701 tag = taglist->data;
6702 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6705 node = info->tag_root;
6708 if (info->toggle_count != 0)
6710 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6711 tag->name, info->toggle_count);
6713 continue; /* no ranges for the tag */
6715 else if (info->toggle_count == 0)
6717 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6720 else if (info->toggle_count & 1)
6722 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6723 tag->name, info->toggle_count);
6725 for (summary = node->summary; summary != NULL;
6726 summary = summary->next)
6728 if (summary->info->tag == tag)
6730 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6734 if (node->level > 0)
6736 for (node = node->children.node ; node != NULL ;
6739 for (summary = node->summary; summary != NULL;
6740 summary = summary->next)
6742 if (summary->info->tag == tag)
6744 count += summary->toggle_count;
6751 GtkTextLineSegmentClass * last = NULL;
6753 for (line = node->children.line ; line != NULL ;
6756 for (seg = line->segments; seg != NULL;
6759 if ((seg->type == >k_text_toggle_on_type ||
6760 seg->type == >k_text_toggle_off_type) &&
6761 seg->body.toggle.info->tag == tag)
6763 if (last == seg->type)
6764 g_error ("Two consecutive toggles on or off weren't merged");
6765 if (!seg->body.toggle.inNodeCounts)
6766 g_error ("Toggle segment not in the node counts");
6775 if (count != info->toggle_count)
6777 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6778 info->toggle_count, tag->name, count);
6783 g_slist_free (taglist);
6787 * Call a recursive procedure to do the main body of checks.
6790 node = tree->root_node;
6791 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6794 * Make sure that there are at least two lines in the text and
6795 * that the last line has no characters except a newline.
6798 if (node->num_lines < 2)
6800 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6802 if (node->num_chars < 2)
6804 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6806 while (node->level > 0)
6808 node = node->children.node;
6809 while (node->next != NULL)
6814 line = node->children.line;
6815 while (line->next != NULL)
6819 seg = line->segments;
6820 while ((seg->type == >k_text_toggle_off_type)
6821 || (seg->type == >k_text_right_mark_type)
6822 || (seg->type == >k_text_left_mark_type))
6825 * It's OK to toggle a tag off in the last line, but
6826 * not to start a new range. It's also OK to have marks
6832 if (seg->type != >k_text_char_type)
6834 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6836 if (seg->next != NULL)
6838 g_error ("_gtk_text_btree_check: last line has too many segments");
6840 if (seg->byte_count != 1)
6842 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6845 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6847 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6852 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6853 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6854 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6855 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6858 _gtk_text_btree_spew (GtkTextBTree *tree)
6863 printf ("%d lines in tree %p\n",
6864 _gtk_text_btree_line_count (tree), tree);
6866 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6868 while (line != NULL)
6870 _gtk_text_btree_spew_line (tree, line);
6871 line = _gtk_text_line_next (line);
6874 printf ("=================== Tag information\n");
6879 list = tree->tag_infos;
6881 while (list != NULL)
6883 GtkTextTagInfo *info;
6887 printf (" tag `%s': root at %p, toggle count %d\n",
6888 info->tag->name, info->tag_root, info->toggle_count);
6890 list = g_slist_next (list);
6893 if (tree->tag_infos == NULL)
6895 printf (" (no tags in the tree)\n");
6899 printf ("=================== Tree nodes\n");
6902 _gtk_text_btree_spew_node (tree->root_node, 0);
6907 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6910 GtkTextLineSegment *seg;
6912 spaces = g_strnfill (indent, ' ');
6914 printf ("%sline %p chars %d bytes %d\n",
6916 _gtk_text_line_char_count (line),
6917 _gtk_text_line_byte_count (line));
6919 seg = line->segments;
6922 if (seg->type == >k_text_char_type)
6924 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6929 if (*s == '\n' || *s == '\r')
6933 printf ("%s chars `%s'...\n", spaces, str);
6936 else if (seg->type == >k_text_right_mark_type)
6938 printf ("%s right mark `%s' visible: %d\n",
6940 seg->body.mark.name,
6941 seg->body.mark.visible);
6943 else if (seg->type == >k_text_left_mark_type)
6945 printf ("%s left mark `%s' visible: %d\n",
6947 seg->body.mark.name,
6948 seg->body.mark.visible);
6950 else if (seg->type == >k_text_toggle_on_type ||
6951 seg->type == >k_text_toggle_off_type)
6953 printf ("%s tag `%s' %s\n",
6954 spaces, seg->body.toggle.info->tag->name,
6955 seg->type == >k_text_toggle_off_type ? "off" : "on");
6965 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6968 GtkTextBTreeNode *iter;
6971 spaces = g_strnfill (indent, ' ');
6973 printf ("%snode %p level %d children %d lines %d chars %d\n",
6974 spaces, node, node->level,
6975 node->num_children, node->num_lines, node->num_chars);
6980 printf ("%s %d toggles of `%s' below this node\n",
6981 spaces, s->toggle_count, s->info->tag->name);
6987 if (node->level > 0)
6989 iter = node->children.node;
6990 while (iter != NULL)
6992 _gtk_text_btree_spew_node (iter, indent + 2);
6999 GtkTextLine *line = node->children.line;
7000 while (line != NULL)
7002 _gtk_text_btree_spew_line_short (line, indent + 2);
7010 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7012 GtkTextLineSegment * seg;
7014 printf ("%4d| line: %p parent: %p next: %p\n",
7015 _gtk_text_line_get_number (line), line, line->parent, line->next);
7017 seg = line->segments;
7021 _gtk_text_btree_spew_segment (tree, seg);
7027 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7029 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7030 seg, seg->type->name, seg->byte_count, seg->char_count);
7032 if (seg->type == >k_text_char_type)
7034 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7035 printf (" `%s'\n", str);
7038 else if (seg->type == >k_text_right_mark_type)
7040 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7041 seg->body.mark.name,
7042 seg->body.mark.visible,
7043 seg->body.mark.not_deleteable);
7045 else if (seg->type == >k_text_left_mark_type)
7047 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7048 seg->body.mark.name,
7049 seg->body.mark.visible,
7050 seg->body.mark.not_deleteable);
7052 else if (seg->type == >k_text_toggle_on_type ||
7053 seg->type == >k_text_toggle_off_type)
7055 printf (" tag `%s' priority %d\n",
7056 seg->body.toggle.info->tag->name,
7057 seg->body.toggle.info->tag->priority);