4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
60 #include "gtktexttag.h"
61 #include "gtktexttagtable.h"
62 #include "gtktextlayout.h"
63 #include "gtktextiterprivate.h"
65 #include "gtktextmarkprivate.h"
73 * The structure below is used to pass information between
74 * _gtk_text_btree_get_tags and inc_count:
77 typedef struct TagInfo {
78 int numTags; /* Number of tags for which there
79 * is currently information in
81 int arraySize; /* Number of entries allocated for
83 GtkTextTag **tags; /* Array of tags seen so far.
85 int *counts; /* Toggle count (so far) for each
86 * entry in tags. Malloc-ed. */
91 * This is used to store per-view width/height info at the tree nodes.
94 typedef struct _NodeData NodeData;
100 /* Height and width of this node */
102 signed int width : 24;
104 /* boolean indicating whether the lines below this node are in need of validation.
105 * However, width/height should always represent the current total width and
106 * max height for lines below this node; the valid flag indicates whether the
107 * width/height on the lines needs recomputing, not whether the totals
110 guint valid : 8; /* Actually a boolean */
115 * The data structure below keeps summary information about one tag as part
116 * of the tag information in a node.
119 typedef struct Summary {
120 GtkTextTagInfo *info; /* Handle for tag. */
121 int toggle_count; /* Number of transitions into or
122 * out of this tag that occur in
123 * the subtree rooted at this node. */
124 struct Summary *next; /* Next in list of all tags for same
125 * node, or NULL if at end of list. */
129 * The data structure below defines a node in the B-tree.
132 struct _GtkTextBTreeNode {
133 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
134 * this is the root. */
135 GtkTextBTreeNode *next; /* Next in list of siblings with the
136 * same parent node, or NULL for end
138 Summary *summary; /* First in malloc-ed list of info
139 * about tags in this subtree (NULL if
140 * no tag info in the subtree). */
141 int level; /* Level of this node in the B-tree.
142 * 0 refers to the bottom of the tree
143 * (children are lines, not nodes). */
144 union { /* First in linked list of children. */
145 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
146 GtkTextLine *line; /* Used if level == 0. */
148 int num_children; /* Number of children of this node. */
149 int num_lines; /* Total number of lines (leaves) in
150 * the subtree rooted here. */
151 int num_chars; /* Number of chars below here */
158 * Used to store the list of views in our btree
161 typedef struct _BTreeView BTreeView;
165 GtkTextLayout *layout;
171 * And the tree itself
174 struct _GtkTextBTree {
175 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
176 GtkTextTagTable *table;
177 GHashTable *mark_table;
179 GtkTextMark *insert_mark;
180 GtkTextMark *selection_bound_mark;
181 GtkTextBuffer *buffer;
184 gulong tag_changed_handler;
186 /* Incremented when a segment with a byte size > 0
187 * is added to or removed from the tree (i.e. the
188 * length of a line may have changed, and lines may
189 * have been added or removed). This invalidates
190 * all outstanding iterators.
192 guint chars_changed_stamp;
193 /* Incremented when any segments are added or deleted;
194 * this makes outstanding iterators recalculate their
195 * pointed-to segment and segment offset.
197 guint segments_changed_stamp;
199 /* Cache the last line in the buffer */
200 GtkTextLine *last_line;
201 guint last_line_stamp;
203 /* Cache the next-to-last line in the buffer,
204 * containing the end iterator
206 GtkTextLine *end_iter_line;
207 GtkTextLineSegment *end_iter_segment;
208 int end_iter_segment_byte_index;
209 int end_iter_segment_char_offset;
210 guint end_iter_line_stamp;
211 guint end_iter_segment_stamp;
213 GHashTable *child_anchor_table;
218 * Upper and lower bounds on how many children a node may have:
219 * rebalance when either of these limits is exceeded. MAX_CHILDREN
220 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
223 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
224 shallow. It appears to be faster to locate a particular line number
225 if the tree is narrow and deep, since it is more finely sorted. I
226 guess this may increase memory use though, and make it slower to
227 walk the tree in order, or locate a particular byte index (which
228 is done by walking the tree in order).
230 There's basically a tradeoff here. However I'm thinking we want to
231 add pixels, byte counts, and char counts to the tree nodes,
232 at that point narrow and deep should speed up all operations,
233 not just the line number searches.
237 #define MAX_CHILDREN 12
238 #define MIN_CHILDREN 6
240 #define MAX_CHILDREN 6
241 #define MIN_CHILDREN 3
248 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
250 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
251 GtkTextBTreeNode *node);
252 static GtkTextLine * get_last_line (GtkTextBTree *tree);
253 static void post_insert_fixup (GtkTextBTree *tree,
254 GtkTextLine *insert_line,
255 gint char_count_delta,
256 gint line_count_delta);
257 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
258 GtkTextTagInfo *info,
260 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
263 static void segments_changed (GtkTextBTree *tree);
264 static void chars_changed (GtkTextBTree *tree);
265 static void summary_list_destroy (Summary *summary);
266 static GtkTextLine *gtk_text_line_new (void);
267 static void gtk_text_line_destroy (GtkTextBTree *tree,
269 static void gtk_text_line_set_parent (GtkTextLine *line,
270 GtkTextBTreeNode *node);
271 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
275 static NodeData *node_data_new (gpointer view_id);
276 static void node_data_destroy (NodeData *nd);
277 static void node_data_list_destroy (NodeData *nd);
278 static NodeData *node_data_find (NodeData *nd,
281 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
282 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
283 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
285 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
289 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
292 static void gtk_text_btree_node_remove_view (BTreeView *view,
293 GtkTextBTreeNode *node,
295 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
296 GtkTextBTreeNode *node);
297 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
301 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
307 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
308 GtkTextBTreeNode *node2);
309 static void get_tree_bounds (GtkTextBTree *tree,
312 static void tag_changed_cb (GtkTextTagTable *table,
314 gboolean size_changed,
316 static void cleanup_line (GtkTextLine *line);
317 static void recompute_node_counts (GtkTextBTree *tree,
318 GtkTextBTreeNode *node);
319 static void inc_count (GtkTextTag *tag,
321 TagInfo *tagInfoPtr);
323 static void summary_destroy (Summary *summary);
325 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
326 const GtkTextIter *iter);
327 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
328 GtkTextLineSegment *seg,
332 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
334 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
336 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
339 static void redisplay_region (GtkTextBTree *tree,
340 const GtkTextIter *start,
341 const GtkTextIter *end);
343 /* Inline thingies */
346 segments_changed (GtkTextBTree *tree)
348 tree->segments_changed_stamp += 1;
352 chars_changed (GtkTextBTree *tree)
354 tree->chars_changed_stamp += 1;
362 _gtk_text_btree_new (GtkTextTagTable *table,
363 GtkTextBuffer *buffer)
366 GtkTextBTreeNode *root_node;
367 GtkTextLine *line, *line2;
369 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
370 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
373 * The tree will initially have two empty lines. The second line
374 * isn't actually part of the tree's contents, but its presence
375 * makes several operations easier. The tree will have one GtkTextBTreeNode,
376 * which is also the root of the tree.
379 /* Create the root node. */
381 root_node = gtk_text_btree_node_new ();
383 line = gtk_text_line_new ();
384 line2 = gtk_text_line_new ();
386 root_node->parent = NULL;
387 root_node->next = NULL;
388 root_node->summary = NULL;
389 root_node->level = 0;
390 root_node->children.line = line;
391 root_node->num_children = 2;
392 root_node->num_lines = 2;
393 root_node->num_chars = 2;
395 line->parent = root_node;
398 line->segments = _gtk_char_segment_new ("\n", 1);
400 line2->parent = root_node;
402 line2->segments = _gtk_char_segment_new ("\n", 1);
404 /* Create the tree itself */
406 tree = g_new0(GtkTextBTree, 1);
407 tree->root_node = root_node;
411 /* Set these to values that are unlikely to be found
412 * in random memory garbage, and also avoid
413 * duplicates between tree instances.
415 tree->chars_changed_stamp = g_random_int ();
416 tree->segments_changed_stamp = g_random_int ();
418 tree->last_line_stamp = tree->chars_changed_stamp - 1;
419 tree->last_line = NULL;
421 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
422 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
423 tree->end_iter_line = NULL;
424 tree->end_iter_segment_byte_index = 0;
425 tree->end_iter_segment_char_offset = 0;
427 g_object_ref (tree->table);
429 tree->tag_changed_handler = g_signal_connect (tree->table,
431 G_CALLBACK (tag_changed_cb),
434 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
435 tree->child_anchor_table = NULL;
437 /* We don't ref the buffer, since the buffer owns us;
438 * we'd have some circularity issues. The buffer always
439 * lasts longer than the BTree
441 tree->buffer = buffer;
445 GtkTextLineSegment *seg;
447 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
450 tree->insert_mark = _gtk_text_btree_set_mark (tree,
457 seg = tree->insert_mark->segment;
459 seg->body.mark.not_deleteable = TRUE;
460 seg->body.mark.visible = TRUE;
462 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
469 seg = tree->selection_bound_mark->segment;
471 seg->body.mark.not_deleteable = TRUE;
473 g_object_ref (tree->insert_mark);
474 g_object_ref (tree->selection_bound_mark);
483 _gtk_text_btree_ref (GtkTextBTree *tree)
485 g_return_if_fail (tree != NULL);
486 g_return_if_fail (tree->refcount > 0);
492 _gtk_text_btree_unref (GtkTextBTree *tree)
494 g_return_if_fail (tree != NULL);
495 g_return_if_fail (tree->refcount > 0);
499 if (tree->refcount == 0)
501 g_signal_handler_disconnect (tree->table,
502 tree->tag_changed_handler);
504 g_object_unref (tree->table);
507 gtk_text_btree_node_destroy (tree, tree->root_node);
508 tree->root_node = NULL;
510 g_assert (g_hash_table_size (tree->mark_table) == 0);
511 g_hash_table_destroy (tree->mark_table);
512 tree->mark_table = NULL;
513 if (tree->child_anchor_table != NULL)
515 g_hash_table_destroy (tree->child_anchor_table);
516 tree->child_anchor_table = NULL;
519 g_object_unref (tree->insert_mark);
520 tree->insert_mark = NULL;
521 g_object_unref (tree->selection_bound_mark);
522 tree->selection_bound_mark = NULL;
529 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
535 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
537 return tree->chars_changed_stamp;
541 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
543 return tree->segments_changed_stamp;
547 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
549 g_return_if_fail (tree != NULL);
550 segments_changed (tree);
554 * Indexable segment mutation
558 _gtk_text_btree_delete (GtkTextIter *start,
561 GtkTextLineSegment *prev_seg; /* The segment just before the start
562 * of the deletion range. */
563 GtkTextLineSegment *last_seg; /* The segment just after the end
564 * of the deletion range. */
565 GtkTextLineSegment *seg, *next;
566 GtkTextLine *curline;
567 GtkTextBTreeNode *curnode, *node;
569 GtkTextLine *start_line;
570 GtkTextLine *end_line;
571 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
572 gint start_byte_offset;
574 g_return_if_fail (start != NULL);
575 g_return_if_fail (end != NULL);
576 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
577 _gtk_text_iter_get_btree (end));
579 gtk_text_iter_order (start, end);
581 tree = _gtk_text_iter_get_btree (start);
583 if (gtk_debug_flags & GTK_DEBUG_TEXT)
584 _gtk_text_btree_check (tree);
586 /* Broadcast the need for redisplay before we break the iterators */
587 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
588 _gtk_text_btree_invalidate_region (tree, start, end);
590 /* Save the byte offset so we can reset the iterators */
591 start_byte_offset = gtk_text_iter_get_line_index (start);
593 start_line = _gtk_text_iter_get_text_line (start);
594 end_line = _gtk_text_iter_get_text_line (end);
597 * Split the start and end segments, so we have a place
598 * to insert our new text.
600 * Tricky point: split at end first; otherwise the split
601 * at end may invalidate seg and/or prev_seg. This allows
602 * us to avoid invalidating segments for start.
605 last_seg = gtk_text_line_segment_split (end);
606 if (last_seg != NULL)
607 last_seg = last_seg->next;
609 last_seg = end_line->segments;
611 prev_seg = gtk_text_line_segment_split (start);
612 if (prev_seg != NULL)
614 seg = prev_seg->next;
615 prev_seg->next = last_seg;
619 seg = start_line->segments;
620 start_line->segments = last_seg;
623 /* notify iterators that their segments need recomputation,
624 just for robustness. */
625 segments_changed (tree);
628 * Delete all of the segments between prev_seg and last_seg.
631 curline = start_line;
632 curnode = curline->parent;
633 while (seg != last_seg)
639 GtkTextLine *nextline;
642 * We just ran off the end of a line. First find the
643 * next line, then go back to the old line and delete it
644 * (unless it's the starting line for the range).
647 nextline = _gtk_text_line_next (curline);
648 if (curline != start_line)
650 if (curnode == start_line->parent)
651 start_line->next = curline->next;
653 curnode->children.line = curline->next;
655 for (node = curnode; node != NULL;
658 /* Don't update node->num_chars, because
659 * that was done when we deleted the segments.
661 node->num_lines -= 1;
664 curnode->num_children -= 1;
665 curline->next = deleted_lines;
666 deleted_lines = curline;
670 seg = curline->segments;
673 * If the GtkTextBTreeNode is empty then delete it and its parents,
674 * recursively upwards until a non-empty GtkTextBTreeNode is found.
677 while (curnode->num_children == 0)
679 GtkTextBTreeNode *parent;
681 parent = curnode->parent;
682 if (parent->children.node == curnode)
684 parent->children.node = curnode->next;
688 GtkTextBTreeNode *prevnode = parent->children.node;
689 while (prevnode->next != curnode)
691 prevnode = prevnode->next;
693 prevnode->next = curnode->next;
695 parent->num_children--;
696 gtk_text_btree_node_free_empty (tree, curnode);
699 curnode = curline->parent;
704 char_count = seg->char_count;
706 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
709 * This segment refuses to die. Move it to prev_seg and
710 * advance prev_seg if the segment has left gravity.
713 if (prev_seg == NULL)
715 seg->next = start_line->segments;
716 start_line->segments = seg;
720 seg->next = prev_seg->next;
721 prev_seg->next = seg;
723 if (seg->type->leftGravity)
730 /* Segment is gone. Decrement the char count of the node and
732 for (node = curnode; node != NULL;
735 node->num_chars -= char_count;
743 * If the beginning and end of the deletion range are in different
744 * lines, join the two lines together and discard the ending line.
747 if (start_line != end_line)
750 GtkTextBTreeNode *ancestor_node;
751 GtkTextLine *prevline;
754 /* last_seg was appended to start_line up at the top of this function */
756 for (seg = last_seg; seg != NULL;
759 chars_moved += seg->char_count;
760 if (seg->type->lineChangeFunc != NULL)
762 (*seg->type->lineChangeFunc)(seg, end_line);
766 for (node = start_line->parent; node != NULL;
769 node->num_chars += chars_moved;
772 curnode = end_line->parent;
773 for (node = curnode; node != NULL;
776 node->num_chars -= chars_moved;
779 curnode->num_children--;
780 prevline = curnode->children.line;
781 if (prevline == end_line)
783 curnode->children.line = end_line->next;
787 while (prevline->next != end_line)
789 prevline = prevline->next;
791 prevline->next = end_line->next;
793 end_line->next = deleted_lines;
794 deleted_lines = end_line;
796 /* We now fix up the per-view aggregates. We add all the height and
797 * width for the deleted lines to the start line, so that when revalidation
798 * occurs, the correct change in size is seen.
800 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
807 gint deleted_width = 0;
808 gint deleted_height = 0;
810 line = deleted_lines;
813 GtkTextLine *next_line = line->next;
814 ld = _gtk_text_line_get_data (line, view->view_id);
818 deleted_width = MAX (deleted_width, ld->width);
819 deleted_height += ld->height;
823 gtk_text_line_destroy (tree, line);
828 if (deleted_width > 0 || deleted_height > 0)
830 ld = _gtk_text_line_get_data (start_line, view->view_id);
834 /* This means that start_line has never been validated.
835 * We don't really want to do the validation here but
836 * we do need to store our temporary sizes. So we
837 * create the line data and assume a line w/h of 0.
839 ld = _gtk_text_line_data_new (view->layout, start_line);
840 _gtk_text_line_add_data (start_line, ld);
846 ld->width = MAX (deleted_width, ld->width);
847 ld->height += deleted_height;
851 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
852 if (ancestor_node->parent)
853 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
858 /* avoid dangling pointer */
859 deleted_lines = NULL;
861 gtk_text_btree_rebalance (tree, curnode);
865 * Cleanup the segments in the new line.
868 cleanup_line (start_line);
871 * Lastly, rebalance the first GtkTextBTreeNode of the range.
874 gtk_text_btree_rebalance (tree, start_line->parent);
876 /* Notify outstanding iterators that they
878 chars_changed (tree);
879 segments_changed (tree);
881 if (gtk_debug_flags & GTK_DEBUG_TEXT)
882 _gtk_text_btree_check (tree);
884 /* Re-initialize our iterators */
885 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
890 _gtk_text_btree_insert (GtkTextIter *iter,
894 GtkTextLineSegment *prev_seg; /* The segment just before the first
895 * new segment (NULL means new segment
896 * is at beginning of line). */
897 GtkTextLineSegment *cur_seg; /* Current segment; new characters
898 * are inserted just after this one.
899 * NULL means insert at beginning of
901 GtkTextLine *line; /* Current line (new segments are
902 * added to this line). */
903 GtkTextLineSegment *seg;
904 GtkTextLine *newline;
905 int chunk_len; /* # characters in current chunk. */
906 gint sol; /* start of line */
907 gint eol; /* Pointer to character just after last
908 * one in current chunk.
910 gint delim; /* index of paragraph delimiter */
911 int line_count_delta; /* Counts change to total number of
915 int char_count_delta; /* change to number of chars */
917 gint start_byte_index;
918 GtkTextLine *start_line;
920 g_return_if_fail (text != NULL);
921 g_return_if_fail (iter != NULL);
926 /* extract iterator info */
927 tree = _gtk_text_iter_get_btree (iter);
928 line = _gtk_text_iter_get_text_line (iter);
931 start_byte_index = gtk_text_iter_get_line_index (iter);
933 /* Get our insertion segment split. Note this assumes line allows
934 * char insertions, which isn't true of the "last" line. But iter
935 * should not be on that line, as we assert here.
937 g_assert (!_gtk_text_line_is_last (line, tree));
938 prev_seg = gtk_text_line_segment_split (iter);
941 /* Invalidate all iterators */
942 chars_changed (tree);
943 segments_changed (tree);
946 * Chop the text up into lines and create a new segment for
947 * each line, plus a new line for the leftovers from the
953 line_count_delta = 0;
954 char_count_delta = 0;
959 pango_find_paragraph_boundary (text + sol,
964 /* make these relative to the start of the text */
968 g_assert (eol >= sol);
969 g_assert (delim >= sol);
970 g_assert (eol >= delim);
972 g_assert (eol <= len);
974 chunk_len = eol - sol;
976 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
977 seg = _gtk_char_segment_new (&text[sol], chunk_len);
979 char_count_delta += seg->char_count;
983 seg->next = line->segments;
984 line->segments = seg;
988 seg->next = cur_seg->next;
994 /* chunk didn't end with a paragraph separator */
995 g_assert (eol == len);
1000 * The chunk ended with a newline, so create a new GtkTextLine
1001 * and move the remainder of the old line to it.
1004 newline = gtk_text_line_new ();
1005 gtk_text_line_set_parent (newline, line->parent);
1006 newline->next = line->next;
1007 line->next = newline;
1008 newline->segments = seg->next;
1016 * Cleanup the starting line for the insertion, plus the ending
1017 * line if it's different.
1020 cleanup_line (start_line);
1021 if (line != start_line)
1023 cleanup_line (line);
1026 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1028 /* Invalidate our region, and reset the iterator the user
1029 passed in to point to the end of the inserted text. */
1035 _gtk_text_btree_get_iter_at_line (tree,
1041 /* We could almost certainly be more efficient here
1042 by saving the information from the insertion loop
1044 gtk_text_iter_forward_chars (&end, char_count_delta);
1046 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1047 _gtk_text_btree_invalidate_region (tree,
1051 /* Convenience for the user */
1057 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1058 GtkTextLineSegment *seg)
1062 GtkTextLineSegment *prevPtr;
1065 gint start_byte_offset;
1067 line = _gtk_text_iter_get_text_line (iter);
1068 tree = _gtk_text_iter_get_btree (iter);
1069 start_byte_offset = gtk_text_iter_get_line_index (iter);
1071 prevPtr = gtk_text_line_segment_split (iter);
1072 if (prevPtr == NULL)
1074 seg->next = line->segments;
1075 line->segments = seg;
1079 seg->next = prevPtr->next;
1080 prevPtr->next = seg;
1083 post_insert_fixup (tree, line, 0, seg->char_count);
1085 chars_changed (tree);
1086 segments_changed (tree);
1088 /* reset *iter for the user, and invalidate tree nodes */
1090 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1093 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1095 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1096 _gtk_text_btree_invalidate_region (tree, &start, iter);
1100 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1103 GtkTextLineSegment *seg;
1105 seg = _gtk_pixbuf_segment_new (pixbuf);
1107 insert_pixbuf_or_widget_segment (iter, seg);
1111 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1112 GtkTextChildAnchor *anchor)
1114 GtkTextLineSegment *seg;
1117 if (anchor->segment != NULL)
1119 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1123 seg = _gtk_widget_segment_new (anchor);
1125 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1126 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1128 insert_pixbuf_or_widget_segment (iter, seg);
1130 if (tree->child_anchor_table == NULL)
1131 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1133 g_hash_table_insert (tree->child_anchor_table,
1134 seg->body.child.obj,
1135 seg->body.child.obj);
1139 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1141 GtkTextLineSegment *seg;
1143 seg = anchor->segment;
1145 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1154 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1155 GtkTextBTreeNode *node, gint y, gint *line_top,
1156 GtkTextLine *last_line)
1160 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1161 _gtk_text_btree_check (tree);
1163 if (node->level == 0)
1167 line = node->children.line;
1169 while (line != NULL && line != last_line)
1171 GtkTextLineData *ld;
1173 ld = _gtk_text_line_get_data (line, view->view_id);
1177 if (y < (current_y + (ld ? ld->height : 0)))
1180 current_y += ld->height;
1181 *line_top += ld->height;
1190 GtkTextBTreeNode *child;
1192 child = node->children.node;
1194 while (child != NULL)
1199 gtk_text_btree_node_get_size (child, view->view_id,
1202 if (y < (current_y + height))
1203 return find_line_by_y (tree, view, child,
1204 y - current_y, line_top,
1207 current_y += height;
1208 *line_top += height;
1210 child = child->next;
1218 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1225 GtkTextLine *last_line;
1228 view = gtk_text_btree_get_view (tree, view_id);
1229 g_return_val_if_fail (view != NULL, NULL);
1231 last_line = get_last_line (tree);
1233 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1237 *line_top_out = line_top;
1243 find_line_top_in_line_list (GtkTextBTree *tree,
1246 GtkTextLine *target_line,
1249 while (line != NULL)
1251 GtkTextLineData *ld;
1253 if (line == target_line)
1256 ld = _gtk_text_line_get_data (line, view->view_id);
1263 g_assert_not_reached (); /* If we get here, our
1264 target line didn't exist
1265 under its parent node */
1270 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1271 GtkTextLine *target_line,
1278 GtkTextBTreeNode *node;
1280 view = gtk_text_btree_get_view (tree, view_id);
1282 g_return_val_if_fail (view != NULL, 0);
1285 node = target_line->parent;
1286 while (node != NULL)
1288 nodes = g_slist_prepend (nodes, node);
1289 node = node->parent;
1293 while (iter != NULL)
1297 if (node->level == 0)
1299 g_slist_free (nodes);
1300 return find_line_top_in_line_list (tree, view,
1301 node->children.line,
1306 GtkTextBTreeNode *child;
1307 GtkTextBTreeNode *target_node;
1309 g_assert (iter->next != NULL); /* not at level 0 */
1310 target_node = iter->next->data;
1312 child = node->children.node;
1314 while (child != NULL)
1319 if (child == target_node)
1323 gtk_text_btree_node_get_size (child, view->view_id,
1327 child = child->next;
1329 g_assert (child != NULL); /* should have broken out before we
1333 iter = g_slist_next (iter);
1336 g_assert_not_reached (); /* we return when we find the target line */
1341 _gtk_text_btree_add_view (GtkTextBTree *tree,
1342 GtkTextLayout *layout)
1345 GtkTextLine *last_line;
1346 GtkTextLineData *line_data;
1348 g_return_if_fail (tree != NULL);
1350 view = g_new (BTreeView, 1);
1352 view->view_id = layout;
1353 view->layout = layout;
1355 view->next = tree->views;
1360 g_assert (tree->views->prev == NULL);
1361 tree->views->prev = view;
1366 /* The last line in the buffer has identity values for the per-view
1367 * data so that we can avoid special case checks for it in a large
1370 last_line = get_last_line (tree);
1372 line_data = g_new (GtkTextLineData, 1);
1373 line_data->view_id = layout;
1374 line_data->next = NULL;
1375 line_data->width = 0;
1376 line_data->height = 0;
1377 line_data->valid = TRUE;
1379 _gtk_text_line_add_data (last_line, line_data);
1383 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1387 GtkTextLine *last_line;
1388 GtkTextLineData *line_data;
1390 g_return_if_fail (tree != NULL);
1394 while (view != NULL)
1396 if (view->view_id == view_id)
1402 g_return_if_fail (view != NULL);
1405 view->next->prev = view->prev;
1408 view->prev->next = view->next;
1410 if (view == tree->views)
1411 tree->views = view->next;
1413 /* Remove the line data for the last line which we added ourselves.
1414 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1416 last_line = get_last_line (tree);
1417 line_data = _gtk_text_line_remove_data (last_line, view_id);
1420 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1422 view->layout = (gpointer) 0xdeadbeef;
1423 view->view_id = (gpointer) 0xdeadbeef;
1429 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1430 const GtkTextIter *start,
1431 const GtkTextIter *end)
1437 while (view != NULL)
1439 gtk_text_layout_invalidate (view->layout, start, end);
1446 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1451 g_return_if_fail (tree != NULL);
1452 g_return_if_fail (view_id != NULL);
1454 gtk_text_btree_node_get_size (tree->root_node, view_id,
1469 iter_stack_new (void)
1472 stack = g_new (IterStack, 1);
1473 stack->iters = NULL;
1480 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1483 if (stack->count > stack->alloced)
1485 stack->alloced = stack->count*2;
1486 stack->iters = g_realloc (stack->iters,
1487 stack->alloced*sizeof (GtkTextIter));
1489 stack->iters[stack->count-1] = *iter;
1493 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1495 if (stack->count == 0)
1500 *iter = stack->iters[stack->count];
1506 iter_stack_free (IterStack *stack)
1508 g_free (stack->iters);
1513 iter_stack_invert (IterStack *stack)
1515 if (stack->count > 0)
1518 guint j = stack->count - 1;
1523 tmp = stack->iters[i];
1524 stack->iters[i] = stack->iters[j];
1525 stack->iters[j] = tmp;
1534 queue_tag_redisplay (GtkTextBTree *tree,
1536 const GtkTextIter *start,
1537 const GtkTextIter *end)
1539 if (_gtk_text_tag_affects_size (tag))
1541 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1542 _gtk_text_btree_invalidate_region (tree, start, end);
1544 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1546 /* We only need to queue a redraw, not a relayout */
1547 redisplay_region (tree, start, end);
1550 /* We don't need to do anything if the tag doesn't affect display */
1554 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1555 const GtkTextIter *end_orig,
1559 GtkTextLineSegment *seg, *prev;
1560 GtkTextLine *cleanupline;
1561 gboolean toggled_on;
1562 GtkTextLine *start_line;
1563 GtkTextLine *end_line;
1565 GtkTextIter start, end;
1568 GtkTextTagInfo *info;
1570 g_return_if_fail (start_orig != NULL);
1571 g_return_if_fail (end_orig != NULL);
1572 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1573 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1574 _gtk_text_iter_get_btree (end_orig));
1575 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1578 printf ("%s tag %s from %d to %d\n",
1579 add ? "Adding" : "Removing",
1581 gtk_text_buffer_get_offset (start_orig),
1582 gtk_text_buffer_get_offset (end_orig));
1585 if (gtk_text_iter_equal (start_orig, end_orig))
1588 start = *start_orig;
1591 gtk_text_iter_order (&start, &end);
1593 tree = _gtk_text_iter_get_btree (&start);
1595 queue_tag_redisplay (tree, tag, &start, &end);
1597 info = gtk_text_btree_get_tag_info (tree, tag);
1599 start_line = _gtk_text_iter_get_text_line (&start);
1600 end_line = _gtk_text_iter_get_text_line (&end);
1602 /* Find all tag toggles in the region; we are going to delete them.
1603 We need to find them in advance, because
1604 forward_find_tag_toggle () won't work once we start playing around
1606 stack = iter_stack_new ();
1609 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1610 * which is deliberate - we don't want to delete a toggle at the
1613 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1615 if (gtk_text_iter_compare (&iter, &end) >= 0)
1618 iter_stack_push (stack, &iter);
1621 /* We need to traverse the toggles in order. */
1622 iter_stack_invert (stack);
1625 * See whether the tag is present at the start of the range. If
1626 * the state doesn't already match what we want then add a toggle
1630 toggled_on = gtk_text_iter_has_tag (&start, tag);
1631 if ( (add && !toggled_on) ||
1632 (!add && toggled_on) )
1634 /* This could create a second toggle at the start position;
1635 cleanup_line () will remove it if so. */
1636 seg = _gtk_toggle_segment_new (info, add);
1638 prev = gtk_text_line_segment_split (&start);
1641 seg->next = start_line->segments;
1642 start_line->segments = seg;
1646 seg->next = prev->next;
1650 /* cleanup_line adds the new toggle to the node counts. */
1652 printf ("added toggle at start\n");
1654 /* we should probably call segments_changed, but in theory
1655 any still-cached segments in the iters we are about to
1656 use are still valid, since they're in front
1662 * Scan the range of characters and delete any internal tag
1663 * transitions. Keep track of what the old state was at the end
1664 * of the range, and add a toggle there if it's needed.
1668 cleanupline = start_line;
1669 while (iter_stack_pop (stack, &iter))
1671 GtkTextLineSegment *indexable_seg;
1674 line = _gtk_text_iter_get_text_line (&iter);
1675 seg = _gtk_text_iter_get_any_segment (&iter);
1676 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1678 g_assert (seg != NULL);
1679 g_assert (indexable_seg != NULL);
1680 g_assert (seg != indexable_seg);
1682 prev = line->segments;
1684 /* Find the segment that actually toggles this tag. */
1685 while (seg != indexable_seg)
1687 g_assert (seg != NULL);
1688 g_assert (indexable_seg != NULL);
1689 g_assert (seg != indexable_seg);
1691 if ( (seg->type == >k_text_toggle_on_type ||
1692 seg->type == >k_text_toggle_off_type) &&
1693 (seg->body.toggle.info == info) )
1699 g_assert (seg != NULL);
1700 g_assert (indexable_seg != NULL);
1702 g_assert (seg != indexable_seg); /* If this happens, then
1703 forward_to_tag_toggle was
1705 g_assert (seg->body.toggle.info->tag == tag);
1707 /* If this happens, when previously tagging we didn't merge
1708 overlapping tags. */
1709 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1710 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1712 toggled_on = !toggled_on;
1715 printf ("deleting %s toggle\n",
1716 seg->type == >k_text_toggle_on_type ? "on" : "off");
1718 /* Remove toggle segment from the list. */
1721 line->segments = seg->next;
1725 while (prev->next != seg)
1729 prev->next = seg->next;
1732 /* Inform iterators we've hosed them. This actually reflects a
1733 bit of inefficiency; if you have the same tag toggled on and
1734 off a lot in a single line, we keep having the rescan from
1735 the front of the line. Of course we have to do that to get
1736 "prev" anyway, but here we are doing it an additional
1738 segments_changed (tree);
1740 /* Update node counts */
1741 if (seg->body.toggle.inNodeCounts)
1743 _gtk_change_node_toggle_count (line->parent,
1745 seg->body.toggle.inNodeCounts = FALSE;
1750 /* We only clean up lines when we're done with them, saves some
1751 gratuitous line-segment-traversals */
1753 if (cleanupline != line)
1755 cleanup_line (cleanupline);
1760 iter_stack_free (stack);
1762 /* toggled_on now reflects the toggle state _just before_ the
1763 end iterator. The end iterator could already have a toggle
1764 on or a toggle off. */
1765 if ( (add && !toggled_on) ||
1766 (!add && toggled_on) )
1768 /* This could create a second toggle at the start position;
1769 cleanup_line () will remove it if so. */
1771 seg = _gtk_toggle_segment_new (info, !add);
1773 prev = gtk_text_line_segment_split (&end);
1776 seg->next = end_line->segments;
1777 end_line->segments = seg;
1781 seg->next = prev->next;
1784 /* cleanup_line adds the new toggle to the node counts. */
1785 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1787 printf ("added toggle at end\n");
1792 * Cleanup cleanupline and the last line of the range, if
1793 * these are different.
1796 cleanup_line (cleanupline);
1797 if (cleanupline != end_line)
1799 cleanup_line (end_line);
1802 segments_changed (tree);
1804 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1805 _gtk_text_btree_check (tree);
1814 get_line_internal (GtkTextBTree *tree,
1816 gint *real_line_number,
1817 gboolean include_last)
1819 GtkTextBTreeNode *node;
1824 line_count = _gtk_text_btree_line_count (tree);
1828 if (line_number < 0)
1830 line_number = line_count;
1832 else if (line_number > line_count)
1834 line_number = line_count;
1837 if (real_line_number)
1838 *real_line_number = line_number;
1840 node = tree->root_node;
1841 lines_left = line_number;
1844 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1848 while (node->level != 0)
1850 for (node = node->children.node;
1851 node->num_lines <= lines_left;
1857 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1860 lines_left -= node->num_lines;
1865 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1868 for (line = node->children.line; lines_left > 0;
1874 g_error ("gtk_text_btree_find_line ran out of lines");
1883 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1886 _gtk_text_btree_get_line (tree,
1887 _gtk_text_btree_line_count (tree) - 1,
1892 _gtk_text_btree_get_line (GtkTextBTree *tree,
1894 gint *real_line_number)
1896 return get_line_internal (tree, line_number, real_line_number, TRUE);
1900 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1902 gint *real_line_number)
1904 return get_line_internal (tree, line_number, real_line_number, FALSE);
1908 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1910 gint *line_start_index,
1911 gint *real_char_index)
1913 GtkTextBTreeNode *node;
1915 GtkTextLineSegment *seg;
1920 node = tree->root_node;
1922 /* Clamp to valid indexes (-1 is magic for "highest index"),
1923 * node->num_chars includes the two newlines that aren't really
1926 if (char_index < 0 || char_index >= (node->num_chars - 1))
1928 char_index = node->num_chars - 2;
1931 *real_char_index = char_index;
1934 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1938 chars_left = char_index;
1939 while (node->level != 0)
1941 for (node = node->children.node;
1942 chars_left >= node->num_chars;
1945 chars_left -= node->num_chars;
1947 g_assert (chars_left >= 0);
1951 if (chars_left == 0)
1953 /* Start of a line */
1955 *line_start_index = char_index;
1956 return node->children.line;
1960 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1966 for (line = node->children.line; line != NULL; line = line->next)
1968 seg = line->segments;
1971 if (chars_in_line + seg->char_count > chars_left)
1972 goto found; /* found our line/segment */
1974 chars_in_line += seg->char_count;
1979 chars_left -= chars_in_line;
1987 g_assert (line != NULL); /* hosage, ran out of lines */
1988 g_assert (seg != NULL);
1990 *line_start_index = char_index - chars_left;
1995 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1998 GtkTextBTreeNode *node;
1999 GtkTextLine *siblingline;
2000 GtkTextLineSegment *seg;
2001 int src, dst, index;
2007 #define NUM_TAG_INFOS 10
2009 line = _gtk_text_iter_get_text_line (iter);
2010 tree = _gtk_text_iter_get_btree (iter);
2011 byte_index = gtk_text_iter_get_line_index (iter);
2013 tagInfo.numTags = 0;
2014 tagInfo.arraySize = NUM_TAG_INFOS;
2015 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2016 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2019 * Record tag toggles within the line of indexPtr but preceding
2020 * indexPtr. Note that if this loop segfaults, your
2021 * byte_index probably points past the sum of all
2022 * seg->byte_count */
2024 for (index = 0, seg = line->segments;
2025 (index + seg->byte_count) <= byte_index;
2026 index += seg->byte_count, seg = seg->next)
2028 if ((seg->type == >k_text_toggle_on_type)
2029 || (seg->type == >k_text_toggle_off_type))
2031 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2036 * Record toggles for tags in lines that are predecessors of
2037 * line but under the same level-0 GtkTextBTreeNode.
2040 for (siblingline = line->parent->children.line;
2041 siblingline != line;
2042 siblingline = siblingline->next)
2044 for (seg = siblingline->segments; seg != NULL;
2047 if ((seg->type == >k_text_toggle_on_type)
2048 || (seg->type == >k_text_toggle_off_type))
2050 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2056 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2057 * toggles for all siblings that precede that GtkTextBTreeNode.
2060 for (node = line->parent; node->parent != NULL;
2061 node = node->parent)
2063 GtkTextBTreeNode *siblingPtr;
2066 for (siblingPtr = node->parent->children.node;
2067 siblingPtr != node; siblingPtr = siblingPtr->next)
2069 for (summary = siblingPtr->summary; summary != NULL;
2070 summary = summary->next)
2072 if (summary->toggle_count & 1)
2074 inc_count (summary->info->tag, summary->toggle_count,
2082 * Go through the tag information and squash out all of the tags
2083 * that have even toggle counts (these tags exist before the point
2084 * of interest, but not at the desired character itself).
2087 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2089 if (tagInfo.counts[src] & 1)
2091 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2092 tagInfo.tags[dst] = tagInfo.tags[src];
2098 g_free (tagInfo.counts);
2101 g_free (tagInfo.tags);
2104 return tagInfo.tags;
2108 copy_segment (GString *string,
2109 gboolean include_hidden,
2110 gboolean include_nonchars,
2111 const GtkTextIter *start,
2112 const GtkTextIter *end)
2114 GtkTextLineSegment *end_seg;
2115 GtkTextLineSegment *seg;
2117 if (gtk_text_iter_equal (start, end))
2120 seg = _gtk_text_iter_get_indexable_segment (start);
2121 end_seg = _gtk_text_iter_get_indexable_segment (end);
2123 if (seg->type == >k_text_char_type)
2125 gboolean copy = TRUE;
2126 gint copy_bytes = 0;
2127 gint copy_start = 0;
2129 /* Don't copy if we're invisible; segments are invisible/not
2130 as a whole, no need to check each char */
2131 if (!include_hidden &&
2132 _gtk_text_btree_char_is_invisible (start))
2135 /* printf (" <invisible>\n"); */
2138 copy_start = _gtk_text_iter_get_segment_byte (start);
2142 /* End is in the same segment; need to copy fewer bytes. */
2143 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2145 copy_bytes = end_byte - copy_start;
2148 copy_bytes = seg->byte_count - copy_start;
2150 g_assert (copy_bytes != 0); /* Due to iter equality check at
2151 front of this function. */
2155 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2157 g_string_append_len (string,
2158 seg->body.chars + copy_start,
2162 /* printf (" :%s\n", string->str); */
2164 else if (seg->type == >k_text_pixbuf_type ||
2165 seg->type == >k_text_child_type)
2167 gboolean copy = TRUE;
2169 if (!include_nonchars)
2173 else if (!include_hidden &&
2174 _gtk_text_btree_char_is_invisible (start))
2181 g_string_append_len (string,
2182 gtk_text_unknown_char_utf8,
2190 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2191 const GtkTextIter *end_orig,
2192 gboolean include_hidden,
2193 gboolean include_nonchars)
2195 GtkTextLineSegment *seg;
2196 GtkTextLineSegment *end_seg;
2204 g_return_val_if_fail (start_orig != NULL, NULL);
2205 g_return_val_if_fail (end_orig != NULL, NULL);
2206 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2207 _gtk_text_iter_get_btree (end_orig), NULL);
2209 start = *start_orig;
2212 gtk_text_iter_order (&start, &end);
2214 retval = g_string_new (NULL);
2216 tree = _gtk_text_iter_get_btree (&start);
2218 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2220 seg = _gtk_text_iter_get_indexable_segment (&iter);
2221 while (seg != end_seg)
2223 copy_segment (retval, include_hidden, include_nonchars,
2226 _gtk_text_iter_forward_indexable_segment (&iter);
2228 seg = _gtk_text_iter_get_indexable_segment (&iter);
2231 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2234 g_string_free (retval, FALSE);
2239 _gtk_text_btree_line_count (GtkTextBTree *tree)
2241 /* Subtract bogus line at the end; we return a count
2243 return tree->root_node->num_lines - 1;
2247 _gtk_text_btree_char_count (GtkTextBTree *tree)
2249 /* Exclude newline in bogus last line and the
2250 * one in the last line that is after the end iterator
2252 return tree->root_node->num_chars - 2;
2255 #define LOTSA_TAGS 1000
2257 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2259 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2261 int deftagCnts[LOTSA_TAGS];
2262 int *tagCnts = deftagCnts;
2263 GtkTextTag *deftags[LOTSA_TAGS];
2264 GtkTextTag **tags = deftags;
2266 GtkTextBTreeNode *node;
2267 GtkTextLine *siblingline;
2268 GtkTextLineSegment *seg;
2275 line = _gtk_text_iter_get_text_line (iter);
2276 tree = _gtk_text_iter_get_btree (iter);
2277 byte_index = gtk_text_iter_get_line_index (iter);
2279 numTags = gtk_text_tag_table_get_size (tree->table);
2281 /* almost always avoid malloc, so stay out of system calls */
2282 if (LOTSA_TAGS < numTags)
2284 tagCnts = g_new (int, numTags);
2285 tags = g_new (GtkTextTag*, numTags);
2288 for (i=0; i<numTags; i++)
2294 * Record tag toggles within the line of indexPtr but preceding
2298 for (index = 0, seg = line->segments;
2299 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2300 index += seg->byte_count, seg = seg->next)
2302 if ((seg->type == >k_text_toggle_on_type)
2303 || (seg->type == >k_text_toggle_off_type))
2305 tag = seg->body.toggle.info->tag;
2306 if (tag->invisible_set && tag->values->invisible)
2308 tags[tag->priority] = tag;
2309 tagCnts[tag->priority]++;
2315 * Record toggles for tags in lines that are predecessors of
2316 * line but under the same level-0 GtkTextBTreeNode.
2319 for (siblingline = line->parent->children.line;
2320 siblingline != line;
2321 siblingline = siblingline->next)
2323 for (seg = siblingline->segments; seg != NULL;
2326 if ((seg->type == >k_text_toggle_on_type)
2327 || (seg->type == >k_text_toggle_off_type))
2329 tag = seg->body.toggle.info->tag;
2330 if (tag->invisible_set && tag->values->invisible)
2332 tags[tag->priority] = tag;
2333 tagCnts[tag->priority]++;
2340 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2341 * for all siblings that precede that GtkTextBTreeNode.
2344 for (node = line->parent; node->parent != NULL;
2345 node = node->parent)
2347 GtkTextBTreeNode *siblingPtr;
2350 for (siblingPtr = node->parent->children.node;
2351 siblingPtr != node; siblingPtr = siblingPtr->next)
2353 for (summary = siblingPtr->summary; summary != NULL;
2354 summary = summary->next)
2356 if (summary->toggle_count & 1)
2358 tag = summary->info->tag;
2359 if (tag->invisible_set && tag->values->invisible)
2361 tags[tag->priority] = tag;
2362 tagCnts[tag->priority] += summary->toggle_count;
2370 * Now traverse from highest priority to lowest,
2371 * take invisible value from first odd count (= on)
2374 for (i = numTags-1; i >=0; i--)
2378 /* FIXME not sure this should be if 0 */
2380 #ifndef ALWAYS_SHOW_SELECTION
2381 /* who would make the selection invisible? */
2382 if ((tag == tkxt->seltag)
2383 && !(tkxt->flags & GOT_FOCUS))
2389 invisible = tags[i]->values->invisible;
2394 if (LOTSA_TAGS < numTags)
2409 redisplay_region (GtkTextBTree *tree,
2410 const GtkTextIter *start,
2411 const GtkTextIter *end)
2414 GtkTextLine *start_line, *end_line;
2416 if (gtk_text_iter_compare (start, end) > 0)
2418 const GtkTextIter *tmp = start;
2423 start_line = _gtk_text_iter_get_text_line (start);
2424 end_line = _gtk_text_iter_get_text_line (end);
2427 while (view != NULL)
2429 gint start_y, end_y;
2430 GtkTextLineData *ld;
2432 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2434 if (end_line == start_line)
2437 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2439 ld = _gtk_text_line_get_data (end_line, view->view_id);
2441 end_y += ld->height;
2443 gtk_text_layout_changed (view->layout, start_y,
2452 redisplay_mark (GtkTextLineSegment *mark)
2457 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2459 mark->body.mark.obj);
2462 gtk_text_iter_forward_char (&end);
2464 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2465 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2470 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2472 if (!mark->body.mark.visible)
2475 redisplay_mark (mark);
2479 ensure_not_off_end (GtkTextBTree *tree,
2480 GtkTextLineSegment *mark,
2483 if (gtk_text_iter_get_line (iter) ==
2484 _gtk_text_btree_line_count (tree))
2485 gtk_text_iter_backward_char (iter);
2488 static GtkTextLineSegment*
2489 real_set_mark (GtkTextBTree *tree,
2490 GtkTextMark *existing_mark,
2492 gboolean left_gravity,
2493 const GtkTextIter *where,
2494 gboolean should_exist,
2495 gboolean redraw_selections)
2497 GtkTextLineSegment *mark;
2500 g_return_val_if_fail (tree != NULL, NULL);
2501 g_return_val_if_fail (where != NULL, NULL);
2502 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2505 mark = existing_mark->segment;
2506 else if (name != NULL)
2507 mark = g_hash_table_lookup (tree->mark_table,
2512 if (should_exist && mark == NULL)
2514 g_warning ("No mark `%s' exists!", name);
2518 /* OK if !should_exist and it does already exist, in that case
2524 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2525 _gtk_text_iter_check (&iter);
2529 if (redraw_selections &&
2530 (mark == tree->insert_mark->segment ||
2531 mark == tree->selection_bound_mark->segment))
2533 GtkTextIter old_pos;
2535 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2536 mark->body.mark.obj);
2537 redisplay_region (tree, &old_pos, where);
2541 * don't let visible marks be after the final newline of the
2545 if (mark->body.mark.visible)
2547 ensure_not_off_end (tree, mark, &iter);
2550 /* Redraw the mark's old location. */
2551 redisplay_mark_if_visible (mark);
2553 /* Unlink mark from its current location.
2554 This could hose our iterator... */
2555 gtk_text_btree_unlink_segment (tree, mark,
2556 mark->body.mark.line);
2557 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2558 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2560 segments_changed (tree); /* make sure the iterator recomputes its
2565 mark = _gtk_mark_segment_new (tree,
2569 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2571 if (mark->body.mark.name)
2572 g_hash_table_insert (tree->mark_table,
2573 mark->body.mark.name,
2577 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2578 _gtk_text_iter_check (&iter);
2580 /* Link mark into new location */
2581 gtk_text_btree_link_segment (mark, &iter);
2583 /* Invalidate some iterators. */
2584 segments_changed (tree);
2587 * update the screen at the mark's new location.
2590 redisplay_mark_if_visible (mark);
2592 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2593 _gtk_text_iter_check (&iter);
2595 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2596 _gtk_text_btree_check (tree);
2603 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2604 GtkTextMark *existing_mark,
2606 gboolean left_gravity,
2607 const GtkTextIter *iter,
2608 gboolean should_exist)
2610 GtkTextLineSegment *seg;
2612 seg = real_set_mark (tree, existing_mark,
2613 name, left_gravity, iter, should_exist,
2616 return seg ? seg->body.mark.obj : NULL;
2620 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2624 GtkTextIter tmp_start, tmp_end;
2626 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2628 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2629 tree->selection_bound_mark);
2631 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2643 gtk_text_iter_order (&tmp_start, &tmp_end);
2656 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2657 const GtkTextIter *iter)
2659 _gtk_text_btree_select_range (tree, iter, iter);
2663 _gtk_text_btree_select_range (GtkTextBTree *tree,
2664 const GtkTextIter *ins,
2665 const GtkTextIter *bound)
2667 GtkTextIter start, end;
2669 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2670 redisplay_region (tree, &start, &end);
2672 /* Move insert AND selection_bound before we redisplay */
2673 real_set_mark (tree, tree->insert_mark,
2674 "insert", FALSE, ins, TRUE, FALSE);
2675 real_set_mark (tree, tree->selection_bound_mark,
2676 "selection_bound", FALSE, bound, TRUE, FALSE);
2681 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2686 g_return_if_fail (tree != NULL);
2687 g_return_if_fail (name != NULL);
2689 mark = g_hash_table_lookup (tree->mark_table,
2692 _gtk_text_btree_remove_mark (tree, mark);
2696 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2697 GtkTextLineSegment *segment)
2700 if (segment->body.mark.name)
2701 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2703 segment->body.mark.tree = NULL;
2704 segment->body.mark.line = NULL;
2706 /* Remove the ref on the mark, which frees segment as a side effect
2707 * if this is the last reference.
2709 g_object_unref (segment->body.mark.obj);
2713 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2716 GtkTextLineSegment *segment;
2718 g_return_if_fail (mark != NULL);
2719 g_return_if_fail (tree != NULL);
2721 segment = mark->segment;
2723 if (segment->body.mark.not_deleteable)
2725 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2729 /* This calls cleanup_line and segments_changed */
2730 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2732 _gtk_text_btree_release_mark_segment (tree, segment);
2736 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2737 GtkTextMark *segment)
2739 return segment == tree->insert_mark;
2743 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2744 GtkTextMark *segment)
2746 return segment == tree->selection_bound_mark;
2750 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2753 GtkTextLineSegment *seg;
2755 g_return_val_if_fail (tree != NULL, NULL);
2756 g_return_val_if_fail (name != NULL, NULL);
2758 seg = g_hash_table_lookup (tree->mark_table, name);
2760 return seg ? seg->body.mark.obj : NULL;
2764 * gtk_text_mark_set_visible:
2765 * @mark: a #GtkTextMark
2766 * @setting: visibility of mark
2768 * Sets the visibility of @mark; the insertion point is normally
2769 * visible, i.e. you can see it as a vertical bar. Also, the text
2770 * widget uses a visible mark to indicate where a drop will occur when
2771 * dragging-and-dropping text. Most other marks are not visible.
2772 * Marks are not visible by default.
2776 gtk_text_mark_set_visible (GtkTextMark *mark,
2779 GtkTextLineSegment *seg;
2781 g_return_if_fail (mark != NULL);
2783 seg = mark->segment;
2785 if (seg->body.mark.visible == setting)
2789 seg->body.mark.visible = setting;
2791 redisplay_mark (seg);
2796 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2799 GtkTextBTreeNode *node;
2800 GtkTextTagInfo *info;
2802 g_return_val_if_fail (tree != NULL, NULL);
2806 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2811 if (info->tag_root == NULL)
2814 node = info->tag_root;
2816 /* We know the tag root has instances of the given
2819 continue_outer_loop:
2820 g_assert (node != NULL);
2821 while (node->level > 0)
2823 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2824 node = node->children.node;
2825 while (node != NULL)
2827 if (gtk_text_btree_node_has_tag (node, tag))
2828 goto continue_outer_loop;
2832 g_assert (node != NULL);
2835 g_assert (node != NULL); /* The tag summaries said some node had
2838 g_assert (node->level == 0);
2840 return node->children.line;
2844 /* Looking for any tag at all (tag == NULL).
2845 Unfortunately this can't be done in a simple and efficient way
2846 right now; so I'm just going to return the
2847 first line in the btree. FIXME */
2848 return _gtk_text_btree_get_line (tree, 0, NULL);
2853 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2856 GtkTextBTreeNode *node;
2857 GtkTextBTreeNode *last_node;
2859 GtkTextTagInfo *info;
2861 g_return_val_if_fail (tree != NULL, NULL);
2865 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2867 if (info->tag_root == NULL)
2870 node = info->tag_root;
2871 /* We know the tag root has instances of the given
2874 while (node->level > 0)
2876 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2878 node = node->children.node;
2879 while (node != NULL)
2881 if (gtk_text_btree_node_has_tag (node, tag))
2889 g_assert (node != NULL); /* The tag summaries said some node had
2892 g_assert (node->level == 0);
2894 /* Find the last line in this node */
2895 line = node->children.line;
2896 while (line->next != NULL)
2903 /* This search can't be done efficiently at the moment,
2904 at least not without complexity.
2905 So, we just return the last line.
2907 return _gtk_text_btree_get_end_iter_line (tree);
2917 _gtk_text_line_get_number (GtkTextLine *line)
2920 GtkTextBTreeNode *node, *parent, *node2;
2924 * First count how many lines precede this one in its level-0
2928 node = line->parent;
2930 for (line2 = node->children.line; line2 != line;
2931 line2 = line2->next)
2935 g_error ("gtk_text_btree_line_number couldn't find line");
2941 * Now work up through the levels of the tree one at a time,
2942 * counting how many lines are in GtkTextBTreeNodes preceding the current
2946 for (parent = node->parent ; parent != NULL;
2947 node = parent, parent = parent->parent)
2949 for (node2 = parent->children.node; node2 != node;
2950 node2 = node2->next)
2954 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2956 index += node2->num_lines;
2962 static GtkTextLineSegment*
2963 find_toggle_segment_before_char (GtkTextLine *line,
2967 GtkTextLineSegment *seg;
2968 GtkTextLineSegment *toggle_seg;
2973 seg = line->segments;
2974 while ( (index + seg->char_count) <= char_in_line )
2976 if (((seg->type == >k_text_toggle_on_type)
2977 || (seg->type == >k_text_toggle_off_type))
2978 && (seg->body.toggle.info->tag == tag))
2981 index += seg->char_count;
2988 static GtkTextLineSegment*
2989 find_toggle_segment_before_byte (GtkTextLine *line,
2993 GtkTextLineSegment *seg;
2994 GtkTextLineSegment *toggle_seg;
2999 seg = line->segments;
3000 while ( (index + seg->byte_count) <= byte_in_line )
3002 if (((seg->type == >k_text_toggle_on_type)
3003 || (seg->type == >k_text_toggle_off_type))
3004 && (seg->body.toggle.info->tag == tag))
3007 index += seg->byte_count;
3015 find_toggle_outside_current_line (GtkTextLine *line,
3019 GtkTextBTreeNode *node;
3020 GtkTextLine *sibling_line;
3021 GtkTextLineSegment *seg;
3022 GtkTextLineSegment *toggle_seg;
3024 GtkTextTagInfo *info = NULL;
3027 * No toggle in this line. Look for toggles for the tag in lines
3028 * that are predecessors of line but under the same
3029 * level-0 GtkTextBTreeNode.
3032 sibling_line = line->parent->children.line;
3033 while (sibling_line != line)
3035 seg = sibling_line->segments;
3038 if (((seg->type == >k_text_toggle_on_type)
3039 || (seg->type == >k_text_toggle_off_type))
3040 && (seg->body.toggle.info->tag == tag))
3046 sibling_line = sibling_line->next;
3049 if (toggle_seg != NULL)
3050 return (toggle_seg->type == >k_text_toggle_on_type);
3053 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3054 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3055 * siblings that precede that GtkTextBTreeNode.
3058 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3064 node = line->parent;
3065 while (node->parent != NULL)
3067 GtkTextBTreeNode *sibling_node;
3069 sibling_node = node->parent->children.node;
3070 while (sibling_node != node)
3074 summary = sibling_node->summary;
3075 while (summary != NULL)
3077 if (summary->info == info)
3078 toggles += summary->toggle_count;
3080 summary = summary->next;
3083 sibling_node = sibling_node->next;
3086 if (node == info->tag_root)
3089 node = node->parent;
3093 * An odd number of toggles means that the tag is present at the
3097 return (toggles & 1) != 0;
3100 /* FIXME this function is far too slow, for no good reason. */
3102 _gtk_text_line_char_has_tag (GtkTextLine *line,
3107 GtkTextLineSegment *toggle_seg;
3109 g_return_val_if_fail (line != NULL, FALSE);
3112 * Check for toggles for the tag in the line but before
3113 * the char. If there is one, its type indicates whether or
3114 * not the character is tagged.
3117 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3119 if (toggle_seg != NULL)
3120 return (toggle_seg->type == >k_text_toggle_on_type);
3122 return find_toggle_outside_current_line (line, tree, tag);
3126 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3131 GtkTextLineSegment *toggle_seg;
3133 g_return_val_if_fail (line != NULL, FALSE);
3136 * Check for toggles for the tag in the line but before
3137 * the char. If there is one, its type indicates whether or
3138 * not the character is tagged.
3141 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3143 if (toggle_seg != NULL)
3144 return (toggle_seg->type == >k_text_toggle_on_type);
3146 return find_toggle_outside_current_line (line, tree, tag);
3150 _gtk_text_line_is_last (GtkTextLine *line,
3153 return line == get_last_line (tree);
3157 ensure_end_iter_line (GtkTextBTree *tree)
3159 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3164 /* n_lines is without the magic line at the end */
3165 n_lines = _gtk_text_btree_line_count (tree);
3167 g_assert (n_lines >= 1);
3169 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3171 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3176 ensure_end_iter_segment (GtkTextBTree *tree)
3178 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3180 GtkTextLineSegment *seg;
3181 GtkTextLineSegment *last_with_chars;
3183 ensure_end_iter_line (tree);
3185 last_with_chars = NULL;
3187 seg = tree->end_iter_line->segments;
3190 if (seg->char_count > 0)
3191 last_with_chars = seg;
3195 tree->end_iter_segment = last_with_chars;
3197 /* We know the last char in the last line is '\n' */
3198 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3199 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3201 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3203 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3204 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3209 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3212 ensure_end_iter_line (tree);
3214 return line == tree->end_iter_line;
3218 _gtk_text_btree_is_end (GtkTextBTree *tree,
3220 GtkTextLineSegment *seg,
3224 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3226 /* Do this first to avoid walking segments in most cases */
3227 if (!_gtk_text_line_contains_end_iter (line, tree))
3230 ensure_end_iter_segment (tree);
3232 if (seg != tree->end_iter_segment)
3235 if (byte_index >= 0)
3236 return byte_index == tree->end_iter_segment_byte_index;
3238 return char_offset == tree->end_iter_segment_char_offset;
3242 _gtk_text_line_next (GtkTextLine *line)
3244 GtkTextBTreeNode *node;
3246 if (line->next != NULL)
3251 * This was the last line associated with the particular parent
3252 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3253 * then search down from that GtkTextBTreeNode to find the first
3257 node = line->parent;
3258 while (node != NULL && node->next == NULL)
3259 node = node->parent;
3265 while (node->level > 0)
3267 node = node->children.node;
3270 g_assert (node->children.line != line);
3272 return node->children.line;
3277 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3281 next = _gtk_text_line_next (line);
3283 /* If we were on the end iter line, we can't go to
3286 if (next && next->next == NULL && /* these checks are optimization only */
3287 _gtk_text_line_next (next) == NULL)
3294 _gtk_text_line_previous (GtkTextLine *line)
3296 GtkTextBTreeNode *node;
3297 GtkTextBTreeNode *node2;
3301 * Find the line under this GtkTextBTreeNode just before the starting line.
3303 prev = line->parent->children.line; /* First line at leaf */
3304 while (prev != line)
3306 if (prev->next == line)
3312 g_error ("gtk_text_btree_previous_line ran out of lines");
3316 * This was the first line associated with the particular parent
3317 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3318 * then search down from that GtkTextBTreeNode to find its last line.
3320 for (node = line->parent; ; node = node->parent)
3322 if (node == NULL || node->parent == NULL)
3324 else if (node != node->parent->children.node)
3328 for (node2 = node->parent->children.node; ;
3329 node2 = node2->children.node)
3331 while (node2->next != node)
3332 node2 = node2->next;
3334 if (node2->level == 0)
3340 for (prev = node2->children.line ; ; prev = prev->next)
3342 if (prev->next == NULL)
3346 g_assert_not_reached ();
3352 _gtk_text_line_data_new (GtkTextLayout *layout,
3355 GtkTextLineData *line_data;
3357 line_data = g_new (GtkTextLineData, 1);
3359 line_data->view_id = layout;
3360 line_data->next = NULL;
3361 line_data->width = 0;
3362 line_data->height = 0;
3363 line_data->valid = FALSE;
3369 _gtk_text_line_add_data (GtkTextLine *line,
3370 GtkTextLineData *data)
3372 g_return_if_fail (line != NULL);
3373 g_return_if_fail (data != NULL);
3374 g_return_if_fail (data->view_id != NULL);
3378 data->next = line->views;
3388 _gtk_text_line_remove_data (GtkTextLine *line,
3391 GtkTextLineData *prev;
3392 GtkTextLineData *iter;
3394 g_return_val_if_fail (line != NULL, NULL);
3395 g_return_val_if_fail (view_id != NULL, NULL);
3399 while (iter != NULL)
3401 if (iter->view_id == view_id)
3410 prev->next = iter->next;
3412 line->views = iter->next;
3421 _gtk_text_line_get_data (GtkTextLine *line,
3424 GtkTextLineData *iter;
3426 g_return_val_if_fail (line != NULL, NULL);
3427 g_return_val_if_fail (view_id != NULL, NULL);
3430 while (iter != NULL)
3432 if (iter->view_id == view_id)
3441 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3442 GtkTextLineData *ld)
3444 /* For now this is totally unoptimized. FIXME?
3446 We could probably optimize the case where the width removed
3447 is less than the max width for the parent node,
3448 and the case where the height is unchanged when we re-wrap.
3451 g_return_if_fail (ld != NULL);
3454 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3458 _gtk_text_line_char_count (GtkTextLine *line)
3460 GtkTextLineSegment *seg;
3464 seg = line->segments;
3467 size += seg->char_count;
3474 _gtk_text_line_byte_count (GtkTextLine *line)
3476 GtkTextLineSegment *seg;
3480 seg = line->segments;
3483 size += seg->byte_count;
3491 _gtk_text_line_char_index (GtkTextLine *target_line)
3493 GSList *node_stack = NULL;
3494 GtkTextBTreeNode *iter;
3498 /* Push all our parent nodes onto a stack */
3499 iter = target_line->parent;
3501 g_assert (iter != NULL);
3503 while (iter != NULL)
3505 node_stack = g_slist_prepend (node_stack, iter);
3507 iter = iter->parent;
3510 /* Check that we have the root node on top of the stack. */
3511 g_assert (node_stack != NULL &&
3512 node_stack->data != NULL &&
3513 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3515 /* Add up chars in all nodes before the nodes in our stack.
3519 iter = node_stack->data;
3520 while (iter != NULL)
3522 GtkTextBTreeNode *child_iter;
3523 GtkTextBTreeNode *next_node;
3525 next_node = node_stack->next ?
3526 node_stack->next->data : NULL;
3527 node_stack = g_slist_remove (node_stack, node_stack->data);
3529 if (iter->level == 0)
3531 /* stack should be empty when we're on the last node */
3532 g_assert (node_stack == NULL);
3533 break; /* Our children are now lines */
3536 g_assert (next_node != NULL);
3537 g_assert (iter != NULL);
3538 g_assert (next_node->parent == iter);
3540 /* Add up chars before us in the tree */
3541 child_iter = iter->children.node;
3542 while (child_iter != next_node)
3544 g_assert (child_iter != NULL);
3546 num_chars += child_iter->num_chars;
3548 child_iter = child_iter->next;
3554 g_assert (iter != NULL);
3555 g_assert (iter == target_line->parent);
3557 /* Since we don't store char counts in lines, only in segments, we
3558 have to iterate over the lines adding up segment char counts
3559 until we find our line. */
3560 line = iter->children.line;
3561 while (line != target_line)
3563 g_assert (line != NULL);
3565 num_chars += _gtk_text_line_char_count (line);
3570 g_assert (line == target_line);
3576 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3580 GtkTextLineSegment *seg;
3583 g_return_val_if_fail (line != NULL, NULL);
3585 offset = byte_offset;
3586 seg = line->segments;
3588 while (offset >= seg->byte_count)
3590 g_assert (seg != NULL); /* means an invalid byte index */
3591 offset -= seg->byte_count;
3596 *seg_offset = offset;
3602 _gtk_text_line_char_to_segment (GtkTextLine *line,
3606 GtkTextLineSegment *seg;
3609 g_return_val_if_fail (line != NULL, NULL);
3611 offset = char_offset;
3612 seg = line->segments;
3614 while (offset >= seg->char_count)
3616 g_assert (seg != NULL); /* means an invalid char index */
3617 offset -= seg->char_count;
3622 *seg_offset = offset;
3628 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3632 GtkTextLineSegment *seg;
3635 g_return_val_if_fail (line != NULL, NULL);
3637 offset = byte_offset;
3638 seg = line->segments;
3640 while (offset > 0 && offset >= seg->byte_count)
3642 g_assert (seg != NULL); /* means an invalid byte index */
3643 offset -= seg->byte_count;
3648 *seg_offset = offset;
3654 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3658 GtkTextLineSegment *seg;
3661 g_return_val_if_fail (line != NULL, NULL);
3663 offset = char_offset;
3664 seg = line->segments;
3666 while (offset > 0 && offset >= seg->char_count)
3668 g_assert (seg != NULL); /* means an invalid byte index */
3669 offset -= seg->char_count;
3674 *seg_offset = offset;
3680 _gtk_text_line_byte_to_char (GtkTextLine *line,
3684 GtkTextLineSegment *seg;
3686 g_return_val_if_fail (line != NULL, 0);
3687 g_return_val_if_fail (byte_offset >= 0, 0);
3690 seg = line->segments;
3691 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3692 the next segment) */
3694 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3696 byte_offset -= seg->byte_count;
3697 char_offset += seg->char_count;
3702 g_assert (seg != NULL);
3704 /* Now byte_offset is the offset into the current segment,
3705 and char_offset is the start of the current segment.
3706 Optimize the case where no chars use > 1 byte */
3707 if (seg->byte_count == seg->char_count)
3708 return char_offset + byte_offset;
3711 if (seg->type == >k_text_char_type)
3712 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3715 g_assert (seg->char_count == 1);
3716 g_assert (byte_offset == 0);
3724 _gtk_text_line_char_to_byte (GtkTextLine *line,
3727 g_warning ("FIXME not implemented");
3732 /* FIXME sync with char_locate (or figure out a clean
3733 way to merge the two functions) */
3735 _gtk_text_line_byte_locate (GtkTextLine *line,
3737 GtkTextLineSegment **segment,
3738 GtkTextLineSegment **any_segment,
3739 gint *seg_byte_offset,
3740 gint *line_byte_offset)
3742 GtkTextLineSegment *seg;
3743 GtkTextLineSegment *after_prev_indexable;
3744 GtkTextLineSegment *after_last_indexable;
3745 GtkTextLineSegment *last_indexable;
3749 g_return_val_if_fail (line != NULL, FALSE);
3750 g_return_val_if_fail (byte_offset >= 0, FALSE);
3753 *any_segment = NULL;
3756 offset = byte_offset;
3758 last_indexable = NULL;
3759 after_last_indexable = line->segments;
3760 after_prev_indexable = line->segments;
3761 seg = line->segments;
3763 /* The loop ends when we're inside a segment;
3764 last_indexable refers to the last segment
3765 we passed entirely. */
3766 while (seg && offset >= seg->byte_count)
3768 if (seg->char_count > 0)
3770 offset -= seg->byte_count;
3771 bytes_in_line += seg->byte_count;
3772 last_indexable = seg;
3773 after_prev_indexable = after_last_indexable;
3774 after_last_indexable = last_indexable->next;
3782 /* We went off the end of the line */
3784 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3791 if (after_last_indexable != NULL)
3792 *any_segment = after_last_indexable;
3794 *any_segment = *segment;
3797 /* Override any_segment if we're in the middle of a segment. */
3799 *any_segment = *segment;
3801 *seg_byte_offset = offset;
3803 g_assert (*segment != NULL);
3804 g_assert (*any_segment != NULL);
3805 g_assert (*seg_byte_offset < (*segment)->byte_count);
3807 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3812 /* FIXME sync with byte_locate (or figure out a clean
3813 way to merge the two functions) */
3815 _gtk_text_line_char_locate (GtkTextLine *line,
3817 GtkTextLineSegment **segment,
3818 GtkTextLineSegment **any_segment,
3819 gint *seg_char_offset,
3820 gint *line_char_offset)
3822 GtkTextLineSegment *seg;
3823 GtkTextLineSegment *after_prev_indexable;
3824 GtkTextLineSegment *after_last_indexable;
3825 GtkTextLineSegment *last_indexable;
3829 g_return_val_if_fail (line != NULL, FALSE);
3830 g_return_val_if_fail (char_offset >= 0, FALSE);
3833 *any_segment = NULL;
3836 offset = char_offset;
3838 last_indexable = NULL;
3839 after_last_indexable = line->segments;
3840 after_prev_indexable = line->segments;
3841 seg = line->segments;
3843 /* The loop ends when we're inside a segment;
3844 last_indexable refers to the last segment
3845 we passed entirely. */
3846 while (seg && offset >= seg->char_count)
3848 if (seg->char_count > 0)
3850 offset -= seg->char_count;
3851 chars_in_line += seg->char_count;
3852 last_indexable = seg;
3853 after_prev_indexable = after_last_indexable;
3854 after_last_indexable = last_indexable->next;
3862 /* end of the line */
3864 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3871 if (after_last_indexable != NULL)
3872 *any_segment = after_last_indexable;
3874 *any_segment = *segment;
3877 /* Override any_segment if we're in the middle of a segment. */
3879 *any_segment = *segment;
3881 *seg_char_offset = offset;
3883 g_assert (*segment != NULL);
3884 g_assert (*any_segment != NULL);
3885 g_assert (*seg_char_offset < (*segment)->char_count);
3887 *line_char_offset = chars_in_line + *seg_char_offset;
3893 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3895 gint *line_char_offset,
3896 gint *seg_char_offset)
3898 GtkTextLineSegment *seg;
3901 g_return_if_fail (line != NULL);
3902 g_return_if_fail (byte_offset >= 0);
3904 *line_char_offset = 0;
3906 offset = byte_offset;
3907 seg = line->segments;
3909 while (offset >= seg->byte_count)
3911 offset -= seg->byte_count;
3912 *line_char_offset += seg->char_count;
3914 g_assert (seg != NULL); /* means an invalid char offset */
3917 g_assert (seg->char_count > 0); /* indexable. */
3919 /* offset is now the number of bytes into the current segment we
3920 * want to go. Count chars into the current segment.
3923 if (seg->type == >k_text_char_type)
3925 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3927 g_assert (*seg_char_offset < seg->char_count);
3929 *line_char_offset += *seg_char_offset;
3933 g_assert (offset == 0);
3934 *seg_char_offset = 0;
3939 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3941 gint *line_byte_offset,
3942 gint *seg_byte_offset)
3944 GtkTextLineSegment *seg;
3947 g_return_if_fail (line != NULL);
3948 g_return_if_fail (char_offset >= 0);
3950 *line_byte_offset = 0;
3952 offset = char_offset;
3953 seg = line->segments;
3955 while (offset >= seg->char_count)
3957 offset -= seg->char_count;
3958 *line_byte_offset += seg->byte_count;
3960 g_assert (seg != NULL); /* means an invalid char offset */
3963 g_assert (seg->char_count > 0); /* indexable. */
3965 /* offset is now the number of chars into the current segment we
3966 want to go. Count bytes into the current segment. */
3968 if (seg->type == >k_text_char_type)
3970 *seg_byte_offset = 0;
3974 const char * start = seg->body.chars + *seg_byte_offset;
3976 bytes = g_utf8_next_char (start) - start;
3977 *seg_byte_offset += bytes;
3981 g_assert (*seg_byte_offset < seg->byte_count);
3983 *line_byte_offset += *seg_byte_offset;
3987 g_assert (offset == 0);
3988 *seg_byte_offset = 0;
3993 node_compare (GtkTextBTreeNode *lhs,
3994 GtkTextBTreeNode *rhs)
3996 GtkTextBTreeNode *iter;
3997 GtkTextBTreeNode *node;
3998 GtkTextBTreeNode *common_parent;
3999 GtkTextBTreeNode *parent_of_lower;
4000 GtkTextBTreeNode *parent_of_higher;
4001 gboolean lhs_is_lower;
4002 GtkTextBTreeNode *lower;
4003 GtkTextBTreeNode *higher;
4005 /* This function assumes that lhs and rhs are not underneath each
4012 if (lhs->level < rhs->level)
4014 lhs_is_lower = TRUE;
4020 lhs_is_lower = FALSE;
4025 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4026 * of the common parent we used to reach the common parent; the
4027 * ordering of these child nodes in the child list is the ordering
4031 /* Get on the same level (may be on same level already) */
4033 while (node->level < higher->level)
4034 node = node->parent;
4036 g_assert (node->level == higher->level);
4038 g_assert (node != higher); /* Happens if lower is underneath higher */
4040 /* Go up until we have two children with a common parent.
4042 parent_of_lower = node;
4043 parent_of_higher = higher;
4045 while (parent_of_lower->parent != parent_of_higher->parent)
4047 parent_of_lower = parent_of_lower->parent;
4048 parent_of_higher = parent_of_higher->parent;
4051 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4053 common_parent = parent_of_lower->parent;
4055 g_assert (common_parent != NULL);
4057 /* See which is first in the list of common_parent's children */
4058 iter = common_parent->children.node;
4059 while (iter != NULL)
4061 if (iter == parent_of_higher)
4063 /* higher is less than lower */
4066 return 1; /* lhs > rhs */
4070 else if (iter == parent_of_lower)
4072 /* lower is less than higher */
4075 return -1; /* lhs < rhs */
4083 g_assert_not_reached ();
4087 /* remember that tag == NULL means "any tag" */
4089 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4093 GtkTextBTreeNode *node;
4094 GtkTextTagInfo *info;
4095 gboolean below_tag_root;
4097 g_return_val_if_fail (line != NULL, NULL);
4099 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4100 _gtk_text_btree_check (tree);
4104 /* Right now we can only offer linear-search if the user wants
4105 * to know about any tag toggle at all.
4107 return _gtk_text_line_next_excluding_last (line);
4110 /* Our tag summaries only have node precision, not line
4111 * precision. This means that if any line under a node could contain a
4112 * tag, then any of the others could also contain a tag.
4114 * In the future we could have some mechanism to keep track of how
4115 * many toggles we've found under a node so far, since we have a
4116 * count of toggles under the node. But for now I'm going with KISS.
4119 /* return same-node line, if any. */
4123 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4127 if (info->tag_root == NULL)
4130 if (info->tag_root == line->parent)
4131 return NULL; /* we were at the last line under the tag root */
4133 /* We need to go up out of this node, and on to the next one with
4134 toggles for the target tag. If we're below the tag root, we need to
4135 find the next node below the tag root that has tag summaries. If
4136 we're not below the tag root, we need to see if the tag root is
4137 after us in the tree, and if so, return the first line underneath
4140 node = line->parent;
4141 below_tag_root = FALSE;
4142 while (node != NULL)
4144 if (node == info->tag_root)
4146 below_tag_root = TRUE;
4150 node = node->parent;
4155 node = line->parent;
4156 while (node != info->tag_root)
4158 if (node->next == NULL)
4159 node = node->parent;
4164 if (gtk_text_btree_node_has_tag (node, tag))
4174 ordering = node_compare (line->parent, info->tag_root);
4178 /* Tag root is ahead of us, so search there. */
4179 node = info->tag_root;
4184 /* Tag root is after us, so no more lines that
4185 * could contain the tag.
4190 g_assert_not_reached ();
4195 g_assert (node != NULL);
4197 /* We have to find the first sub-node of this node that contains
4201 while (node->level > 0)
4203 g_assert (node != NULL); /* If this fails, it likely means an
4204 incorrect tag summary led us on a
4205 wild goose chase down this branch of
4207 node = node->children.node;
4208 while (node != NULL)
4210 if (gtk_text_btree_node_has_tag (node, tag))
4216 g_assert (node != NULL);
4217 g_assert (node->level == 0);
4219 return node->children.line;
4223 prev_line_under_node (GtkTextBTreeNode *node,
4228 prev = node->children.line;
4234 while (prev->next != line)
4244 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4248 GtkTextBTreeNode *node;
4249 GtkTextBTreeNode *found_node = NULL;
4250 GtkTextTagInfo *info;
4251 gboolean below_tag_root;
4253 GtkTextBTreeNode *line_ancestor;
4254 GtkTextBTreeNode *line_ancestor_parent;
4256 /* See next_could_contain_tag () for more extensive comments
4257 * on what's going on here.
4260 g_return_val_if_fail (line != NULL, NULL);
4262 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4263 _gtk_text_btree_check (tree);
4267 /* Right now we can only offer linear-search if the user wants
4268 * to know about any tag toggle at all.
4270 return _gtk_text_line_previous (line);
4273 /* Return same-node line, if any. */
4274 prev = prev_line_under_node (line->parent, line);
4278 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4282 if (info->tag_root == NULL)
4285 if (info->tag_root == line->parent)
4286 return NULL; /* we were at the first line under the tag root */
4288 /* Are we below the tag root */
4289 node = line->parent;
4290 below_tag_root = FALSE;
4291 while (node != NULL)
4293 if (node == info->tag_root)
4295 below_tag_root = TRUE;
4299 node = node->parent;
4304 /* Look for a previous node under this tag root that has our
4308 /* this assertion holds because line->parent is not the
4309 * tag root, we are below the tag root, and the tag
4312 g_assert (line->parent->parent != NULL);
4314 line_ancestor = line->parent;
4315 line_ancestor_parent = line->parent->parent;
4317 node = line_ancestor_parent->children.node;
4318 while (node != line_ancestor &&
4319 line_ancestor != info->tag_root)
4321 GSList *child_nodes = NULL;
4324 /* Create reverse-order list of nodes before
4327 while (node != line_ancestor
4330 child_nodes = g_slist_prepend (child_nodes, node);
4335 /* Try to find a node with our tag on it in the list */
4339 GtkTextBTreeNode *this_node = tmp->data;
4341 g_assert (this_node != line_ancestor);
4343 if (gtk_text_btree_node_has_tag (this_node, tag))
4345 found_node = this_node;
4346 g_slist_free (child_nodes);
4350 tmp = g_slist_next (tmp);
4353 g_slist_free (child_nodes);
4355 /* Didn't find anything on this level; go up one level. */
4356 line_ancestor = line_ancestor_parent;
4357 line_ancestor_parent = line_ancestor->parent;
4359 if (line_ancestor_parent != NULL)
4361 node = line_ancestor_parent->children.node;
4372 ordering = node_compare (line->parent, info->tag_root);
4376 /* Tag root is ahead of us, so no more lines
4383 /* Tag root is after us, so grab last tagged
4384 * line underneath the tag root.
4386 found_node = info->tag_root;
4390 g_assert_not_reached ();
4395 g_assert (found_node != NULL);
4397 /* We have to find the last sub-node of this node that contains
4402 while (node->level > 0)
4404 GSList *child_nodes = NULL;
4406 g_assert (node != NULL); /* If this fails, it likely means an
4407 incorrect tag summary led us on a
4408 wild goose chase down this branch of
4411 node = node->children.node;
4412 while (node != NULL)
4414 child_nodes = g_slist_prepend (child_nodes, node);
4418 node = NULL; /* detect failure to find a child node. */
4421 while (iter != NULL)
4423 if (gtk_text_btree_node_has_tag (iter->data, tag))
4425 /* recurse into this node. */
4430 iter = g_slist_next (iter);
4433 g_slist_free (child_nodes);
4435 g_assert (node != NULL);
4438 g_assert (node != NULL);
4439 g_assert (node->level == 0);
4441 /* this assertion is correct, but slow. */
4442 /* g_assert (node_compare (node, line->parent) < 0); */
4444 /* Return last line in this node. */
4446 prev = node->children.line;
4454 * Non-public function implementations
4458 summary_list_destroy (Summary *summary)
4461 while (summary != NULL)
4463 next = summary->next;
4464 summary_destroy (summary);
4470 get_last_line (GtkTextBTree *tree)
4472 if (tree->last_line_stamp != tree->chars_changed_stamp)
4478 n_lines = _gtk_text_btree_line_count (tree);
4480 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4482 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4484 tree->last_line_stamp = tree->chars_changed_stamp;
4485 tree->last_line = line;
4488 return tree->last_line;
4496 gtk_text_line_new (void)
4500 line = g_new0(GtkTextLine, 1);
4506 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4508 GtkTextLineData *ld;
4509 GtkTextLineData *next;
4511 g_return_if_fail (line != NULL);
4518 view = gtk_text_btree_get_view (tree, ld->view_id);
4520 g_assert (view != NULL);
4523 gtk_text_layout_free_line_data (view->layout, line, ld);
4532 gtk_text_line_set_parent (GtkTextLine *line,
4533 GtkTextBTreeNode *node)
4535 if (line->parent == node)
4537 line->parent = node;
4538 gtk_text_btree_node_invalidate_upward (node, NULL);
4542 cleanup_line (GtkTextLine *line)
4544 GtkTextLineSegment *seg, **prev_p;
4548 * Make a pass over all of the segments in the line, giving each
4549 * a chance to clean itself up. This could potentially change
4550 * the structure of the line, e.g. by merging two segments
4551 * together or having two segments cancel themselves; if so,
4552 * then repeat the whole process again, since the first structure
4553 * change might make other structure changes possible. Repeat
4554 * until eventually there are no changes.
4561 for (prev_p = &line->segments, seg = *prev_p;
4563 prev_p = &(*prev_p)->next, seg = *prev_p)
4565 if (seg->type->cleanupFunc != NULL)
4567 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4580 node_data_new (gpointer view_id)
4584 nd = g_new (NodeData, 1);
4586 nd->view_id = view_id;
4596 node_data_destroy (NodeData *nd)
4602 node_data_list_destroy (NodeData *nd)
4608 while (iter != NULL)
4611 node_data_destroy (iter);
4617 node_data_find (NodeData *nd, gpointer view_id)
4621 if (nd->view_id == view_id)
4629 summary_destroy (Summary *summary)
4631 /* Fill with error-triggering garbage */
4632 summary->info = (void*)0x1;
4633 summary->toggle_count = 567;
4634 summary->next = (void*)0x1;
4638 static GtkTextBTreeNode*
4639 gtk_text_btree_node_new (void)
4641 GtkTextBTreeNode *node;
4643 node = g_new (GtkTextBTreeNode, 1);
4645 node->node_data = NULL;
4651 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4652 GtkTextTagInfo *info,
4657 summary = node->summary;
4658 while (summary != NULL)
4660 if (summary->info == info)
4662 summary->toggle_count += adjust;
4666 summary = summary->next;
4669 if (summary == NULL)
4671 /* didn't find a summary for our tag. */
4672 g_return_if_fail (adjust > 0);
4673 summary = g_new (Summary, 1);
4674 summary->info = info;
4675 summary->toggle_count = adjust;
4676 summary->next = node->summary;
4677 node->summary = summary;
4681 /* Note that the tag root and above do not have summaries
4682 for the tag; only nodes below the tag root have
4685 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4689 summary = node->summary;
4690 while (summary != NULL)
4693 summary->info->tag == tag)
4696 summary = summary->next;
4702 /* Add node and all children to the damage region. */
4704 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4708 nd = node->node_data;
4715 if (node->level == 0)
4719 line = node->children.line;
4720 while (line != NULL)
4722 GtkTextLineData *ld;
4736 GtkTextBTreeNode *child;
4738 child = node->children.node;
4740 while (child != NULL)
4742 gtk_text_btree_node_invalidate_downward (child);
4744 child = child->next;
4750 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4752 GtkTextBTreeNode *iter;
4755 while (iter != NULL)
4761 nd = node_data_find (iter->node_data, view_id);
4763 if (nd == NULL || !nd->valid)
4764 break; /* Once a node is invalid, we know its parents are as well. */
4770 gboolean should_continue = FALSE;
4772 nd = iter->node_data;
4777 should_continue = TRUE;
4784 if (!should_continue)
4785 break; /* This node was totally invalidated, so are its
4789 iter = iter->parent;
4795 * _gtk_text_btree_is_valid:
4796 * @tree: a #GtkTextBTree
4797 * @view_id: ID for the view
4799 * Check to see if the entire #GtkTextBTree is valid or not for
4802 * Return value: %TRUE if the entire #GtkTextBTree is valid
4805 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4809 g_return_val_if_fail (tree != NULL, FALSE);
4811 nd = node_data_find (tree->root_node->node_data, view_id);
4812 return (nd && nd->valid);
4815 typedef struct _ValidateState ValidateState;
4817 struct _ValidateState
4819 gint remaining_pixels;
4820 gboolean in_validation;
4827 gtk_text_btree_node_validate (BTreeView *view,
4828 GtkTextBTreeNode *node,
4830 ValidateState *state)
4832 gint node_valid = TRUE;
4833 gint node_width = 0;
4834 gint node_height = 0;
4836 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4837 g_return_if_fail (!nd->valid);
4839 if (node->level == 0)
4841 GtkTextLine *line = node->children.line;
4842 GtkTextLineData *ld;
4844 /* Iterate over leading valid lines */
4845 while (line != NULL)
4847 ld = _gtk_text_line_get_data (line, view_id);
4849 if (!ld || !ld->valid)
4851 else if (state->in_validation)
4853 state->in_validation = FALSE;
4858 state->y += ld->height;
4859 node_width = MAX (ld->width, node_width);
4860 node_height += ld->height;
4866 state->in_validation = TRUE;
4868 /* Iterate over invalid lines */
4869 while (line != NULL)
4871 ld = _gtk_text_line_get_data (line, view_id);
4873 if (ld && ld->valid)
4878 state->old_height += ld->height;
4879 ld = gtk_text_layout_wrap (view->layout, line, ld);
4880 state->new_height += ld->height;
4882 node_width = MAX (ld->width, node_width);
4883 node_height += ld->height;
4885 state->remaining_pixels -= ld->height;
4886 if (state->remaining_pixels <= 0)
4896 /* Iterate over the remaining lines */
4897 while (line != NULL)
4899 ld = _gtk_text_line_get_data (line, view_id);
4900 state->in_validation = FALSE;
4902 if (!ld || !ld->valid)
4907 node_width = MAX (ld->width, node_width);
4908 node_height += ld->height;
4916 GtkTextBTreeNode *child;
4919 child = node->children.node;
4921 /* Iterate over leading valid nodes */
4924 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4926 if (!child_nd->valid)
4928 else if (state->in_validation)
4930 state->in_validation = FALSE;
4935 state->y += child_nd->height;
4936 node_width = MAX (node_width, child_nd->width);
4937 node_height += child_nd->height;
4940 child = child->next;
4943 /* Iterate over invalid nodes */
4946 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4948 if (child_nd->valid)
4952 gtk_text_btree_node_validate (view, child, view_id, state);
4954 if (!child_nd->valid)
4956 node_width = MAX (node_width, child_nd->width);
4957 node_height += child_nd->height;
4959 if (!state->in_validation || state->remaining_pixels <= 0)
4961 child = child->next;
4966 child = child->next;
4969 /* Iterate over the remaining lines */
4972 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4973 state->in_validation = FALSE;
4975 if (!child_nd->valid)
4978 node_width = MAX (child_nd->width, node_width);
4979 node_height += child_nd->height;
4981 child = child->next;
4985 nd->width = node_width;
4986 nd->height = node_height;
4987 nd->valid = node_valid;
4991 * _gtk_text_btree_validate:
4992 * @tree: a #GtkTextBTree
4994 * @max_pixels: the maximum number of pixels to validate. (No more
4995 * than one paragraph beyond this limit will be validated)
4996 * @y: location to store starting y coordinate of validated region
4997 * @old_height: location to store old height of validated region
4998 * @new_height: location to store new height of validated region
5000 * Validate a single contiguous invalid region of a #GtkTextBTree for
5003 * Return value: %TRUE if a region has been validated, %FALSE if the
5004 * entire tree was already valid.
5007 _gtk_text_btree_validate (GtkTextBTree *tree,
5016 g_return_val_if_fail (tree != NULL, FALSE);
5018 view = gtk_text_btree_get_view (tree, view_id);
5019 g_return_val_if_fail (view != NULL, FALSE);
5021 if (!_gtk_text_btree_is_valid (tree, view_id))
5023 ValidateState state;
5025 state.remaining_pixels = max_pixels;
5026 state.in_validation = FALSE;
5028 state.old_height = 0;
5029 state.new_height = 0;
5031 gtk_text_btree_node_validate (view,
5038 *old_height = state.old_height;
5040 *new_height = state.new_height;
5042 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5043 _gtk_text_btree_check (tree);
5052 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5056 gboolean *valid_out)
5060 gboolean valid = TRUE;
5062 if (node->level == 0)
5064 GtkTextLine *line = node->children.line;
5066 while (line != NULL)
5068 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5070 if (!ld || !ld->valid)
5075 width = MAX (ld->width, width);
5076 height += ld->height;
5084 GtkTextBTreeNode *child = node->children.node;
5088 NodeData *child_nd = node_data_find (child->node_data, view_id);
5090 if (!child_nd || !child_nd->valid)
5095 width = MAX (child_nd->width, width);
5096 height += child_nd->height;
5099 child = child->next;
5104 *height_out = height;
5109 /* Recompute the validity and size of the view data for a given
5110 * view at this node from the immediate children of the node
5113 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5116 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5121 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5122 &width, &height, &valid);
5124 nd->height = height;
5131 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5136 gtk_text_btree_node_check_valid (node, view_id);
5137 node = node->parent;
5142 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5145 if (node->level == 0)
5147 return gtk_text_btree_node_check_valid (node, view_id);
5151 GtkTextBTreeNode *child = node->children.node;
5153 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5161 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5163 if (!child_nd->valid)
5165 nd->width = MAX (child_nd->width, nd->width);
5166 nd->height += child_nd->height;
5168 child = child->next;
5177 * _gtk_text_btree_validate_line:
5178 * @tree: a #GtkTextBTree
5179 * @line: line to validate
5180 * @view_id: view ID for the view to validate
5182 * Revalidate a single line of the btree for the given view, propagate
5183 * results up through the entire tree.
5186 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5190 GtkTextLineData *ld;
5193 g_return_if_fail (tree != NULL);
5194 g_return_if_fail (line != NULL);
5196 view = gtk_text_btree_get_view (tree, view_id);
5197 g_return_if_fail (view != NULL);
5199 ld = _gtk_text_line_get_data (line, view_id);
5200 if (!ld || !ld->valid)
5202 ld = gtk_text_layout_wrap (view->layout, line, ld);
5204 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5209 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5211 if (node->level == 0)
5215 line = node->children.line;
5216 while (line != NULL)
5218 GtkTextLineData *ld;
5220 ld = _gtk_text_line_remove_data (line, view_id);
5223 gtk_text_layout_free_line_data (view->layout, line, ld);
5230 GtkTextBTreeNode *child;
5232 child = node->children.node;
5234 while (child != NULL)
5237 gtk_text_btree_node_remove_view (view, child, view_id);
5239 child = child->next;
5243 gtk_text_btree_node_remove_data (node, view_id);
5247 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5249 if (node->level == 0)
5252 GtkTextLineSegment *seg;
5254 while (node->children.line != NULL)
5256 line = node->children.line;
5257 node->children.line = line->next;
5258 while (line->segments != NULL)
5260 seg = line->segments;
5261 line->segments = seg->next;
5263 (*seg->type->deleteFunc) (seg, line, TRUE);
5265 gtk_text_line_destroy (tree, line);
5270 GtkTextBTreeNode *childPtr;
5272 while (node->children.node != NULL)
5274 childPtr = node->children.node;
5275 node->children.node = childPtr->next;
5276 gtk_text_btree_node_destroy (tree, childPtr);
5280 gtk_text_btree_node_free_empty (tree, node);
5284 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5285 GtkTextBTreeNode *node)
5287 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5288 (node->level == 0 && node->children.line == NULL));
5290 summary_list_destroy (node->summary);
5291 node_data_list_destroy (node->node_data);
5296 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5300 nd = node->node_data;
5303 if (nd->view_id == view_id)
5311 nd = node_data_new (view_id);
5313 if (node->node_data)
5314 nd->next = node->node_data;
5316 node->node_data = nd;
5323 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5329 nd = node->node_data;
5332 if (nd->view_id == view_id)
5343 prev->next = nd->next;
5345 if (node->node_data == nd)
5346 node->node_data = nd->next;
5350 node_data_destroy (nd);
5354 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5355 gint *width, gint *height)
5359 g_return_if_fail (width != NULL);
5360 g_return_if_fail (height != NULL);
5362 nd = gtk_text_btree_node_ensure_data (node, view_id);
5367 *height = nd->height;
5370 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5371 * here isn't quite right, since for a lot of operations we want to
5372 * know which children of the common parent correspond to the two nodes
5373 * (e.g., when computing the order of two iters)
5375 static GtkTextBTreeNode *
5376 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5377 GtkTextBTreeNode *node2)
5379 while (node1->level < node2->level)
5380 node1 = node1->parent;
5381 while (node2->level < node1->level)
5382 node2 = node2->parent;
5383 while (node1 != node2)
5385 node1 = node1->parent;
5386 node2 = node2->parent;
5397 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5402 while (view != NULL)
5404 if (view->view_id == view_id)
5413 get_tree_bounds (GtkTextBTree *tree,
5417 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5418 _gtk_text_btree_get_end_iter (tree, end);
5422 tag_changed_cb (GtkTextTagTable *table,
5424 gboolean size_changed,
5429 /* We need to queue a relayout on all regions that are tagged with
5436 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5438 /* Must be a last toggle if there was a first one. */
5439 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5440 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5441 _gtk_text_btree_invalidate_region (tree,
5448 /* We only need to queue a redraw, not a relayout */
5453 while (view != NULL)
5457 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5458 gtk_text_layout_changed (view->layout, 0, height, height);
5466 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5469 /* Remove the tag from the tree */
5474 get_tree_bounds (tree, &start, &end);
5476 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5477 gtk_text_btree_remove_tag_info (tree, tag);
5481 /* Rebalance the out-of-whack node "node" */
5483 gtk_text_btree_rebalance (GtkTextBTree *tree,
5484 GtkTextBTreeNode *node)
5487 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5488 * up through the tree one GtkTextBTreeNode at a time until the root
5489 * GtkTextBTreeNode has been processed.
5492 while (node != NULL)
5494 GtkTextBTreeNode *new_node, *child;
5499 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5500 * then split off all but the first MIN_CHILDREN into a separate
5501 * GtkTextBTreeNode following the original one. Then repeat until the
5502 * GtkTextBTreeNode has a decent size.
5505 if (node->num_children > MAX_CHILDREN)
5510 * If the GtkTextBTreeNode being split is the root
5511 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5515 if (node->parent == NULL)
5517 new_node = gtk_text_btree_node_new ();
5518 new_node->parent = NULL;
5519 new_node->next = NULL;
5520 new_node->summary = NULL;
5521 new_node->level = node->level + 1;
5522 new_node->children.node = node;
5523 recompute_node_counts (tree, new_node);
5524 tree->root_node = new_node;
5526 new_node = gtk_text_btree_node_new ();
5527 new_node->parent = node->parent;
5528 new_node->next = node->next;
5529 node->next = new_node;
5530 new_node->summary = NULL;
5531 new_node->level = node->level;
5532 new_node->num_children = node->num_children - MIN_CHILDREN;
5533 if (node->level == 0)
5535 for (i = MIN_CHILDREN-1,
5536 line = node->children.line;
5537 i > 0; i--, line = line->next)
5539 /* Empty loop body. */
5541 new_node->children.line = line->next;
5546 for (i = MIN_CHILDREN-1,
5547 child = node->children.node;
5548 i > 0; i--, child = child->next)
5550 /* Empty loop body. */
5552 new_node->children.node = child->next;
5555 recompute_node_counts (tree, node);
5556 node->parent->num_children++;
5558 if (node->num_children <= MAX_CHILDREN)
5560 recompute_node_counts (tree, node);
5566 while (node->num_children < MIN_CHILDREN)
5568 GtkTextBTreeNode *other;
5569 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5570 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5571 int total_children, first_children, i;
5574 * Too few children for this GtkTextBTreeNode. If this is the root then,
5575 * it's OK for it to have less than MIN_CHILDREN children
5576 * as long as it's got at least two. If it has only one
5577 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5578 * the tree and use its child as the new root.
5581 if (node->parent == NULL)
5583 if ((node->num_children == 1) && (node->level > 0))
5585 tree->root_node = node->children.node;
5586 tree->root_node->parent = NULL;
5588 node->children.node = NULL;
5589 gtk_text_btree_node_free_empty (tree, node);
5595 * Not the root. Make sure that there are siblings to
5599 if (node->parent->num_children < 2)
5601 gtk_text_btree_rebalance (tree, node->parent);
5606 * Find a sibling neighbor to borrow from, and arrange for
5607 * node to be the earlier of the pair.
5610 if (node->next == NULL)
5612 for (other = node->parent->children.node;
5613 other->next != node;
5614 other = other->next)
5616 /* Empty loop body. */
5623 * We're going to either merge the two siblings together
5624 * into one GtkTextBTreeNode or redivide the children among them to
5625 * balance their loads. As preparation, join their two
5626 * child lists into a single list and remember the half-way
5627 * point in the list.
5630 total_children = node->num_children + other->num_children;
5631 first_children = total_children/2;
5632 if (node->children.node == NULL)
5634 node->children = other->children;
5635 other->children.node = NULL;
5636 other->children.line = NULL;
5638 if (node->level == 0)
5642 for (line = node->children.line, i = 1;
5644 line = line->next, i++)
5646 if (i == first_children)
5651 line->next = other->children.line;
5652 while (i <= first_children)
5661 GtkTextBTreeNode *child;
5663 for (child = node->children.node, i = 1;
5664 child->next != NULL;
5665 child = child->next, i++)
5667 if (i <= first_children)
5669 if (i == first_children)
5671 halfwaynode = child;
5675 child->next = other->children.node;
5676 while (i <= first_children)
5678 halfwaynode = child;
5679 child = child->next;
5685 * If the two siblings can simply be merged together, do it.
5688 if (total_children <= MAX_CHILDREN)
5690 recompute_node_counts (tree, node);
5691 node->next = other->next;
5692 node->parent->num_children--;
5694 other->children.node = NULL;
5695 other->children.line = NULL;
5696 gtk_text_btree_node_free_empty (tree, other);
5701 * The siblings can't be merged, so just divide their
5702 * children evenly between them.
5705 if (node->level == 0)
5707 other->children.line = halfwayline->next;
5708 halfwayline->next = NULL;
5712 other->children.node = halfwaynode->next;
5713 halfwaynode->next = NULL;
5716 recompute_node_counts (tree, node);
5717 recompute_node_counts (tree, other);
5720 node = node->parent;
5725 post_insert_fixup (GtkTextBTree *tree,
5727 gint line_count_delta,
5728 gint char_count_delta)
5731 GtkTextBTreeNode *node;
5734 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5735 * point, then rebalance the tree if necessary.
5738 for (node = line->parent ; node != NULL;
5739 node = node->parent)
5741 node->num_lines += line_count_delta;
5742 node->num_chars += char_count_delta;
5744 node = line->parent;
5745 node->num_children += line_count_delta;
5747 if (node->num_children > MAX_CHILDREN)
5749 gtk_text_btree_rebalance (tree, node);
5752 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5753 _gtk_text_btree_check (tree);
5756 static GtkTextTagInfo*
5757 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5760 GtkTextTagInfo *info;
5764 list = tree->tag_infos;
5765 while (list != NULL)
5768 if (info->tag == tag)
5771 list = g_slist_next (list);
5777 static GtkTextTagInfo*
5778 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5781 GtkTextTagInfo *info;
5783 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5787 /* didn't find it, create. */
5789 info = g_new (GtkTextTagInfo, 1);
5793 info->tag_root = NULL;
5794 info->toggle_count = 0;
5796 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5799 g_print ("Created tag info %p for tag %s(%p)\n",
5800 info, info->tag->name ? info->tag->name : "anon",
5809 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5812 GtkTextTagInfo *info;
5817 list = tree->tag_infos;
5818 while (list != NULL)
5821 if (info->tag == tag)
5824 g_print ("Removing tag info %p for tag %s(%p)\n",
5825 info, info->tag->name ? info->tag->name : "anon",
5831 prev->next = list->next;
5835 tree->tag_infos = list->next;
5838 g_slist_free (list);
5840 g_object_unref (info->tag);
5847 list = g_slist_next (list);
5852 recompute_level_zero_counts (GtkTextBTreeNode *node)
5855 GtkTextLineSegment *seg;
5857 g_assert (node->level == 0);
5859 line = node->children.line;
5860 while (line != NULL)
5862 node->num_children++;
5865 if (line->parent != node)
5866 gtk_text_line_set_parent (line, node);
5868 seg = line->segments;
5872 node->num_chars += seg->char_count;
5874 if (((seg->type != >k_text_toggle_on_type)
5875 && (seg->type != >k_text_toggle_off_type))
5876 || !(seg->body.toggle.inNodeCounts))
5882 GtkTextTagInfo *info;
5884 info = seg->body.toggle.info;
5886 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5897 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5900 GtkTextBTreeNode *child;
5902 g_assert (node->level > 0);
5904 child = node->children.node;
5905 while (child != NULL)
5907 node->num_children += 1;
5908 node->num_lines += child->num_lines;
5909 node->num_chars += child->num_chars;
5911 if (child->parent != node)
5913 child->parent = node;
5914 gtk_text_btree_node_invalidate_upward (node, NULL);
5917 summary = child->summary;
5918 while (summary != NULL)
5920 gtk_text_btree_node_adjust_toggle_count (node,
5922 summary->toggle_count);
5924 summary = summary->next;
5927 child = child->next;
5932 *----------------------------------------------------------------------
5934 * recompute_node_counts --
5936 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5937 * (tags, child information, etc.) by scanning the information in
5938 * its descendants. This procedure is called during rebalancing
5939 * when a GtkTextBTreeNode's child structure has changed.
5945 * The tag counts for node are modified to reflect its current
5946 * child structure, as are its num_children, num_lines, num_chars fields.
5947 * Also, all of the childrens' parent fields are made to point
5950 *----------------------------------------------------------------------
5954 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5957 Summary *summary, *summary2;
5960 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5961 * the existing Summary records (most of them will probably be reused).
5964 summary = node->summary;
5965 while (summary != NULL)
5967 summary->toggle_count = 0;
5968 summary = summary->next;
5971 node->num_children = 0;
5972 node->num_lines = 0;
5973 node->num_chars = 0;
5976 * Scan through the children, adding the childrens' tag counts into
5977 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5981 if (node->level == 0)
5982 recompute_level_zero_counts (node);
5984 recompute_level_nonzero_counts (node);
5989 gtk_text_btree_node_check_valid (node, view->view_id);
5994 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5995 * records that still have a zero count, or that have all the toggles.
5996 * The GtkTextBTreeNode with the children that account for all the tags toggles
5997 * have no summary information, and they become the tag_root for the tag.
6001 for (summary = node->summary; summary != NULL; )
6003 if (summary->toggle_count > 0 &&
6004 summary->toggle_count < summary->info->toggle_count)
6006 if (node->level == summary->info->tag_root->level)
6009 * The tag's root GtkTextBTreeNode split and some toggles left.
6010 * The tag root must move up a level.
6012 summary->info->tag_root = node->parent;
6015 summary = summary->next;
6018 if (summary->toggle_count == summary->info->toggle_count)
6021 * A GtkTextBTreeNode merge has collected all the toggles under
6022 * one GtkTextBTreeNode. Push the root down to this level.
6024 summary->info->tag_root = node;
6026 if (summary2 != NULL)
6028 summary2->next = summary->next;
6029 summary_destroy (summary);
6030 summary = summary2->next;
6034 node->summary = summary->next;
6035 summary_destroy (summary);
6036 summary = node->summary;
6042 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6043 GtkTextTagInfo *info,
6044 gint delta) /* may be negative */
6046 Summary *summary, *prevPtr;
6047 GtkTextBTreeNode *node2Ptr;
6048 int rootLevel; /* Level of original tag root */
6050 info->toggle_count += delta;
6052 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6054 info->tag_root = node;
6059 * Note the level of the existing root for the tag so we can detect
6060 * if it needs to be moved because of the toggle count change.
6063 rootLevel = info->tag_root->level;
6066 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6067 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6071 for ( ; node != info->tag_root; node = node->parent)
6074 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6075 * perhaps all we have to do is adjust its count.
6078 for (prevPtr = NULL, summary = node->summary;
6080 prevPtr = summary, summary = summary->next)
6082 if (summary->info == info)
6087 if (summary != NULL)
6089 summary->toggle_count += delta;
6090 if (summary->toggle_count > 0 &&
6091 summary->toggle_count < info->toggle_count)
6095 if (summary->toggle_count != 0)
6098 * Should never find a GtkTextBTreeNode with max toggle count at this
6099 * point (there shouldn't have been a summary entry in the
6103 g_error ("%s: bad toggle count (%d) max (%d)",
6104 G_STRLOC, summary->toggle_count, info->toggle_count);
6108 * Zero toggle count; must remove this tag from the list.
6111 if (prevPtr == NULL)
6113 node->summary = summary->next;
6117 prevPtr->next = summary->next;
6119 summary_destroy (summary);
6124 * This tag isn't currently in the summary information list.
6127 if (rootLevel == node->level)
6131 * The old tag root is at the same level in the tree as this
6132 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6133 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6134 * as well as the old root (if not, we'll move it up again
6135 * the next time through the loop). To push it up one level
6136 * we copy the original toggle count into the summary
6137 * information at the old root and change the root to its
6138 * parent GtkTextBTreeNode.
6141 GtkTextBTreeNode *rootnode = info->tag_root;
6142 summary = (Summary *) g_malloc (sizeof (Summary));
6143 summary->info = info;
6144 summary->toggle_count = info->toggle_count - delta;
6145 summary->next = rootnode->summary;
6146 rootnode->summary = summary;
6147 rootnode = rootnode->parent;
6148 rootLevel = rootnode->level;
6149 info->tag_root = rootnode;
6151 summary = (Summary *) g_malloc (sizeof (Summary));
6152 summary->info = info;
6153 summary->toggle_count = delta;
6154 summary->next = node->summary;
6155 node->summary = summary;
6160 * If we've decremented the toggle count, then it may be necessary
6161 * to push the tag root down one or more levels.
6168 if (info->toggle_count == 0)
6170 info->tag_root = (GtkTextBTreeNode *) NULL;
6173 node = info->tag_root;
6174 while (node->level > 0)
6177 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6178 * toggles. If so, push the root down one level.
6181 for (node2Ptr = node->children.node;
6182 node2Ptr != (GtkTextBTreeNode *)NULL ;
6183 node2Ptr = node2Ptr->next)
6185 for (prevPtr = NULL, summary = node2Ptr->summary;
6187 prevPtr = summary, summary = summary->next)
6189 if (summary->info == info)
6194 if (summary == NULL)
6198 if (summary->toggle_count != info->toggle_count)
6201 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6208 * This GtkTextBTreeNode has all the toggles, so push down the root.
6211 if (prevPtr == NULL)
6213 node2Ptr->summary = summary->next;
6217 prevPtr->next = summary->next;
6219 summary_destroy (summary);
6220 info->tag_root = node2Ptr;
6223 node = info->tag_root;
6228 *----------------------------------------------------------------------
6232 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6233 * increments the count for a particular tag, adding a new
6234 * entry for that tag if there wasn't one previously.
6240 * The information at *tagInfoPtr may be modified, and the arrays
6241 * may be reallocated to make them larger.
6243 *----------------------------------------------------------------------
6247 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6252 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6253 count > 0; tag_p++, count--)
6257 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6263 * There isn't currently an entry for this tag, so we have to
6264 * make a new one. If the arrays are full, then enlarge the
6268 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6270 GtkTextTag **newTags;
6271 int *newCounts, newSize;
6273 newSize = 2*tagInfoPtr->arraySize;
6274 newTags = (GtkTextTag **) g_malloc ((unsigned)
6275 (newSize*sizeof (GtkTextTag *)));
6276 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6277 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6278 g_free ((char *) tagInfoPtr->tags);
6279 tagInfoPtr->tags = newTags;
6280 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6281 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6282 tagInfoPtr->arraySize *sizeof (int));
6283 g_free ((char *) tagInfoPtr->counts);
6284 tagInfoPtr->counts = newCounts;
6285 tagInfoPtr->arraySize = newSize;
6288 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6289 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6290 tagInfoPtr->numTags++;
6294 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6295 const GtkTextIter *iter)
6297 GtkTextLineSegment *prev;
6301 line = _gtk_text_iter_get_text_line (iter);
6302 tree = _gtk_text_iter_get_btree (iter);
6304 prev = gtk_text_line_segment_split (iter);
6307 seg->next = line->segments;
6308 line->segments = seg;
6312 seg->next = prev->next;
6315 cleanup_line (line);
6316 segments_changed (tree);
6318 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6319 _gtk_text_btree_check (tree);
6323 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6324 GtkTextLineSegment *seg,
6327 GtkTextLineSegment *prev;
6329 if (line->segments == seg)
6331 line->segments = seg->next;
6335 for (prev = line->segments; prev->next != seg;
6338 /* Empty loop body. */
6340 prev->next = seg->next;
6342 cleanup_line (line);
6343 segments_changed (tree);
6347 * This is here because it requires BTree internals, it logically
6348 * belongs in gtktextsegment.c
6353 *--------------------------------------------------------------
6355 * _gtk_toggle_segment_check_func --
6357 * This procedure is invoked to perform consistency checks
6358 * on toggle segments.
6364 * If a consistency problem is found the procedure g_errors.
6366 *--------------------------------------------------------------
6370 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6376 if (segPtr->byte_count != 0)
6378 g_error ("toggle_segment_check_func: segment had non-zero size");
6380 if (!segPtr->body.toggle.inNodeCounts)
6382 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6384 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6385 for (summary = line->parent->summary; ;
6386 summary = summary->next)
6388 if (summary == NULL)
6392 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6399 if (summary->info == segPtr->body.toggle.info)
6403 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6415 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6416 GtkTextBTreeNode *node,
6426 while (view != NULL)
6428 if (view->view_id == nd->view_id)
6435 g_error ("Node has data for a view %p no longer attached to the tree",
6438 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6439 &width, &height, &valid);
6441 /* valid aggregate not checked the same as width/height, because on
6442 * btree rebalance we can have invalid nodes where all lines below
6443 * them are actually valid, due to moving lines around between
6446 * The guarantee is that if there are invalid lines the node is
6447 * invalid - we don't guarantee that if the node is invalid there
6448 * are invalid lines.
6451 if (nd->width != width ||
6452 nd->height != height ||
6453 (nd->valid && !valid))
6455 g_error ("Node aggregates for view %p are invalid:\n"
6456 "Are (%d,%d,%s), should be (%d,%d,%s)",
6458 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6459 width, height, valid ? "TRUE" : "FALSE");
6464 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6465 GtkTextBTreeNode *node)
6467 GtkTextBTreeNode *childnode;
6468 Summary *summary, *summary2;
6470 GtkTextLineSegment *segPtr;
6471 int num_children, num_lines, num_chars, toggle_count, min_children;
6472 GtkTextLineData *ld;
6475 if (node->parent != NULL)
6477 min_children = MIN_CHILDREN;
6479 else if (node->level > 0)
6486 if ((node->num_children < min_children)
6487 || (node->num_children > MAX_CHILDREN))
6489 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6490 node->num_children);
6493 nd = node->node_data;
6496 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6503 if (node->level == 0)
6505 for (line = node->children.line; line != NULL;
6508 if (line->parent != node)
6510 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6512 if (line->segments == NULL)
6514 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6520 /* Just ensuring we don't segv while doing this loop */
6525 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6527 if (segPtr->type->checkFunc != NULL)
6529 (*segPtr->type->checkFunc)(segPtr, line);
6531 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6532 && (segPtr->next != NULL)
6533 && (segPtr->next->byte_count == 0)
6534 && (segPtr->next->type->leftGravity))
6536 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6538 if ((segPtr->next == NULL)
6539 && (segPtr->type != >k_text_char_type))
6541 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6544 num_chars += segPtr->char_count;
6553 for (childnode = node->children.node; childnode != NULL;
6554 childnode = childnode->next)
6556 if (childnode->parent != node)
6558 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6560 if (childnode->level != (node->level-1))
6562 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6563 node->level, childnode->level);
6565 gtk_text_btree_node_check_consistency (tree, childnode);
6566 for (summary = childnode->summary; summary != NULL;
6567 summary = summary->next)
6569 for (summary2 = node->summary; ;
6570 summary2 = summary2->next)
6572 if (summary2 == NULL)
6574 if (summary->info->tag_root == node)
6578 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6579 summary->info->tag->name,
6580 "present in parent summaries");
6582 if (summary->info == summary2->info)
6589 num_lines += childnode->num_lines;
6590 num_chars += childnode->num_chars;
6593 if (num_children != node->num_children)
6595 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6596 num_children, node->num_children);
6598 if (num_lines != node->num_lines)
6600 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6601 num_lines, node->num_lines);
6603 if (num_chars != node->num_chars)
6605 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6606 num_chars, node->num_chars);
6609 for (summary = node->summary; summary != NULL;
6610 summary = summary->next)
6612 if (summary->info->toggle_count == summary->toggle_count)
6614 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6615 summary->info->tag->name);
6618 if (node->level == 0)
6620 for (line = node->children.line; line != NULL;
6623 for (segPtr = line->segments; segPtr != NULL;
6624 segPtr = segPtr->next)
6626 if ((segPtr->type != >k_text_toggle_on_type)
6627 && (segPtr->type != >k_text_toggle_off_type))
6631 if (segPtr->body.toggle.info == summary->info)
6633 if (!segPtr->body.toggle.inNodeCounts)
6634 g_error ("Toggle segment not in the node counts");
6643 for (childnode = node->children.node;
6645 childnode = childnode->next)
6647 for (summary2 = childnode->summary;
6649 summary2 = summary2->next)
6651 if (summary2->info == summary->info)
6653 toggle_count += summary2->toggle_count;
6658 if (toggle_count != summary->toggle_count)
6660 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6661 toggle_count, summary->toggle_count);
6663 for (summary2 = summary->next; summary2 != NULL;
6664 summary2 = summary2->next)
6666 if (summary2->info == summary->info)
6668 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6669 summary->info->tag->name);
6676 listify_foreach (GtkTextTag *tag, gpointer user_data)
6678 GSList** listp = user_data;
6680 *listp = g_slist_prepend (*listp, tag);
6684 list_of_tags (GtkTextTagTable *table)
6686 GSList *list = NULL;
6688 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6694 _gtk_text_btree_check (GtkTextBTree *tree)
6697 GtkTextBTreeNode *node;
6699 GtkTextLineSegment *seg;
6701 GSList *taglist = NULL;
6703 GtkTextTagInfo *info;
6706 * Make sure that the tag toggle counts and the tag root pointers are OK.
6708 for (taglist = list_of_tags (tree->table);
6709 taglist != NULL ; taglist = taglist->next)
6711 tag = taglist->data;
6712 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6715 node = info->tag_root;
6718 if (info->toggle_count != 0)
6720 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6721 tag->name, info->toggle_count);
6723 continue; /* no ranges for the tag */
6725 else if (info->toggle_count == 0)
6727 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6730 else if (info->toggle_count & 1)
6732 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6733 tag->name, info->toggle_count);
6735 for (summary = node->summary; summary != NULL;
6736 summary = summary->next)
6738 if (summary->info->tag == tag)
6740 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6744 if (node->level > 0)
6746 for (node = node->children.node ; node != NULL ;
6749 for (summary = node->summary; summary != NULL;
6750 summary = summary->next)
6752 if (summary->info->tag == tag)
6754 count += summary->toggle_count;
6761 GtkTextLineSegmentClass * last = NULL;
6763 for (line = node->children.line ; line != NULL ;
6766 for (seg = line->segments; seg != NULL;
6769 if ((seg->type == >k_text_toggle_on_type ||
6770 seg->type == >k_text_toggle_off_type) &&
6771 seg->body.toggle.info->tag == tag)
6773 if (last == seg->type)
6774 g_error ("Two consecutive toggles on or off weren't merged");
6775 if (!seg->body.toggle.inNodeCounts)
6776 g_error ("Toggle segment not in the node counts");
6785 if (count != info->toggle_count)
6787 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6788 info->toggle_count, tag->name, count);
6793 g_slist_free (taglist);
6797 * Call a recursive procedure to do the main body of checks.
6800 node = tree->root_node;
6801 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6804 * Make sure that there are at least two lines in the text and
6805 * that the last line has no characters except a newline.
6808 if (node->num_lines < 2)
6810 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6812 if (node->num_chars < 2)
6814 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6816 while (node->level > 0)
6818 node = node->children.node;
6819 while (node->next != NULL)
6824 line = node->children.line;
6825 while (line->next != NULL)
6829 seg = line->segments;
6830 while ((seg->type == >k_text_toggle_off_type)
6831 || (seg->type == >k_text_right_mark_type)
6832 || (seg->type == >k_text_left_mark_type))
6835 * It's OK to toggle a tag off in the last line, but
6836 * not to start a new range. It's also OK to have marks
6842 if (seg->type != >k_text_char_type)
6844 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6846 if (seg->next != NULL)
6848 g_error ("_gtk_text_btree_check: last line has too many segments");
6850 if (seg->byte_count != 1)
6852 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6855 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6857 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6862 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6863 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6864 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6865 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6868 _gtk_text_btree_spew (GtkTextBTree *tree)
6873 printf ("%d lines in tree %p\n",
6874 _gtk_text_btree_line_count (tree), tree);
6876 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6878 while (line != NULL)
6880 _gtk_text_btree_spew_line (tree, line);
6881 line = _gtk_text_line_next (line);
6884 printf ("=================== Tag information\n");
6889 list = tree->tag_infos;
6891 while (list != NULL)
6893 GtkTextTagInfo *info;
6897 printf (" tag `%s': root at %p, toggle count %d\n",
6898 info->tag->name, info->tag_root, info->toggle_count);
6900 list = g_slist_next (list);
6903 if (tree->tag_infos == NULL)
6905 printf (" (no tags in the tree)\n");
6909 printf ("=================== Tree nodes\n");
6912 _gtk_text_btree_spew_node (tree->root_node, 0);
6917 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6920 GtkTextLineSegment *seg;
6922 spaces = g_strnfill (indent, ' ');
6924 printf ("%sline %p chars %d bytes %d\n",
6926 _gtk_text_line_char_count (line),
6927 _gtk_text_line_byte_count (line));
6929 seg = line->segments;
6932 if (seg->type == >k_text_char_type)
6934 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6939 if (*s == '\n' || *s == '\r')
6943 printf ("%s chars `%s'...\n", spaces, str);
6946 else if (seg->type == >k_text_right_mark_type)
6948 printf ("%s right mark `%s' visible: %d\n",
6950 seg->body.mark.name,
6951 seg->body.mark.visible);
6953 else if (seg->type == >k_text_left_mark_type)
6955 printf ("%s left mark `%s' visible: %d\n",
6957 seg->body.mark.name,
6958 seg->body.mark.visible);
6960 else if (seg->type == >k_text_toggle_on_type ||
6961 seg->type == >k_text_toggle_off_type)
6963 printf ("%s tag `%s' %s\n",
6964 spaces, seg->body.toggle.info->tag->name,
6965 seg->type == >k_text_toggle_off_type ? "off" : "on");
6975 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6978 GtkTextBTreeNode *iter;
6981 spaces = g_strnfill (indent, ' ');
6983 printf ("%snode %p level %d children %d lines %d chars %d\n",
6984 spaces, node, node->level,
6985 node->num_children, node->num_lines, node->num_chars);
6990 printf ("%s %d toggles of `%s' below this node\n",
6991 spaces, s->toggle_count, s->info->tag->name);
6997 if (node->level > 0)
6999 iter = node->children.node;
7000 while (iter != NULL)
7002 _gtk_text_btree_spew_node (iter, indent + 2);
7009 GtkTextLine *line = node->children.line;
7010 while (line != NULL)
7012 _gtk_text_btree_spew_line_short (line, indent + 2);
7020 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7022 GtkTextLineSegment * seg;
7024 printf ("%4d| line: %p parent: %p next: %p\n",
7025 _gtk_text_line_get_number (line), line, line->parent, line->next);
7027 seg = line->segments;
7031 _gtk_text_btree_spew_segment (tree, seg);
7037 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7039 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7040 seg, seg->type->name, seg->byte_count, seg->char_count);
7042 if (seg->type == >k_text_char_type)
7044 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7045 printf (" `%s'\n", str);
7048 else if (seg->type == >k_text_right_mark_type)
7050 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7051 seg->body.mark.name,
7052 seg->body.mark.visible,
7053 seg->body.mark.not_deleteable);
7055 else if (seg->type == >k_text_left_mark_type)
7057 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7058 seg->body.mark.name,
7059 seg->body.mark.visible,
7060 seg->body.mark.not_deleteable);
7062 else if (seg->type == >k_text_toggle_on_type ||
7063 seg->type == >k_text_toggle_off_type)
7065 printf (" tag `%s' priority %d\n",
7066 seg->body.toggle.info->tag->name,
7067 seg->body.toggle.info->tag->priority);