4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
61 #include "gtksignal.h"
62 #include "gtktexttag.h"
63 #include "gtktexttagtable.h"
64 #include "gtktextlayout.h"
65 #include "gtktextiterprivate.h"
67 #include "gtktextmarkprivate.h"
75 * The structure below is used to pass information between
76 * _gtk_text_btree_get_tags and inc_count:
79 typedef struct TagInfo {
80 int numTags; /* Number of tags for which there
81 * is currently information in
83 int arraySize; /* Number of entries allocated for
85 GtkTextTag **tags; /* Array of tags seen so far.
87 int *counts; /* Toggle count (so far) for each
88 * entry in tags. Malloc-ed. */
93 * This is used to store per-view width/height info at the tree nodes.
96 typedef struct _NodeData NodeData;
102 /* Height and width of this node */
106 /* boolean indicating whether the lines below this node are in need of validation.
107 * However, width/height should always represent the current total width and
108 * max height for lines below this node; the valid flag indicates whether the
109 * width/height on the lines needs recomputing, not whether the totals
117 * The data structure below keeps summary information about one tag as part
118 * of the tag information in a node.
121 typedef struct Summary {
122 GtkTextTagInfo *info; /* Handle for tag. */
123 int toggle_count; /* Number of transitions into or
124 * out of this tag that occur in
125 * the subtree rooted at this node. */
126 struct Summary *next; /* Next in list of all tags for same
127 * node, or NULL if at end of list. */
131 * The data structure below defines a node in the B-tree.
134 struct _GtkTextBTreeNode {
135 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
136 * this is the root. */
137 GtkTextBTreeNode *next; /* Next in list of siblings with the
138 * same parent node, or NULL for end
140 Summary *summary; /* First in malloc-ed list of info
141 * about tags in this subtree (NULL if
142 * no tag info in the subtree). */
143 int level; /* Level of this node in the B-tree.
144 * 0 refers to the bottom of the tree
145 * (children are lines, not nodes). */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
150 int num_children; /* Number of children of this node. */
151 int num_lines; /* Total number of lines (leaves) in
152 * the subtree rooted here. */
153 int num_chars; /* Number of chars below here */
160 * Used to store the list of views in our btree
163 typedef struct _BTreeView BTreeView;
167 GtkTextLayout *layout;
173 * And the tree itself
176 struct _GtkTextBTree {
177 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
178 GtkTextTagTable *table;
179 GHashTable *mark_table;
181 GtkTextMark *insert_mark;
182 GtkTextMark *selection_bound_mark;
183 GtkTextBuffer *buffer;
186 guint tag_changed_handler;
188 /* Incremented when a segment with a byte size > 0
189 * is added to or removed from the tree (i.e. the
190 * length of a line may have changed, and lines may
191 * have been added or removed). This invalidates
192 * all outstanding iterators.
194 guint chars_changed_stamp;
195 /* Incremented when any segments are added or deleted;
196 * this makes outstanding iterators recalculate their
197 * pointed-to segment and segment offset.
199 guint segments_changed_stamp;
201 /* Cache the last line in the buffer */
202 GtkTextLine *last_line;
203 guint last_line_stamp;
205 /* Cache the next-to-last line in the buffer,
206 * containing the end iterator
208 GtkTextLine *end_iter_line;
209 GtkTextLineSegment *end_iter_segment;
210 int end_iter_segment_byte_index;
211 int end_iter_segment_char_offset;
212 guint end_iter_line_stamp;
213 guint end_iter_segment_stamp;
215 GHashTable *child_anchor_table;
220 * Upper and lower bounds on how many children a node may have:
221 * rebalance when either of these limits is exceeded. MAX_CHILDREN
222 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
225 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
226 shallow. It appears to be faster to locate a particular line number
227 if the tree is narrow and deep, since it is more finely sorted. I
228 guess this may increase memory use though, and make it slower to
229 walk the tree in order, or locate a particular byte index (which
230 is done by walking the tree in order).
232 There's basically a tradeoff here. However I'm thinking we want to
233 add pixels, byte counts, and char counts to the tree nodes,
234 at that point narrow and deep should speed up all operations,
235 not just the line number searches.
239 #define MAX_CHILDREN 12
240 #define MIN_CHILDREN 6
242 #define MAX_CHILDREN 6
243 #define MIN_CHILDREN 3
250 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
252 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
253 GtkTextBTreeNode *node);
254 static GtkTextLine * get_last_line (GtkTextBTree *tree);
255 static void post_insert_fixup (GtkTextBTree *tree,
256 GtkTextLine *insert_line,
257 gint char_count_delta,
258 gint line_count_delta);
259 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
260 GtkTextTagInfo *info,
262 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
265 static void segments_changed (GtkTextBTree *tree);
266 static void chars_changed (GtkTextBTree *tree);
267 static void summary_list_destroy (Summary *summary);
268 static GtkTextLine *gtk_text_line_new (void);
269 static void gtk_text_line_destroy (GtkTextBTree *tree,
271 static void gtk_text_line_set_parent (GtkTextLine *line,
272 GtkTextBTreeNode *node);
273 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277 static NodeData *node_data_new (gpointer view_id);
278 static void node_data_destroy (NodeData *nd);
279 static void node_data_list_destroy (NodeData *nd);
280 static NodeData *node_data_find (NodeData *nd,
283 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
284 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
285 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
289 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
291 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
294 static void gtk_text_btree_node_remove_view (BTreeView *view,
295 GtkTextBTreeNode *node,
297 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
300 GtkTextBTreeNode *node);
301 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
305 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
309 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
310 GtkTextBTreeNode *node2);
311 static void get_tree_bounds (GtkTextBTree *tree,
314 static void tag_changed_cb (GtkTextTagTable *table,
316 gboolean size_changed,
318 static void cleanup_line (GtkTextLine *line);
319 static void recompute_node_counts (GtkTextBTree *tree,
320 GtkTextBTreeNode *node);
321 static void inc_count (GtkTextTag *tag,
323 TagInfo *tagInfoPtr);
325 static void summary_destroy (Summary *summary);
327 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
328 const GtkTextIter *iter);
329 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
330 GtkTextLineSegment *seg,
334 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
336 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
338 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
341 static void redisplay_region (GtkTextBTree *tree,
342 const GtkTextIter *start,
343 const GtkTextIter *end);
345 /* Inline thingies */
348 segments_changed (GtkTextBTree *tree)
350 tree->segments_changed_stamp += 1;
354 chars_changed (GtkTextBTree *tree)
356 tree->chars_changed_stamp += 1;
364 _gtk_text_btree_new (GtkTextTagTable *table,
365 GtkTextBuffer *buffer)
368 GtkTextBTreeNode *root_node;
369 GtkTextLine *line, *line2;
371 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
372 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
375 * The tree will initially have two empty lines. The second line
376 * isn't actually part of the tree's contents, but its presence
377 * makes several operations easier. The tree will have one GtkTextBTreeNode,
378 * which is also the root of the tree.
381 /* Create the root node. */
383 root_node = gtk_text_btree_node_new ();
385 line = gtk_text_line_new ();
386 line2 = gtk_text_line_new ();
388 root_node->parent = NULL;
389 root_node->next = NULL;
390 root_node->summary = NULL;
391 root_node->level = 0;
392 root_node->children.line = line;
393 root_node->num_children = 2;
394 root_node->num_lines = 2;
395 root_node->num_chars = 2;
397 line->parent = root_node;
400 line->segments = _gtk_char_segment_new ("\n", 1);
402 line2->parent = root_node;
404 line2->segments = _gtk_char_segment_new ("\n", 1);
406 /* Create the tree itself */
408 tree = g_new0(GtkTextBTree, 1);
409 tree->root_node = root_node;
413 /* Set these to values that are unlikely to be found
414 * in random memory garbage, and also avoid
415 * duplicates between tree instances.
417 tree->chars_changed_stamp = g_random_int ();
418 tree->segments_changed_stamp = g_random_int ();
420 tree->last_line_stamp = tree->chars_changed_stamp - 1;
421 tree->last_line = NULL;
423 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
424 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
425 tree->end_iter_line = NULL;
426 tree->end_iter_segment_byte_index = 0;
427 tree->end_iter_segment_char_offset = 0;
429 g_object_ref (G_OBJECT (tree->table));
431 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
433 G_CALLBACK (tag_changed_cb),
436 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
437 tree->child_anchor_table = NULL;
439 /* We don't ref the buffer, since the buffer owns us;
440 * we'd have some circularity issues. The buffer always
441 * lasts longer than the BTree
443 tree->buffer = buffer;
447 GtkTextLineSegment *seg;
449 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
452 tree->insert_mark = _gtk_text_btree_set_mark (tree,
459 seg = tree->insert_mark->segment;
461 seg->body.mark.not_deleteable = TRUE;
462 seg->body.mark.visible = TRUE;
464 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
471 seg = tree->selection_bound_mark->segment;
473 seg->body.mark.not_deleteable = TRUE;
475 g_object_ref (G_OBJECT (tree->insert_mark));
476 g_object_ref (G_OBJECT (tree->selection_bound_mark));
485 _gtk_text_btree_ref (GtkTextBTree *tree)
487 g_return_if_fail (tree != NULL);
488 g_return_if_fail (tree->refcount > 0);
494 _gtk_text_btree_unref (GtkTextBTree *tree)
496 g_return_if_fail (tree != NULL);
497 g_return_if_fail (tree->refcount > 0);
501 if (tree->refcount == 0)
503 gtk_text_btree_node_destroy (tree, tree->root_node);
505 g_assert (g_hash_table_size (tree->mark_table) == 0);
506 g_hash_table_destroy (tree->mark_table);
508 g_object_unref (G_OBJECT (tree->insert_mark));
509 g_object_unref (G_OBJECT (tree->selection_bound_mark));
511 g_signal_handler_disconnect (G_OBJECT (tree->table),
512 tree->tag_changed_handler);
514 g_object_unref (G_OBJECT (tree->table));
521 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
527 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
529 return tree->chars_changed_stamp;
533 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
535 return tree->segments_changed_stamp;
539 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
541 g_return_if_fail (tree != NULL);
542 segments_changed (tree);
546 * Indexable segment mutation
550 _gtk_text_btree_delete (GtkTextIter *start,
553 GtkTextLineSegment *prev_seg; /* The segment just before the start
554 * of the deletion range. */
555 GtkTextLineSegment *last_seg; /* The segment just after the end
556 * of the deletion range. */
557 GtkTextLineSegment *seg, *next;
558 GtkTextLine *curline;
559 GtkTextBTreeNode *curnode, *node;
561 GtkTextLine *start_line;
562 GtkTextLine *end_line;
563 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
564 gint start_byte_offset;
566 g_return_if_fail (start != NULL);
567 g_return_if_fail (end != NULL);
568 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
569 _gtk_text_iter_get_btree (end));
571 gtk_text_iter_order (start, end);
573 tree = _gtk_text_iter_get_btree (start);
575 if (gtk_debug_flags & GTK_DEBUG_TEXT)
576 _gtk_text_btree_check (tree);
579 /* FIXME this code should no longer be required */
581 * The code below is ugly, but it's needed to make sure there
582 * is always a dummy empty line at the end of the text. If the
583 * final newline of the file (just before the dummy line) is being
584 * deleted, then back up index to just before the newline. If
585 * there is a newline just before the first character being deleted,
586 * then back up the first index too, so that an even number of lines
587 * gets deleted. Furthermore, remove any tags that are present on
588 * the newline that isn't going to be deleted after all (this simulates
589 * deleting the newline and then adding a "clean" one back again).
595 line1 = gtk_text_iter_get_line (start);
596 line2 = gtk_text_iter_get_line (end);
598 if (line2 == _gtk_text_btree_line_count (tree))
602 GtkTextIter orig_end;
605 gtk_text_iter_backward_char (end);
609 if (gtk_text_iter_get_line_offset (start) == 0 &&
612 gtk_text_iter_backward_char (start);
616 tags = _gtk_text_btree_get_tags (end,
624 while (i < array_size)
626 _gtk_text_btree_tag (end, &orig_end, tags[i], FALSE);
636 /* Broadcast the need for redisplay before we break the iterators */
637 _gtk_text_btree_invalidate_region (tree, start, end);
639 /* Save the byte offset so we can reset the iterators */
640 start_byte_offset = gtk_text_iter_get_line_index (start);
642 start_line = _gtk_text_iter_get_text_line (start);
643 end_line = _gtk_text_iter_get_text_line (end);
646 * Split the start and end segments, so we have a place
647 * to insert our new text.
649 * Tricky point: split at end first; otherwise the split
650 * at end may invalidate seg and/or prev_seg. This allows
651 * us to avoid invalidating segments for start.
654 last_seg = gtk_text_line_segment_split (end);
655 if (last_seg != NULL)
656 last_seg = last_seg->next;
658 last_seg = end_line->segments;
660 prev_seg = gtk_text_line_segment_split (start);
661 if (prev_seg != NULL)
663 seg = prev_seg->next;
664 prev_seg->next = last_seg;
668 seg = start_line->segments;
669 start_line->segments = last_seg;
672 /* notify iterators that their segments need recomputation,
673 just for robustness. */
674 segments_changed (tree);
677 * Delete all of the segments between prev_seg and last_seg.
680 curline = start_line;
681 curnode = curline->parent;
682 while (seg != last_seg)
688 GtkTextLine *nextline;
691 * We just ran off the end of a line. First find the
692 * next line, then go back to the old line and delete it
693 * (unless it's the starting line for the range).
696 nextline = _gtk_text_line_next (curline);
697 if (curline != start_line)
699 if (curnode == start_line->parent)
700 start_line->next = curline->next;
702 curnode->children.line = curline->next;
704 for (node = curnode; node != NULL;
707 /* Don't update node->num_chars, because
708 * that was done when we deleted the segments.
710 node->num_lines -= 1;
713 curnode->num_children -= 1;
714 curline->next = deleted_lines;
715 deleted_lines = curline;
719 seg = curline->segments;
722 * If the GtkTextBTreeNode is empty then delete it and its parents,
723 * recursively upwards until a non-empty GtkTextBTreeNode is found.
726 while (curnode->num_children == 0)
728 GtkTextBTreeNode *parent;
730 parent = curnode->parent;
731 if (parent->children.node == curnode)
733 parent->children.node = curnode->next;
737 GtkTextBTreeNode *prevnode = parent->children.node;
738 while (prevnode->next != curnode)
740 prevnode = prevnode->next;
742 prevnode->next = curnode->next;
744 parent->num_children--;
745 gtk_text_btree_node_free_empty (tree, curnode);
748 curnode = curline->parent;
753 char_count = seg->char_count;
755 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
758 * This segment refuses to die. Move it to prev_seg and
759 * advance prev_seg if the segment has left gravity.
762 if (prev_seg == NULL)
764 seg->next = start_line->segments;
765 start_line->segments = seg;
769 seg->next = prev_seg->next;
770 prev_seg->next = seg;
772 if (seg->type->leftGravity)
779 /* Segment is gone. Decrement the char count of the node and
781 for (node = curnode; node != NULL;
784 node->num_chars -= char_count;
792 * If the beginning and end of the deletion range are in different
793 * lines, join the two lines together and discard the ending line.
796 if (start_line != end_line)
799 GtkTextBTreeNode *ancestor_node;
800 GtkTextLine *prevline;
803 /* last_seg was appended to start_line up at the top of this function */
805 for (seg = last_seg; seg != NULL;
808 chars_moved += seg->char_count;
809 if (seg->type->lineChangeFunc != NULL)
811 (*seg->type->lineChangeFunc)(seg, end_line);
815 for (node = start_line->parent; node != NULL;
818 node->num_chars += chars_moved;
821 curnode = end_line->parent;
822 for (node = curnode; node != NULL;
825 node->num_chars -= chars_moved;
828 curnode->num_children--;
829 prevline = curnode->children.line;
830 if (prevline == end_line)
832 curnode->children.line = end_line->next;
836 while (prevline->next != end_line)
838 prevline = prevline->next;
840 prevline->next = end_line->next;
842 end_line->next = deleted_lines;
843 deleted_lines = end_line;
845 /* We now fix up the per-view aggregates. We add all the height and
846 * width for the deleted lines to the start line, so that when revalidation
847 * occurs, the correct change in size is seen.
849 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
856 gint deleted_width = 0;
857 gint deleted_height = 0;
859 line = deleted_lines;
862 GtkTextLine *next_line = line->next;
863 ld = _gtk_text_line_get_data (line, view->view_id);
867 deleted_width = MAX (deleted_width, ld->width);
868 deleted_height += ld->height;
872 gtk_text_line_destroy (tree, line);
877 if (deleted_width > 0 || deleted_height > 0)
879 ld = _gtk_text_line_get_data (start_line, view->view_id);
881 /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
882 * no way to add ld without also validating the node, which would
883 * be improper at this point.
885 /* This assertion does actually fail sometimes, must
886 fix before stable release -hp */
889 ld->width = MAX (deleted_width, ld->width);
890 ld->height += deleted_height;
894 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
895 if (ancestor_node->parent)
896 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
901 /* avoid dangling pointer */
902 deleted_lines = NULL;
904 gtk_text_btree_rebalance (tree, curnode);
908 * Cleanup the segments in the new line.
911 cleanup_line (start_line);
914 * Lastly, rebalance the first GtkTextBTreeNode of the range.
917 gtk_text_btree_rebalance (tree, start_line->parent);
919 /* Notify outstanding iterators that they
921 chars_changed (tree);
922 segments_changed (tree);
924 if (gtk_debug_flags & GTK_DEBUG_TEXT)
925 _gtk_text_btree_check (tree);
927 /* Re-initialize our iterators */
928 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
933 _gtk_text_btree_insert (GtkTextIter *iter,
937 GtkTextLineSegment *prev_seg; /* The segment just before the first
938 * new segment (NULL means new segment
939 * is at beginning of line). */
940 GtkTextLineSegment *cur_seg; /* Current segment; new characters
941 * are inserted just after this one.
942 * NULL means insert at beginning of
944 GtkTextLine *line; /* Current line (new segments are
945 * added to this line). */
946 GtkTextLineSegment *seg;
947 GtkTextLine *newline;
948 int chunk_len; /* # characters in current chunk. */
949 gint sol; /* start of line */
950 gint eol; /* Pointer to character just after last
951 * one in current chunk.
953 gint delim; /* index of paragraph delimiter */
954 int line_count_delta; /* Counts change to total number of
958 int char_count_delta; /* change to number of chars */
960 gint start_byte_index;
961 GtkTextLine *start_line;
963 g_return_if_fail (text != NULL);
964 g_return_if_fail (iter != NULL);
969 /* extract iterator info */
970 tree = _gtk_text_iter_get_btree (iter);
971 line = _gtk_text_iter_get_text_line (iter);
974 start_byte_index = gtk_text_iter_get_line_index (iter);
976 /* Get our insertion segment split. Note this assumes line allows
977 * char insertions, which isn't true of the "last" line. But iter
978 * should not be on that line, as we assert here.
980 g_assert (!_gtk_text_line_is_last (line, tree));
981 prev_seg = gtk_text_line_segment_split (iter);
984 /* Invalidate all iterators */
985 chars_changed (tree);
986 segments_changed (tree);
989 * Chop the text up into lines and create a new segment for
990 * each line, plus a new line for the leftovers from the
996 line_count_delta = 0;
997 char_count_delta = 0;
1002 pango_find_paragraph_boundary (text + sol,
1007 /* make these relative to the start of the text */
1011 g_assert (eol >= sol);
1012 g_assert (delim >= sol);
1013 g_assert (eol >= delim);
1014 g_assert (sol >= 0);
1015 g_assert (eol <= len);
1017 chunk_len = eol - sol;
1019 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1020 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1022 char_count_delta += seg->char_count;
1024 if (cur_seg == NULL)
1026 seg->next = line->segments;
1027 line->segments = seg;
1031 seg->next = cur_seg->next;
1032 cur_seg->next = seg;
1037 /* chunk didn't end with a paragraph separator */
1038 g_assert (eol == len);
1043 * The chunk ended with a newline, so create a new GtkTextLine
1044 * and move the remainder of the old line to it.
1047 newline = gtk_text_line_new ();
1048 gtk_text_line_set_parent (newline, line->parent);
1049 newline->next = line->next;
1050 line->next = newline;
1051 newline->segments = seg->next;
1059 * Cleanup the starting line for the insertion, plus the ending
1060 * line if it's different.
1063 cleanup_line (start_line);
1064 if (line != start_line)
1066 cleanup_line (line);
1069 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1071 /* Invalidate our region, and reset the iterator the user
1072 passed in to point to the end of the inserted text. */
1078 _gtk_text_btree_get_iter_at_line (tree,
1084 /* We could almost certainly be more efficient here
1085 by saving the information from the insertion loop
1087 gtk_text_iter_forward_chars (&end, char_count_delta);
1089 _gtk_text_btree_invalidate_region (tree,
1093 /* Convenience for the user */
1099 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1100 GtkTextLineSegment *seg)
1104 GtkTextLineSegment *prevPtr;
1107 gint start_byte_offset;
1109 line = _gtk_text_iter_get_text_line (iter);
1110 tree = _gtk_text_iter_get_btree (iter);
1111 start_byte_offset = gtk_text_iter_get_line_index (iter);
1113 prevPtr = gtk_text_line_segment_split (iter);
1114 if (prevPtr == NULL)
1116 seg->next = line->segments;
1117 line->segments = seg;
1121 seg->next = prevPtr->next;
1122 prevPtr->next = seg;
1125 post_insert_fixup (tree, line, 0, seg->char_count);
1127 chars_changed (tree);
1128 segments_changed (tree);
1130 /* reset *iter for the user, and invalidate tree nodes */
1132 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1135 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1137 _gtk_text_btree_invalidate_region (tree, &start, iter);
1141 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1144 GtkTextLineSegment *seg;
1146 seg = _gtk_pixbuf_segment_new (pixbuf);
1148 insert_pixbuf_or_widget_segment (iter, seg);
1152 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1153 GtkTextChildAnchor *anchor)
1155 GtkTextLineSegment *seg;
1158 if (anchor->segment != NULL)
1160 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1164 seg = _gtk_widget_segment_new (anchor);
1166 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1168 insert_pixbuf_or_widget_segment (iter, seg);
1170 if (tree->child_anchor_table == NULL)
1171 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1173 g_hash_table_insert (tree->child_anchor_table,
1174 seg->body.child.obj,
1175 seg->body.child.obj);
1179 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1181 GtkTextLineSegment *seg;
1183 seg = anchor->segment;
1185 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1194 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1195 GtkTextBTreeNode *node, gint y, gint *line_top,
1196 GtkTextLine *last_line)
1200 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1201 _gtk_text_btree_check (tree);
1203 if (node->level == 0)
1207 line = node->children.line;
1209 while (line != NULL && line != last_line)
1211 GtkTextLineData *ld;
1213 ld = _gtk_text_line_get_data (line, view->view_id);
1217 if (y < (current_y + (ld ? ld->height : 0)))
1220 current_y += ld->height;
1221 *line_top += ld->height;
1230 GtkTextBTreeNode *child;
1232 child = node->children.node;
1234 while (child != NULL)
1239 gtk_text_btree_node_get_size (child, view->view_id,
1242 if (y < (current_y + height))
1243 return find_line_by_y (tree, view, child,
1244 y - current_y, line_top,
1247 current_y += height;
1248 *line_top += height;
1250 child = child->next;
1258 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1265 GtkTextLine *last_line;
1268 view = gtk_text_btree_get_view (tree, view_id);
1269 g_return_val_if_fail (view != NULL, NULL);
1271 last_line = get_last_line (tree);
1273 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1277 *line_top_out = line_top;
1283 find_line_top_in_line_list (GtkTextBTree *tree,
1286 GtkTextLine *target_line,
1289 while (line != NULL)
1291 GtkTextLineData *ld;
1293 if (line == target_line)
1296 ld = _gtk_text_line_get_data (line, view->view_id);
1303 g_assert_not_reached (); /* If we get here, our
1304 target line didn't exist
1305 under its parent node */
1310 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1311 GtkTextLine *target_line,
1318 GtkTextBTreeNode *node;
1320 view = gtk_text_btree_get_view (tree, view_id);
1322 g_return_val_if_fail (view != NULL, 0);
1325 node = target_line->parent;
1326 while (node != NULL)
1328 nodes = g_slist_prepend (nodes, node);
1329 node = node->parent;
1333 while (iter != NULL)
1337 if (node->level == 0)
1339 g_slist_free (nodes);
1340 return find_line_top_in_line_list (tree, view,
1341 node->children.line,
1346 GtkTextBTreeNode *child;
1347 GtkTextBTreeNode *target_node;
1349 g_assert (iter->next != NULL); /* not at level 0 */
1350 target_node = iter->next->data;
1352 child = node->children.node;
1354 while (child != NULL)
1359 if (child == target_node)
1363 gtk_text_btree_node_get_size (child, view->view_id,
1367 child = child->next;
1369 g_assert (child != NULL); /* should have broken out before we
1373 iter = g_slist_next (iter);
1376 g_assert_not_reached (); /* we return when we find the target line */
1381 _gtk_text_btree_add_view (GtkTextBTree *tree,
1382 GtkTextLayout *layout)
1385 GtkTextLine *last_line;
1386 GtkTextLineData *line_data;
1388 g_return_if_fail (tree != NULL);
1390 view = g_new (BTreeView, 1);
1392 view->view_id = layout;
1393 view->layout = layout;
1395 view->next = tree->views;
1400 g_assert (tree->views->prev == NULL);
1401 tree->views->prev = view;
1406 /* The last line in the buffer has identity values for the per-view
1407 * data so that we can avoid special case checks for it in a large
1410 last_line = get_last_line (tree);
1412 line_data = g_new (GtkTextLineData, 1);
1413 line_data->view_id = layout;
1414 line_data->next = NULL;
1415 line_data->width = 0;
1416 line_data->height = 0;
1417 line_data->valid = TRUE;
1419 _gtk_text_line_add_data (last_line, line_data);
1423 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1427 GtkTextLine *last_line;
1428 GtkTextLineData *line_data;
1430 g_return_if_fail (tree != NULL);
1434 while (view != NULL)
1436 if (view->view_id == view_id)
1442 g_return_if_fail (view != NULL);
1445 view->next->prev = view->prev;
1448 view->prev->next = view->next;
1450 if (view == tree->views)
1451 tree->views = view->next;
1453 /* Remove the line data for the last line which we added ourselves.
1454 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1456 last_line = get_last_line (tree);
1457 line_data = _gtk_text_line_remove_data (last_line, view_id);
1460 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1462 view->layout = (gpointer) 0xdeadbeef;
1463 view->view_id = (gpointer) 0xdeadbeef;
1469 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1470 const GtkTextIter *start,
1471 const GtkTextIter *end)
1477 while (view != NULL)
1479 gtk_text_layout_invalidate (view->layout, start, end);
1486 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1491 g_return_if_fail (tree != NULL);
1492 g_return_if_fail (view_id != NULL);
1494 gtk_text_btree_node_get_size (tree->root_node, view_id,
1509 iter_stack_new (void)
1512 stack = g_new (IterStack, 1);
1513 stack->iters = NULL;
1520 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1523 if (stack->count > stack->alloced)
1525 stack->alloced = stack->count*2;
1526 stack->iters = g_realloc (stack->iters,
1527 stack->alloced*sizeof (GtkTextIter));
1529 stack->iters[stack->count-1] = *iter;
1533 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1535 if (stack->count == 0)
1540 *iter = stack->iters[stack->count];
1546 iter_stack_free (IterStack *stack)
1548 g_free (stack->iters);
1553 iter_stack_invert (IterStack *stack)
1555 if (stack->count > 0)
1558 guint j = stack->count - 1;
1563 tmp = stack->iters[i];
1564 stack->iters[i] = stack->iters[j];
1565 stack->iters[j] = tmp;
1574 queue_tag_redisplay (GtkTextBTree *tree,
1576 const GtkTextIter *start,
1577 const GtkTextIter *end)
1579 if (_gtk_text_tag_affects_size (tag))
1581 _gtk_text_btree_invalidate_region (tree, start, end);
1583 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1585 /* We only need to queue a redraw, not a relayout */
1586 redisplay_region (tree, start, end);
1589 /* We don't need to do anything if the tag doesn't affect display */
1593 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1594 const GtkTextIter *end_orig,
1598 GtkTextLineSegment *seg, *prev;
1599 GtkTextLine *cleanupline;
1600 gboolean toggled_on;
1601 GtkTextLine *start_line;
1602 GtkTextLine *end_line;
1604 GtkTextIter start, end;
1607 GtkTextTagInfo *info;
1609 g_return_if_fail (start_orig != NULL);
1610 g_return_if_fail (end_orig != NULL);
1611 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1612 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1613 _gtk_text_iter_get_btree (end_orig));
1614 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1617 printf ("%s tag %s from %d to %d\n",
1618 add ? "Adding" : "Removing",
1620 gtk_text_buffer_get_offset (start_orig),
1621 gtk_text_buffer_get_offset (end_orig));
1624 if (gtk_text_iter_equal (start_orig, end_orig))
1627 start = *start_orig;
1630 gtk_text_iter_order (&start, &end);
1632 tree = _gtk_text_iter_get_btree (&start);
1634 queue_tag_redisplay (tree, tag, &start, &end);
1636 info = gtk_text_btree_get_tag_info (tree, tag);
1638 start_line = _gtk_text_iter_get_text_line (&start);
1639 end_line = _gtk_text_iter_get_text_line (&end);
1641 /* Find all tag toggles in the region; we are going to delete them.
1642 We need to find them in advance, because
1643 forward_find_tag_toggle () won't work once we start playing around
1645 stack = iter_stack_new ();
1648 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1649 * which is deliberate - we don't want to delete a toggle at the
1652 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1654 if (gtk_text_iter_compare (&iter, &end) >= 0)
1657 iter_stack_push (stack, &iter);
1660 /* We need to traverse the toggles in order. */
1661 iter_stack_invert (stack);
1664 * See whether the tag is present at the start of the range. If
1665 * the state doesn't already match what we want then add a toggle
1669 toggled_on = gtk_text_iter_has_tag (&start, tag);
1670 if ( (add && !toggled_on) ||
1671 (!add && toggled_on) )
1673 /* This could create a second toggle at the start position;
1674 cleanup_line () will remove it if so. */
1675 seg = _gtk_toggle_segment_new (info, add);
1677 prev = gtk_text_line_segment_split (&start);
1680 seg->next = start_line->segments;
1681 start_line->segments = seg;
1685 seg->next = prev->next;
1689 /* cleanup_line adds the new toggle to the node counts. */
1691 printf ("added toggle at start\n");
1693 /* we should probably call segments_changed, but in theory
1694 any still-cached segments in the iters we are about to
1695 use are still valid, since they're in front
1701 * Scan the range of characters and delete any internal tag
1702 * transitions. Keep track of what the old state was at the end
1703 * of the range, and add a toggle there if it's needed.
1707 cleanupline = start_line;
1708 while (iter_stack_pop (stack, &iter))
1710 GtkTextLineSegment *indexable_seg;
1713 line = _gtk_text_iter_get_text_line (&iter);
1714 seg = _gtk_text_iter_get_any_segment (&iter);
1715 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1717 g_assert (seg != NULL);
1718 g_assert (indexable_seg != NULL);
1719 g_assert (seg != indexable_seg);
1721 prev = line->segments;
1723 /* Find the segment that actually toggles this tag. */
1724 while (seg != indexable_seg)
1726 g_assert (seg != NULL);
1727 g_assert (indexable_seg != NULL);
1728 g_assert (seg != indexable_seg);
1730 if ( (seg->type == >k_text_toggle_on_type ||
1731 seg->type == >k_text_toggle_off_type) &&
1732 (seg->body.toggle.info == info) )
1738 g_assert (seg != NULL);
1739 g_assert (indexable_seg != NULL);
1741 g_assert (seg != indexable_seg); /* If this happens, then
1742 forward_to_tag_toggle was
1744 g_assert (seg->body.toggle.info->tag == tag);
1746 /* If this happens, when previously tagging we didn't merge
1747 overlapping tags. */
1748 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1749 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1751 toggled_on = !toggled_on;
1754 printf ("deleting %s toggle\n",
1755 seg->type == >k_text_toggle_on_type ? "on" : "off");
1757 /* Remove toggle segment from the list. */
1760 line->segments = seg->next;
1764 while (prev->next != seg)
1768 prev->next = seg->next;
1771 /* Inform iterators we've hosed them. This actually reflects a
1772 bit of inefficiency; if you have the same tag toggled on and
1773 off a lot in a single line, we keep having the rescan from
1774 the front of the line. Of course we have to do that to get
1775 "prev" anyway, but here we are doing it an additional
1777 segments_changed (tree);
1779 /* Update node counts */
1780 if (seg->body.toggle.inNodeCounts)
1782 _gtk_change_node_toggle_count (line->parent,
1784 seg->body.toggle.inNodeCounts = FALSE;
1789 /* We only clean up lines when we're done with them, saves some
1790 gratuitous line-segment-traversals */
1792 if (cleanupline != line)
1794 cleanup_line (cleanupline);
1799 iter_stack_free (stack);
1801 /* toggled_on now reflects the toggle state _just before_ the
1802 end iterator. The end iterator could already have a toggle
1803 on or a toggle off. */
1804 if ( (add && !toggled_on) ||
1805 (!add && toggled_on) )
1807 /* This could create a second toggle at the start position;
1808 cleanup_line () will remove it if so. */
1810 seg = _gtk_toggle_segment_new (info, !add);
1812 prev = gtk_text_line_segment_split (&end);
1815 seg->next = end_line->segments;
1816 end_line->segments = seg;
1820 seg->next = prev->next;
1823 /* cleanup_line adds the new toggle to the node counts. */
1824 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1826 printf ("added toggle at end\n");
1831 * Cleanup cleanupline and the last line of the range, if
1832 * these are different.
1835 cleanup_line (cleanupline);
1836 if (cleanupline != end_line)
1838 cleanup_line (end_line);
1841 segments_changed (tree);
1843 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1844 _gtk_text_btree_check (tree);
1853 get_line_internal (GtkTextBTree *tree,
1855 gint *real_line_number,
1856 gboolean include_last)
1858 GtkTextBTreeNode *node;
1863 line_count = _gtk_text_btree_line_count (tree);
1867 if (line_number < 0)
1869 line_number = line_count;
1871 else if (line_number > line_count)
1873 line_number = line_count;
1876 if (real_line_number)
1877 *real_line_number = line_number;
1879 node = tree->root_node;
1880 lines_left = line_number;
1883 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1887 while (node->level != 0)
1889 for (node = node->children.node;
1890 node->num_lines <= lines_left;
1896 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1899 lines_left -= node->num_lines;
1904 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1907 for (line = node->children.line; lines_left > 0;
1913 g_error ("gtk_text_btree_find_line ran out of lines");
1922 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1925 _gtk_text_btree_get_line (tree,
1926 _gtk_text_btree_line_count (tree) - 1,
1931 _gtk_text_btree_get_line (GtkTextBTree *tree,
1933 gint *real_line_number)
1935 return get_line_internal (tree, line_number, real_line_number, TRUE);
1939 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1941 gint *real_line_number)
1943 return get_line_internal (tree, line_number, real_line_number, FALSE);
1947 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1949 gint *line_start_index,
1950 gint *real_char_index)
1952 GtkTextBTreeNode *node;
1954 GtkTextLineSegment *seg;
1959 node = tree->root_node;
1961 /* Clamp to valid indexes (-1 is magic for "highest index"),
1962 * node->num_chars includes the two newlines that aren't really
1965 if (char_index < 0 || char_index >= (node->num_chars - 1))
1967 char_index = node->num_chars - 2;
1970 *real_char_index = char_index;
1973 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1977 chars_left = char_index;
1978 while (node->level != 0)
1980 for (node = node->children.node;
1981 chars_left >= node->num_chars;
1984 chars_left -= node->num_chars;
1986 g_assert (chars_left >= 0);
1990 if (chars_left == 0)
1992 /* Start of a line */
1994 *line_start_index = char_index;
1995 return node->children.line;
1999 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2005 for (line = node->children.line; line != NULL; line = line->next)
2007 seg = line->segments;
2010 if (chars_in_line + seg->char_count > chars_left)
2011 goto found; /* found our line/segment */
2013 chars_in_line += seg->char_count;
2018 chars_left -= chars_in_line;
2026 g_assert (line != NULL); /* hosage, ran out of lines */
2027 g_assert (seg != NULL);
2029 *line_start_index = char_index - chars_left;
2034 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2037 GtkTextBTreeNode *node;
2038 GtkTextLine *siblingline;
2039 GtkTextLineSegment *seg;
2040 int src, dst, index;
2046 #define NUM_TAG_INFOS 10
2048 line = _gtk_text_iter_get_text_line (iter);
2049 tree = _gtk_text_iter_get_btree (iter);
2050 byte_index = gtk_text_iter_get_line_index (iter);
2052 tagInfo.numTags = 0;
2053 tagInfo.arraySize = NUM_TAG_INFOS;
2054 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2055 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2058 * Record tag toggles within the line of indexPtr but preceding
2059 * indexPtr. Note that if this loop segfaults, your
2060 * byte_index probably points past the sum of all
2061 * seg->byte_count */
2063 for (index = 0, seg = line->segments;
2064 (index + seg->byte_count) <= byte_index;
2065 index += seg->byte_count, seg = seg->next)
2067 if ((seg->type == >k_text_toggle_on_type)
2068 || (seg->type == >k_text_toggle_off_type))
2070 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2075 * Record toggles for tags in lines that are predecessors of
2076 * line but under the same level-0 GtkTextBTreeNode.
2079 for (siblingline = line->parent->children.line;
2080 siblingline != line;
2081 siblingline = siblingline->next)
2083 for (seg = siblingline->segments; seg != NULL;
2086 if ((seg->type == >k_text_toggle_on_type)
2087 || (seg->type == >k_text_toggle_off_type))
2089 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2095 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2096 * toggles for all siblings that precede that GtkTextBTreeNode.
2099 for (node = line->parent; node->parent != NULL;
2100 node = node->parent)
2102 GtkTextBTreeNode *siblingPtr;
2105 for (siblingPtr = node->parent->children.node;
2106 siblingPtr != node; siblingPtr = siblingPtr->next)
2108 for (summary = siblingPtr->summary; summary != NULL;
2109 summary = summary->next)
2111 if (summary->toggle_count & 1)
2113 inc_count (summary->info->tag, summary->toggle_count,
2121 * Go through the tag information and squash out all of the tags
2122 * that have even toggle counts (these tags exist before the point
2123 * of interest, but not at the desired character itself).
2126 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2128 if (tagInfo.counts[src] & 1)
2130 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2131 tagInfo.tags[dst] = tagInfo.tags[src];
2137 g_free (tagInfo.counts);
2140 g_free (tagInfo.tags);
2143 return tagInfo.tags;
2147 copy_segment (GString *string,
2148 gboolean include_hidden,
2149 gboolean include_nonchars,
2150 const GtkTextIter *start,
2151 const GtkTextIter *end)
2153 GtkTextLineSegment *end_seg;
2154 GtkTextLineSegment *seg;
2156 if (gtk_text_iter_equal (start, end))
2159 seg = _gtk_text_iter_get_indexable_segment (start);
2160 end_seg = _gtk_text_iter_get_indexable_segment (end);
2162 if (seg->type == >k_text_char_type)
2164 gboolean copy = TRUE;
2165 gint copy_bytes = 0;
2166 gint copy_start = 0;
2168 /* Don't copy if we're invisible; segments are invisible/not
2169 as a whole, no need to check each char */
2170 if (!include_hidden &&
2171 _gtk_text_btree_char_is_invisible (start))
2174 /* printf (" <invisible>\n"); */
2177 copy_start = _gtk_text_iter_get_segment_byte (start);
2181 /* End is in the same segment; need to copy fewer bytes. */
2182 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2184 copy_bytes = end_byte - copy_start;
2187 copy_bytes = seg->byte_count - copy_start;
2189 g_assert (copy_bytes != 0); /* Due to iter equality check at
2190 front of this function. */
2194 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2196 g_string_append_len (string,
2197 seg->body.chars + copy_start,
2201 /* printf (" :%s\n", string->str); */
2203 else if (seg->type == >k_text_pixbuf_type ||
2204 seg->type == >k_text_child_type)
2206 gboolean copy = TRUE;
2208 if (!include_nonchars)
2212 else if (!include_hidden &&
2213 _gtk_text_btree_char_is_invisible (start))
2220 g_string_append_len (string,
2221 gtk_text_unknown_char_utf8,
2229 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2230 const GtkTextIter *end_orig,
2231 gboolean include_hidden,
2232 gboolean include_nonchars)
2234 GtkTextLineSegment *seg;
2235 GtkTextLineSegment *end_seg;
2243 g_return_val_if_fail (start_orig != NULL, NULL);
2244 g_return_val_if_fail (end_orig != NULL, NULL);
2245 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2246 _gtk_text_iter_get_btree (end_orig), NULL);
2248 start = *start_orig;
2251 gtk_text_iter_order (&start, &end);
2253 retval = g_string_new ("");
2255 tree = _gtk_text_iter_get_btree (&start);
2257 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2259 seg = _gtk_text_iter_get_indexable_segment (&iter);
2260 while (seg != end_seg)
2262 copy_segment (retval, include_hidden, include_nonchars,
2265 _gtk_text_iter_forward_indexable_segment (&iter);
2267 seg = _gtk_text_iter_get_indexable_segment (&iter);
2270 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2273 g_string_free (retval, FALSE);
2278 _gtk_text_btree_line_count (GtkTextBTree *tree)
2280 /* Subtract bogus line at the end; we return a count
2282 return tree->root_node->num_lines - 1;
2286 _gtk_text_btree_char_count (GtkTextBTree *tree)
2288 /* Exclude newline in bogus last line and the
2289 * one in the last line that is after the end iterator
2291 return tree->root_node->num_chars - 2;
2294 #define LOTSA_TAGS 1000
2296 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2298 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2300 int deftagCnts[LOTSA_TAGS];
2301 int *tagCnts = deftagCnts;
2302 GtkTextTag *deftags[LOTSA_TAGS];
2303 GtkTextTag **tags = deftags;
2305 GtkTextBTreeNode *node;
2306 GtkTextLine *siblingline;
2307 GtkTextLineSegment *seg;
2314 line = _gtk_text_iter_get_text_line (iter);
2315 tree = _gtk_text_iter_get_btree (iter);
2316 byte_index = gtk_text_iter_get_line_index (iter);
2318 numTags = gtk_text_tag_table_get_size (tree->table);
2320 /* almost always avoid malloc, so stay out of system calls */
2321 if (LOTSA_TAGS < numTags)
2323 tagCnts = g_new (int, numTags);
2324 tags = g_new (GtkTextTag*, numTags);
2327 for (i=0; i<numTags; i++)
2333 * Record tag toggles within the line of indexPtr but preceding
2337 for (index = 0, seg = line->segments;
2338 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2339 index += seg->byte_count, seg = seg->next)
2341 if ((seg->type == >k_text_toggle_on_type)
2342 || (seg->type == >k_text_toggle_off_type))
2344 tag = seg->body.toggle.info->tag;
2345 if (tag->invisible_set && tag->values->invisible)
2347 tags[tag->priority] = tag;
2348 tagCnts[tag->priority]++;
2354 * Record toggles for tags in lines that are predecessors of
2355 * line but under the same level-0 GtkTextBTreeNode.
2358 for (siblingline = line->parent->children.line;
2359 siblingline != line;
2360 siblingline = siblingline->next)
2362 for (seg = siblingline->segments; seg != NULL;
2365 if ((seg->type == >k_text_toggle_on_type)
2366 || (seg->type == >k_text_toggle_off_type))
2368 tag = seg->body.toggle.info->tag;
2369 if (tag->invisible_set && tag->values->invisible)
2371 tags[tag->priority] = tag;
2372 tagCnts[tag->priority]++;
2379 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2380 * for all siblings that precede that GtkTextBTreeNode.
2383 for (node = line->parent; node->parent != NULL;
2384 node = node->parent)
2386 GtkTextBTreeNode *siblingPtr;
2389 for (siblingPtr = node->parent->children.node;
2390 siblingPtr != node; siblingPtr = siblingPtr->next)
2392 for (summary = siblingPtr->summary; summary != NULL;
2393 summary = summary->next)
2395 if (summary->toggle_count & 1)
2397 tag = summary->info->tag;
2398 if (tag->invisible_set && tag->values->invisible)
2400 tags[tag->priority] = tag;
2401 tagCnts[tag->priority] += summary->toggle_count;
2409 * Now traverse from highest priority to lowest,
2410 * take invisible value from first odd count (= on)
2413 for (i = numTags-1; i >=0; i--)
2417 /* FIXME not sure this should be if 0 */
2419 #ifndef ALWAYS_SHOW_SELECTION
2420 /* who would make the selection invisible? */
2421 if ((tag == tkxt->seltag)
2422 && !(tkxt->flags & GOT_FOCUS))
2428 invisible = tags[i]->values->invisible;
2433 if (LOTSA_TAGS < numTags)
2448 redisplay_region (GtkTextBTree *tree,
2449 const GtkTextIter *start,
2450 const GtkTextIter *end)
2453 GtkTextLine *start_line, *end_line;
2455 if (gtk_text_iter_compare (start, end) > 0)
2457 const GtkTextIter *tmp = start;
2462 start_line = _gtk_text_iter_get_text_line (start);
2463 end_line = _gtk_text_iter_get_text_line (end);
2466 while (view != NULL)
2468 gint start_y, end_y;
2469 GtkTextLineData *ld;
2471 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2473 if (end_line == start_line)
2476 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2478 ld = _gtk_text_line_get_data (end_line, view->view_id);
2480 end_y += ld->height;
2482 gtk_text_layout_changed (view->layout, start_y,
2491 redisplay_mark (GtkTextLineSegment *mark)
2496 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2498 mark->body.mark.obj);
2501 gtk_text_iter_forward_char (&end);
2503 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2508 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2510 if (!mark->body.mark.visible)
2513 redisplay_mark (mark);
2517 ensure_not_off_end (GtkTextBTree *tree,
2518 GtkTextLineSegment *mark,
2521 if (gtk_text_iter_get_line (iter) ==
2522 _gtk_text_btree_line_count (tree))
2523 gtk_text_iter_backward_char (iter);
2526 static GtkTextLineSegment*
2527 real_set_mark (GtkTextBTree *tree,
2528 GtkTextMark *existing_mark,
2530 gboolean left_gravity,
2531 const GtkTextIter *where,
2532 gboolean should_exist,
2533 gboolean redraw_selections)
2535 GtkTextLineSegment *mark;
2538 g_return_val_if_fail (tree != NULL, NULL);
2539 g_return_val_if_fail (where != NULL, NULL);
2540 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2543 mark = existing_mark->segment;
2544 else if (name != NULL)
2545 mark = g_hash_table_lookup (tree->mark_table,
2550 if (should_exist && mark == NULL)
2552 g_warning ("No mark `%s' exists!", name);
2556 /* OK if !should_exist and it does already exist, in that case
2562 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2563 _gtk_text_iter_check (&iter);
2567 if (redraw_selections &&
2568 (mark == tree->insert_mark->segment ||
2569 mark == tree->selection_bound_mark->segment))
2571 GtkTextIter old_pos;
2573 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2574 mark->body.mark.obj);
2575 redisplay_region (tree, &old_pos, where);
2579 * don't let visible marks be after the final newline of the
2583 if (mark->body.mark.visible)
2585 ensure_not_off_end (tree, mark, &iter);
2588 /* Redraw the mark's old location. */
2589 redisplay_mark_if_visible (mark);
2591 /* Unlink mark from its current location.
2592 This could hose our iterator... */
2593 gtk_text_btree_unlink_segment (tree, mark,
2594 mark->body.mark.line);
2595 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2596 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2598 segments_changed (tree); /* make sure the iterator recomputes its
2603 mark = _gtk_mark_segment_new (tree,
2607 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2609 if (mark->body.mark.name)
2610 g_hash_table_insert (tree->mark_table,
2611 mark->body.mark.name,
2615 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2616 _gtk_text_iter_check (&iter);
2618 /* Link mark into new location */
2619 gtk_text_btree_link_segment (mark, &iter);
2621 /* Invalidate some iterators. */
2622 segments_changed (tree);
2625 * update the screen at the mark's new location.
2628 redisplay_mark_if_visible (mark);
2630 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2631 _gtk_text_iter_check (&iter);
2633 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2634 _gtk_text_btree_check (tree);
2641 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2642 GtkTextMark *existing_mark,
2644 gboolean left_gravity,
2645 const GtkTextIter *iter,
2646 gboolean should_exist)
2648 GtkTextLineSegment *seg;
2650 seg = real_set_mark (tree, existing_mark,
2651 name, left_gravity, iter, should_exist,
2654 return seg ? seg->body.mark.obj : NULL;
2658 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2662 GtkTextIter tmp_start, tmp_end;
2664 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2666 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2667 tree->selection_bound_mark);
2669 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2681 gtk_text_iter_order (&tmp_start, &tmp_end);
2694 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2695 const GtkTextIter *iter)
2697 GtkTextIter start, end;
2699 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2700 redisplay_region (tree, &start, &end);
2702 /* Move insert AND selection_bound before we redisplay */
2703 real_set_mark (tree, tree->insert_mark,
2704 "insert", FALSE, iter, TRUE, FALSE);
2705 real_set_mark (tree, tree->selection_bound_mark,
2706 "selection_bound", FALSE, iter, TRUE, FALSE);
2710 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2715 g_return_if_fail (tree != NULL);
2716 g_return_if_fail (name != NULL);
2718 mark = g_hash_table_lookup (tree->mark_table,
2721 _gtk_text_btree_remove_mark (tree, mark);
2725 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2726 GtkTextLineSegment *segment)
2729 if (segment->body.mark.name)
2730 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2732 segment->body.mark.tree = NULL;
2733 segment->body.mark.line = NULL;
2735 /* Remove the ref on the mark, which frees segment as a side effect
2736 * if this is the last reference.
2738 g_object_unref (G_OBJECT (segment->body.mark.obj));
2742 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2745 GtkTextLineSegment *segment;
2747 g_return_if_fail (mark != NULL);
2748 g_return_if_fail (tree != NULL);
2750 segment = mark->segment;
2752 if (segment->body.mark.not_deleteable)
2754 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2758 /* This calls cleanup_line and segments_changed */
2759 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2761 _gtk_text_btree_release_mark_segment (tree, segment);
2765 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2766 GtkTextMark *segment)
2768 return segment == tree->insert_mark;
2772 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2773 GtkTextMark *segment)
2775 return segment == tree->selection_bound_mark;
2779 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2782 GtkTextLineSegment *seg;
2784 g_return_val_if_fail (tree != NULL, NULL);
2785 g_return_val_if_fail (name != NULL, NULL);
2787 seg = g_hash_table_lookup (tree->mark_table, name);
2789 return seg ? seg->body.mark.obj : NULL;
2793 * gtk_text_mark_set_visible:
2794 * @mark: a #GtkTextMark
2795 * @setting: visibility of mark
2797 * Sets the visibility of @mark; the insertion point is normally
2798 * visible, i.e. you can see it as a vertical bar. Also, the text
2799 * widget uses a visible mark to indicate where a drop will occur when
2800 * dragging-and-dropping text. Most other marks are not visible.
2801 * Marks are not visible by default.
2805 gtk_text_mark_set_visible (GtkTextMark *mark,
2808 GtkTextLineSegment *seg;
2810 g_return_if_fail (mark != NULL);
2812 seg = mark->segment;
2814 if (seg->body.mark.visible == setting)
2818 seg->body.mark.visible = setting;
2820 redisplay_mark (seg);
2825 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2828 GtkTextBTreeNode *node;
2829 GtkTextTagInfo *info;
2831 g_return_val_if_fail (tree != NULL, NULL);
2835 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2840 if (info->tag_root == NULL)
2843 node = info->tag_root;
2845 /* We know the tag root has instances of the given
2848 continue_outer_loop:
2849 g_assert (node != NULL);
2850 while (node->level > 0)
2852 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2853 node = node->children.node;
2854 while (node != NULL)
2856 if (gtk_text_btree_node_has_tag (node, tag))
2857 goto continue_outer_loop;
2861 g_assert (node != NULL);
2864 g_assert (node != NULL); /* The tag summaries said some node had
2867 g_assert (node->level == 0);
2869 return node->children.line;
2873 /* Looking for any tag at all (tag == NULL).
2874 Unfortunately this can't be done in a simple and efficient way
2875 right now; so I'm just going to return the
2876 first line in the btree. FIXME */
2877 return _gtk_text_btree_get_line (tree, 0, NULL);
2882 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2885 GtkTextBTreeNode *node;
2886 GtkTextBTreeNode *last_node;
2888 GtkTextTagInfo *info;
2890 g_return_val_if_fail (tree != NULL, NULL);
2894 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2896 if (info->tag_root == NULL)
2899 node = info->tag_root;
2900 /* We know the tag root has instances of the given
2903 while (node->level > 0)
2905 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2907 node = node->children.node;
2908 while (node != NULL)
2910 if (gtk_text_btree_node_has_tag (node, tag))
2918 g_assert (node != NULL); /* The tag summaries said some node had
2921 g_assert (node->level == 0);
2923 /* Find the last line in this node */
2924 line = node->children.line;
2925 while (line->next != NULL)
2932 /* This search can't be done efficiently at the moment,
2933 at least not without complexity.
2934 So, we just return the last line.
2936 return _gtk_text_btree_get_end_iter_line (tree);
2946 _gtk_text_line_get_number (GtkTextLine *line)
2949 GtkTextBTreeNode *node, *parent, *node2;
2953 * First count how many lines precede this one in its level-0
2957 node = line->parent;
2959 for (line2 = node->children.line; line2 != line;
2960 line2 = line2->next)
2964 g_error ("gtk_text_btree_line_number couldn't find line");
2970 * Now work up through the levels of the tree one at a time,
2971 * counting how many lines are in GtkTextBTreeNodes preceding the current
2975 for (parent = node->parent ; parent != NULL;
2976 node = parent, parent = parent->parent)
2978 for (node2 = parent->children.node; node2 != node;
2979 node2 = node2->next)
2983 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2985 index += node2->num_lines;
2991 static GtkTextLineSegment*
2992 find_toggle_segment_before_char (GtkTextLine *line,
2996 GtkTextLineSegment *seg;
2997 GtkTextLineSegment *toggle_seg;
3002 seg = line->segments;
3003 while ( (index + seg->char_count) <= char_in_line )
3005 if (((seg->type == >k_text_toggle_on_type)
3006 || (seg->type == >k_text_toggle_off_type))
3007 && (seg->body.toggle.info->tag == tag))
3010 index += seg->char_count;
3017 static GtkTextLineSegment*
3018 find_toggle_segment_before_byte (GtkTextLine *line,
3022 GtkTextLineSegment *seg;
3023 GtkTextLineSegment *toggle_seg;
3028 seg = line->segments;
3029 while ( (index + seg->byte_count) <= byte_in_line )
3031 if (((seg->type == >k_text_toggle_on_type)
3032 || (seg->type == >k_text_toggle_off_type))
3033 && (seg->body.toggle.info->tag == tag))
3036 index += seg->byte_count;
3044 find_toggle_outside_current_line (GtkTextLine *line,
3048 GtkTextBTreeNode *node;
3049 GtkTextLine *sibling_line;
3050 GtkTextLineSegment *seg;
3051 GtkTextLineSegment *toggle_seg;
3053 GtkTextTagInfo *info = NULL;
3056 * No toggle in this line. Look for toggles for the tag in lines
3057 * that are predecessors of line but under the same
3058 * level-0 GtkTextBTreeNode.
3061 sibling_line = line->parent->children.line;
3062 while (sibling_line != line)
3064 seg = sibling_line->segments;
3067 if (((seg->type == >k_text_toggle_on_type)
3068 || (seg->type == >k_text_toggle_off_type))
3069 && (seg->body.toggle.info->tag == tag))
3075 sibling_line = sibling_line->next;
3078 if (toggle_seg != NULL)
3079 return (toggle_seg->type == >k_text_toggle_on_type);
3082 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3083 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3084 * siblings that precede that GtkTextBTreeNode.
3087 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3093 node = line->parent;
3094 while (node->parent != NULL)
3096 GtkTextBTreeNode *sibling_node;
3098 sibling_node = node->parent->children.node;
3099 while (sibling_node != node)
3103 summary = sibling_node->summary;
3104 while (summary != NULL)
3106 if (summary->info == info)
3107 toggles += summary->toggle_count;
3109 summary = summary->next;
3112 sibling_node = sibling_node->next;
3115 if (node == info->tag_root)
3118 node = node->parent;
3122 * An odd number of toggles means that the tag is present at the
3126 return (toggles & 1) != 0;
3129 /* FIXME this function is far too slow, for no good reason. */
3131 _gtk_text_line_char_has_tag (GtkTextLine *line,
3136 GtkTextLineSegment *toggle_seg;
3138 g_return_val_if_fail (line != NULL, FALSE);
3141 * Check for toggles for the tag in the line but before
3142 * the char. If there is one, its type indicates whether or
3143 * not the character is tagged.
3146 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3148 if (toggle_seg != NULL)
3149 return (toggle_seg->type == >k_text_toggle_on_type);
3151 return find_toggle_outside_current_line (line, tree, tag);
3155 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3160 GtkTextLineSegment *toggle_seg;
3162 g_return_val_if_fail (line != NULL, FALSE);
3165 * Check for toggles for the tag in the line but before
3166 * the char. If there is one, its type indicates whether or
3167 * not the character is tagged.
3170 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3172 if (toggle_seg != NULL)
3173 return (toggle_seg->type == >k_text_toggle_on_type);
3175 return find_toggle_outside_current_line (line, tree, tag);
3179 _gtk_text_line_is_last (GtkTextLine *line,
3182 return line == get_last_line (tree);
3186 ensure_end_iter_line (GtkTextBTree *tree)
3188 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3193 /* n_lines is without the magic line at the end */
3194 n_lines = _gtk_text_btree_line_count (tree);
3196 g_assert (n_lines >= 1);
3198 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3200 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3205 ensure_end_iter_segment (GtkTextBTree *tree)
3207 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3209 GtkTextLineSegment *seg;
3210 GtkTextLineSegment *last_with_chars;
3212 ensure_end_iter_line (tree);
3214 last_with_chars = NULL;
3216 seg = tree->end_iter_line->segments;
3219 if (seg->char_count > 0)
3220 last_with_chars = seg;
3224 tree->end_iter_segment = last_with_chars;
3226 /* We know the last char in the last line is '\n' */
3227 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3228 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3230 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3232 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3233 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3238 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3241 ensure_end_iter_line (tree);
3243 return line == tree->end_iter_line;
3247 _gtk_text_btree_is_end (GtkTextBTree *tree,
3249 GtkTextLineSegment *seg,
3253 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3255 /* Do this first to avoid walking segments in most cases */
3256 if (!_gtk_text_line_contains_end_iter (line, tree))
3259 ensure_end_iter_segment (tree);
3261 if (seg != tree->end_iter_segment)
3264 if (byte_index >= 0)
3265 return byte_index == tree->end_iter_segment_byte_index;
3267 return char_offset == tree->end_iter_segment_char_offset;
3271 _gtk_text_line_next (GtkTextLine *line)
3273 GtkTextBTreeNode *node;
3275 if (line->next != NULL)
3280 * This was the last line associated with the particular parent
3281 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3282 * then search down from that GtkTextBTreeNode to find the first
3286 node = line->parent;
3287 while (node != NULL && node->next == NULL)
3288 node = node->parent;
3294 while (node->level > 0)
3296 node = node->children.node;
3299 g_assert (node->children.line != line);
3301 return node->children.line;
3306 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3310 next = _gtk_text_line_next (line);
3312 /* If we were on the end iter line, we can't go to
3315 if (next && next->next == NULL && /* these checks are optimization only */
3316 _gtk_text_line_next (next) == NULL)
3323 _gtk_text_line_previous (GtkTextLine *line)
3325 GtkTextBTreeNode *node;
3326 GtkTextBTreeNode *node2;
3330 * Find the line under this GtkTextBTreeNode just before the starting line.
3332 prev = line->parent->children.line; /* First line at leaf */
3333 while (prev != line)
3335 if (prev->next == line)
3341 g_error ("gtk_text_btree_previous_line ran out of lines");
3345 * This was the first line associated with the particular parent
3346 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3347 * then search down from that GtkTextBTreeNode to find its last line.
3349 for (node = line->parent; ; node = node->parent)
3351 if (node == NULL || node->parent == NULL)
3353 else if (node != node->parent->children.node)
3357 for (node2 = node->parent->children.node; ;
3358 node2 = node2->children.node)
3360 while (node2->next != node)
3361 node2 = node2->next;
3363 if (node2->level == 0)
3369 for (prev = node2->children.line ; ; prev = prev->next)
3371 if (prev->next == NULL)
3375 g_assert_not_reached ();
3380 _gtk_text_line_add_data (GtkTextLine *line,
3381 GtkTextLineData *data)
3383 g_return_if_fail (line != NULL);
3384 g_return_if_fail (data != NULL);
3385 g_return_if_fail (data->view_id != NULL);
3389 data->next = line->views;
3399 _gtk_text_line_remove_data (GtkTextLine *line,
3402 GtkTextLineData *prev;
3403 GtkTextLineData *iter;
3405 g_return_val_if_fail (line != NULL, NULL);
3406 g_return_val_if_fail (view_id != NULL, NULL);
3410 while (iter != NULL)
3412 if (iter->view_id == view_id)
3421 prev->next = iter->next;
3423 line->views = iter->next;
3432 _gtk_text_line_get_data (GtkTextLine *line,
3435 GtkTextLineData *iter;
3437 g_return_val_if_fail (line != NULL, NULL);
3438 g_return_val_if_fail (view_id != NULL, NULL);
3441 while (iter != NULL)
3443 if (iter->view_id == view_id)
3452 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3453 GtkTextLineData *ld)
3455 /* For now this is totally unoptimized. FIXME?
3457 We could probably optimize the case where the width removed
3458 is less than the max width for the parent node,
3459 and the case where the height is unchanged when we re-wrap.
3462 g_return_if_fail (ld != NULL);
3465 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3469 _gtk_text_line_char_count (GtkTextLine *line)
3471 GtkTextLineSegment *seg;
3475 seg = line->segments;
3478 size += seg->char_count;
3485 _gtk_text_line_byte_count (GtkTextLine *line)
3487 GtkTextLineSegment *seg;
3491 seg = line->segments;
3494 size += seg->byte_count;
3502 _gtk_text_line_char_index (GtkTextLine *target_line)
3504 GSList *node_stack = NULL;
3505 GtkTextBTreeNode *iter;
3509 /* Push all our parent nodes onto a stack */
3510 iter = target_line->parent;
3512 g_assert (iter != NULL);
3514 while (iter != NULL)
3516 node_stack = g_slist_prepend (node_stack, iter);
3518 iter = iter->parent;
3521 /* Check that we have the root node on top of the stack. */
3522 g_assert (node_stack != NULL &&
3523 node_stack->data != NULL &&
3524 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3526 /* Add up chars in all nodes before the nodes in our stack.
3530 iter = node_stack->data;
3531 while (iter != NULL)
3533 GtkTextBTreeNode *child_iter;
3534 GtkTextBTreeNode *next_node;
3536 next_node = node_stack->next ?
3537 node_stack->next->data : NULL;
3538 node_stack = g_slist_remove (node_stack, node_stack->data);
3540 if (iter->level == 0)
3542 /* stack should be empty when we're on the last node */
3543 g_assert (node_stack == NULL);
3544 break; /* Our children are now lines */
3547 g_assert (next_node != NULL);
3548 g_assert (iter != NULL);
3549 g_assert (next_node->parent == iter);
3551 /* Add up chars before us in the tree */
3552 child_iter = iter->children.node;
3553 while (child_iter != next_node)
3555 g_assert (child_iter != NULL);
3557 num_chars += child_iter->num_chars;
3559 child_iter = child_iter->next;
3565 g_assert (iter != NULL);
3566 g_assert (iter == target_line->parent);
3568 /* Since we don't store char counts in lines, only in segments, we
3569 have to iterate over the lines adding up segment char counts
3570 until we find our line. */
3571 line = iter->children.line;
3572 while (line != target_line)
3574 g_assert (line != NULL);
3576 num_chars += _gtk_text_line_char_count (line);
3581 g_assert (line == target_line);
3587 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3591 GtkTextLineSegment *seg;
3594 g_return_val_if_fail (line != NULL, NULL);
3596 offset = byte_offset;
3597 seg = line->segments;
3599 while (offset >= seg->byte_count)
3601 g_assert (seg != NULL); /* means an invalid byte index */
3602 offset -= seg->byte_count;
3607 *seg_offset = offset;
3613 _gtk_text_line_char_to_segment (GtkTextLine *line,
3617 GtkTextLineSegment *seg;
3620 g_return_val_if_fail (line != NULL, NULL);
3622 offset = char_offset;
3623 seg = line->segments;
3625 while (offset >= seg->char_count)
3627 g_assert (seg != NULL); /* means an invalid char index */
3628 offset -= seg->char_count;
3633 *seg_offset = offset;
3639 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3643 GtkTextLineSegment *seg;
3646 g_return_val_if_fail (line != NULL, NULL);
3648 offset = byte_offset;
3649 seg = line->segments;
3651 while (offset > 0 && offset >= seg->byte_count)
3653 g_assert (seg != NULL); /* means an invalid byte index */
3654 offset -= seg->byte_count;
3659 *seg_offset = offset;
3665 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3669 GtkTextLineSegment *seg;
3672 g_return_val_if_fail (line != NULL, NULL);
3674 offset = char_offset;
3675 seg = line->segments;
3677 while (offset > 0 && offset >= seg->char_count)
3679 g_assert (seg != NULL); /* means an invalid byte index */
3680 offset -= seg->char_count;
3685 *seg_offset = offset;
3691 _gtk_text_line_byte_to_char (GtkTextLine *line,
3695 GtkTextLineSegment *seg;
3697 g_return_val_if_fail (line != NULL, 0);
3698 g_return_val_if_fail (byte_offset >= 0, 0);
3701 seg = line->segments;
3702 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3703 the next segment) */
3705 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3707 byte_offset -= seg->byte_count;
3708 char_offset += seg->char_count;
3713 g_assert (seg != NULL);
3715 /* Now byte_offset is the offset into the current segment,
3716 and char_offset is the start of the current segment.
3717 Optimize the case where no chars use > 1 byte */
3718 if (seg->byte_count == seg->char_count)
3719 return char_offset + byte_offset;
3722 if (seg->type == >k_text_char_type)
3723 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3726 g_assert (seg->char_count == 1);
3727 g_assert (byte_offset == 0);
3735 _gtk_text_line_char_to_byte (GtkTextLine *line,
3738 g_warning ("FIXME not implemented");
3743 /* FIXME sync with char_locate (or figure out a clean
3744 way to merge the two functions) */
3746 _gtk_text_line_byte_locate (GtkTextLine *line,
3748 GtkTextLineSegment **segment,
3749 GtkTextLineSegment **any_segment,
3750 gint *seg_byte_offset,
3751 gint *line_byte_offset)
3753 GtkTextLineSegment *seg;
3754 GtkTextLineSegment *after_prev_indexable;
3755 GtkTextLineSegment *after_last_indexable;
3756 GtkTextLineSegment *last_indexable;
3760 g_return_val_if_fail (line != NULL, FALSE);
3761 g_return_val_if_fail (byte_offset >= 0, FALSE);
3764 *any_segment = NULL;
3767 offset = byte_offset;
3769 last_indexable = NULL;
3770 after_last_indexable = line->segments;
3771 after_prev_indexable = line->segments;
3772 seg = line->segments;
3774 /* The loop ends when we're inside a segment;
3775 last_indexable refers to the last segment
3776 we passed entirely. */
3777 while (seg && offset >= seg->byte_count)
3779 if (seg->char_count > 0)
3781 offset -= seg->byte_count;
3782 bytes_in_line += seg->byte_count;
3783 last_indexable = seg;
3784 after_prev_indexable = after_last_indexable;
3785 after_last_indexable = last_indexable->next;
3793 /* We went off the end of the line */
3795 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3802 if (after_last_indexable != NULL)
3803 *any_segment = after_last_indexable;
3805 *any_segment = *segment;
3808 /* Override any_segment if we're in the middle of a segment. */
3810 *any_segment = *segment;
3812 *seg_byte_offset = offset;
3814 g_assert (*segment != NULL);
3815 g_assert (*any_segment != NULL);
3816 g_assert (*seg_byte_offset < (*segment)->byte_count);
3818 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3823 /* FIXME sync with byte_locate (or figure out a clean
3824 way to merge the two functions) */
3826 _gtk_text_line_char_locate (GtkTextLine *line,
3828 GtkTextLineSegment **segment,
3829 GtkTextLineSegment **any_segment,
3830 gint *seg_char_offset,
3831 gint *line_char_offset)
3833 GtkTextLineSegment *seg;
3834 GtkTextLineSegment *after_prev_indexable;
3835 GtkTextLineSegment *after_last_indexable;
3836 GtkTextLineSegment *last_indexable;
3840 g_return_val_if_fail (line != NULL, FALSE);
3841 g_return_val_if_fail (char_offset >= 0, FALSE);
3844 *any_segment = NULL;
3847 offset = char_offset;
3849 last_indexable = NULL;
3850 after_last_indexable = line->segments;
3851 after_prev_indexable = line->segments;
3852 seg = line->segments;
3854 /* The loop ends when we're inside a segment;
3855 last_indexable refers to the last segment
3856 we passed entirely. */
3857 while (seg && offset >= seg->char_count)
3859 if (seg->char_count > 0)
3861 offset -= seg->char_count;
3862 chars_in_line += seg->char_count;
3863 last_indexable = seg;
3864 after_prev_indexable = after_last_indexable;
3865 after_last_indexable = last_indexable->next;
3873 /* end of the line */
3875 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3882 if (after_last_indexable != NULL)
3883 *any_segment = after_last_indexable;
3885 *any_segment = *segment;
3888 /* Override any_segment if we're in the middle of a segment. */
3890 *any_segment = *segment;
3892 *seg_char_offset = offset;
3894 g_assert (*segment != NULL);
3895 g_assert (*any_segment != NULL);
3896 g_assert (*seg_char_offset < (*segment)->char_count);
3898 *line_char_offset = chars_in_line + *seg_char_offset;
3904 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3906 gint *line_char_offset,
3907 gint *seg_char_offset)
3909 GtkTextLineSegment *seg;
3912 g_return_if_fail (line != NULL);
3913 g_return_if_fail (byte_offset >= 0);
3915 *line_char_offset = 0;
3917 offset = byte_offset;
3918 seg = line->segments;
3920 while (offset >= seg->byte_count)
3922 offset -= seg->byte_count;
3923 *line_char_offset += seg->char_count;
3925 g_assert (seg != NULL); /* means an invalid char offset */
3928 g_assert (seg->char_count > 0); /* indexable. */
3930 /* offset is now the number of bytes into the current segment we
3931 * want to go. Count chars into the current segment.
3934 if (seg->type == >k_text_char_type)
3936 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3938 g_assert (*seg_char_offset < seg->char_count);
3940 *line_char_offset += *seg_char_offset;
3944 g_assert (offset == 0);
3945 *seg_char_offset = 0;
3950 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3952 gint *line_byte_offset,
3953 gint *seg_byte_offset)
3955 GtkTextLineSegment *seg;
3958 g_return_if_fail (line != NULL);
3959 g_return_if_fail (char_offset >= 0);
3961 *line_byte_offset = 0;
3963 offset = char_offset;
3964 seg = line->segments;
3966 while (offset >= seg->char_count)
3968 offset -= seg->char_count;
3969 *line_byte_offset += seg->byte_count;
3971 g_assert (seg != NULL); /* means an invalid char offset */
3974 g_assert (seg->char_count > 0); /* indexable. */
3976 /* offset is now the number of chars into the current segment we
3977 want to go. Count bytes into the current segment. */
3979 if (seg->type == >k_text_char_type)
3981 *seg_byte_offset = 0;
3985 const char * start = seg->body.chars + *seg_byte_offset;
3987 bytes = g_utf8_next_char (start) - start;
3988 *seg_byte_offset += bytes;
3992 g_assert (*seg_byte_offset < seg->byte_count);
3994 *line_byte_offset += *seg_byte_offset;
3998 g_assert (offset == 0);
3999 *seg_byte_offset = 0;
4004 node_compare (GtkTextBTreeNode *lhs,
4005 GtkTextBTreeNode *rhs)
4007 GtkTextBTreeNode *iter;
4008 GtkTextBTreeNode *node;
4009 GtkTextBTreeNode *common_parent;
4010 GtkTextBTreeNode *parent_of_lower;
4011 GtkTextBTreeNode *parent_of_higher;
4012 gboolean lhs_is_lower;
4013 GtkTextBTreeNode *lower;
4014 GtkTextBTreeNode *higher;
4016 /* This function assumes that lhs and rhs are not underneath each
4023 if (lhs->level < rhs->level)
4025 lhs_is_lower = TRUE;
4031 lhs_is_lower = FALSE;
4036 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4037 * of the common parent we used to reach the common parent; the
4038 * ordering of these child nodes in the child list is the ordering
4042 /* Get on the same level (may be on same level already) */
4044 while (node->level < higher->level)
4045 node = node->parent;
4047 g_assert (node->level == higher->level);
4049 g_assert (node != higher); /* Happens if lower is underneath higher */
4051 /* Go up until we have two children with a common parent.
4053 parent_of_lower = node;
4054 parent_of_higher = higher;
4056 while (parent_of_lower->parent != parent_of_higher->parent)
4058 parent_of_lower = parent_of_lower->parent;
4059 parent_of_higher = parent_of_higher->parent;
4062 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4064 common_parent = parent_of_lower->parent;
4066 g_assert (common_parent != NULL);
4068 /* See which is first in the list of common_parent's children */
4069 iter = common_parent->children.node;
4070 while (iter != NULL)
4072 if (iter == parent_of_higher)
4074 /* higher is less than lower */
4077 return 1; /* lhs > rhs */
4081 else if (iter == parent_of_lower)
4083 /* lower is less than higher */
4086 return -1; /* lhs < rhs */
4094 g_assert_not_reached ();
4098 /* remember that tag == NULL means "any tag" */
4100 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4104 GtkTextBTreeNode *node;
4105 GtkTextTagInfo *info;
4106 gboolean below_tag_root;
4108 g_return_val_if_fail (line != NULL, NULL);
4110 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4111 _gtk_text_btree_check (tree);
4115 /* Right now we can only offer linear-search if the user wants
4116 * to know about any tag toggle at all.
4118 return _gtk_text_line_next_excluding_last (line);
4121 /* Our tag summaries only have node precision, not line
4122 * precision. This means that if any line under a node could contain a
4123 * tag, then any of the others could also contain a tag.
4125 * In the future we could have some mechanism to keep track of how
4126 * many toggles we've found under a node so far, since we have a
4127 * count of toggles under the node. But for now I'm going with KISS.
4130 /* return same-node line, if any. */
4134 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4138 if (info->tag_root == NULL)
4141 if (info->tag_root == line->parent)
4142 return NULL; /* we were at the last line under the tag root */
4144 /* We need to go up out of this node, and on to the next one with
4145 toggles for the target tag. If we're below the tag root, we need to
4146 find the next node below the tag root that has tag summaries. If
4147 we're not below the tag root, we need to see if the tag root is
4148 after us in the tree, and if so, return the first line underneath
4151 node = line->parent;
4152 below_tag_root = FALSE;
4153 while (node != NULL)
4155 if (node == info->tag_root)
4157 below_tag_root = TRUE;
4161 node = node->parent;
4166 node = line->parent;
4167 while (node != info->tag_root)
4169 if (node->next == NULL)
4170 node = node->parent;
4175 if (gtk_text_btree_node_has_tag (node, tag))
4185 ordering = node_compare (line->parent, info->tag_root);
4189 /* Tag root is ahead of us, so search there. */
4190 node = info->tag_root;
4195 /* Tag root is after us, so no more lines that
4196 * could contain the tag.
4201 g_assert_not_reached ();
4206 g_assert (node != NULL);
4208 /* We have to find the first sub-node of this node that contains
4212 while (node->level > 0)
4214 g_assert (node != NULL); /* If this fails, it likely means an
4215 incorrect tag summary led us on a
4216 wild goose chase down this branch of
4218 node = node->children.node;
4219 while (node != NULL)
4221 if (gtk_text_btree_node_has_tag (node, tag))
4227 g_assert (node != NULL);
4228 g_assert (node->level == 0);
4230 return node->children.line;
4234 prev_line_under_node (GtkTextBTreeNode *node,
4239 prev = node->children.line;
4245 while (prev->next != line)
4255 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4259 GtkTextBTreeNode *node;
4260 GtkTextBTreeNode *found_node = NULL;
4261 GtkTextTagInfo *info;
4262 gboolean below_tag_root;
4264 GtkTextBTreeNode *line_ancestor;
4265 GtkTextBTreeNode *line_ancestor_parent;
4267 /* See next_could_contain_tag () for more extensive comments
4268 * on what's going on here.
4271 g_return_val_if_fail (line != NULL, NULL);
4273 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4274 _gtk_text_btree_check (tree);
4278 /* Right now we can only offer linear-search if the user wants
4279 * to know about any tag toggle at all.
4281 return _gtk_text_line_previous (line);
4284 /* Return same-node line, if any. */
4285 prev = prev_line_under_node (line->parent, line);
4289 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4293 if (info->tag_root == NULL)
4296 if (info->tag_root == line->parent)
4297 return NULL; /* we were at the first line under the tag root */
4299 /* Are we below the tag root */
4300 node = line->parent;
4301 below_tag_root = FALSE;
4302 while (node != NULL)
4304 if (node == info->tag_root)
4306 below_tag_root = TRUE;
4310 node = node->parent;
4315 /* Look for a previous node under this tag root that has our
4319 /* this assertion holds because line->parent is not the
4320 * tag root, we are below the tag root, and the tag
4323 g_assert (line->parent->parent != NULL);
4325 line_ancestor = line->parent;
4326 line_ancestor_parent = line->parent->parent;
4328 node = line_ancestor_parent->children.node;
4329 while (node != line_ancestor &&
4330 line_ancestor != info->tag_root)
4332 GSList *child_nodes = NULL;
4335 /* Create reverse-order list of nodes before
4338 while (node != line_ancestor
4341 child_nodes = g_slist_prepend (child_nodes, node);
4346 /* Try to find a node with our tag on it in the list */
4350 GtkTextBTreeNode *this_node = tmp->data;
4352 g_assert (this_node != line_ancestor);
4354 if (gtk_text_btree_node_has_tag (this_node, tag))
4356 found_node = this_node;
4357 g_slist_free (child_nodes);
4361 tmp = g_slist_next (tmp);
4364 g_slist_free (child_nodes);
4366 /* Didn't find anything on this level; go up one level. */
4367 line_ancestor = line_ancestor_parent;
4368 line_ancestor_parent = line_ancestor->parent;
4370 node = line_ancestor_parent->children.node;
4380 ordering = node_compare (line->parent, info->tag_root);
4384 /* Tag root is ahead of us, so no more lines
4391 /* Tag root is after us, so grab last tagged
4392 * line underneath the tag root.
4394 found_node = info->tag_root;
4398 g_assert_not_reached ();
4403 g_assert (found_node != NULL);
4405 /* We have to find the last sub-node of this node that contains
4410 while (node->level > 0)
4412 GSList *child_nodes = NULL;
4414 g_assert (node != NULL); /* If this fails, it likely means an
4415 incorrect tag summary led us on a
4416 wild goose chase down this branch of
4419 node = node->children.node;
4420 while (node != NULL)
4422 child_nodes = g_slist_prepend (child_nodes, node);
4426 node = NULL; /* detect failure to find a child node. */
4429 while (iter != NULL)
4431 if (gtk_text_btree_node_has_tag (iter->data, tag))
4433 /* recurse into this node. */
4438 iter = g_slist_next (iter);
4441 g_slist_free (child_nodes);
4443 g_assert (node != NULL);
4446 g_assert (node != NULL);
4447 g_assert (node->level == 0);
4449 /* this assertion is correct, but slow. */
4450 /* g_assert (node_compare (node, line->parent) < 0); */
4452 /* Return last line in this node. */
4454 prev = node->children.line;
4462 * Non-public function implementations
4466 summary_list_destroy (Summary *summary)
4469 while (summary != NULL)
4471 next = summary->next;
4472 summary_destroy (summary);
4478 get_last_line (GtkTextBTree *tree)
4480 if (tree->last_line_stamp != tree->chars_changed_stamp)
4486 n_lines = _gtk_text_btree_line_count (tree);
4488 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4490 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4492 tree->last_line_stamp = tree->chars_changed_stamp;
4493 tree->last_line = line;
4496 return tree->last_line;
4504 gtk_text_line_new (void)
4508 line = g_new0(GtkTextLine, 1);
4514 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4516 GtkTextLineData *ld;
4517 GtkTextLineData *next;
4519 g_return_if_fail (line != NULL);
4526 view = gtk_text_btree_get_view (tree, ld->view_id);
4528 g_assert (view != NULL);
4531 gtk_text_layout_free_line_data (view->layout, line, ld);
4540 gtk_text_line_set_parent (GtkTextLine *line,
4541 GtkTextBTreeNode *node)
4543 if (line->parent == node)
4545 line->parent = node;
4546 gtk_text_btree_node_invalidate_upward (node, NULL);
4550 cleanup_line (GtkTextLine *line)
4552 GtkTextLineSegment *seg, **prev_p;
4556 * Make a pass over all of the segments in the line, giving each
4557 * a chance to clean itself up. This could potentially change
4558 * the structure of the line, e.g. by merging two segments
4559 * together or having two segments cancel themselves; if so,
4560 * then repeat the whole process again, since the first structure
4561 * change might make other structure changes possible. Repeat
4562 * until eventually there are no changes.
4569 for (prev_p = &line->segments, seg = *prev_p;
4571 prev_p = &(*prev_p)->next, seg = *prev_p)
4573 if (seg->type->cleanupFunc != NULL)
4575 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4588 node_data_new (gpointer view_id)
4592 nd = g_new (NodeData, 1);
4594 nd->view_id = view_id;
4604 node_data_destroy (NodeData *nd)
4610 node_data_list_destroy (NodeData *nd)
4616 while (iter != NULL)
4619 node_data_destroy (iter);
4625 node_data_find (NodeData *nd, gpointer view_id)
4629 if (nd->view_id == view_id)
4637 summary_destroy (Summary *summary)
4639 /* Fill with error-triggering garbage */
4640 summary->info = (void*)0x1;
4641 summary->toggle_count = 567;
4642 summary->next = (void*)0x1;
4646 static GtkTextBTreeNode*
4647 gtk_text_btree_node_new (void)
4649 GtkTextBTreeNode *node;
4651 node = g_new (GtkTextBTreeNode, 1);
4653 node->node_data = NULL;
4659 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4660 GtkTextTagInfo *info,
4665 summary = node->summary;
4666 while (summary != NULL)
4668 if (summary->info == info)
4670 summary->toggle_count += adjust;
4674 summary = summary->next;
4677 if (summary == NULL)
4679 /* didn't find a summary for our tag. */
4680 g_return_if_fail (adjust > 0);
4681 summary = g_new (Summary, 1);
4682 summary->info = info;
4683 summary->toggle_count = adjust;
4684 summary->next = node->summary;
4685 node->summary = summary;
4689 /* Note that the tag root and above do not have summaries
4690 for the tag; only nodes below the tag root have
4693 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4697 summary = node->summary;
4698 while (summary != NULL)
4701 summary->info->tag == tag)
4704 summary = summary->next;
4710 /* Add node and all children to the damage region. */
4712 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4716 nd = node->node_data;
4723 if (node->level == 0)
4727 line = node->children.line;
4728 while (line != NULL)
4730 GtkTextLineData *ld;
4744 GtkTextBTreeNode *child;
4746 child = node->children.node;
4748 while (child != NULL)
4750 gtk_text_btree_node_invalidate_downward (child);
4752 child = child->next;
4758 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4760 GtkTextBTreeNode *iter;
4763 while (iter != NULL)
4769 nd = node_data_find (iter->node_data, view_id);
4771 if (nd == NULL || !nd->valid)
4772 break; /* Once a node is invalid, we know its parents are as well. */
4778 gboolean should_continue = FALSE;
4780 nd = iter->node_data;
4785 should_continue = TRUE;
4792 if (!should_continue)
4793 break; /* This node was totally invalidated, so are its
4797 iter = iter->parent;
4803 * _gtk_text_btree_is_valid:
4804 * @tree: a #GtkTextBTree
4805 * @view_id: ID for the view
4807 * Check to see if the entire #GtkTextBTree is valid or not for
4810 * Return value: %TRUE if the entire #GtkTextBTree is valid
4813 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4817 g_return_val_if_fail (tree != NULL, FALSE);
4819 nd = node_data_find (tree->root_node->node_data, view_id);
4820 return (nd && nd->valid);
4823 typedef struct _ValidateState ValidateState;
4825 struct _ValidateState
4827 gint remaining_pixels;
4828 gboolean in_validation;
4835 gtk_text_btree_node_validate (BTreeView *view,
4836 GtkTextBTreeNode *node,
4838 ValidateState *state)
4840 gint node_valid = TRUE;
4841 gint node_width = 0;
4842 gint node_height = 0;
4844 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4845 g_return_if_fail (!nd->valid);
4847 if (node->level == 0)
4849 GtkTextLine *line = node->children.line;
4850 GtkTextLineData *ld;
4852 /* Iterate over leading valid lines */
4853 while (line != NULL)
4855 ld = _gtk_text_line_get_data (line, view_id);
4857 if (!ld || !ld->valid)
4859 else if (state->in_validation)
4861 state->in_validation = FALSE;
4866 state->y += ld->height;
4867 node_width = MAX (ld->width, node_width);
4868 node_height += ld->height;
4874 state->in_validation = TRUE;
4876 /* Iterate over invalid lines */
4877 while (line != NULL)
4879 ld = _gtk_text_line_get_data (line, view_id);
4881 if (ld && ld->valid)
4886 state->old_height += ld->height;
4887 ld = gtk_text_layout_wrap (view->layout, line, ld);
4888 state->new_height += ld->height;
4890 node_width = MAX (ld->width, node_width);
4891 node_height += ld->height;
4893 state->remaining_pixels -= ld->height;
4894 if (state->remaining_pixels <= 0)
4904 /* Iterate over the remaining lines */
4905 while (line != NULL)
4907 ld = _gtk_text_line_get_data (line, view_id);
4908 state->in_validation = FALSE;
4910 if (!ld || !ld->valid)
4915 node_width = MAX (ld->width, node_width);
4916 node_height += ld->height;
4924 GtkTextBTreeNode *child;
4927 child = node->children.node;
4929 /* Iterate over leading valid nodes */
4932 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4934 if (!child_nd->valid)
4936 else if (state->in_validation)
4938 state->in_validation = FALSE;
4943 state->y += child_nd->height;
4944 node_width = MAX (node_width, child_nd->width);
4945 node_height += child_nd->height;
4948 child = child->next;
4951 /* Iterate over invalid nodes */
4954 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4956 if (child_nd->valid)
4960 gtk_text_btree_node_validate (view, child, view_id, state);
4962 if (!child_nd->valid)
4964 node_width = MAX (node_width, child_nd->width);
4965 node_height += child_nd->height;
4967 if (!state->in_validation || state->remaining_pixels <= 0)
4969 child = child->next;
4974 child = child->next;
4977 /* Iterate over the remaining lines */
4980 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4981 state->in_validation = FALSE;
4983 if (!child_nd->valid)
4986 node_width = MAX (child_nd->width, node_width);
4987 node_height += child_nd->height;
4989 child = child->next;
4993 nd->width = node_width;
4994 nd->height = node_height;
4995 nd->valid = node_valid;
4999 * _gtk_text_btree_validate:
5000 * @tree: a #GtkTextBTree
5002 * @max_pixels: the maximum number of pixels to validate. (No more
5003 * than one paragraph beyond this limit will be validated)
5004 * @y: location to store starting y coordinate of validated region
5005 * @old_height: location to store old height of validated region
5006 * @new_height: location to store new height of validated region
5008 * Validate a single contiguous invalid region of a #GtkTextBTree for
5011 * Return value: %TRUE if a region has been validated, %FALSE if the
5012 * entire tree was already valid.
5015 _gtk_text_btree_validate (GtkTextBTree *tree,
5024 g_return_val_if_fail (tree != NULL, FALSE);
5026 view = gtk_text_btree_get_view (tree, view_id);
5027 g_return_val_if_fail (view != NULL, FALSE);
5029 if (!_gtk_text_btree_is_valid (tree, view_id))
5031 ValidateState state;
5033 state.remaining_pixels = max_pixels;
5034 state.in_validation = FALSE;
5036 state.old_height = 0;
5037 state.new_height = 0;
5039 gtk_text_btree_node_validate (view,
5046 *old_height = state.old_height;
5048 *new_height = state.new_height;
5050 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5051 _gtk_text_btree_check (tree);
5060 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5064 gboolean *valid_out)
5068 gboolean valid = TRUE;
5070 if (node->level == 0)
5072 GtkTextLine *line = node->children.line;
5074 while (line != NULL)
5076 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5078 if (!ld || !ld->valid)
5083 width = MAX (ld->width, width);
5084 height += ld->height;
5092 GtkTextBTreeNode *child = node->children.node;
5096 NodeData *child_nd = node_data_find (child->node_data, view_id);
5098 if (!child_nd || !child_nd->valid)
5103 width = MAX (child_nd->width, width);
5104 height += child_nd->height;
5107 child = child->next;
5112 *height_out = height;
5117 /* Recompute the validity and size of the view data for a given
5118 * view at this node from the immediate children of the node
5121 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5124 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5129 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5130 &width, &height, &valid);
5132 nd->height = height;
5139 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5144 gtk_text_btree_node_check_valid (node, view_id);
5145 node = node->parent;
5150 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5153 if (node->level == 0)
5155 return gtk_text_btree_node_check_valid (node, view_id);
5159 GtkTextBTreeNode *child = node->children.node;
5161 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5169 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5171 if (!child_nd->valid)
5173 nd->width = MAX (child_nd->width, nd->width);
5174 nd->height += child_nd->height;
5176 child = child->next;
5185 * _gtk_text_btree_validate_line:
5186 * @tree: a #GtkTextBTree
5187 * @line: line to validate
5188 * @view_id: view ID for the view to validate
5190 * Revalidate a single line of the btree for the given view, propagate
5191 * results up through the entire tree.
5194 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5198 GtkTextLineData *ld;
5201 g_return_if_fail (tree != NULL);
5202 g_return_if_fail (line != NULL);
5204 view = gtk_text_btree_get_view (tree, view_id);
5205 g_return_if_fail (view != NULL);
5207 ld = _gtk_text_line_get_data (line, view_id);
5208 if (!ld || !ld->valid)
5210 ld = gtk_text_layout_wrap (view->layout, line, ld);
5212 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5217 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5219 if (node->level == 0)
5223 line = node->children.line;
5224 while (line != NULL)
5226 GtkTextLineData *ld;
5228 ld = _gtk_text_line_remove_data (line, view_id);
5231 gtk_text_layout_free_line_data (view->layout, line, ld);
5238 GtkTextBTreeNode *child;
5240 child = node->children.node;
5242 while (child != NULL)
5245 gtk_text_btree_node_remove_view (view, child, view_id);
5247 child = child->next;
5251 gtk_text_btree_node_remove_data (node, view_id);
5255 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5257 if (node->level == 0)
5260 GtkTextLineSegment *seg;
5262 while (node->children.line != NULL)
5264 line = node->children.line;
5265 node->children.line = line->next;
5266 while (line->segments != NULL)
5268 seg = line->segments;
5269 line->segments = seg->next;
5271 (*seg->type->deleteFunc) (seg, line, TRUE);
5273 gtk_text_line_destroy (tree, line);
5278 GtkTextBTreeNode *childPtr;
5280 while (node->children.node != NULL)
5282 childPtr = node->children.node;
5283 node->children.node = childPtr->next;
5284 gtk_text_btree_node_destroy (tree, childPtr);
5288 gtk_text_btree_node_free_empty (tree, node);
5292 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5293 GtkTextBTreeNode *node)
5295 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5296 (node->level == 0 && node->children.line == NULL));
5298 summary_list_destroy (node->summary);
5299 node_data_list_destroy (node->node_data);
5304 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5308 nd = node->node_data;
5311 if (nd->view_id == view_id)
5319 nd = node_data_new (view_id);
5321 if (node->node_data)
5322 nd->next = node->node_data;
5324 node->node_data = nd;
5331 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5337 nd = node->node_data;
5340 if (nd->view_id == view_id)
5351 prev->next = nd->next;
5353 if (node->node_data == nd)
5354 node->node_data = nd->next;
5358 node_data_destroy (nd);
5362 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5363 gint *width, gint *height)
5367 g_return_if_fail (width != NULL);
5368 g_return_if_fail (height != NULL);
5370 nd = gtk_text_btree_node_ensure_data (node, view_id);
5375 *height = nd->height;
5378 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5379 * here isn't quite right, since for a lot of operations we want to
5380 * know which children of the common parent correspond to the two nodes
5381 * (e.g., when computing the order of two iters)
5383 static GtkTextBTreeNode *
5384 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5385 GtkTextBTreeNode *node2)
5387 while (node1->level < node2->level)
5388 node1 = node1->parent;
5389 while (node2->level < node1->level)
5390 node2 = node2->parent;
5391 while (node1 != node2)
5393 node1 = node1->parent;
5394 node2 = node2->parent;
5405 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5410 while (view != NULL)
5412 if (view->view_id == view_id)
5421 get_tree_bounds (GtkTextBTree *tree,
5425 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5426 _gtk_text_btree_get_end_iter (tree, end);
5430 tag_changed_cb (GtkTextTagTable *table,
5432 gboolean size_changed,
5437 /* We need to queue a relayout on all regions that are tagged with
5444 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5446 /* Must be a last toggle if there was a first one. */
5447 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5448 _gtk_text_btree_invalidate_region (tree,
5455 /* We only need to queue a redraw, not a relayout */
5460 while (view != NULL)
5464 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5465 gtk_text_layout_changed (view->layout, 0, height, height);
5473 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5476 /* Remove the tag from the tree */
5481 get_tree_bounds (tree, &start, &end);
5483 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5484 gtk_text_btree_remove_tag_info (tree, tag);
5488 /* Rebalance the out-of-whack node "node" */
5490 gtk_text_btree_rebalance (GtkTextBTree *tree,
5491 GtkTextBTreeNode *node)
5494 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5495 * up through the tree one GtkTextBTreeNode at a time until the root
5496 * GtkTextBTreeNode has been processed.
5499 while (node != NULL)
5501 GtkTextBTreeNode *new_node, *child;
5506 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5507 * then split off all but the first MIN_CHILDREN into a separate
5508 * GtkTextBTreeNode following the original one. Then repeat until the
5509 * GtkTextBTreeNode has a decent size.
5512 if (node->num_children > MAX_CHILDREN)
5517 * If the GtkTextBTreeNode being split is the root
5518 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5522 if (node->parent == NULL)
5524 new_node = gtk_text_btree_node_new ();
5525 new_node->parent = NULL;
5526 new_node->next = NULL;
5527 new_node->summary = NULL;
5528 new_node->level = node->level + 1;
5529 new_node->children.node = node;
5530 recompute_node_counts (tree, new_node);
5531 tree->root_node = new_node;
5533 new_node = gtk_text_btree_node_new ();
5534 new_node->parent = node->parent;
5535 new_node->next = node->next;
5536 node->next = new_node;
5537 new_node->summary = NULL;
5538 new_node->level = node->level;
5539 new_node->num_children = node->num_children - MIN_CHILDREN;
5540 if (node->level == 0)
5542 for (i = MIN_CHILDREN-1,
5543 line = node->children.line;
5544 i > 0; i--, line = line->next)
5546 /* Empty loop body. */
5548 new_node->children.line = line->next;
5553 for (i = MIN_CHILDREN-1,
5554 child = node->children.node;
5555 i > 0; i--, child = child->next)
5557 /* Empty loop body. */
5559 new_node->children.node = child->next;
5562 recompute_node_counts (tree, node);
5563 node->parent->num_children++;
5565 if (node->num_children <= MAX_CHILDREN)
5567 recompute_node_counts (tree, node);
5573 while (node->num_children < MIN_CHILDREN)
5575 GtkTextBTreeNode *other;
5576 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5577 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5578 int total_children, first_children, i;
5581 * Too few children for this GtkTextBTreeNode. If this is the root then,
5582 * it's OK for it to have less than MIN_CHILDREN children
5583 * as long as it's got at least two. If it has only one
5584 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5585 * the tree and use its child as the new root.
5588 if (node->parent == NULL)
5590 if ((node->num_children == 1) && (node->level > 0))
5592 tree->root_node = node->children.node;
5593 tree->root_node->parent = NULL;
5595 node->children.node = NULL;
5596 gtk_text_btree_node_free_empty (tree, node);
5602 * Not the root. Make sure that there are siblings to
5606 if (node->parent->num_children < 2)
5608 gtk_text_btree_rebalance (tree, node->parent);
5613 * Find a sibling neighbor to borrow from, and arrange for
5614 * node to be the earlier of the pair.
5617 if (node->next == NULL)
5619 for (other = node->parent->children.node;
5620 other->next != node;
5621 other = other->next)
5623 /* Empty loop body. */
5630 * We're going to either merge the two siblings together
5631 * into one GtkTextBTreeNode or redivide the children among them to
5632 * balance their loads. As preparation, join their two
5633 * child lists into a single list and remember the half-way
5634 * point in the list.
5637 total_children = node->num_children + other->num_children;
5638 first_children = total_children/2;
5639 if (node->children.node == NULL)
5641 node->children = other->children;
5642 other->children.node = NULL;
5643 other->children.line = NULL;
5645 if (node->level == 0)
5649 for (line = node->children.line, i = 1;
5651 line = line->next, i++)
5653 if (i == first_children)
5658 line->next = other->children.line;
5659 while (i <= first_children)
5668 GtkTextBTreeNode *child;
5670 for (child = node->children.node, i = 1;
5671 child->next != NULL;
5672 child = child->next, i++)
5674 if (i <= first_children)
5676 if (i == first_children)
5678 halfwaynode = child;
5682 child->next = other->children.node;
5683 while (i <= first_children)
5685 halfwaynode = child;
5686 child = child->next;
5692 * If the two siblings can simply be merged together, do it.
5695 if (total_children <= MAX_CHILDREN)
5697 recompute_node_counts (tree, node);
5698 node->next = other->next;
5699 node->parent->num_children--;
5701 other->children.node = NULL;
5702 other->children.line = NULL;
5703 gtk_text_btree_node_free_empty (tree, other);
5708 * The siblings can't be merged, so just divide their
5709 * children evenly between them.
5712 if (node->level == 0)
5714 other->children.line = halfwayline->next;
5715 halfwayline->next = NULL;
5719 other->children.node = halfwaynode->next;
5720 halfwaynode->next = NULL;
5723 recompute_node_counts (tree, node);
5724 recompute_node_counts (tree, other);
5727 node = node->parent;
5732 post_insert_fixup (GtkTextBTree *tree,
5734 gint line_count_delta,
5735 gint char_count_delta)
5738 GtkTextBTreeNode *node;
5741 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5742 * point, then rebalance the tree if necessary.
5745 for (node = line->parent ; node != NULL;
5746 node = node->parent)
5748 node->num_lines += line_count_delta;
5749 node->num_chars += char_count_delta;
5751 node = line->parent;
5752 node->num_children += line_count_delta;
5754 if (node->num_children > MAX_CHILDREN)
5756 gtk_text_btree_rebalance (tree, node);
5759 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5760 _gtk_text_btree_check (tree);
5763 static GtkTextTagInfo*
5764 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5767 GtkTextTagInfo *info;
5771 list = tree->tag_infos;
5772 while (list != NULL)
5775 if (info->tag == tag)
5778 list = g_slist_next (list);
5784 static GtkTextTagInfo*
5785 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5788 GtkTextTagInfo *info;
5790 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5794 /* didn't find it, create. */
5796 info = g_new (GtkTextTagInfo, 1);
5799 g_object_ref (G_OBJECT (tag));
5800 info->tag_root = NULL;
5801 info->toggle_count = 0;
5803 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5810 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5813 GtkTextTagInfo *info;
5818 list = tree->tag_infos;
5819 while (list != NULL)
5822 if (info->tag == tag)
5826 prev->next = list->next;
5830 tree->tag_infos = list->next;
5833 g_slist_free (list);
5835 g_object_unref (G_OBJECT (info->tag));
5841 list = g_slist_next (list);
5846 recompute_level_zero_counts (GtkTextBTreeNode *node)
5849 GtkTextLineSegment *seg;
5851 g_assert (node->level == 0);
5853 line = node->children.line;
5854 while (line != NULL)
5856 node->num_children++;
5859 if (line->parent != node)
5860 gtk_text_line_set_parent (line, node);
5862 seg = line->segments;
5866 node->num_chars += seg->char_count;
5868 if (((seg->type != >k_text_toggle_on_type)
5869 && (seg->type != >k_text_toggle_off_type))
5870 || !(seg->body.toggle.inNodeCounts))
5876 GtkTextTagInfo *info;
5878 info = seg->body.toggle.info;
5880 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5891 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5894 GtkTextBTreeNode *child;
5896 g_assert (node->level > 0);
5898 child = node->children.node;
5899 while (child != NULL)
5901 node->num_children += 1;
5902 node->num_lines += child->num_lines;
5903 node->num_chars += child->num_chars;
5905 if (child->parent != node)
5907 child->parent = node;
5908 gtk_text_btree_node_invalidate_upward (node, NULL);
5911 summary = child->summary;
5912 while (summary != NULL)
5914 gtk_text_btree_node_adjust_toggle_count (node,
5916 summary->toggle_count);
5918 summary = summary->next;
5921 child = child->next;
5926 *----------------------------------------------------------------------
5928 * recompute_node_counts --
5930 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5931 * (tags, child information, etc.) by scanning the information in
5932 * its descendants. This procedure is called during rebalancing
5933 * when a GtkTextBTreeNode's child structure has changed.
5939 * The tag counts for node are modified to reflect its current
5940 * child structure, as are its num_children, num_lines, num_chars fields.
5941 * Also, all of the childrens' parent fields are made to point
5944 *----------------------------------------------------------------------
5948 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5951 Summary *summary, *summary2;
5954 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5955 * the existing Summary records (most of them will probably be reused).
5958 summary = node->summary;
5959 while (summary != NULL)
5961 summary->toggle_count = 0;
5962 summary = summary->next;
5965 node->num_children = 0;
5966 node->num_lines = 0;
5967 node->num_chars = 0;
5970 * Scan through the children, adding the childrens' tag counts into
5971 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5975 if (node->level == 0)
5976 recompute_level_zero_counts (node);
5978 recompute_level_nonzero_counts (node);
5983 gtk_text_btree_node_check_valid (node, view->view_id);
5988 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5989 * records that still have a zero count, or that have all the toggles.
5990 * The GtkTextBTreeNode with the children that account for all the tags toggles
5991 * have no summary information, and they become the tag_root for the tag.
5995 for (summary = node->summary; summary != NULL; )
5997 if (summary->toggle_count > 0 &&
5998 summary->toggle_count < summary->info->toggle_count)
6000 if (node->level == summary->info->tag_root->level)
6003 * The tag's root GtkTextBTreeNode split and some toggles left.
6004 * The tag root must move up a level.
6006 summary->info->tag_root = node->parent;
6009 summary = summary->next;
6012 if (summary->toggle_count == summary->info->toggle_count)
6015 * A GtkTextBTreeNode merge has collected all the toggles under
6016 * one GtkTextBTreeNode. Push the root down to this level.
6018 summary->info->tag_root = node;
6020 if (summary2 != NULL)
6022 summary2->next = summary->next;
6023 summary_destroy (summary);
6024 summary = summary2->next;
6028 node->summary = summary->next;
6029 summary_destroy (summary);
6030 summary = node->summary;
6036 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6037 GtkTextTagInfo *info,
6038 gint delta) /* may be negative */
6040 Summary *summary, *prevPtr;
6041 GtkTextBTreeNode *node2Ptr;
6042 int rootLevel; /* Level of original tag root */
6044 info->toggle_count += delta;
6046 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6048 info->tag_root = node;
6053 * Note the level of the existing root for the tag so we can detect
6054 * if it needs to be moved because of the toggle count change.
6057 rootLevel = info->tag_root->level;
6060 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6061 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6065 for ( ; node != info->tag_root; node = node->parent)
6068 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6069 * perhaps all we have to do is adjust its count.
6072 for (prevPtr = NULL, summary = node->summary;
6074 prevPtr = summary, summary = summary->next)
6076 if (summary->info == info)
6081 if (summary != NULL)
6083 summary->toggle_count += delta;
6084 if (summary->toggle_count > 0 &&
6085 summary->toggle_count < info->toggle_count)
6089 if (summary->toggle_count != 0)
6092 * Should never find a GtkTextBTreeNode with max toggle count at this
6093 * point (there shouldn't have been a summary entry in the
6097 g_error ("%s: bad toggle count (%d) max (%d)",
6098 G_STRLOC, summary->toggle_count, info->toggle_count);
6102 * Zero toggle count; must remove this tag from the list.
6105 if (prevPtr == NULL)
6107 node->summary = summary->next;
6111 prevPtr->next = summary->next;
6113 summary_destroy (summary);
6118 * This tag isn't currently in the summary information list.
6121 if (rootLevel == node->level)
6125 * The old tag root is at the same level in the tree as this
6126 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6127 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6128 * as well as the old root (if not, we'll move it up again
6129 * the next time through the loop). To push it up one level
6130 * we copy the original toggle count into the summary
6131 * information at the old root and change the root to its
6132 * parent GtkTextBTreeNode.
6135 GtkTextBTreeNode *rootnode = info->tag_root;
6136 summary = (Summary *) g_malloc (sizeof (Summary));
6137 summary->info = info;
6138 summary->toggle_count = info->toggle_count - delta;
6139 summary->next = rootnode->summary;
6140 rootnode->summary = summary;
6141 rootnode = rootnode->parent;
6142 rootLevel = rootnode->level;
6143 info->tag_root = rootnode;
6145 summary = (Summary *) g_malloc (sizeof (Summary));
6146 summary->info = info;
6147 summary->toggle_count = delta;
6148 summary->next = node->summary;
6149 node->summary = summary;
6154 * If we've decremented the toggle count, then it may be necessary
6155 * to push the tag root down one or more levels.
6162 if (info->toggle_count == 0)
6164 info->tag_root = (GtkTextBTreeNode *) NULL;
6167 node = info->tag_root;
6168 while (node->level > 0)
6171 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6172 * toggles. If so, push the root down one level.
6175 for (node2Ptr = node->children.node;
6176 node2Ptr != (GtkTextBTreeNode *)NULL ;
6177 node2Ptr = node2Ptr->next)
6179 for (prevPtr = NULL, summary = node2Ptr->summary;
6181 prevPtr = summary, summary = summary->next)
6183 if (summary->info == info)
6188 if (summary == NULL)
6192 if (summary->toggle_count != info->toggle_count)
6195 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6202 * This GtkTextBTreeNode has all the toggles, so push down the root.
6205 if (prevPtr == NULL)
6207 node2Ptr->summary = summary->next;
6211 prevPtr->next = summary->next;
6213 summary_destroy (summary);
6214 info->tag_root = node2Ptr;
6217 node = info->tag_root;
6222 *----------------------------------------------------------------------
6226 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6227 * increments the count for a particular tag, adding a new
6228 * entry for that tag if there wasn't one previously.
6234 * The information at *tagInfoPtr may be modified, and the arrays
6235 * may be reallocated to make them larger.
6237 *----------------------------------------------------------------------
6241 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6246 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6247 count > 0; tag_p++, count--)
6251 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6257 * There isn't currently an entry for this tag, so we have to
6258 * make a new one. If the arrays are full, then enlarge the
6262 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6264 GtkTextTag **newTags;
6265 int *newCounts, newSize;
6267 newSize = 2*tagInfoPtr->arraySize;
6268 newTags = (GtkTextTag **) g_malloc ((unsigned)
6269 (newSize*sizeof (GtkTextTag *)));
6270 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6271 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6272 g_free ((char *) tagInfoPtr->tags);
6273 tagInfoPtr->tags = newTags;
6274 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6275 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6276 tagInfoPtr->arraySize *sizeof (int));
6277 g_free ((char *) tagInfoPtr->counts);
6278 tagInfoPtr->counts = newCounts;
6279 tagInfoPtr->arraySize = newSize;
6282 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6283 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6284 tagInfoPtr->numTags++;
6288 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6289 const GtkTextIter *iter)
6291 GtkTextLineSegment *prev;
6295 line = _gtk_text_iter_get_text_line (iter);
6296 tree = _gtk_text_iter_get_btree (iter);
6298 prev = gtk_text_line_segment_split (iter);
6301 seg->next = line->segments;
6302 line->segments = seg;
6306 seg->next = prev->next;
6309 cleanup_line (line);
6310 segments_changed (tree);
6312 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6313 _gtk_text_btree_check (tree);
6317 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6318 GtkTextLineSegment *seg,
6321 GtkTextLineSegment *prev;
6323 if (line->segments == seg)
6325 line->segments = seg->next;
6329 for (prev = line->segments; prev->next != seg;
6332 /* Empty loop body. */
6334 prev->next = seg->next;
6336 cleanup_line (line);
6337 segments_changed (tree);
6341 * This is here because it requires BTree internals, it logically
6342 * belongs in gtktextsegment.c
6347 *--------------------------------------------------------------
6349 * _gtk_toggle_segment_check_func --
6351 * This procedure is invoked to perform consistency checks
6352 * on toggle segments.
6358 * If a consistency problem is found the procedure g_errors.
6360 *--------------------------------------------------------------
6364 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6370 if (segPtr->byte_count != 0)
6372 g_error ("toggle_segment_check_func: segment had non-zero size");
6374 if (!segPtr->body.toggle.inNodeCounts)
6376 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6378 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6379 for (summary = line->parent->summary; ;
6380 summary = summary->next)
6382 if (summary == NULL)
6386 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6393 if (summary->info == segPtr->body.toggle.info)
6397 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6409 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6410 GtkTextBTreeNode *node,
6420 while (view != NULL)
6422 if (view->view_id == nd->view_id)
6429 g_error ("Node has data for a view %p no longer attached to the tree",
6432 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6433 &width, &height, &valid);
6435 /* valid aggregate not checked the same as width/height, because on
6436 * btree rebalance we can have invalid nodes where all lines below
6437 * them are actually valid, due to moving lines around between
6440 * The guarantee is that if there are invalid lines the node is
6441 * invalid - we don't guarantee that if the node is invalid there
6442 * are invalid lines.
6445 if (nd->width != width ||
6446 nd->height != height ||
6447 (nd->valid && !valid))
6449 g_error ("Node aggregates for view %p are invalid:\n"
6450 "Are (%d,%d,%s), should be (%d,%d,%s)",
6452 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6453 width, height, valid ? "TRUE" : "FALSE");
6458 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6459 GtkTextBTreeNode *node)
6461 GtkTextBTreeNode *childnode;
6462 Summary *summary, *summary2;
6464 GtkTextLineSegment *segPtr;
6465 int num_children, num_lines, num_chars, toggle_count, min_children;
6466 GtkTextLineData *ld;
6469 if (node->parent != NULL)
6471 min_children = MIN_CHILDREN;
6473 else if (node->level > 0)
6480 if ((node->num_children < min_children)
6481 || (node->num_children > MAX_CHILDREN))
6483 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6484 node->num_children);
6487 nd = node->node_data;
6490 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6497 if (node->level == 0)
6499 for (line = node->children.line; line != NULL;
6502 if (line->parent != node)
6504 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6506 if (line->segments == NULL)
6508 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6514 /* Just ensuring we don't segv while doing this loop */
6519 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6521 if (segPtr->type->checkFunc != NULL)
6523 (*segPtr->type->checkFunc)(segPtr, line);
6525 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6526 && (segPtr->next != NULL)
6527 && (segPtr->next->byte_count == 0)
6528 && (segPtr->next->type->leftGravity))
6530 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6532 if ((segPtr->next == NULL)
6533 && (segPtr->type != >k_text_char_type))
6535 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6538 num_chars += segPtr->char_count;
6547 for (childnode = node->children.node; childnode != NULL;
6548 childnode = childnode->next)
6550 if (childnode->parent != node)
6552 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6554 if (childnode->level != (node->level-1))
6556 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6557 node->level, childnode->level);
6559 gtk_text_btree_node_check_consistency (tree, childnode);
6560 for (summary = childnode->summary; summary != NULL;
6561 summary = summary->next)
6563 for (summary2 = node->summary; ;
6564 summary2 = summary2->next)
6566 if (summary2 == NULL)
6568 if (summary->info->tag_root == node)
6572 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6573 summary->info->tag->name,
6574 "present in parent summaries");
6576 if (summary->info == summary2->info)
6583 num_lines += childnode->num_lines;
6584 num_chars += childnode->num_chars;
6587 if (num_children != node->num_children)
6589 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6590 num_children, node->num_children);
6592 if (num_lines != node->num_lines)
6594 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6595 num_lines, node->num_lines);
6597 if (num_chars != node->num_chars)
6599 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6600 num_chars, node->num_chars);
6603 for (summary = node->summary; summary != NULL;
6604 summary = summary->next)
6606 if (summary->info->toggle_count == summary->toggle_count)
6608 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6609 summary->info->tag->name);
6612 if (node->level == 0)
6614 for (line = node->children.line; line != NULL;
6617 for (segPtr = line->segments; segPtr != NULL;
6618 segPtr = segPtr->next)
6620 if ((segPtr->type != >k_text_toggle_on_type)
6621 && (segPtr->type != >k_text_toggle_off_type))
6625 if (segPtr->body.toggle.info == summary->info)
6627 if (!segPtr->body.toggle.inNodeCounts)
6628 g_error ("Toggle segment not in the node counts");
6637 for (childnode = node->children.node;
6639 childnode = childnode->next)
6641 for (summary2 = childnode->summary;
6643 summary2 = summary2->next)
6645 if (summary2->info == summary->info)
6647 toggle_count += summary2->toggle_count;
6652 if (toggle_count != summary->toggle_count)
6654 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6655 toggle_count, summary->toggle_count);
6657 for (summary2 = summary->next; summary2 != NULL;
6658 summary2 = summary2->next)
6660 if (summary2->info == summary->info)
6662 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6663 summary->info->tag->name);
6670 listify_foreach (GtkTextTag *tag, gpointer user_data)
6672 GSList** listp = user_data;
6674 *listp = g_slist_prepend (*listp, tag);
6678 list_of_tags (GtkTextTagTable *table)
6680 GSList *list = NULL;
6682 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6688 _gtk_text_btree_check (GtkTextBTree *tree)
6691 GtkTextBTreeNode *node;
6693 GtkTextLineSegment *seg;
6695 GSList *taglist = NULL;
6697 GtkTextTagInfo *info;
6700 * Make sure that the tag toggle counts and the tag root pointers are OK.
6702 for (taglist = list_of_tags (tree->table);
6703 taglist != NULL ; taglist = taglist->next)
6705 tag = taglist->data;
6706 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6709 node = info->tag_root;
6712 if (info->toggle_count != 0)
6714 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6715 tag->name, info->toggle_count);
6717 continue; /* no ranges for the tag */
6719 else if (info->toggle_count == 0)
6721 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6724 else if (info->toggle_count & 1)
6726 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6727 tag->name, info->toggle_count);
6729 for (summary = node->summary; summary != NULL;
6730 summary = summary->next)
6732 if (summary->info->tag == tag)
6734 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6738 if (node->level > 0)
6740 for (node = node->children.node ; node != NULL ;
6743 for (summary = node->summary; summary != NULL;
6744 summary = summary->next)
6746 if (summary->info->tag == tag)
6748 count += summary->toggle_count;
6755 GtkTextLineSegmentClass * last = NULL;
6757 for (line = node->children.line ; line != NULL ;
6760 for (seg = line->segments; seg != NULL;
6763 if ((seg->type == >k_text_toggle_on_type ||
6764 seg->type == >k_text_toggle_off_type) &&
6765 seg->body.toggle.info->tag == tag)
6767 if (last == seg->type)
6768 g_error ("Two consecutive toggles on or off weren't merged");
6769 if (!seg->body.toggle.inNodeCounts)
6770 g_error ("Toggle segment not in the node counts");
6779 if (count != info->toggle_count)
6781 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6782 info->toggle_count, tag->name, count);
6787 g_slist_free (taglist);
6791 * Call a recursive procedure to do the main body of checks.
6794 node = tree->root_node;
6795 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6798 * Make sure that there are at least two lines in the text and
6799 * that the last line has no characters except a newline.
6802 if (node->num_lines < 2)
6804 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6806 if (node->num_chars < 2)
6808 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6810 while (node->level > 0)
6812 node = node->children.node;
6813 while (node->next != NULL)
6818 line = node->children.line;
6819 while (line->next != NULL)
6823 seg = line->segments;
6824 while ((seg->type == >k_text_toggle_off_type)
6825 || (seg->type == >k_text_right_mark_type)
6826 || (seg->type == >k_text_left_mark_type))
6829 * It's OK to toggle a tag off in the last line, but
6830 * not to start a new range. It's also OK to have marks
6836 if (seg->type != >k_text_char_type)
6838 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6840 if (seg->next != NULL)
6842 g_error ("_gtk_text_btree_check: last line has too many segments");
6844 if (seg->byte_count != 1)
6846 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6849 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6851 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6856 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6857 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6858 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6859 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6862 _gtk_text_btree_spew (GtkTextBTree *tree)
6867 printf ("%d lines in tree %p\n",
6868 _gtk_text_btree_line_count (tree), tree);
6870 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6872 while (line != NULL)
6874 _gtk_text_btree_spew_line (tree, line);
6875 line = _gtk_text_line_next (line);
6878 printf ("=================== Tag information\n");
6883 list = tree->tag_infos;
6885 while (list != NULL)
6887 GtkTextTagInfo *info;
6891 printf (" tag `%s': root at %p, toggle count %d\n",
6892 info->tag->name, info->tag_root, info->toggle_count);
6894 list = g_slist_next (list);
6897 if (tree->tag_infos == NULL)
6899 printf (" (no tags in the tree)\n");
6903 printf ("=================== Tree nodes\n");
6906 _gtk_text_btree_spew_node (tree->root_node, 0);
6911 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6914 GtkTextLineSegment *seg;
6916 spaces = g_strnfill (indent, ' ');
6918 printf ("%sline %p chars %d bytes %d\n",
6920 _gtk_text_line_char_count (line),
6921 _gtk_text_line_byte_count (line));
6923 seg = line->segments;
6926 if (seg->type == >k_text_char_type)
6928 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6933 if (*s == '\n' || *s == '\r')
6937 printf ("%s chars `%s'...\n", spaces, str);
6940 else if (seg->type == >k_text_right_mark_type)
6942 printf ("%s right mark `%s' visible: %d\n",
6944 seg->body.mark.name,
6945 seg->body.mark.visible);
6947 else if (seg->type == >k_text_left_mark_type)
6949 printf ("%s left mark `%s' visible: %d\n",
6951 seg->body.mark.name,
6952 seg->body.mark.visible);
6954 else if (seg->type == >k_text_toggle_on_type ||
6955 seg->type == >k_text_toggle_off_type)
6957 printf ("%s tag `%s' %s\n",
6958 spaces, seg->body.toggle.info->tag->name,
6959 seg->type == >k_text_toggle_off_type ? "off" : "on");
6969 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6972 GtkTextBTreeNode *iter;
6975 spaces = g_strnfill (indent, ' ');
6977 printf ("%snode %p level %d children %d lines %d chars %d\n",
6978 spaces, node, node->level,
6979 node->num_children, node->num_lines, node->num_chars);
6984 printf ("%s %d toggles of `%s' below this node\n",
6985 spaces, s->toggle_count, s->info->tag->name);
6991 if (node->level > 0)
6993 iter = node->children.node;
6994 while (iter != NULL)
6996 _gtk_text_btree_spew_node (iter, indent + 2);
7003 GtkTextLine *line = node->children.line;
7004 while (line != NULL)
7006 _gtk_text_btree_spew_line_short (line, indent + 2);
7014 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7016 GtkTextLineSegment * seg;
7018 printf ("%4d| line: %p parent: %p next: %p\n",
7019 _gtk_text_line_get_number (line), line, line->parent, line->next);
7021 seg = line->segments;
7025 _gtk_text_btree_spew_segment (tree, seg);
7031 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7033 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7034 seg, seg->type->name, seg->byte_count, seg->char_count);
7036 if (seg->type == >k_text_char_type)
7038 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7039 printf (" `%s'\n", str);
7042 else if (seg->type == >k_text_right_mark_type)
7044 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7045 seg->body.mark.name,
7046 seg->body.mark.visible,
7047 seg->body.mark.not_deleteable);
7049 else if (seg->type == >k_text_left_mark_type)
7051 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7052 seg->body.mark.name,
7053 seg->body.mark.visible,
7054 seg->body.mark.not_deleteable);
7056 else if (seg->type == >k_text_toggle_on_type ||
7057 seg->type == >k_text_toggle_off_type)
7059 printf (" tag `%s' priority %d\n",
7060 seg->body.toggle.info->tag->name,
7061 seg->body.toggle.info->tag->priority);