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"
60 #include "gtksignal.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
74 * The structure below is used to pass information between
75 * _gtk_text_btree_get_tags and inc_count:
78 typedef struct TagInfo {
79 int numTags; /* Number of tags for which there
80 * is currently information in
82 int arraySize; /* Number of entries allocated for
84 GtkTextTag **tags; /* Array of tags seen so far.
86 int *counts; /* Toggle count (so far) for each
87 * entry in tags. Malloc-ed. */
92 * This is used to store per-view width/height info at the tree nodes.
95 typedef struct _NodeData NodeData;
101 /* Height and width of this node */
105 /* boolean indicating whether the lines below this node are in need of validation.
106 * However, width/height should always represent the current total width and
107 * max height for lines below this node; the valid flag indicates whether the
108 * width/height on the lines needs recomputing, not whether the totals
116 * The data structure below keeps summary information about one tag as part
117 * of the tag information in a node.
120 typedef struct Summary {
121 GtkTextTagInfo *info; /* Handle for tag. */
122 int toggle_count; /* Number of transitions into or
123 * out of this tag that occur in
124 * the subtree rooted at this node. */
125 struct Summary *next; /* Next in list of all tags for same
126 * node, or NULL if at end of list. */
130 * The data structure below defines a node in the B-tree.
133 struct _GtkTextBTreeNode {
134 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
135 * this is the root. */
136 GtkTextBTreeNode *next; /* Next in list of siblings with the
137 * same parent node, or NULL for end
139 Summary *summary; /* First in malloc-ed list of info
140 * about tags in this subtree (NULL if
141 * no tag info in the subtree). */
142 int level; /* Level of this node in the B-tree.
143 * 0 refers to the bottom of the tree
144 * (children are lines, not nodes). */
145 union { /* First in linked list of children. */
146 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
147 GtkTextLine *line; /* Used if level == 0. */
149 int num_children; /* Number of children of this node. */
150 int num_lines; /* Total number of lines (leaves) in
151 * the subtree rooted here. */
152 int num_chars; /* Number of chars below here */
159 * Used to store the list of views in our btree
162 typedef struct _BTreeView BTreeView;
166 GtkTextLayout *layout;
172 * And the tree itself
175 struct _GtkTextBTree {
176 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
177 GtkTextTagTable *table;
178 GHashTable *mark_table;
180 GtkTextMark *insert_mark;
181 GtkTextMark *selection_bound_mark;
182 GtkTextBuffer *buffer;
185 guint tag_changed_handler;
187 /* Incremented when a segment with a byte size > 0
188 * is added to or removed from the tree (i.e. the
189 * length of a line may have changed, and lines may
190 * have been added or removed). This invalidates
191 * all outstanding iterators.
193 guint chars_changed_stamp;
194 /* Incremented when any segments are added or deleted;
195 * this makes outstanding iterators recalculate their
196 * pointed-to segment and segment offset.
198 guint segments_changed_stamp;
200 /* Cache the last line in the buffer */
201 GtkTextLine *last_line;
202 guint last_line_stamp;
204 /* Cache the next-to-last line in the buffer,
205 * containing the end iterator
207 GtkTextLine *end_iter_line;
208 GtkTextLineSegment *end_iter_segment;
209 int end_iter_segment_byte_index;
210 int end_iter_segment_char_offset;
211 guint end_iter_line_stamp;
212 guint end_iter_segment_stamp;
214 GHashTable *child_anchor_table;
219 * Upper and lower bounds on how many children a node may have:
220 * rebalance when either of these limits is exceeded. MAX_CHILDREN
221 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
224 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
225 shallow. It appears to be faster to locate a particular line number
226 if the tree is narrow and deep, since it is more finely sorted. I
227 guess this may increase memory use though, and make it slower to
228 walk the tree in order, or locate a particular byte index (which
229 is done by walking the tree in order).
231 There's basically a tradeoff here. However I'm thinking we want to
232 add pixels, byte counts, and char counts to the tree nodes,
233 at that point narrow and deep should speed up all operations,
234 not just the line number searches.
238 #define MAX_CHILDREN 12
239 #define MIN_CHILDREN 6
241 #define MAX_CHILDREN 6
242 #define MIN_CHILDREN 3
249 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
251 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
252 GtkTextBTreeNode *node);
253 static GtkTextLine * get_last_line (GtkTextBTree *tree);
254 static void post_insert_fixup (GtkTextBTree *tree,
255 GtkTextLine *insert_line,
256 gint char_count_delta,
257 gint line_count_delta);
258 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
259 GtkTextTagInfo *info,
261 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
264 static void segments_changed (GtkTextBTree *tree);
265 static void chars_changed (GtkTextBTree *tree);
266 static void summary_list_destroy (Summary *summary);
267 static GtkTextLine *gtk_text_line_new (void);
268 static void gtk_text_line_destroy (GtkTextBTree *tree,
270 static void gtk_text_line_set_parent (GtkTextLine *line,
271 GtkTextBTreeNode *node);
272 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
276 static NodeData *node_data_new (gpointer view_id);
277 static void node_data_destroy (NodeData *nd);
278 static void node_data_list_destroy (NodeData *nd);
279 static NodeData *node_data_find (NodeData *nd,
282 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
283 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
284 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
286 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
288 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
290 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
293 static void gtk_text_btree_node_remove_view (BTreeView *view,
294 GtkTextBTreeNode *node,
296 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
297 GtkTextBTreeNode *node);
298 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
299 GtkTextBTreeNode *node);
300 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
302 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
304 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
308 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
309 GtkTextBTreeNode *node2);
310 static void get_tree_bounds (GtkTextBTree *tree,
313 static void tag_changed_cb (GtkTextTagTable *table,
315 gboolean size_changed,
317 static void cleanup_line (GtkTextLine *line);
318 static void recompute_node_counts (GtkTextBTree *tree,
319 GtkTextBTreeNode *node);
320 static void inc_count (GtkTextTag *tag,
322 TagInfo *tagInfoPtr);
324 static void summary_destroy (Summary *summary);
326 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
327 const GtkTextIter *iter);
328 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
329 GtkTextLineSegment *seg,
333 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
335 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
337 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
340 static void redisplay_region (GtkTextBTree *tree,
341 const GtkTextIter *start,
342 const GtkTextIter *end);
344 /* Inline thingies */
347 segments_changed (GtkTextBTree *tree)
349 tree->segments_changed_stamp += 1;
353 chars_changed (GtkTextBTree *tree)
355 tree->chars_changed_stamp += 1;
363 _gtk_text_btree_new (GtkTextTagTable *table,
364 GtkTextBuffer *buffer)
367 GtkTextBTreeNode *root_node;
368 GtkTextLine *line, *line2;
370 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
371 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
374 * The tree will initially have two empty lines. The second line
375 * isn't actually part of the tree's contents, but its presence
376 * makes several operations easier. The tree will have one GtkTextBTreeNode,
377 * which is also the root of the tree.
380 /* Create the root node. */
382 root_node = gtk_text_btree_node_new ();
384 line = gtk_text_line_new ();
385 line2 = gtk_text_line_new ();
387 root_node->parent = NULL;
388 root_node->next = NULL;
389 root_node->summary = NULL;
390 root_node->level = 0;
391 root_node->children.line = line;
392 root_node->num_children = 2;
393 root_node->num_lines = 2;
394 root_node->num_chars = 2;
396 line->parent = root_node;
399 line->segments = _gtk_char_segment_new ("\n", 1);
401 line2->parent = root_node;
403 line2->segments = _gtk_char_segment_new ("\n", 1);
405 /* Create the tree itself */
407 tree = g_new0(GtkTextBTree, 1);
408 tree->root_node = root_node;
412 /* Set these to values that are unlikely to be found
413 * in random memory garbage, and also avoid
414 * duplicates between tree instances.
416 tree->chars_changed_stamp = g_random_int ();
417 tree->segments_changed_stamp = g_random_int ();
419 tree->last_line_stamp = tree->chars_changed_stamp - 1;
420 tree->last_line = NULL;
422 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
423 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
424 tree->end_iter_line = NULL;
425 tree->end_iter_segment_byte_index = 0;
426 tree->end_iter_segment_char_offset = 0;
428 g_object_ref (G_OBJECT (tree->table));
430 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
432 G_CALLBACK (tag_changed_cb),
435 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
436 tree->child_anchor_table = NULL;
438 /* We don't ref the buffer, since the buffer owns us;
439 * we'd have some circularity issues. The buffer always
440 * lasts longer than the BTree
442 tree->buffer = buffer;
446 GtkTextLineSegment *seg;
448 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
451 tree->insert_mark = _gtk_text_btree_set_mark (tree,
458 seg = tree->insert_mark->segment;
460 seg->body.mark.not_deleteable = TRUE;
461 seg->body.mark.visible = TRUE;
463 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
470 seg = tree->selection_bound_mark->segment;
472 seg->body.mark.not_deleteable = TRUE;
474 g_object_ref (G_OBJECT (tree->insert_mark));
475 g_object_ref (G_OBJECT (tree->selection_bound_mark));
484 _gtk_text_btree_ref (GtkTextBTree *tree)
486 g_return_if_fail (tree != NULL);
487 g_return_if_fail (tree->refcount > 0);
493 _gtk_text_btree_unref (GtkTextBTree *tree)
495 g_return_if_fail (tree != NULL);
496 g_return_if_fail (tree->refcount > 0);
500 if (tree->refcount == 0)
502 gtk_text_btree_node_destroy (tree, tree->root_node);
504 g_assert (g_hash_table_size (tree->mark_table) == 0);
505 g_hash_table_destroy (tree->mark_table);
507 g_object_unref (G_OBJECT (tree->insert_mark));
508 g_object_unref (G_OBJECT (tree->selection_bound_mark));
510 g_signal_handler_disconnect (G_OBJECT (tree->table),
511 tree->tag_changed_handler);
513 g_object_unref (G_OBJECT (tree->table));
520 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
526 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
528 return tree->chars_changed_stamp;
532 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
534 return tree->segments_changed_stamp;
538 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
540 g_return_if_fail (tree != NULL);
541 segments_changed (tree);
545 * Indexable segment mutation
549 _gtk_text_btree_delete (GtkTextIter *start,
552 GtkTextLineSegment *prev_seg; /* The segment just before the start
553 * of the deletion range. */
554 GtkTextLineSegment *last_seg; /* The segment just after the end
555 * of the deletion range. */
556 GtkTextLineSegment *seg, *next;
557 GtkTextLine *curline;
558 GtkTextBTreeNode *curnode, *node;
560 GtkTextLine *start_line;
561 GtkTextLine *end_line;
562 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
563 gint start_byte_offset;
565 g_return_if_fail (start != NULL);
566 g_return_if_fail (end != NULL);
567 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
568 _gtk_text_iter_get_btree (end));
570 gtk_text_iter_order (start, end);
572 tree = _gtk_text_iter_get_btree (start);
574 if (gtk_debug_flags & GTK_DEBUG_TEXT)
575 _gtk_text_btree_check (tree);
577 /* Broadcast the need for redisplay before we break the iterators */
578 _gtk_text_btree_invalidate_region (tree, start, end);
580 /* Save the byte offset so we can reset the iterators */
581 start_byte_offset = gtk_text_iter_get_line_index (start);
583 start_line = _gtk_text_iter_get_text_line (start);
584 end_line = _gtk_text_iter_get_text_line (end);
587 * Split the start and end segments, so we have a place
588 * to insert our new text.
590 * Tricky point: split at end first; otherwise the split
591 * at end may invalidate seg and/or prev_seg. This allows
592 * us to avoid invalidating segments for start.
595 last_seg = gtk_text_line_segment_split (end);
596 if (last_seg != NULL)
597 last_seg = last_seg->next;
599 last_seg = end_line->segments;
601 prev_seg = gtk_text_line_segment_split (start);
602 if (prev_seg != NULL)
604 seg = prev_seg->next;
605 prev_seg->next = last_seg;
609 seg = start_line->segments;
610 start_line->segments = last_seg;
613 /* notify iterators that their segments need recomputation,
614 just for robustness. */
615 segments_changed (tree);
618 * Delete all of the segments between prev_seg and last_seg.
621 curline = start_line;
622 curnode = curline->parent;
623 while (seg != last_seg)
629 GtkTextLine *nextline;
632 * We just ran off the end of a line. First find the
633 * next line, then go back to the old line and delete it
634 * (unless it's the starting line for the range).
637 nextline = _gtk_text_line_next (curline);
638 if (curline != start_line)
640 if (curnode == start_line->parent)
641 start_line->next = curline->next;
643 curnode->children.line = curline->next;
645 for (node = curnode; node != NULL;
648 /* Don't update node->num_chars, because
649 * that was done when we deleted the segments.
651 node->num_lines -= 1;
654 curnode->num_children -= 1;
655 curline->next = deleted_lines;
656 deleted_lines = curline;
660 seg = curline->segments;
663 * If the GtkTextBTreeNode is empty then delete it and its parents,
664 * recursively upwards until a non-empty GtkTextBTreeNode is found.
667 while (curnode->num_children == 0)
669 GtkTextBTreeNode *parent;
671 parent = curnode->parent;
672 if (parent->children.node == curnode)
674 parent->children.node = curnode->next;
678 GtkTextBTreeNode *prevnode = parent->children.node;
679 while (prevnode->next != curnode)
681 prevnode = prevnode->next;
683 prevnode->next = curnode->next;
685 parent->num_children--;
686 gtk_text_btree_node_free_empty (tree, curnode);
689 curnode = curline->parent;
694 char_count = seg->char_count;
696 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
699 * This segment refuses to die. Move it to prev_seg and
700 * advance prev_seg if the segment has left gravity.
703 if (prev_seg == NULL)
705 seg->next = start_line->segments;
706 start_line->segments = seg;
710 seg->next = prev_seg->next;
711 prev_seg->next = seg;
713 if (seg->type->leftGravity)
720 /* Segment is gone. Decrement the char count of the node and
722 for (node = curnode; node != NULL;
725 node->num_chars -= char_count;
733 * If the beginning and end of the deletion range are in different
734 * lines, join the two lines together and discard the ending line.
737 if (start_line != end_line)
740 GtkTextBTreeNode *ancestor_node;
741 GtkTextLine *prevline;
744 /* last_seg was appended to start_line up at the top of this function */
746 for (seg = last_seg; seg != NULL;
749 chars_moved += seg->char_count;
750 if (seg->type->lineChangeFunc != NULL)
752 (*seg->type->lineChangeFunc)(seg, end_line);
756 for (node = start_line->parent; node != NULL;
759 node->num_chars += chars_moved;
762 curnode = end_line->parent;
763 for (node = curnode; node != NULL;
766 node->num_chars -= chars_moved;
769 curnode->num_children--;
770 prevline = curnode->children.line;
771 if (prevline == end_line)
773 curnode->children.line = end_line->next;
777 while (prevline->next != end_line)
779 prevline = prevline->next;
781 prevline->next = end_line->next;
783 end_line->next = deleted_lines;
784 deleted_lines = end_line;
786 /* We now fix up the per-view aggregates. We add all the height and
787 * width for the deleted lines to the start line, so that when revalidation
788 * occurs, the correct change in size is seen.
790 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
797 gint deleted_width = 0;
798 gint deleted_height = 0;
800 line = deleted_lines;
803 GtkTextLine *next_line = line->next;
804 ld = _gtk_text_line_get_data (line, view->view_id);
808 deleted_width = MAX (deleted_width, ld->width);
809 deleted_height += ld->height;
813 gtk_text_line_destroy (tree, line);
818 if (deleted_width > 0 || deleted_height > 0)
820 ld = _gtk_text_line_get_data (start_line, view->view_id);
824 /* This means that start_line has never been validated.
825 * We don't really want to do the validation here but
826 * we do need to store our temporary sizes. So we
827 * create the line data and assume a line w/h of 0.
829 ld = _gtk_text_line_data_new (view->layout, line);
830 _gtk_text_line_add_data (line, ld);
836 ld->width = MAX (deleted_width, ld->width);
837 ld->height += deleted_height;
841 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
842 if (ancestor_node->parent)
843 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
848 /* avoid dangling pointer */
849 deleted_lines = NULL;
851 gtk_text_btree_rebalance (tree, curnode);
855 * Cleanup the segments in the new line.
858 cleanup_line (start_line);
861 * Lastly, rebalance the first GtkTextBTreeNode of the range.
864 gtk_text_btree_rebalance (tree, start_line->parent);
866 /* Notify outstanding iterators that they
868 chars_changed (tree);
869 segments_changed (tree);
871 if (gtk_debug_flags & GTK_DEBUG_TEXT)
872 _gtk_text_btree_check (tree);
874 /* Re-initialize our iterators */
875 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
880 _gtk_text_btree_insert (GtkTextIter *iter,
884 GtkTextLineSegment *prev_seg; /* The segment just before the first
885 * new segment (NULL means new segment
886 * is at beginning of line). */
887 GtkTextLineSegment *cur_seg; /* Current segment; new characters
888 * are inserted just after this one.
889 * NULL means insert at beginning of
891 GtkTextLine *line; /* Current line (new segments are
892 * added to this line). */
893 GtkTextLineSegment *seg;
894 GtkTextLine *newline;
895 int chunk_len; /* # characters in current chunk. */
896 gint sol; /* start of line */
897 gint eol; /* Pointer to character just after last
898 * one in current chunk.
900 gint delim; /* index of paragraph delimiter */
901 int line_count_delta; /* Counts change to total number of
905 int char_count_delta; /* change to number of chars */
907 gint start_byte_index;
908 GtkTextLine *start_line;
910 g_return_if_fail (text != NULL);
911 g_return_if_fail (iter != NULL);
916 /* extract iterator info */
917 tree = _gtk_text_iter_get_btree (iter);
918 line = _gtk_text_iter_get_text_line (iter);
921 start_byte_index = gtk_text_iter_get_line_index (iter);
923 /* Get our insertion segment split. Note this assumes line allows
924 * char insertions, which isn't true of the "last" line. But iter
925 * should not be on that line, as we assert here.
927 g_assert (!_gtk_text_line_is_last (line, tree));
928 prev_seg = gtk_text_line_segment_split (iter);
931 /* Invalidate all iterators */
932 chars_changed (tree);
933 segments_changed (tree);
936 * Chop the text up into lines and create a new segment for
937 * each line, plus a new line for the leftovers from the
943 line_count_delta = 0;
944 char_count_delta = 0;
949 pango_find_paragraph_boundary (text + sol,
954 /* make these relative to the start of the text */
958 g_assert (eol >= sol);
959 g_assert (delim >= sol);
960 g_assert (eol >= delim);
962 g_assert (eol <= len);
964 chunk_len = eol - sol;
966 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
967 seg = _gtk_char_segment_new (&text[sol], chunk_len);
969 char_count_delta += seg->char_count;
973 seg->next = line->segments;
974 line->segments = seg;
978 seg->next = cur_seg->next;
984 /* chunk didn't end with a paragraph separator */
985 g_assert (eol == len);
990 * The chunk ended with a newline, so create a new GtkTextLine
991 * and move the remainder of the old line to it.
994 newline = gtk_text_line_new ();
995 gtk_text_line_set_parent (newline, line->parent);
996 newline->next = line->next;
997 line->next = newline;
998 newline->segments = seg->next;
1006 * Cleanup the starting line for the insertion, plus the ending
1007 * line if it's different.
1010 cleanup_line (start_line);
1011 if (line != start_line)
1013 cleanup_line (line);
1016 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1018 /* Invalidate our region, and reset the iterator the user
1019 passed in to point to the end of the inserted text. */
1025 _gtk_text_btree_get_iter_at_line (tree,
1031 /* We could almost certainly be more efficient here
1032 by saving the information from the insertion loop
1034 gtk_text_iter_forward_chars (&end, char_count_delta);
1036 _gtk_text_btree_invalidate_region (tree,
1040 /* Convenience for the user */
1046 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1047 GtkTextLineSegment *seg)
1051 GtkTextLineSegment *prevPtr;
1054 gint start_byte_offset;
1056 line = _gtk_text_iter_get_text_line (iter);
1057 tree = _gtk_text_iter_get_btree (iter);
1058 start_byte_offset = gtk_text_iter_get_line_index (iter);
1060 prevPtr = gtk_text_line_segment_split (iter);
1061 if (prevPtr == NULL)
1063 seg->next = line->segments;
1064 line->segments = seg;
1068 seg->next = prevPtr->next;
1069 prevPtr->next = seg;
1072 post_insert_fixup (tree, line, 0, seg->char_count);
1074 chars_changed (tree);
1075 segments_changed (tree);
1077 /* reset *iter for the user, and invalidate tree nodes */
1079 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1082 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1084 _gtk_text_btree_invalidate_region (tree, &start, iter);
1088 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1091 GtkTextLineSegment *seg;
1093 seg = _gtk_pixbuf_segment_new (pixbuf);
1095 insert_pixbuf_or_widget_segment (iter, seg);
1099 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1100 GtkTextChildAnchor *anchor)
1102 GtkTextLineSegment *seg;
1105 if (anchor->segment != NULL)
1107 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1111 seg = _gtk_widget_segment_new (anchor);
1113 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1114 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1116 insert_pixbuf_or_widget_segment (iter, seg);
1118 if (tree->child_anchor_table == NULL)
1119 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1121 g_hash_table_insert (tree->child_anchor_table,
1122 seg->body.child.obj,
1123 seg->body.child.obj);
1127 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1129 GtkTextLineSegment *seg;
1131 seg = anchor->segment;
1133 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1142 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1143 GtkTextBTreeNode *node, gint y, gint *line_top,
1144 GtkTextLine *last_line)
1148 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1149 _gtk_text_btree_check (tree);
1151 if (node->level == 0)
1155 line = node->children.line;
1157 while (line != NULL && line != last_line)
1159 GtkTextLineData *ld;
1161 ld = _gtk_text_line_get_data (line, view->view_id);
1165 if (y < (current_y + (ld ? ld->height : 0)))
1168 current_y += ld->height;
1169 *line_top += ld->height;
1178 GtkTextBTreeNode *child;
1180 child = node->children.node;
1182 while (child != NULL)
1187 gtk_text_btree_node_get_size (child, view->view_id,
1190 if (y < (current_y + height))
1191 return find_line_by_y (tree, view, child,
1192 y - current_y, line_top,
1195 current_y += height;
1196 *line_top += height;
1198 child = child->next;
1206 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1213 GtkTextLine *last_line;
1216 view = gtk_text_btree_get_view (tree, view_id);
1217 g_return_val_if_fail (view != NULL, NULL);
1219 last_line = get_last_line (tree);
1221 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1225 *line_top_out = line_top;
1231 find_line_top_in_line_list (GtkTextBTree *tree,
1234 GtkTextLine *target_line,
1237 while (line != NULL)
1239 GtkTextLineData *ld;
1241 if (line == target_line)
1244 ld = _gtk_text_line_get_data (line, view->view_id);
1251 g_assert_not_reached (); /* If we get here, our
1252 target line didn't exist
1253 under its parent node */
1258 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1259 GtkTextLine *target_line,
1266 GtkTextBTreeNode *node;
1268 view = gtk_text_btree_get_view (tree, view_id);
1270 g_return_val_if_fail (view != NULL, 0);
1273 node = target_line->parent;
1274 while (node != NULL)
1276 nodes = g_slist_prepend (nodes, node);
1277 node = node->parent;
1281 while (iter != NULL)
1285 if (node->level == 0)
1287 g_slist_free (nodes);
1288 return find_line_top_in_line_list (tree, view,
1289 node->children.line,
1294 GtkTextBTreeNode *child;
1295 GtkTextBTreeNode *target_node;
1297 g_assert (iter->next != NULL); /* not at level 0 */
1298 target_node = iter->next->data;
1300 child = node->children.node;
1302 while (child != NULL)
1307 if (child == target_node)
1311 gtk_text_btree_node_get_size (child, view->view_id,
1315 child = child->next;
1317 g_assert (child != NULL); /* should have broken out before we
1321 iter = g_slist_next (iter);
1324 g_assert_not_reached (); /* we return when we find the target line */
1329 _gtk_text_btree_add_view (GtkTextBTree *tree,
1330 GtkTextLayout *layout)
1333 GtkTextLine *last_line;
1334 GtkTextLineData *line_data;
1336 g_return_if_fail (tree != NULL);
1338 view = g_new (BTreeView, 1);
1340 view->view_id = layout;
1341 view->layout = layout;
1343 view->next = tree->views;
1348 g_assert (tree->views->prev == NULL);
1349 tree->views->prev = view;
1354 /* The last line in the buffer has identity values for the per-view
1355 * data so that we can avoid special case checks for it in a large
1358 last_line = get_last_line (tree);
1360 line_data = g_new (GtkTextLineData, 1);
1361 line_data->view_id = layout;
1362 line_data->next = NULL;
1363 line_data->width = 0;
1364 line_data->height = 0;
1365 line_data->valid = TRUE;
1367 _gtk_text_line_add_data (last_line, line_data);
1371 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1375 GtkTextLine *last_line;
1376 GtkTextLineData *line_data;
1378 g_return_if_fail (tree != NULL);
1382 while (view != NULL)
1384 if (view->view_id == view_id)
1390 g_return_if_fail (view != NULL);
1393 view->next->prev = view->prev;
1396 view->prev->next = view->next;
1398 if (view == tree->views)
1399 tree->views = view->next;
1401 /* Remove the line data for the last line which we added ourselves.
1402 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1404 last_line = get_last_line (tree);
1405 line_data = _gtk_text_line_remove_data (last_line, view_id);
1408 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1410 view->layout = (gpointer) 0xdeadbeef;
1411 view->view_id = (gpointer) 0xdeadbeef;
1417 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1418 const GtkTextIter *start,
1419 const GtkTextIter *end)
1425 while (view != NULL)
1427 gtk_text_layout_invalidate (view->layout, start, end);
1434 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1439 g_return_if_fail (tree != NULL);
1440 g_return_if_fail (view_id != NULL);
1442 gtk_text_btree_node_get_size (tree->root_node, view_id,
1457 iter_stack_new (void)
1460 stack = g_new (IterStack, 1);
1461 stack->iters = NULL;
1468 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1471 if (stack->count > stack->alloced)
1473 stack->alloced = stack->count*2;
1474 stack->iters = g_realloc (stack->iters,
1475 stack->alloced*sizeof (GtkTextIter));
1477 stack->iters[stack->count-1] = *iter;
1481 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1483 if (stack->count == 0)
1488 *iter = stack->iters[stack->count];
1494 iter_stack_free (IterStack *stack)
1496 g_free (stack->iters);
1501 iter_stack_invert (IterStack *stack)
1503 if (stack->count > 0)
1506 guint j = stack->count - 1;
1511 tmp = stack->iters[i];
1512 stack->iters[i] = stack->iters[j];
1513 stack->iters[j] = tmp;
1522 queue_tag_redisplay (GtkTextBTree *tree,
1524 const GtkTextIter *start,
1525 const GtkTextIter *end)
1527 if (_gtk_text_tag_affects_size (tag))
1529 _gtk_text_btree_invalidate_region (tree, start, end);
1531 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1533 /* We only need to queue a redraw, not a relayout */
1534 redisplay_region (tree, start, end);
1537 /* We don't need to do anything if the tag doesn't affect display */
1541 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1542 const GtkTextIter *end_orig,
1546 GtkTextLineSegment *seg, *prev;
1547 GtkTextLine *cleanupline;
1548 gboolean toggled_on;
1549 GtkTextLine *start_line;
1550 GtkTextLine *end_line;
1552 GtkTextIter start, end;
1555 GtkTextTagInfo *info;
1557 g_return_if_fail (start_orig != NULL);
1558 g_return_if_fail (end_orig != NULL);
1559 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1560 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1561 _gtk_text_iter_get_btree (end_orig));
1562 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1565 printf ("%s tag %s from %d to %d\n",
1566 add ? "Adding" : "Removing",
1568 gtk_text_buffer_get_offset (start_orig),
1569 gtk_text_buffer_get_offset (end_orig));
1572 if (gtk_text_iter_equal (start_orig, end_orig))
1575 start = *start_orig;
1578 gtk_text_iter_order (&start, &end);
1580 tree = _gtk_text_iter_get_btree (&start);
1582 queue_tag_redisplay (tree, tag, &start, &end);
1584 info = gtk_text_btree_get_tag_info (tree, tag);
1586 start_line = _gtk_text_iter_get_text_line (&start);
1587 end_line = _gtk_text_iter_get_text_line (&end);
1589 /* Find all tag toggles in the region; we are going to delete them.
1590 We need to find them in advance, because
1591 forward_find_tag_toggle () won't work once we start playing around
1593 stack = iter_stack_new ();
1596 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1597 * which is deliberate - we don't want to delete a toggle at the
1600 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1602 if (gtk_text_iter_compare (&iter, &end) >= 0)
1605 iter_stack_push (stack, &iter);
1608 /* We need to traverse the toggles in order. */
1609 iter_stack_invert (stack);
1612 * See whether the tag is present at the start of the range. If
1613 * the state doesn't already match what we want then add a toggle
1617 toggled_on = gtk_text_iter_has_tag (&start, tag);
1618 if ( (add && !toggled_on) ||
1619 (!add && toggled_on) )
1621 /* This could create a second toggle at the start position;
1622 cleanup_line () will remove it if so. */
1623 seg = _gtk_toggle_segment_new (info, add);
1625 prev = gtk_text_line_segment_split (&start);
1628 seg->next = start_line->segments;
1629 start_line->segments = seg;
1633 seg->next = prev->next;
1637 /* cleanup_line adds the new toggle to the node counts. */
1639 printf ("added toggle at start\n");
1641 /* we should probably call segments_changed, but in theory
1642 any still-cached segments in the iters we are about to
1643 use are still valid, since they're in front
1649 * Scan the range of characters and delete any internal tag
1650 * transitions. Keep track of what the old state was at the end
1651 * of the range, and add a toggle there if it's needed.
1655 cleanupline = start_line;
1656 while (iter_stack_pop (stack, &iter))
1658 GtkTextLineSegment *indexable_seg;
1661 line = _gtk_text_iter_get_text_line (&iter);
1662 seg = _gtk_text_iter_get_any_segment (&iter);
1663 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1665 g_assert (seg != NULL);
1666 g_assert (indexable_seg != NULL);
1667 g_assert (seg != indexable_seg);
1669 prev = line->segments;
1671 /* Find the segment that actually toggles this tag. */
1672 while (seg != indexable_seg)
1674 g_assert (seg != NULL);
1675 g_assert (indexable_seg != NULL);
1676 g_assert (seg != indexable_seg);
1678 if ( (seg->type == >k_text_toggle_on_type ||
1679 seg->type == >k_text_toggle_off_type) &&
1680 (seg->body.toggle.info == info) )
1686 g_assert (seg != NULL);
1687 g_assert (indexable_seg != NULL);
1689 g_assert (seg != indexable_seg); /* If this happens, then
1690 forward_to_tag_toggle was
1692 g_assert (seg->body.toggle.info->tag == tag);
1694 /* If this happens, when previously tagging we didn't merge
1695 overlapping tags. */
1696 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1697 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1699 toggled_on = !toggled_on;
1702 printf ("deleting %s toggle\n",
1703 seg->type == >k_text_toggle_on_type ? "on" : "off");
1705 /* Remove toggle segment from the list. */
1708 line->segments = seg->next;
1712 while (prev->next != seg)
1716 prev->next = seg->next;
1719 /* Inform iterators we've hosed them. This actually reflects a
1720 bit of inefficiency; if you have the same tag toggled on and
1721 off a lot in a single line, we keep having the rescan from
1722 the front of the line. Of course we have to do that to get
1723 "prev" anyway, but here we are doing it an additional
1725 segments_changed (tree);
1727 /* Update node counts */
1728 if (seg->body.toggle.inNodeCounts)
1730 _gtk_change_node_toggle_count (line->parent,
1732 seg->body.toggle.inNodeCounts = FALSE;
1737 /* We only clean up lines when we're done with them, saves some
1738 gratuitous line-segment-traversals */
1740 if (cleanupline != line)
1742 cleanup_line (cleanupline);
1747 iter_stack_free (stack);
1749 /* toggled_on now reflects the toggle state _just before_ the
1750 end iterator. The end iterator could already have a toggle
1751 on or a toggle off. */
1752 if ( (add && !toggled_on) ||
1753 (!add && toggled_on) )
1755 /* This could create a second toggle at the start position;
1756 cleanup_line () will remove it if so. */
1758 seg = _gtk_toggle_segment_new (info, !add);
1760 prev = gtk_text_line_segment_split (&end);
1763 seg->next = end_line->segments;
1764 end_line->segments = seg;
1768 seg->next = prev->next;
1771 /* cleanup_line adds the new toggle to the node counts. */
1772 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1774 printf ("added toggle at end\n");
1779 * Cleanup cleanupline and the last line of the range, if
1780 * these are different.
1783 cleanup_line (cleanupline);
1784 if (cleanupline != end_line)
1786 cleanup_line (end_line);
1789 segments_changed (tree);
1791 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1792 _gtk_text_btree_check (tree);
1801 get_line_internal (GtkTextBTree *tree,
1803 gint *real_line_number,
1804 gboolean include_last)
1806 GtkTextBTreeNode *node;
1811 line_count = _gtk_text_btree_line_count (tree);
1815 if (line_number < 0)
1817 line_number = line_count;
1819 else if (line_number > line_count)
1821 line_number = line_count;
1824 if (real_line_number)
1825 *real_line_number = line_number;
1827 node = tree->root_node;
1828 lines_left = line_number;
1831 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1835 while (node->level != 0)
1837 for (node = node->children.node;
1838 node->num_lines <= lines_left;
1844 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1847 lines_left -= node->num_lines;
1852 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1855 for (line = node->children.line; lines_left > 0;
1861 g_error ("gtk_text_btree_find_line ran out of lines");
1870 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1873 _gtk_text_btree_get_line (tree,
1874 _gtk_text_btree_line_count (tree) - 1,
1879 _gtk_text_btree_get_line (GtkTextBTree *tree,
1881 gint *real_line_number)
1883 return get_line_internal (tree, line_number, real_line_number, TRUE);
1887 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1889 gint *real_line_number)
1891 return get_line_internal (tree, line_number, real_line_number, FALSE);
1895 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1897 gint *line_start_index,
1898 gint *real_char_index)
1900 GtkTextBTreeNode *node;
1902 GtkTextLineSegment *seg;
1907 node = tree->root_node;
1909 /* Clamp to valid indexes (-1 is magic for "highest index"),
1910 * node->num_chars includes the two newlines that aren't really
1913 if (char_index < 0 || char_index >= (node->num_chars - 1))
1915 char_index = node->num_chars - 2;
1918 *real_char_index = char_index;
1921 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1925 chars_left = char_index;
1926 while (node->level != 0)
1928 for (node = node->children.node;
1929 chars_left >= node->num_chars;
1932 chars_left -= node->num_chars;
1934 g_assert (chars_left >= 0);
1938 if (chars_left == 0)
1940 /* Start of a line */
1942 *line_start_index = char_index;
1943 return node->children.line;
1947 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1953 for (line = node->children.line; line != NULL; line = line->next)
1955 seg = line->segments;
1958 if (chars_in_line + seg->char_count > chars_left)
1959 goto found; /* found our line/segment */
1961 chars_in_line += seg->char_count;
1966 chars_left -= chars_in_line;
1974 g_assert (line != NULL); /* hosage, ran out of lines */
1975 g_assert (seg != NULL);
1977 *line_start_index = char_index - chars_left;
1982 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1985 GtkTextBTreeNode *node;
1986 GtkTextLine *siblingline;
1987 GtkTextLineSegment *seg;
1988 int src, dst, index;
1994 #define NUM_TAG_INFOS 10
1996 line = _gtk_text_iter_get_text_line (iter);
1997 tree = _gtk_text_iter_get_btree (iter);
1998 byte_index = gtk_text_iter_get_line_index (iter);
2000 tagInfo.numTags = 0;
2001 tagInfo.arraySize = NUM_TAG_INFOS;
2002 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2003 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2006 * Record tag toggles within the line of indexPtr but preceding
2007 * indexPtr. Note that if this loop segfaults, your
2008 * byte_index probably points past the sum of all
2009 * seg->byte_count */
2011 for (index = 0, seg = line->segments;
2012 (index + seg->byte_count) <= byte_index;
2013 index += seg->byte_count, seg = seg->next)
2015 if ((seg->type == >k_text_toggle_on_type)
2016 || (seg->type == >k_text_toggle_off_type))
2018 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2023 * Record toggles for tags in lines that are predecessors of
2024 * line but under the same level-0 GtkTextBTreeNode.
2027 for (siblingline = line->parent->children.line;
2028 siblingline != line;
2029 siblingline = siblingline->next)
2031 for (seg = siblingline->segments; seg != NULL;
2034 if ((seg->type == >k_text_toggle_on_type)
2035 || (seg->type == >k_text_toggle_off_type))
2037 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2043 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2044 * toggles for all siblings that precede that GtkTextBTreeNode.
2047 for (node = line->parent; node->parent != NULL;
2048 node = node->parent)
2050 GtkTextBTreeNode *siblingPtr;
2053 for (siblingPtr = node->parent->children.node;
2054 siblingPtr != node; siblingPtr = siblingPtr->next)
2056 for (summary = siblingPtr->summary; summary != NULL;
2057 summary = summary->next)
2059 if (summary->toggle_count & 1)
2061 inc_count (summary->info->tag, summary->toggle_count,
2069 * Go through the tag information and squash out all of the tags
2070 * that have even toggle counts (these tags exist before the point
2071 * of interest, but not at the desired character itself).
2074 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2076 if (tagInfo.counts[src] & 1)
2078 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2079 tagInfo.tags[dst] = tagInfo.tags[src];
2085 g_free (tagInfo.counts);
2088 g_free (tagInfo.tags);
2091 return tagInfo.tags;
2095 copy_segment (GString *string,
2096 gboolean include_hidden,
2097 gboolean include_nonchars,
2098 const GtkTextIter *start,
2099 const GtkTextIter *end)
2101 GtkTextLineSegment *end_seg;
2102 GtkTextLineSegment *seg;
2104 if (gtk_text_iter_equal (start, end))
2107 seg = _gtk_text_iter_get_indexable_segment (start);
2108 end_seg = _gtk_text_iter_get_indexable_segment (end);
2110 if (seg->type == >k_text_char_type)
2112 gboolean copy = TRUE;
2113 gint copy_bytes = 0;
2114 gint copy_start = 0;
2116 /* Don't copy if we're invisible; segments are invisible/not
2117 as a whole, no need to check each char */
2118 if (!include_hidden &&
2119 _gtk_text_btree_char_is_invisible (start))
2122 /* printf (" <invisible>\n"); */
2125 copy_start = _gtk_text_iter_get_segment_byte (start);
2129 /* End is in the same segment; need to copy fewer bytes. */
2130 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2132 copy_bytes = end_byte - copy_start;
2135 copy_bytes = seg->byte_count - copy_start;
2137 g_assert (copy_bytes != 0); /* Due to iter equality check at
2138 front of this function. */
2142 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2144 g_string_append_len (string,
2145 seg->body.chars + copy_start,
2149 /* printf (" :%s\n", string->str); */
2151 else if (seg->type == >k_text_pixbuf_type ||
2152 seg->type == >k_text_child_type)
2154 gboolean copy = TRUE;
2156 if (!include_nonchars)
2160 else if (!include_hidden &&
2161 _gtk_text_btree_char_is_invisible (start))
2168 g_string_append_len (string,
2169 gtk_text_unknown_char_utf8,
2177 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2178 const GtkTextIter *end_orig,
2179 gboolean include_hidden,
2180 gboolean include_nonchars)
2182 GtkTextLineSegment *seg;
2183 GtkTextLineSegment *end_seg;
2191 g_return_val_if_fail (start_orig != NULL, NULL);
2192 g_return_val_if_fail (end_orig != NULL, NULL);
2193 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2194 _gtk_text_iter_get_btree (end_orig), NULL);
2196 start = *start_orig;
2199 gtk_text_iter_order (&start, &end);
2201 retval = g_string_new ("");
2203 tree = _gtk_text_iter_get_btree (&start);
2205 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2207 seg = _gtk_text_iter_get_indexable_segment (&iter);
2208 while (seg != end_seg)
2210 copy_segment (retval, include_hidden, include_nonchars,
2213 _gtk_text_iter_forward_indexable_segment (&iter);
2215 seg = _gtk_text_iter_get_indexable_segment (&iter);
2218 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2221 g_string_free (retval, FALSE);
2226 _gtk_text_btree_line_count (GtkTextBTree *tree)
2228 /* Subtract bogus line at the end; we return a count
2230 return tree->root_node->num_lines - 1;
2234 _gtk_text_btree_char_count (GtkTextBTree *tree)
2236 /* Exclude newline in bogus last line and the
2237 * one in the last line that is after the end iterator
2239 return tree->root_node->num_chars - 2;
2242 #define LOTSA_TAGS 1000
2244 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2246 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2248 int deftagCnts[LOTSA_TAGS];
2249 int *tagCnts = deftagCnts;
2250 GtkTextTag *deftags[LOTSA_TAGS];
2251 GtkTextTag **tags = deftags;
2253 GtkTextBTreeNode *node;
2254 GtkTextLine *siblingline;
2255 GtkTextLineSegment *seg;
2262 line = _gtk_text_iter_get_text_line (iter);
2263 tree = _gtk_text_iter_get_btree (iter);
2264 byte_index = gtk_text_iter_get_line_index (iter);
2266 numTags = gtk_text_tag_table_get_size (tree->table);
2268 /* almost always avoid malloc, so stay out of system calls */
2269 if (LOTSA_TAGS < numTags)
2271 tagCnts = g_new (int, numTags);
2272 tags = g_new (GtkTextTag*, numTags);
2275 for (i=0; i<numTags; i++)
2281 * Record tag toggles within the line of indexPtr but preceding
2285 for (index = 0, seg = line->segments;
2286 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2287 index += seg->byte_count, seg = seg->next)
2289 if ((seg->type == >k_text_toggle_on_type)
2290 || (seg->type == >k_text_toggle_off_type))
2292 tag = seg->body.toggle.info->tag;
2293 if (tag->invisible_set && tag->values->invisible)
2295 tags[tag->priority] = tag;
2296 tagCnts[tag->priority]++;
2302 * Record toggles for tags in lines that are predecessors of
2303 * line but under the same level-0 GtkTextBTreeNode.
2306 for (siblingline = line->parent->children.line;
2307 siblingline != line;
2308 siblingline = siblingline->next)
2310 for (seg = siblingline->segments; seg != NULL;
2313 if ((seg->type == >k_text_toggle_on_type)
2314 || (seg->type == >k_text_toggle_off_type))
2316 tag = seg->body.toggle.info->tag;
2317 if (tag->invisible_set && tag->values->invisible)
2319 tags[tag->priority] = tag;
2320 tagCnts[tag->priority]++;
2327 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2328 * for all siblings that precede that GtkTextBTreeNode.
2331 for (node = line->parent; node->parent != NULL;
2332 node = node->parent)
2334 GtkTextBTreeNode *siblingPtr;
2337 for (siblingPtr = node->parent->children.node;
2338 siblingPtr != node; siblingPtr = siblingPtr->next)
2340 for (summary = siblingPtr->summary; summary != NULL;
2341 summary = summary->next)
2343 if (summary->toggle_count & 1)
2345 tag = summary->info->tag;
2346 if (tag->invisible_set && tag->values->invisible)
2348 tags[tag->priority] = tag;
2349 tagCnts[tag->priority] += summary->toggle_count;
2357 * Now traverse from highest priority to lowest,
2358 * take invisible value from first odd count (= on)
2361 for (i = numTags-1; i >=0; i--)
2365 /* FIXME not sure this should be if 0 */
2367 #ifndef ALWAYS_SHOW_SELECTION
2368 /* who would make the selection invisible? */
2369 if ((tag == tkxt->seltag)
2370 && !(tkxt->flags & GOT_FOCUS))
2376 invisible = tags[i]->values->invisible;
2381 if (LOTSA_TAGS < numTags)
2396 redisplay_region (GtkTextBTree *tree,
2397 const GtkTextIter *start,
2398 const GtkTextIter *end)
2401 GtkTextLine *start_line, *end_line;
2403 if (gtk_text_iter_compare (start, end) > 0)
2405 const GtkTextIter *tmp = start;
2410 start_line = _gtk_text_iter_get_text_line (start);
2411 end_line = _gtk_text_iter_get_text_line (end);
2414 while (view != NULL)
2416 gint start_y, end_y;
2417 GtkTextLineData *ld;
2419 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2421 if (end_line == start_line)
2424 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2426 ld = _gtk_text_line_get_data (end_line, view->view_id);
2428 end_y += ld->height;
2430 gtk_text_layout_changed (view->layout, start_y,
2439 redisplay_mark (GtkTextLineSegment *mark)
2444 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2446 mark->body.mark.obj);
2449 gtk_text_iter_forward_char (&end);
2451 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2456 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2458 if (!mark->body.mark.visible)
2461 redisplay_mark (mark);
2465 ensure_not_off_end (GtkTextBTree *tree,
2466 GtkTextLineSegment *mark,
2469 if (gtk_text_iter_get_line (iter) ==
2470 _gtk_text_btree_line_count (tree))
2471 gtk_text_iter_backward_char (iter);
2474 static GtkTextLineSegment*
2475 real_set_mark (GtkTextBTree *tree,
2476 GtkTextMark *existing_mark,
2478 gboolean left_gravity,
2479 const GtkTextIter *where,
2480 gboolean should_exist,
2481 gboolean redraw_selections)
2483 GtkTextLineSegment *mark;
2486 g_return_val_if_fail (tree != NULL, NULL);
2487 g_return_val_if_fail (where != NULL, NULL);
2488 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2491 mark = existing_mark->segment;
2492 else if (name != NULL)
2493 mark = g_hash_table_lookup (tree->mark_table,
2498 if (should_exist && mark == NULL)
2500 g_warning ("No mark `%s' exists!", name);
2504 /* OK if !should_exist and it does already exist, in that case
2510 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2511 _gtk_text_iter_check (&iter);
2515 if (redraw_selections &&
2516 (mark == tree->insert_mark->segment ||
2517 mark == tree->selection_bound_mark->segment))
2519 GtkTextIter old_pos;
2521 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2522 mark->body.mark.obj);
2523 redisplay_region (tree, &old_pos, where);
2527 * don't let visible marks be after the final newline of the
2531 if (mark->body.mark.visible)
2533 ensure_not_off_end (tree, mark, &iter);
2536 /* Redraw the mark's old location. */
2537 redisplay_mark_if_visible (mark);
2539 /* Unlink mark from its current location.
2540 This could hose our iterator... */
2541 gtk_text_btree_unlink_segment (tree, mark,
2542 mark->body.mark.line);
2543 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2544 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2546 segments_changed (tree); /* make sure the iterator recomputes its
2551 mark = _gtk_mark_segment_new (tree,
2555 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2557 if (mark->body.mark.name)
2558 g_hash_table_insert (tree->mark_table,
2559 mark->body.mark.name,
2563 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2564 _gtk_text_iter_check (&iter);
2566 /* Link mark into new location */
2567 gtk_text_btree_link_segment (mark, &iter);
2569 /* Invalidate some iterators. */
2570 segments_changed (tree);
2573 * update the screen at the mark's new location.
2576 redisplay_mark_if_visible (mark);
2578 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2579 _gtk_text_iter_check (&iter);
2581 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2582 _gtk_text_btree_check (tree);
2589 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2590 GtkTextMark *existing_mark,
2592 gboolean left_gravity,
2593 const GtkTextIter *iter,
2594 gboolean should_exist)
2596 GtkTextLineSegment *seg;
2598 seg = real_set_mark (tree, existing_mark,
2599 name, left_gravity, iter, should_exist,
2602 return seg ? seg->body.mark.obj : NULL;
2606 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2610 GtkTextIter tmp_start, tmp_end;
2612 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2614 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2615 tree->selection_bound_mark);
2617 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2629 gtk_text_iter_order (&tmp_start, &tmp_end);
2642 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2643 const GtkTextIter *iter)
2645 GtkTextIter start, end;
2647 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2648 redisplay_region (tree, &start, &end);
2650 /* Move insert AND selection_bound before we redisplay */
2651 real_set_mark (tree, tree->insert_mark,
2652 "insert", FALSE, iter, TRUE, FALSE);
2653 real_set_mark (tree, tree->selection_bound_mark,
2654 "selection_bound", FALSE, iter, TRUE, FALSE);
2658 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2663 g_return_if_fail (tree != NULL);
2664 g_return_if_fail (name != NULL);
2666 mark = g_hash_table_lookup (tree->mark_table,
2669 _gtk_text_btree_remove_mark (tree, mark);
2673 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2674 GtkTextLineSegment *segment)
2677 if (segment->body.mark.name)
2678 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2680 segment->body.mark.tree = NULL;
2681 segment->body.mark.line = NULL;
2683 /* Remove the ref on the mark, which frees segment as a side effect
2684 * if this is the last reference.
2686 g_object_unref (G_OBJECT (segment->body.mark.obj));
2690 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2693 GtkTextLineSegment *segment;
2695 g_return_if_fail (mark != NULL);
2696 g_return_if_fail (tree != NULL);
2698 segment = mark->segment;
2700 if (segment->body.mark.not_deleteable)
2702 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2706 /* This calls cleanup_line and segments_changed */
2707 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2709 _gtk_text_btree_release_mark_segment (tree, segment);
2713 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2714 GtkTextMark *segment)
2716 return segment == tree->insert_mark;
2720 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2721 GtkTextMark *segment)
2723 return segment == tree->selection_bound_mark;
2727 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2730 GtkTextLineSegment *seg;
2732 g_return_val_if_fail (tree != NULL, NULL);
2733 g_return_val_if_fail (name != NULL, NULL);
2735 seg = g_hash_table_lookup (tree->mark_table, name);
2737 return seg ? seg->body.mark.obj : NULL;
2741 * gtk_text_mark_set_visible:
2742 * @mark: a #GtkTextMark
2743 * @setting: visibility of mark
2745 * Sets the visibility of @mark; the insertion point is normally
2746 * visible, i.e. you can see it as a vertical bar. Also, the text
2747 * widget uses a visible mark to indicate where a drop will occur when
2748 * dragging-and-dropping text. Most other marks are not visible.
2749 * Marks are not visible by default.
2753 gtk_text_mark_set_visible (GtkTextMark *mark,
2756 GtkTextLineSegment *seg;
2758 g_return_if_fail (mark != NULL);
2760 seg = mark->segment;
2762 if (seg->body.mark.visible == setting)
2766 seg->body.mark.visible = setting;
2768 redisplay_mark (seg);
2773 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2776 GtkTextBTreeNode *node;
2777 GtkTextTagInfo *info;
2779 g_return_val_if_fail (tree != NULL, NULL);
2783 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2788 if (info->tag_root == NULL)
2791 node = info->tag_root;
2793 /* We know the tag root has instances of the given
2796 continue_outer_loop:
2797 g_assert (node != NULL);
2798 while (node->level > 0)
2800 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2801 node = node->children.node;
2802 while (node != NULL)
2804 if (gtk_text_btree_node_has_tag (node, tag))
2805 goto continue_outer_loop;
2809 g_assert (node != NULL);
2812 g_assert (node != NULL); /* The tag summaries said some node had
2815 g_assert (node->level == 0);
2817 return node->children.line;
2821 /* Looking for any tag at all (tag == NULL).
2822 Unfortunately this can't be done in a simple and efficient way
2823 right now; so I'm just going to return the
2824 first line in the btree. FIXME */
2825 return _gtk_text_btree_get_line (tree, 0, NULL);
2830 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2833 GtkTextBTreeNode *node;
2834 GtkTextBTreeNode *last_node;
2836 GtkTextTagInfo *info;
2838 g_return_val_if_fail (tree != NULL, NULL);
2842 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2844 if (info->tag_root == NULL)
2847 node = info->tag_root;
2848 /* We know the tag root has instances of the given
2851 while (node->level > 0)
2853 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2855 node = node->children.node;
2856 while (node != NULL)
2858 if (gtk_text_btree_node_has_tag (node, tag))
2866 g_assert (node != NULL); /* The tag summaries said some node had
2869 g_assert (node->level == 0);
2871 /* Find the last line in this node */
2872 line = node->children.line;
2873 while (line->next != NULL)
2880 /* This search can't be done efficiently at the moment,
2881 at least not without complexity.
2882 So, we just return the last line.
2884 return _gtk_text_btree_get_end_iter_line (tree);
2894 _gtk_text_line_get_number (GtkTextLine *line)
2897 GtkTextBTreeNode *node, *parent, *node2;
2901 * First count how many lines precede this one in its level-0
2905 node = line->parent;
2907 for (line2 = node->children.line; line2 != line;
2908 line2 = line2->next)
2912 g_error ("gtk_text_btree_line_number couldn't find line");
2918 * Now work up through the levels of the tree one at a time,
2919 * counting how many lines are in GtkTextBTreeNodes preceding the current
2923 for (parent = node->parent ; parent != NULL;
2924 node = parent, parent = parent->parent)
2926 for (node2 = parent->children.node; node2 != node;
2927 node2 = node2->next)
2931 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2933 index += node2->num_lines;
2939 static GtkTextLineSegment*
2940 find_toggle_segment_before_char (GtkTextLine *line,
2944 GtkTextLineSegment *seg;
2945 GtkTextLineSegment *toggle_seg;
2950 seg = line->segments;
2951 while ( (index + seg->char_count) <= char_in_line )
2953 if (((seg->type == >k_text_toggle_on_type)
2954 || (seg->type == >k_text_toggle_off_type))
2955 && (seg->body.toggle.info->tag == tag))
2958 index += seg->char_count;
2965 static GtkTextLineSegment*
2966 find_toggle_segment_before_byte (GtkTextLine *line,
2970 GtkTextLineSegment *seg;
2971 GtkTextLineSegment *toggle_seg;
2976 seg = line->segments;
2977 while ( (index + seg->byte_count) <= byte_in_line )
2979 if (((seg->type == >k_text_toggle_on_type)
2980 || (seg->type == >k_text_toggle_off_type))
2981 && (seg->body.toggle.info->tag == tag))
2984 index += seg->byte_count;
2992 find_toggle_outside_current_line (GtkTextLine *line,
2996 GtkTextBTreeNode *node;
2997 GtkTextLine *sibling_line;
2998 GtkTextLineSegment *seg;
2999 GtkTextLineSegment *toggle_seg;
3001 GtkTextTagInfo *info = NULL;
3004 * No toggle in this line. Look for toggles for the tag in lines
3005 * that are predecessors of line but under the same
3006 * level-0 GtkTextBTreeNode.
3009 sibling_line = line->parent->children.line;
3010 while (sibling_line != line)
3012 seg = sibling_line->segments;
3015 if (((seg->type == >k_text_toggle_on_type)
3016 || (seg->type == >k_text_toggle_off_type))
3017 && (seg->body.toggle.info->tag == tag))
3023 sibling_line = sibling_line->next;
3026 if (toggle_seg != NULL)
3027 return (toggle_seg->type == >k_text_toggle_on_type);
3030 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3031 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3032 * siblings that precede that GtkTextBTreeNode.
3035 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3041 node = line->parent;
3042 while (node->parent != NULL)
3044 GtkTextBTreeNode *sibling_node;
3046 sibling_node = node->parent->children.node;
3047 while (sibling_node != node)
3051 summary = sibling_node->summary;
3052 while (summary != NULL)
3054 if (summary->info == info)
3055 toggles += summary->toggle_count;
3057 summary = summary->next;
3060 sibling_node = sibling_node->next;
3063 if (node == info->tag_root)
3066 node = node->parent;
3070 * An odd number of toggles means that the tag is present at the
3074 return (toggles & 1) != 0;
3077 /* FIXME this function is far too slow, for no good reason. */
3079 _gtk_text_line_char_has_tag (GtkTextLine *line,
3084 GtkTextLineSegment *toggle_seg;
3086 g_return_val_if_fail (line != NULL, FALSE);
3089 * Check for toggles for the tag in the line but before
3090 * the char. If there is one, its type indicates whether or
3091 * not the character is tagged.
3094 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3096 if (toggle_seg != NULL)
3097 return (toggle_seg->type == >k_text_toggle_on_type);
3099 return find_toggle_outside_current_line (line, tree, tag);
3103 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3108 GtkTextLineSegment *toggle_seg;
3110 g_return_val_if_fail (line != NULL, FALSE);
3113 * Check for toggles for the tag in the line but before
3114 * the char. If there is one, its type indicates whether or
3115 * not the character is tagged.
3118 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3120 if (toggle_seg != NULL)
3121 return (toggle_seg->type == >k_text_toggle_on_type);
3123 return find_toggle_outside_current_line (line, tree, tag);
3127 _gtk_text_line_is_last (GtkTextLine *line,
3130 return line == get_last_line (tree);
3134 ensure_end_iter_line (GtkTextBTree *tree)
3136 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3141 /* n_lines is without the magic line at the end */
3142 n_lines = _gtk_text_btree_line_count (tree);
3144 g_assert (n_lines >= 1);
3146 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3148 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3153 ensure_end_iter_segment (GtkTextBTree *tree)
3155 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3157 GtkTextLineSegment *seg;
3158 GtkTextLineSegment *last_with_chars;
3160 ensure_end_iter_line (tree);
3162 last_with_chars = NULL;
3164 seg = tree->end_iter_line->segments;
3167 if (seg->char_count > 0)
3168 last_with_chars = seg;
3172 tree->end_iter_segment = last_with_chars;
3174 /* We know the last char in the last line is '\n' */
3175 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3176 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3178 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3180 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3181 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3186 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3189 ensure_end_iter_line (tree);
3191 return line == tree->end_iter_line;
3195 _gtk_text_btree_is_end (GtkTextBTree *tree,
3197 GtkTextLineSegment *seg,
3201 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3203 /* Do this first to avoid walking segments in most cases */
3204 if (!_gtk_text_line_contains_end_iter (line, tree))
3207 ensure_end_iter_segment (tree);
3209 if (seg != tree->end_iter_segment)
3212 if (byte_index >= 0)
3213 return byte_index == tree->end_iter_segment_byte_index;
3215 return char_offset == tree->end_iter_segment_char_offset;
3219 _gtk_text_line_next (GtkTextLine *line)
3221 GtkTextBTreeNode *node;
3223 if (line->next != NULL)
3228 * This was the last line associated with the particular parent
3229 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3230 * then search down from that GtkTextBTreeNode to find the first
3234 node = line->parent;
3235 while (node != NULL && node->next == NULL)
3236 node = node->parent;
3242 while (node->level > 0)
3244 node = node->children.node;
3247 g_assert (node->children.line != line);
3249 return node->children.line;
3254 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3258 next = _gtk_text_line_next (line);
3260 /* If we were on the end iter line, we can't go to
3263 if (next && next->next == NULL && /* these checks are optimization only */
3264 _gtk_text_line_next (next) == NULL)
3271 _gtk_text_line_previous (GtkTextLine *line)
3273 GtkTextBTreeNode *node;
3274 GtkTextBTreeNode *node2;
3278 * Find the line under this GtkTextBTreeNode just before the starting line.
3280 prev = line->parent->children.line; /* First line at leaf */
3281 while (prev != line)
3283 if (prev->next == line)
3289 g_error ("gtk_text_btree_previous_line ran out of lines");
3293 * This was the first line associated with the particular parent
3294 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3295 * then search down from that GtkTextBTreeNode to find its last line.
3297 for (node = line->parent; ; node = node->parent)
3299 if (node == NULL || node->parent == NULL)
3301 else if (node != node->parent->children.node)
3305 for (node2 = node->parent->children.node; ;
3306 node2 = node2->children.node)
3308 while (node2->next != node)
3309 node2 = node2->next;
3311 if (node2->level == 0)
3317 for (prev = node2->children.line ; ; prev = prev->next)
3319 if (prev->next == NULL)
3323 g_assert_not_reached ();
3329 _gtk_text_line_data_new (GtkTextLayout *layout,
3332 GtkTextLineData *line_data;
3334 line_data = g_new (GtkTextLineData, 1);
3336 line_data->view_id = layout;
3337 line_data->next = NULL;
3338 line_data->width = 0;
3339 line_data->height = 0;
3340 line_data->valid = FALSE;
3346 _gtk_text_line_add_data (GtkTextLine *line,
3347 GtkTextLineData *data)
3349 g_return_if_fail (line != NULL);
3350 g_return_if_fail (data != NULL);
3351 g_return_if_fail (data->view_id != NULL);
3355 data->next = line->views;
3365 _gtk_text_line_remove_data (GtkTextLine *line,
3368 GtkTextLineData *prev;
3369 GtkTextLineData *iter;
3371 g_return_val_if_fail (line != NULL, NULL);
3372 g_return_val_if_fail (view_id != NULL, NULL);
3376 while (iter != NULL)
3378 if (iter->view_id == view_id)
3387 prev->next = iter->next;
3389 line->views = iter->next;
3398 _gtk_text_line_get_data (GtkTextLine *line,
3401 GtkTextLineData *iter;
3403 g_return_val_if_fail (line != NULL, NULL);
3404 g_return_val_if_fail (view_id != NULL, NULL);
3407 while (iter != NULL)
3409 if (iter->view_id == view_id)
3418 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3419 GtkTextLineData *ld)
3421 /* For now this is totally unoptimized. FIXME?
3423 We could probably optimize the case where the width removed
3424 is less than the max width for the parent node,
3425 and the case where the height is unchanged when we re-wrap.
3428 g_return_if_fail (ld != NULL);
3431 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3435 _gtk_text_line_char_count (GtkTextLine *line)
3437 GtkTextLineSegment *seg;
3441 seg = line->segments;
3444 size += seg->char_count;
3451 _gtk_text_line_byte_count (GtkTextLine *line)
3453 GtkTextLineSegment *seg;
3457 seg = line->segments;
3460 size += seg->byte_count;
3468 _gtk_text_line_char_index (GtkTextLine *target_line)
3470 GSList *node_stack = NULL;
3471 GtkTextBTreeNode *iter;
3475 /* Push all our parent nodes onto a stack */
3476 iter = target_line->parent;
3478 g_assert (iter != NULL);
3480 while (iter != NULL)
3482 node_stack = g_slist_prepend (node_stack, iter);
3484 iter = iter->parent;
3487 /* Check that we have the root node on top of the stack. */
3488 g_assert (node_stack != NULL &&
3489 node_stack->data != NULL &&
3490 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3492 /* Add up chars in all nodes before the nodes in our stack.
3496 iter = node_stack->data;
3497 while (iter != NULL)
3499 GtkTextBTreeNode *child_iter;
3500 GtkTextBTreeNode *next_node;
3502 next_node = node_stack->next ?
3503 node_stack->next->data : NULL;
3504 node_stack = g_slist_remove (node_stack, node_stack->data);
3506 if (iter->level == 0)
3508 /* stack should be empty when we're on the last node */
3509 g_assert (node_stack == NULL);
3510 break; /* Our children are now lines */
3513 g_assert (next_node != NULL);
3514 g_assert (iter != NULL);
3515 g_assert (next_node->parent == iter);
3517 /* Add up chars before us in the tree */
3518 child_iter = iter->children.node;
3519 while (child_iter != next_node)
3521 g_assert (child_iter != NULL);
3523 num_chars += child_iter->num_chars;
3525 child_iter = child_iter->next;
3531 g_assert (iter != NULL);
3532 g_assert (iter == target_line->parent);
3534 /* Since we don't store char counts in lines, only in segments, we
3535 have to iterate over the lines adding up segment char counts
3536 until we find our line. */
3537 line = iter->children.line;
3538 while (line != target_line)
3540 g_assert (line != NULL);
3542 num_chars += _gtk_text_line_char_count (line);
3547 g_assert (line == target_line);
3553 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3557 GtkTextLineSegment *seg;
3560 g_return_val_if_fail (line != NULL, NULL);
3562 offset = byte_offset;
3563 seg = line->segments;
3565 while (offset >= seg->byte_count)
3567 g_assert (seg != NULL); /* means an invalid byte index */
3568 offset -= seg->byte_count;
3573 *seg_offset = offset;
3579 _gtk_text_line_char_to_segment (GtkTextLine *line,
3583 GtkTextLineSegment *seg;
3586 g_return_val_if_fail (line != NULL, NULL);
3588 offset = char_offset;
3589 seg = line->segments;
3591 while (offset >= seg->char_count)
3593 g_assert (seg != NULL); /* means an invalid char index */
3594 offset -= seg->char_count;
3599 *seg_offset = offset;
3605 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3609 GtkTextLineSegment *seg;
3612 g_return_val_if_fail (line != NULL, NULL);
3614 offset = byte_offset;
3615 seg = line->segments;
3617 while (offset > 0 && offset >= seg->byte_count)
3619 g_assert (seg != NULL); /* means an invalid byte index */
3620 offset -= seg->byte_count;
3625 *seg_offset = offset;
3631 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3635 GtkTextLineSegment *seg;
3638 g_return_val_if_fail (line != NULL, NULL);
3640 offset = char_offset;
3641 seg = line->segments;
3643 while (offset > 0 && offset >= seg->char_count)
3645 g_assert (seg != NULL); /* means an invalid byte index */
3646 offset -= seg->char_count;
3651 *seg_offset = offset;
3657 _gtk_text_line_byte_to_char (GtkTextLine *line,
3661 GtkTextLineSegment *seg;
3663 g_return_val_if_fail (line != NULL, 0);
3664 g_return_val_if_fail (byte_offset >= 0, 0);
3667 seg = line->segments;
3668 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3669 the next segment) */
3671 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3673 byte_offset -= seg->byte_count;
3674 char_offset += seg->char_count;
3679 g_assert (seg != NULL);
3681 /* Now byte_offset is the offset into the current segment,
3682 and char_offset is the start of the current segment.
3683 Optimize the case where no chars use > 1 byte */
3684 if (seg->byte_count == seg->char_count)
3685 return char_offset + byte_offset;
3688 if (seg->type == >k_text_char_type)
3689 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3692 g_assert (seg->char_count == 1);
3693 g_assert (byte_offset == 0);
3701 _gtk_text_line_char_to_byte (GtkTextLine *line,
3704 g_warning ("FIXME not implemented");
3709 /* FIXME sync with char_locate (or figure out a clean
3710 way to merge the two functions) */
3712 _gtk_text_line_byte_locate (GtkTextLine *line,
3714 GtkTextLineSegment **segment,
3715 GtkTextLineSegment **any_segment,
3716 gint *seg_byte_offset,
3717 gint *line_byte_offset)
3719 GtkTextLineSegment *seg;
3720 GtkTextLineSegment *after_prev_indexable;
3721 GtkTextLineSegment *after_last_indexable;
3722 GtkTextLineSegment *last_indexable;
3726 g_return_val_if_fail (line != NULL, FALSE);
3727 g_return_val_if_fail (byte_offset >= 0, FALSE);
3730 *any_segment = NULL;
3733 offset = byte_offset;
3735 last_indexable = NULL;
3736 after_last_indexable = line->segments;
3737 after_prev_indexable = line->segments;
3738 seg = line->segments;
3740 /* The loop ends when we're inside a segment;
3741 last_indexable refers to the last segment
3742 we passed entirely. */
3743 while (seg && offset >= seg->byte_count)
3745 if (seg->char_count > 0)
3747 offset -= seg->byte_count;
3748 bytes_in_line += seg->byte_count;
3749 last_indexable = seg;
3750 after_prev_indexable = after_last_indexable;
3751 after_last_indexable = last_indexable->next;
3759 /* We went off the end of the line */
3761 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3768 if (after_last_indexable != NULL)
3769 *any_segment = after_last_indexable;
3771 *any_segment = *segment;
3774 /* Override any_segment if we're in the middle of a segment. */
3776 *any_segment = *segment;
3778 *seg_byte_offset = offset;
3780 g_assert (*segment != NULL);
3781 g_assert (*any_segment != NULL);
3782 g_assert (*seg_byte_offset < (*segment)->byte_count);
3784 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3789 /* FIXME sync with byte_locate (or figure out a clean
3790 way to merge the two functions) */
3792 _gtk_text_line_char_locate (GtkTextLine *line,
3794 GtkTextLineSegment **segment,
3795 GtkTextLineSegment **any_segment,
3796 gint *seg_char_offset,
3797 gint *line_char_offset)
3799 GtkTextLineSegment *seg;
3800 GtkTextLineSegment *after_prev_indexable;
3801 GtkTextLineSegment *after_last_indexable;
3802 GtkTextLineSegment *last_indexable;
3806 g_return_val_if_fail (line != NULL, FALSE);
3807 g_return_val_if_fail (char_offset >= 0, FALSE);
3810 *any_segment = NULL;
3813 offset = char_offset;
3815 last_indexable = NULL;
3816 after_last_indexable = line->segments;
3817 after_prev_indexable = line->segments;
3818 seg = line->segments;
3820 /* The loop ends when we're inside a segment;
3821 last_indexable refers to the last segment
3822 we passed entirely. */
3823 while (seg && offset >= seg->char_count)
3825 if (seg->char_count > 0)
3827 offset -= seg->char_count;
3828 chars_in_line += seg->char_count;
3829 last_indexable = seg;
3830 after_prev_indexable = after_last_indexable;
3831 after_last_indexable = last_indexable->next;
3839 /* end of the line */
3841 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3848 if (after_last_indexable != NULL)
3849 *any_segment = after_last_indexable;
3851 *any_segment = *segment;
3854 /* Override any_segment if we're in the middle of a segment. */
3856 *any_segment = *segment;
3858 *seg_char_offset = offset;
3860 g_assert (*segment != NULL);
3861 g_assert (*any_segment != NULL);
3862 g_assert (*seg_char_offset < (*segment)->char_count);
3864 *line_char_offset = chars_in_line + *seg_char_offset;
3870 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3872 gint *line_char_offset,
3873 gint *seg_char_offset)
3875 GtkTextLineSegment *seg;
3878 g_return_if_fail (line != NULL);
3879 g_return_if_fail (byte_offset >= 0);
3881 *line_char_offset = 0;
3883 offset = byte_offset;
3884 seg = line->segments;
3886 while (offset >= seg->byte_count)
3888 offset -= seg->byte_count;
3889 *line_char_offset += seg->char_count;
3891 g_assert (seg != NULL); /* means an invalid char offset */
3894 g_assert (seg->char_count > 0); /* indexable. */
3896 /* offset is now the number of bytes into the current segment we
3897 * want to go. Count chars into the current segment.
3900 if (seg->type == >k_text_char_type)
3902 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3904 g_assert (*seg_char_offset < seg->char_count);
3906 *line_char_offset += *seg_char_offset;
3910 g_assert (offset == 0);
3911 *seg_char_offset = 0;
3916 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3918 gint *line_byte_offset,
3919 gint *seg_byte_offset)
3921 GtkTextLineSegment *seg;
3924 g_return_if_fail (line != NULL);
3925 g_return_if_fail (char_offset >= 0);
3927 *line_byte_offset = 0;
3929 offset = char_offset;
3930 seg = line->segments;
3932 while (offset >= seg->char_count)
3934 offset -= seg->char_count;
3935 *line_byte_offset += seg->byte_count;
3937 g_assert (seg != NULL); /* means an invalid char offset */
3940 g_assert (seg->char_count > 0); /* indexable. */
3942 /* offset is now the number of chars into the current segment we
3943 want to go. Count bytes into the current segment. */
3945 if (seg->type == >k_text_char_type)
3947 *seg_byte_offset = 0;
3951 const char * start = seg->body.chars + *seg_byte_offset;
3953 bytes = g_utf8_next_char (start) - start;
3954 *seg_byte_offset += bytes;
3958 g_assert (*seg_byte_offset < seg->byte_count);
3960 *line_byte_offset += *seg_byte_offset;
3964 g_assert (offset == 0);
3965 *seg_byte_offset = 0;
3970 node_compare (GtkTextBTreeNode *lhs,
3971 GtkTextBTreeNode *rhs)
3973 GtkTextBTreeNode *iter;
3974 GtkTextBTreeNode *node;
3975 GtkTextBTreeNode *common_parent;
3976 GtkTextBTreeNode *parent_of_lower;
3977 GtkTextBTreeNode *parent_of_higher;
3978 gboolean lhs_is_lower;
3979 GtkTextBTreeNode *lower;
3980 GtkTextBTreeNode *higher;
3982 /* This function assumes that lhs and rhs are not underneath each
3989 if (lhs->level < rhs->level)
3991 lhs_is_lower = TRUE;
3997 lhs_is_lower = FALSE;
4002 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4003 * of the common parent we used to reach the common parent; the
4004 * ordering of these child nodes in the child list is the ordering
4008 /* Get on the same level (may be on same level already) */
4010 while (node->level < higher->level)
4011 node = node->parent;
4013 g_assert (node->level == higher->level);
4015 g_assert (node != higher); /* Happens if lower is underneath higher */
4017 /* Go up until we have two children with a common parent.
4019 parent_of_lower = node;
4020 parent_of_higher = higher;
4022 while (parent_of_lower->parent != parent_of_higher->parent)
4024 parent_of_lower = parent_of_lower->parent;
4025 parent_of_higher = parent_of_higher->parent;
4028 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4030 common_parent = parent_of_lower->parent;
4032 g_assert (common_parent != NULL);
4034 /* See which is first in the list of common_parent's children */
4035 iter = common_parent->children.node;
4036 while (iter != NULL)
4038 if (iter == parent_of_higher)
4040 /* higher is less than lower */
4043 return 1; /* lhs > rhs */
4047 else if (iter == parent_of_lower)
4049 /* lower is less than higher */
4052 return -1; /* lhs < rhs */
4060 g_assert_not_reached ();
4064 /* remember that tag == NULL means "any tag" */
4066 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4070 GtkTextBTreeNode *node;
4071 GtkTextTagInfo *info;
4072 gboolean below_tag_root;
4074 g_return_val_if_fail (line != NULL, NULL);
4076 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4077 _gtk_text_btree_check (tree);
4081 /* Right now we can only offer linear-search if the user wants
4082 * to know about any tag toggle at all.
4084 return _gtk_text_line_next_excluding_last (line);
4087 /* Our tag summaries only have node precision, not line
4088 * precision. This means that if any line under a node could contain a
4089 * tag, then any of the others could also contain a tag.
4091 * In the future we could have some mechanism to keep track of how
4092 * many toggles we've found under a node so far, since we have a
4093 * count of toggles under the node. But for now I'm going with KISS.
4096 /* return same-node line, if any. */
4100 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4104 if (info->tag_root == NULL)
4107 if (info->tag_root == line->parent)
4108 return NULL; /* we were at the last line under the tag root */
4110 /* We need to go up out of this node, and on to the next one with
4111 toggles for the target tag. If we're below the tag root, we need to
4112 find the next node below the tag root that has tag summaries. If
4113 we're not below the tag root, we need to see if the tag root is
4114 after us in the tree, and if so, return the first line underneath
4117 node = line->parent;
4118 below_tag_root = FALSE;
4119 while (node != NULL)
4121 if (node == info->tag_root)
4123 below_tag_root = TRUE;
4127 node = node->parent;
4132 node = line->parent;
4133 while (node != info->tag_root)
4135 if (node->next == NULL)
4136 node = node->parent;
4141 if (gtk_text_btree_node_has_tag (node, tag))
4151 ordering = node_compare (line->parent, info->tag_root);
4155 /* Tag root is ahead of us, so search there. */
4156 node = info->tag_root;
4161 /* Tag root is after us, so no more lines that
4162 * could contain the tag.
4167 g_assert_not_reached ();
4172 g_assert (node != NULL);
4174 /* We have to find the first sub-node of this node that contains
4178 while (node->level > 0)
4180 g_assert (node != NULL); /* If this fails, it likely means an
4181 incorrect tag summary led us on a
4182 wild goose chase down this branch of
4184 node = node->children.node;
4185 while (node != NULL)
4187 if (gtk_text_btree_node_has_tag (node, tag))
4193 g_assert (node != NULL);
4194 g_assert (node->level == 0);
4196 return node->children.line;
4200 prev_line_under_node (GtkTextBTreeNode *node,
4205 prev = node->children.line;
4211 while (prev->next != line)
4221 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4225 GtkTextBTreeNode *node;
4226 GtkTextBTreeNode *found_node = NULL;
4227 GtkTextTagInfo *info;
4228 gboolean below_tag_root;
4230 GtkTextBTreeNode *line_ancestor;
4231 GtkTextBTreeNode *line_ancestor_parent;
4233 /* See next_could_contain_tag () for more extensive comments
4234 * on what's going on here.
4237 g_return_val_if_fail (line != NULL, NULL);
4239 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4240 _gtk_text_btree_check (tree);
4244 /* Right now we can only offer linear-search if the user wants
4245 * to know about any tag toggle at all.
4247 return _gtk_text_line_previous (line);
4250 /* Return same-node line, if any. */
4251 prev = prev_line_under_node (line->parent, line);
4255 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4259 if (info->tag_root == NULL)
4262 if (info->tag_root == line->parent)
4263 return NULL; /* we were at the first line under the tag root */
4265 /* Are we below the tag root */
4266 node = line->parent;
4267 below_tag_root = FALSE;
4268 while (node != NULL)
4270 if (node == info->tag_root)
4272 below_tag_root = TRUE;
4276 node = node->parent;
4281 /* Look for a previous node under this tag root that has our
4285 /* this assertion holds because line->parent is not the
4286 * tag root, we are below the tag root, and the tag
4289 g_assert (line->parent->parent != NULL);
4291 line_ancestor = line->parent;
4292 line_ancestor_parent = line->parent->parent;
4294 node = line_ancestor_parent->children.node;
4295 while (node != line_ancestor &&
4296 line_ancestor != info->tag_root)
4298 GSList *child_nodes = NULL;
4301 /* Create reverse-order list of nodes before
4304 while (node != line_ancestor
4307 child_nodes = g_slist_prepend (child_nodes, node);
4312 /* Try to find a node with our tag on it in the list */
4316 GtkTextBTreeNode *this_node = tmp->data;
4318 g_assert (this_node != line_ancestor);
4320 if (gtk_text_btree_node_has_tag (this_node, tag))
4322 found_node = this_node;
4323 g_slist_free (child_nodes);
4327 tmp = g_slist_next (tmp);
4330 g_slist_free (child_nodes);
4332 /* Didn't find anything on this level; go up one level. */
4333 line_ancestor = line_ancestor_parent;
4334 line_ancestor_parent = line_ancestor->parent;
4336 node = line_ancestor_parent->children.node;
4346 ordering = node_compare (line->parent, info->tag_root);
4350 /* Tag root is ahead of us, so no more lines
4357 /* Tag root is after us, so grab last tagged
4358 * line underneath the tag root.
4360 found_node = info->tag_root;
4364 g_assert_not_reached ();
4369 g_assert (found_node != NULL);
4371 /* We have to find the last sub-node of this node that contains
4376 while (node->level > 0)
4378 GSList *child_nodes = NULL;
4380 g_assert (node != NULL); /* If this fails, it likely means an
4381 incorrect tag summary led us on a
4382 wild goose chase down this branch of
4385 node = node->children.node;
4386 while (node != NULL)
4388 child_nodes = g_slist_prepend (child_nodes, node);
4392 node = NULL; /* detect failure to find a child node. */
4395 while (iter != NULL)
4397 if (gtk_text_btree_node_has_tag (iter->data, tag))
4399 /* recurse into this node. */
4404 iter = g_slist_next (iter);
4407 g_slist_free (child_nodes);
4409 g_assert (node != NULL);
4412 g_assert (node != NULL);
4413 g_assert (node->level == 0);
4415 /* this assertion is correct, but slow. */
4416 /* g_assert (node_compare (node, line->parent) < 0); */
4418 /* Return last line in this node. */
4420 prev = node->children.line;
4428 * Non-public function implementations
4432 summary_list_destroy (Summary *summary)
4435 while (summary != NULL)
4437 next = summary->next;
4438 summary_destroy (summary);
4444 get_last_line (GtkTextBTree *tree)
4446 if (tree->last_line_stamp != tree->chars_changed_stamp)
4452 n_lines = _gtk_text_btree_line_count (tree);
4454 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4456 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4458 tree->last_line_stamp = tree->chars_changed_stamp;
4459 tree->last_line = line;
4462 return tree->last_line;
4470 gtk_text_line_new (void)
4474 line = g_new0(GtkTextLine, 1);
4480 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4482 GtkTextLineData *ld;
4483 GtkTextLineData *next;
4485 g_return_if_fail (line != NULL);
4492 view = gtk_text_btree_get_view (tree, ld->view_id);
4494 g_assert (view != NULL);
4497 gtk_text_layout_free_line_data (view->layout, line, ld);
4506 gtk_text_line_set_parent (GtkTextLine *line,
4507 GtkTextBTreeNode *node)
4509 if (line->parent == node)
4511 line->parent = node;
4512 gtk_text_btree_node_invalidate_upward (node, NULL);
4516 cleanup_line (GtkTextLine *line)
4518 GtkTextLineSegment *seg, **prev_p;
4522 * Make a pass over all of the segments in the line, giving each
4523 * a chance to clean itself up. This could potentially change
4524 * the structure of the line, e.g. by merging two segments
4525 * together or having two segments cancel themselves; if so,
4526 * then repeat the whole process again, since the first structure
4527 * change might make other structure changes possible. Repeat
4528 * until eventually there are no changes.
4535 for (prev_p = &line->segments, seg = *prev_p;
4537 prev_p = &(*prev_p)->next, seg = *prev_p)
4539 if (seg->type->cleanupFunc != NULL)
4541 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4554 node_data_new (gpointer view_id)
4558 nd = g_new (NodeData, 1);
4560 nd->view_id = view_id;
4570 node_data_destroy (NodeData *nd)
4576 node_data_list_destroy (NodeData *nd)
4582 while (iter != NULL)
4585 node_data_destroy (iter);
4591 node_data_find (NodeData *nd, gpointer view_id)
4595 if (nd->view_id == view_id)
4603 summary_destroy (Summary *summary)
4605 /* Fill with error-triggering garbage */
4606 summary->info = (void*)0x1;
4607 summary->toggle_count = 567;
4608 summary->next = (void*)0x1;
4612 static GtkTextBTreeNode*
4613 gtk_text_btree_node_new (void)
4615 GtkTextBTreeNode *node;
4617 node = g_new (GtkTextBTreeNode, 1);
4619 node->node_data = NULL;
4625 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4626 GtkTextTagInfo *info,
4631 summary = node->summary;
4632 while (summary != NULL)
4634 if (summary->info == info)
4636 summary->toggle_count += adjust;
4640 summary = summary->next;
4643 if (summary == NULL)
4645 /* didn't find a summary for our tag. */
4646 g_return_if_fail (adjust > 0);
4647 summary = g_new (Summary, 1);
4648 summary->info = info;
4649 summary->toggle_count = adjust;
4650 summary->next = node->summary;
4651 node->summary = summary;
4655 /* Note that the tag root and above do not have summaries
4656 for the tag; only nodes below the tag root have
4659 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4663 summary = node->summary;
4664 while (summary != NULL)
4667 summary->info->tag == tag)
4670 summary = summary->next;
4676 /* Add node and all children to the damage region. */
4678 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4682 nd = node->node_data;
4689 if (node->level == 0)
4693 line = node->children.line;
4694 while (line != NULL)
4696 GtkTextLineData *ld;
4710 GtkTextBTreeNode *child;
4712 child = node->children.node;
4714 while (child != NULL)
4716 gtk_text_btree_node_invalidate_downward (child);
4718 child = child->next;
4724 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4726 GtkTextBTreeNode *iter;
4729 while (iter != NULL)
4735 nd = node_data_find (iter->node_data, view_id);
4737 if (nd == NULL || !nd->valid)
4738 break; /* Once a node is invalid, we know its parents are as well. */
4744 gboolean should_continue = FALSE;
4746 nd = iter->node_data;
4751 should_continue = TRUE;
4758 if (!should_continue)
4759 break; /* This node was totally invalidated, so are its
4763 iter = iter->parent;
4769 * _gtk_text_btree_is_valid:
4770 * @tree: a #GtkTextBTree
4771 * @view_id: ID for the view
4773 * Check to see if the entire #GtkTextBTree is valid or not for
4776 * Return value: %TRUE if the entire #GtkTextBTree is valid
4779 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4783 g_return_val_if_fail (tree != NULL, FALSE);
4785 nd = node_data_find (tree->root_node->node_data, view_id);
4786 return (nd && nd->valid);
4789 typedef struct _ValidateState ValidateState;
4791 struct _ValidateState
4793 gint remaining_pixels;
4794 gboolean in_validation;
4801 gtk_text_btree_node_validate (BTreeView *view,
4802 GtkTextBTreeNode *node,
4804 ValidateState *state)
4806 gint node_valid = TRUE;
4807 gint node_width = 0;
4808 gint node_height = 0;
4810 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4811 g_return_if_fail (!nd->valid);
4813 if (node->level == 0)
4815 GtkTextLine *line = node->children.line;
4816 GtkTextLineData *ld;
4818 /* Iterate over leading valid lines */
4819 while (line != NULL)
4821 ld = _gtk_text_line_get_data (line, view_id);
4823 if (!ld || !ld->valid)
4825 else if (state->in_validation)
4827 state->in_validation = FALSE;
4832 state->y += ld->height;
4833 node_width = MAX (ld->width, node_width);
4834 node_height += ld->height;
4840 state->in_validation = TRUE;
4842 /* Iterate over invalid lines */
4843 while (line != NULL)
4845 ld = _gtk_text_line_get_data (line, view_id);
4847 if (ld && ld->valid)
4852 state->old_height += ld->height;
4853 ld = gtk_text_layout_wrap (view->layout, line, ld);
4854 state->new_height += ld->height;
4856 node_width = MAX (ld->width, node_width);
4857 node_height += ld->height;
4859 state->remaining_pixels -= ld->height;
4860 if (state->remaining_pixels <= 0)
4870 /* Iterate over the remaining lines */
4871 while (line != NULL)
4873 ld = _gtk_text_line_get_data (line, view_id);
4874 state->in_validation = FALSE;
4876 if (!ld || !ld->valid)
4881 node_width = MAX (ld->width, node_width);
4882 node_height += ld->height;
4890 GtkTextBTreeNode *child;
4893 child = node->children.node;
4895 /* Iterate over leading valid nodes */
4898 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4900 if (!child_nd->valid)
4902 else if (state->in_validation)
4904 state->in_validation = FALSE;
4909 state->y += child_nd->height;
4910 node_width = MAX (node_width, child_nd->width);
4911 node_height += child_nd->height;
4914 child = child->next;
4917 /* Iterate over invalid nodes */
4920 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4922 if (child_nd->valid)
4926 gtk_text_btree_node_validate (view, child, view_id, state);
4928 if (!child_nd->valid)
4930 node_width = MAX (node_width, child_nd->width);
4931 node_height += child_nd->height;
4933 if (!state->in_validation || state->remaining_pixels <= 0)
4935 child = child->next;
4940 child = child->next;
4943 /* Iterate over the remaining lines */
4946 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4947 state->in_validation = FALSE;
4949 if (!child_nd->valid)
4952 node_width = MAX (child_nd->width, node_width);
4953 node_height += child_nd->height;
4955 child = child->next;
4959 nd->width = node_width;
4960 nd->height = node_height;
4961 nd->valid = node_valid;
4965 * _gtk_text_btree_validate:
4966 * @tree: a #GtkTextBTree
4968 * @max_pixels: the maximum number of pixels to validate. (No more
4969 * than one paragraph beyond this limit will be validated)
4970 * @y: location to store starting y coordinate of validated region
4971 * @old_height: location to store old height of validated region
4972 * @new_height: location to store new height of validated region
4974 * Validate a single contiguous invalid region of a #GtkTextBTree for
4977 * Return value: %TRUE if a region has been validated, %FALSE if the
4978 * entire tree was already valid.
4981 _gtk_text_btree_validate (GtkTextBTree *tree,
4990 g_return_val_if_fail (tree != NULL, FALSE);
4992 view = gtk_text_btree_get_view (tree, view_id);
4993 g_return_val_if_fail (view != NULL, FALSE);
4995 if (!_gtk_text_btree_is_valid (tree, view_id))
4997 ValidateState state;
4999 state.remaining_pixels = max_pixels;
5000 state.in_validation = FALSE;
5002 state.old_height = 0;
5003 state.new_height = 0;
5005 gtk_text_btree_node_validate (view,
5012 *old_height = state.old_height;
5014 *new_height = state.new_height;
5016 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5017 _gtk_text_btree_check (tree);
5026 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5030 gboolean *valid_out)
5034 gboolean valid = TRUE;
5036 if (node->level == 0)
5038 GtkTextLine *line = node->children.line;
5040 while (line != NULL)
5042 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5044 if (!ld || !ld->valid)
5049 width = MAX (ld->width, width);
5050 height += ld->height;
5058 GtkTextBTreeNode *child = node->children.node;
5062 NodeData *child_nd = node_data_find (child->node_data, view_id);
5064 if (!child_nd || !child_nd->valid)
5069 width = MAX (child_nd->width, width);
5070 height += child_nd->height;
5073 child = child->next;
5078 *height_out = height;
5083 /* Recompute the validity and size of the view data for a given
5084 * view at this node from the immediate children of the node
5087 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5090 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5095 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5096 &width, &height, &valid);
5098 nd->height = height;
5105 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5110 gtk_text_btree_node_check_valid (node, view_id);
5111 node = node->parent;
5116 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5119 if (node->level == 0)
5121 return gtk_text_btree_node_check_valid (node, view_id);
5125 GtkTextBTreeNode *child = node->children.node;
5127 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5135 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5137 if (!child_nd->valid)
5139 nd->width = MAX (child_nd->width, nd->width);
5140 nd->height += child_nd->height;
5142 child = child->next;
5151 * _gtk_text_btree_validate_line:
5152 * @tree: a #GtkTextBTree
5153 * @line: line to validate
5154 * @view_id: view ID for the view to validate
5156 * Revalidate a single line of the btree for the given view, propagate
5157 * results up through the entire tree.
5160 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5164 GtkTextLineData *ld;
5167 g_return_if_fail (tree != NULL);
5168 g_return_if_fail (line != NULL);
5170 view = gtk_text_btree_get_view (tree, view_id);
5171 g_return_if_fail (view != NULL);
5173 ld = _gtk_text_line_get_data (line, view_id);
5174 if (!ld || !ld->valid)
5176 ld = gtk_text_layout_wrap (view->layout, line, ld);
5178 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5183 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5185 if (node->level == 0)
5189 line = node->children.line;
5190 while (line != NULL)
5192 GtkTextLineData *ld;
5194 ld = _gtk_text_line_remove_data (line, view_id);
5197 gtk_text_layout_free_line_data (view->layout, line, ld);
5204 GtkTextBTreeNode *child;
5206 child = node->children.node;
5208 while (child != NULL)
5211 gtk_text_btree_node_remove_view (view, child, view_id);
5213 child = child->next;
5217 gtk_text_btree_node_remove_data (node, view_id);
5221 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5223 if (node->level == 0)
5226 GtkTextLineSegment *seg;
5228 while (node->children.line != NULL)
5230 line = node->children.line;
5231 node->children.line = line->next;
5232 while (line->segments != NULL)
5234 seg = line->segments;
5235 line->segments = seg->next;
5237 (*seg->type->deleteFunc) (seg, line, TRUE);
5239 gtk_text_line_destroy (tree, line);
5244 GtkTextBTreeNode *childPtr;
5246 while (node->children.node != NULL)
5248 childPtr = node->children.node;
5249 node->children.node = childPtr->next;
5250 gtk_text_btree_node_destroy (tree, childPtr);
5254 gtk_text_btree_node_free_empty (tree, node);
5258 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5259 GtkTextBTreeNode *node)
5261 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5262 (node->level == 0 && node->children.line == NULL));
5264 summary_list_destroy (node->summary);
5265 node_data_list_destroy (node->node_data);
5270 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5274 nd = node->node_data;
5277 if (nd->view_id == view_id)
5285 nd = node_data_new (view_id);
5287 if (node->node_data)
5288 nd->next = node->node_data;
5290 node->node_data = nd;
5297 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5303 nd = node->node_data;
5306 if (nd->view_id == view_id)
5317 prev->next = nd->next;
5319 if (node->node_data == nd)
5320 node->node_data = nd->next;
5324 node_data_destroy (nd);
5328 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5329 gint *width, gint *height)
5333 g_return_if_fail (width != NULL);
5334 g_return_if_fail (height != NULL);
5336 nd = gtk_text_btree_node_ensure_data (node, view_id);
5341 *height = nd->height;
5344 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5345 * here isn't quite right, since for a lot of operations we want to
5346 * know which children of the common parent correspond to the two nodes
5347 * (e.g., when computing the order of two iters)
5349 static GtkTextBTreeNode *
5350 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5351 GtkTextBTreeNode *node2)
5353 while (node1->level < node2->level)
5354 node1 = node1->parent;
5355 while (node2->level < node1->level)
5356 node2 = node2->parent;
5357 while (node1 != node2)
5359 node1 = node1->parent;
5360 node2 = node2->parent;
5371 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5376 while (view != NULL)
5378 if (view->view_id == view_id)
5387 get_tree_bounds (GtkTextBTree *tree,
5391 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5392 _gtk_text_btree_get_end_iter (tree, end);
5396 tag_changed_cb (GtkTextTagTable *table,
5398 gboolean size_changed,
5403 /* We need to queue a relayout on all regions that are tagged with
5410 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5412 /* Must be a last toggle if there was a first one. */
5413 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5414 _gtk_text_btree_invalidate_region (tree,
5421 /* We only need to queue a redraw, not a relayout */
5426 while (view != NULL)
5430 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5431 gtk_text_layout_changed (view->layout, 0, height, height);
5439 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5442 /* Remove the tag from the tree */
5447 get_tree_bounds (tree, &start, &end);
5449 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5450 gtk_text_btree_remove_tag_info (tree, tag);
5454 /* Rebalance the out-of-whack node "node" */
5456 gtk_text_btree_rebalance (GtkTextBTree *tree,
5457 GtkTextBTreeNode *node)
5460 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5461 * up through the tree one GtkTextBTreeNode at a time until the root
5462 * GtkTextBTreeNode has been processed.
5465 while (node != NULL)
5467 GtkTextBTreeNode *new_node, *child;
5472 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5473 * then split off all but the first MIN_CHILDREN into a separate
5474 * GtkTextBTreeNode following the original one. Then repeat until the
5475 * GtkTextBTreeNode has a decent size.
5478 if (node->num_children > MAX_CHILDREN)
5483 * If the GtkTextBTreeNode being split is the root
5484 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5488 if (node->parent == NULL)
5490 new_node = gtk_text_btree_node_new ();
5491 new_node->parent = NULL;
5492 new_node->next = NULL;
5493 new_node->summary = NULL;
5494 new_node->level = node->level + 1;
5495 new_node->children.node = node;
5496 recompute_node_counts (tree, new_node);
5497 tree->root_node = new_node;
5499 new_node = gtk_text_btree_node_new ();
5500 new_node->parent = node->parent;
5501 new_node->next = node->next;
5502 node->next = new_node;
5503 new_node->summary = NULL;
5504 new_node->level = node->level;
5505 new_node->num_children = node->num_children - MIN_CHILDREN;
5506 if (node->level == 0)
5508 for (i = MIN_CHILDREN-1,
5509 line = node->children.line;
5510 i > 0; i--, line = line->next)
5512 /* Empty loop body. */
5514 new_node->children.line = line->next;
5519 for (i = MIN_CHILDREN-1,
5520 child = node->children.node;
5521 i > 0; i--, child = child->next)
5523 /* Empty loop body. */
5525 new_node->children.node = child->next;
5528 recompute_node_counts (tree, node);
5529 node->parent->num_children++;
5531 if (node->num_children <= MAX_CHILDREN)
5533 recompute_node_counts (tree, node);
5539 while (node->num_children < MIN_CHILDREN)
5541 GtkTextBTreeNode *other;
5542 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5543 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5544 int total_children, first_children, i;
5547 * Too few children for this GtkTextBTreeNode. If this is the root then,
5548 * it's OK for it to have less than MIN_CHILDREN children
5549 * as long as it's got at least two. If it has only one
5550 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5551 * the tree and use its child as the new root.
5554 if (node->parent == NULL)
5556 if ((node->num_children == 1) && (node->level > 0))
5558 tree->root_node = node->children.node;
5559 tree->root_node->parent = NULL;
5561 node->children.node = NULL;
5562 gtk_text_btree_node_free_empty (tree, node);
5568 * Not the root. Make sure that there are siblings to
5572 if (node->parent->num_children < 2)
5574 gtk_text_btree_rebalance (tree, node->parent);
5579 * Find a sibling neighbor to borrow from, and arrange for
5580 * node to be the earlier of the pair.
5583 if (node->next == NULL)
5585 for (other = node->parent->children.node;
5586 other->next != node;
5587 other = other->next)
5589 /* Empty loop body. */
5596 * We're going to either merge the two siblings together
5597 * into one GtkTextBTreeNode or redivide the children among them to
5598 * balance their loads. As preparation, join their two
5599 * child lists into a single list and remember the half-way
5600 * point in the list.
5603 total_children = node->num_children + other->num_children;
5604 first_children = total_children/2;
5605 if (node->children.node == NULL)
5607 node->children = other->children;
5608 other->children.node = NULL;
5609 other->children.line = NULL;
5611 if (node->level == 0)
5615 for (line = node->children.line, i = 1;
5617 line = line->next, i++)
5619 if (i == first_children)
5624 line->next = other->children.line;
5625 while (i <= first_children)
5634 GtkTextBTreeNode *child;
5636 for (child = node->children.node, i = 1;
5637 child->next != NULL;
5638 child = child->next, i++)
5640 if (i <= first_children)
5642 if (i == first_children)
5644 halfwaynode = child;
5648 child->next = other->children.node;
5649 while (i <= first_children)
5651 halfwaynode = child;
5652 child = child->next;
5658 * If the two siblings can simply be merged together, do it.
5661 if (total_children <= MAX_CHILDREN)
5663 recompute_node_counts (tree, node);
5664 node->next = other->next;
5665 node->parent->num_children--;
5667 other->children.node = NULL;
5668 other->children.line = NULL;
5669 gtk_text_btree_node_free_empty (tree, other);
5674 * The siblings can't be merged, so just divide their
5675 * children evenly between them.
5678 if (node->level == 0)
5680 other->children.line = halfwayline->next;
5681 halfwayline->next = NULL;
5685 other->children.node = halfwaynode->next;
5686 halfwaynode->next = NULL;
5689 recompute_node_counts (tree, node);
5690 recompute_node_counts (tree, other);
5693 node = node->parent;
5698 post_insert_fixup (GtkTextBTree *tree,
5700 gint line_count_delta,
5701 gint char_count_delta)
5704 GtkTextBTreeNode *node;
5707 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5708 * point, then rebalance the tree if necessary.
5711 for (node = line->parent ; node != NULL;
5712 node = node->parent)
5714 node->num_lines += line_count_delta;
5715 node->num_chars += char_count_delta;
5717 node = line->parent;
5718 node->num_children += line_count_delta;
5720 if (node->num_children > MAX_CHILDREN)
5722 gtk_text_btree_rebalance (tree, node);
5725 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5726 _gtk_text_btree_check (tree);
5729 static GtkTextTagInfo*
5730 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5733 GtkTextTagInfo *info;
5737 list = tree->tag_infos;
5738 while (list != NULL)
5741 if (info->tag == tag)
5744 list = g_slist_next (list);
5750 static GtkTextTagInfo*
5751 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5754 GtkTextTagInfo *info;
5756 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5760 /* didn't find it, create. */
5762 info = g_new (GtkTextTagInfo, 1);
5765 g_object_ref (G_OBJECT (tag));
5766 info->tag_root = NULL;
5767 info->toggle_count = 0;
5769 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5776 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5779 GtkTextTagInfo *info;
5784 list = tree->tag_infos;
5785 while (list != NULL)
5788 if (info->tag == tag)
5792 prev->next = list->next;
5796 tree->tag_infos = list->next;
5799 g_slist_free (list);
5801 g_object_unref (G_OBJECT (info->tag));
5807 list = g_slist_next (list);
5812 recompute_level_zero_counts (GtkTextBTreeNode *node)
5815 GtkTextLineSegment *seg;
5817 g_assert (node->level == 0);
5819 line = node->children.line;
5820 while (line != NULL)
5822 node->num_children++;
5825 if (line->parent != node)
5826 gtk_text_line_set_parent (line, node);
5828 seg = line->segments;
5832 node->num_chars += seg->char_count;
5834 if (((seg->type != >k_text_toggle_on_type)
5835 && (seg->type != >k_text_toggle_off_type))
5836 || !(seg->body.toggle.inNodeCounts))
5842 GtkTextTagInfo *info;
5844 info = seg->body.toggle.info;
5846 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5857 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5860 GtkTextBTreeNode *child;
5862 g_assert (node->level > 0);
5864 child = node->children.node;
5865 while (child != NULL)
5867 node->num_children += 1;
5868 node->num_lines += child->num_lines;
5869 node->num_chars += child->num_chars;
5871 if (child->parent != node)
5873 child->parent = node;
5874 gtk_text_btree_node_invalidate_upward (node, NULL);
5877 summary = child->summary;
5878 while (summary != NULL)
5880 gtk_text_btree_node_adjust_toggle_count (node,
5882 summary->toggle_count);
5884 summary = summary->next;
5887 child = child->next;
5892 *----------------------------------------------------------------------
5894 * recompute_node_counts --
5896 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5897 * (tags, child information, etc.) by scanning the information in
5898 * its descendants. This procedure is called during rebalancing
5899 * when a GtkTextBTreeNode's child structure has changed.
5905 * The tag counts for node are modified to reflect its current
5906 * child structure, as are its num_children, num_lines, num_chars fields.
5907 * Also, all of the childrens' parent fields are made to point
5910 *----------------------------------------------------------------------
5914 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5917 Summary *summary, *summary2;
5920 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5921 * the existing Summary records (most of them will probably be reused).
5924 summary = node->summary;
5925 while (summary != NULL)
5927 summary->toggle_count = 0;
5928 summary = summary->next;
5931 node->num_children = 0;
5932 node->num_lines = 0;
5933 node->num_chars = 0;
5936 * Scan through the children, adding the childrens' tag counts into
5937 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5941 if (node->level == 0)
5942 recompute_level_zero_counts (node);
5944 recompute_level_nonzero_counts (node);
5949 gtk_text_btree_node_check_valid (node, view->view_id);
5954 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5955 * records that still have a zero count, or that have all the toggles.
5956 * The GtkTextBTreeNode with the children that account for all the tags toggles
5957 * have no summary information, and they become the tag_root for the tag.
5961 for (summary = node->summary; summary != NULL; )
5963 if (summary->toggle_count > 0 &&
5964 summary->toggle_count < summary->info->toggle_count)
5966 if (node->level == summary->info->tag_root->level)
5969 * The tag's root GtkTextBTreeNode split and some toggles left.
5970 * The tag root must move up a level.
5972 summary->info->tag_root = node->parent;
5975 summary = summary->next;
5978 if (summary->toggle_count == summary->info->toggle_count)
5981 * A GtkTextBTreeNode merge has collected all the toggles under
5982 * one GtkTextBTreeNode. Push the root down to this level.
5984 summary->info->tag_root = node;
5986 if (summary2 != NULL)
5988 summary2->next = summary->next;
5989 summary_destroy (summary);
5990 summary = summary2->next;
5994 node->summary = summary->next;
5995 summary_destroy (summary);
5996 summary = node->summary;
6002 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6003 GtkTextTagInfo *info,
6004 gint delta) /* may be negative */
6006 Summary *summary, *prevPtr;
6007 GtkTextBTreeNode *node2Ptr;
6008 int rootLevel; /* Level of original tag root */
6010 info->toggle_count += delta;
6012 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6014 info->tag_root = node;
6019 * Note the level of the existing root for the tag so we can detect
6020 * if it needs to be moved because of the toggle count change.
6023 rootLevel = info->tag_root->level;
6026 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6027 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6031 for ( ; node != info->tag_root; node = node->parent)
6034 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6035 * perhaps all we have to do is adjust its count.
6038 for (prevPtr = NULL, summary = node->summary;
6040 prevPtr = summary, summary = summary->next)
6042 if (summary->info == info)
6047 if (summary != NULL)
6049 summary->toggle_count += delta;
6050 if (summary->toggle_count > 0 &&
6051 summary->toggle_count < info->toggle_count)
6055 if (summary->toggle_count != 0)
6058 * Should never find a GtkTextBTreeNode with max toggle count at this
6059 * point (there shouldn't have been a summary entry in the
6063 g_error ("%s: bad toggle count (%d) max (%d)",
6064 G_STRLOC, summary->toggle_count, info->toggle_count);
6068 * Zero toggle count; must remove this tag from the list.
6071 if (prevPtr == NULL)
6073 node->summary = summary->next;
6077 prevPtr->next = summary->next;
6079 summary_destroy (summary);
6084 * This tag isn't currently in the summary information list.
6087 if (rootLevel == node->level)
6091 * The old tag root is at the same level in the tree as this
6092 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6093 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6094 * as well as the old root (if not, we'll move it up again
6095 * the next time through the loop). To push it up one level
6096 * we copy the original toggle count into the summary
6097 * information at the old root and change the root to its
6098 * parent GtkTextBTreeNode.
6101 GtkTextBTreeNode *rootnode = info->tag_root;
6102 summary = (Summary *) g_malloc (sizeof (Summary));
6103 summary->info = info;
6104 summary->toggle_count = info->toggle_count - delta;
6105 summary->next = rootnode->summary;
6106 rootnode->summary = summary;
6107 rootnode = rootnode->parent;
6108 rootLevel = rootnode->level;
6109 info->tag_root = rootnode;
6111 summary = (Summary *) g_malloc (sizeof (Summary));
6112 summary->info = info;
6113 summary->toggle_count = delta;
6114 summary->next = node->summary;
6115 node->summary = summary;
6120 * If we've decremented the toggle count, then it may be necessary
6121 * to push the tag root down one or more levels.
6128 if (info->toggle_count == 0)
6130 info->tag_root = (GtkTextBTreeNode *) NULL;
6133 node = info->tag_root;
6134 while (node->level > 0)
6137 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6138 * toggles. If so, push the root down one level.
6141 for (node2Ptr = node->children.node;
6142 node2Ptr != (GtkTextBTreeNode *)NULL ;
6143 node2Ptr = node2Ptr->next)
6145 for (prevPtr = NULL, summary = node2Ptr->summary;
6147 prevPtr = summary, summary = summary->next)
6149 if (summary->info == info)
6154 if (summary == NULL)
6158 if (summary->toggle_count != info->toggle_count)
6161 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6168 * This GtkTextBTreeNode has all the toggles, so push down the root.
6171 if (prevPtr == NULL)
6173 node2Ptr->summary = summary->next;
6177 prevPtr->next = summary->next;
6179 summary_destroy (summary);
6180 info->tag_root = node2Ptr;
6183 node = info->tag_root;
6188 *----------------------------------------------------------------------
6192 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6193 * increments the count for a particular tag, adding a new
6194 * entry for that tag if there wasn't one previously.
6200 * The information at *tagInfoPtr may be modified, and the arrays
6201 * may be reallocated to make them larger.
6203 *----------------------------------------------------------------------
6207 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6212 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6213 count > 0; tag_p++, count--)
6217 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6223 * There isn't currently an entry for this tag, so we have to
6224 * make a new one. If the arrays are full, then enlarge the
6228 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6230 GtkTextTag **newTags;
6231 int *newCounts, newSize;
6233 newSize = 2*tagInfoPtr->arraySize;
6234 newTags = (GtkTextTag **) g_malloc ((unsigned)
6235 (newSize*sizeof (GtkTextTag *)));
6236 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6237 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6238 g_free ((char *) tagInfoPtr->tags);
6239 tagInfoPtr->tags = newTags;
6240 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6241 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6242 tagInfoPtr->arraySize *sizeof (int));
6243 g_free ((char *) tagInfoPtr->counts);
6244 tagInfoPtr->counts = newCounts;
6245 tagInfoPtr->arraySize = newSize;
6248 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6249 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6250 tagInfoPtr->numTags++;
6254 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6255 const GtkTextIter *iter)
6257 GtkTextLineSegment *prev;
6261 line = _gtk_text_iter_get_text_line (iter);
6262 tree = _gtk_text_iter_get_btree (iter);
6264 prev = gtk_text_line_segment_split (iter);
6267 seg->next = line->segments;
6268 line->segments = seg;
6272 seg->next = prev->next;
6275 cleanup_line (line);
6276 segments_changed (tree);
6278 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6279 _gtk_text_btree_check (tree);
6283 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6284 GtkTextLineSegment *seg,
6287 GtkTextLineSegment *prev;
6289 if (line->segments == seg)
6291 line->segments = seg->next;
6295 for (prev = line->segments; prev->next != seg;
6298 /* Empty loop body. */
6300 prev->next = seg->next;
6302 cleanup_line (line);
6303 segments_changed (tree);
6307 * This is here because it requires BTree internals, it logically
6308 * belongs in gtktextsegment.c
6313 *--------------------------------------------------------------
6315 * _gtk_toggle_segment_check_func --
6317 * This procedure is invoked to perform consistency checks
6318 * on toggle segments.
6324 * If a consistency problem is found the procedure g_errors.
6326 *--------------------------------------------------------------
6330 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6336 if (segPtr->byte_count != 0)
6338 g_error ("toggle_segment_check_func: segment had non-zero size");
6340 if (!segPtr->body.toggle.inNodeCounts)
6342 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6344 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6345 for (summary = line->parent->summary; ;
6346 summary = summary->next)
6348 if (summary == NULL)
6352 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6359 if (summary->info == segPtr->body.toggle.info)
6363 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6375 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6376 GtkTextBTreeNode *node,
6386 while (view != NULL)
6388 if (view->view_id == nd->view_id)
6395 g_error ("Node has data for a view %p no longer attached to the tree",
6398 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6399 &width, &height, &valid);
6401 /* valid aggregate not checked the same as width/height, because on
6402 * btree rebalance we can have invalid nodes where all lines below
6403 * them are actually valid, due to moving lines around between
6406 * The guarantee is that if there are invalid lines the node is
6407 * invalid - we don't guarantee that if the node is invalid there
6408 * are invalid lines.
6411 if (nd->width != width ||
6412 nd->height != height ||
6413 (nd->valid && !valid))
6415 g_error ("Node aggregates for view %p are invalid:\n"
6416 "Are (%d,%d,%s), should be (%d,%d,%s)",
6418 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6419 width, height, valid ? "TRUE" : "FALSE");
6424 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6425 GtkTextBTreeNode *node)
6427 GtkTextBTreeNode *childnode;
6428 Summary *summary, *summary2;
6430 GtkTextLineSegment *segPtr;
6431 int num_children, num_lines, num_chars, toggle_count, min_children;
6432 GtkTextLineData *ld;
6435 if (node->parent != NULL)
6437 min_children = MIN_CHILDREN;
6439 else if (node->level > 0)
6446 if ((node->num_children < min_children)
6447 || (node->num_children > MAX_CHILDREN))
6449 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6450 node->num_children);
6453 nd = node->node_data;
6456 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6463 if (node->level == 0)
6465 for (line = node->children.line; line != NULL;
6468 if (line->parent != node)
6470 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6472 if (line->segments == NULL)
6474 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6480 /* Just ensuring we don't segv while doing this loop */
6485 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6487 if (segPtr->type->checkFunc != NULL)
6489 (*segPtr->type->checkFunc)(segPtr, line);
6491 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6492 && (segPtr->next != NULL)
6493 && (segPtr->next->byte_count == 0)
6494 && (segPtr->next->type->leftGravity))
6496 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6498 if ((segPtr->next == NULL)
6499 && (segPtr->type != >k_text_char_type))
6501 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6504 num_chars += segPtr->char_count;
6513 for (childnode = node->children.node; childnode != NULL;
6514 childnode = childnode->next)
6516 if (childnode->parent != node)
6518 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6520 if (childnode->level != (node->level-1))
6522 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6523 node->level, childnode->level);
6525 gtk_text_btree_node_check_consistency (tree, childnode);
6526 for (summary = childnode->summary; summary != NULL;
6527 summary = summary->next)
6529 for (summary2 = node->summary; ;
6530 summary2 = summary2->next)
6532 if (summary2 == NULL)
6534 if (summary->info->tag_root == node)
6538 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6539 summary->info->tag->name,
6540 "present in parent summaries");
6542 if (summary->info == summary2->info)
6549 num_lines += childnode->num_lines;
6550 num_chars += childnode->num_chars;
6553 if (num_children != node->num_children)
6555 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6556 num_children, node->num_children);
6558 if (num_lines != node->num_lines)
6560 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6561 num_lines, node->num_lines);
6563 if (num_chars != node->num_chars)
6565 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6566 num_chars, node->num_chars);
6569 for (summary = node->summary; summary != NULL;
6570 summary = summary->next)
6572 if (summary->info->toggle_count == summary->toggle_count)
6574 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6575 summary->info->tag->name);
6578 if (node->level == 0)
6580 for (line = node->children.line; line != NULL;
6583 for (segPtr = line->segments; segPtr != NULL;
6584 segPtr = segPtr->next)
6586 if ((segPtr->type != >k_text_toggle_on_type)
6587 && (segPtr->type != >k_text_toggle_off_type))
6591 if (segPtr->body.toggle.info == summary->info)
6593 if (!segPtr->body.toggle.inNodeCounts)
6594 g_error ("Toggle segment not in the node counts");
6603 for (childnode = node->children.node;
6605 childnode = childnode->next)
6607 for (summary2 = childnode->summary;
6609 summary2 = summary2->next)
6611 if (summary2->info == summary->info)
6613 toggle_count += summary2->toggle_count;
6618 if (toggle_count != summary->toggle_count)
6620 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6621 toggle_count, summary->toggle_count);
6623 for (summary2 = summary->next; summary2 != NULL;
6624 summary2 = summary2->next)
6626 if (summary2->info == summary->info)
6628 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6629 summary->info->tag->name);
6636 listify_foreach (GtkTextTag *tag, gpointer user_data)
6638 GSList** listp = user_data;
6640 *listp = g_slist_prepend (*listp, tag);
6644 list_of_tags (GtkTextTagTable *table)
6646 GSList *list = NULL;
6648 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6654 _gtk_text_btree_check (GtkTextBTree *tree)
6657 GtkTextBTreeNode *node;
6659 GtkTextLineSegment *seg;
6661 GSList *taglist = NULL;
6663 GtkTextTagInfo *info;
6666 * Make sure that the tag toggle counts and the tag root pointers are OK.
6668 for (taglist = list_of_tags (tree->table);
6669 taglist != NULL ; taglist = taglist->next)
6671 tag = taglist->data;
6672 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6675 node = info->tag_root;
6678 if (info->toggle_count != 0)
6680 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6681 tag->name, info->toggle_count);
6683 continue; /* no ranges for the tag */
6685 else if (info->toggle_count == 0)
6687 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6690 else if (info->toggle_count & 1)
6692 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6693 tag->name, info->toggle_count);
6695 for (summary = node->summary; summary != NULL;
6696 summary = summary->next)
6698 if (summary->info->tag == tag)
6700 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6704 if (node->level > 0)
6706 for (node = node->children.node ; node != NULL ;
6709 for (summary = node->summary; summary != NULL;
6710 summary = summary->next)
6712 if (summary->info->tag == tag)
6714 count += summary->toggle_count;
6721 GtkTextLineSegmentClass * last = NULL;
6723 for (line = node->children.line ; line != NULL ;
6726 for (seg = line->segments; seg != NULL;
6729 if ((seg->type == >k_text_toggle_on_type ||
6730 seg->type == >k_text_toggle_off_type) &&
6731 seg->body.toggle.info->tag == tag)
6733 if (last == seg->type)
6734 g_error ("Two consecutive toggles on or off weren't merged");
6735 if (!seg->body.toggle.inNodeCounts)
6736 g_error ("Toggle segment not in the node counts");
6745 if (count != info->toggle_count)
6747 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6748 info->toggle_count, tag->name, count);
6753 g_slist_free (taglist);
6757 * Call a recursive procedure to do the main body of checks.
6760 node = tree->root_node;
6761 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6764 * Make sure that there are at least two lines in the text and
6765 * that the last line has no characters except a newline.
6768 if (node->num_lines < 2)
6770 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6772 if (node->num_chars < 2)
6774 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6776 while (node->level > 0)
6778 node = node->children.node;
6779 while (node->next != NULL)
6784 line = node->children.line;
6785 while (line->next != NULL)
6789 seg = line->segments;
6790 while ((seg->type == >k_text_toggle_off_type)
6791 || (seg->type == >k_text_right_mark_type)
6792 || (seg->type == >k_text_left_mark_type))
6795 * It's OK to toggle a tag off in the last line, but
6796 * not to start a new range. It's also OK to have marks
6802 if (seg->type != >k_text_char_type)
6804 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6806 if (seg->next != NULL)
6808 g_error ("_gtk_text_btree_check: last line has too many segments");
6810 if (seg->byte_count != 1)
6812 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6815 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6817 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6822 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6823 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6824 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6825 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6828 _gtk_text_btree_spew (GtkTextBTree *tree)
6833 printf ("%d lines in tree %p\n",
6834 _gtk_text_btree_line_count (tree), tree);
6836 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6838 while (line != NULL)
6840 _gtk_text_btree_spew_line (tree, line);
6841 line = _gtk_text_line_next (line);
6844 printf ("=================== Tag information\n");
6849 list = tree->tag_infos;
6851 while (list != NULL)
6853 GtkTextTagInfo *info;
6857 printf (" tag `%s': root at %p, toggle count %d\n",
6858 info->tag->name, info->tag_root, info->toggle_count);
6860 list = g_slist_next (list);
6863 if (tree->tag_infos == NULL)
6865 printf (" (no tags in the tree)\n");
6869 printf ("=================== Tree nodes\n");
6872 _gtk_text_btree_spew_node (tree->root_node, 0);
6877 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6880 GtkTextLineSegment *seg;
6882 spaces = g_strnfill (indent, ' ');
6884 printf ("%sline %p chars %d bytes %d\n",
6886 _gtk_text_line_char_count (line),
6887 _gtk_text_line_byte_count (line));
6889 seg = line->segments;
6892 if (seg->type == >k_text_char_type)
6894 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6899 if (*s == '\n' || *s == '\r')
6903 printf ("%s chars `%s'...\n", spaces, str);
6906 else if (seg->type == >k_text_right_mark_type)
6908 printf ("%s right mark `%s' visible: %d\n",
6910 seg->body.mark.name,
6911 seg->body.mark.visible);
6913 else if (seg->type == >k_text_left_mark_type)
6915 printf ("%s left mark `%s' visible: %d\n",
6917 seg->body.mark.name,
6918 seg->body.mark.visible);
6920 else if (seg->type == >k_text_toggle_on_type ||
6921 seg->type == >k_text_toggle_off_type)
6923 printf ("%s tag `%s' %s\n",
6924 spaces, seg->body.toggle.info->tag->name,
6925 seg->type == >k_text_toggle_off_type ? "off" : "on");
6935 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6938 GtkTextBTreeNode *iter;
6941 spaces = g_strnfill (indent, ' ');
6943 printf ("%snode %p level %d children %d lines %d chars %d\n",
6944 spaces, node, node->level,
6945 node->num_children, node->num_lines, node->num_chars);
6950 printf ("%s %d toggles of `%s' below this node\n",
6951 spaces, s->toggle_count, s->info->tag->name);
6957 if (node->level > 0)
6959 iter = node->children.node;
6960 while (iter != NULL)
6962 _gtk_text_btree_spew_node (iter, indent + 2);
6969 GtkTextLine *line = node->children.line;
6970 while (line != NULL)
6972 _gtk_text_btree_spew_line_short (line, indent + 2);
6980 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
6982 GtkTextLineSegment * seg;
6984 printf ("%4d| line: %p parent: %p next: %p\n",
6985 _gtk_text_line_get_number (line), line, line->parent, line->next);
6987 seg = line->segments;
6991 _gtk_text_btree_spew_segment (tree, seg);
6997 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
6999 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7000 seg, seg->type->name, seg->byte_count, seg->char_count);
7002 if (seg->type == >k_text_char_type)
7004 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7005 printf (" `%s'\n", str);
7008 else if (seg->type == >k_text_right_mark_type)
7010 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7011 seg->body.mark.name,
7012 seg->body.mark.visible,
7013 seg->body.mark.not_deleteable);
7015 else if (seg->type == >k_text_left_mark_type)
7017 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7018 seg->body.mark.name,
7019 seg->body.mark.visible,
7020 seg->body.mark.not_deleteable);
7022 else if (seg->type == >k_text_toggle_on_type ||
7023 seg->type == >k_text_toggle_off_type)
7025 printf (" tag `%s' priority %d\n",
7026 seg->body.toggle.info->tag->name,
7027 seg->body.toggle.info->tag->priority);