4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
60 #include "gtksignal.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
74 * The structure below is used to pass information between
75 * _gtk_text_btree_get_tags and inc_count:
78 typedef struct TagInfo {
79 int numTags; /* Number of tags for which there
80 * is currently information in
82 int arraySize; /* Number of entries allocated for
84 GtkTextTag **tags; /* Array of tags seen so far.
86 int *counts; /* Toggle count (so far) for each
87 * entry in tags. Malloc-ed. */
92 * This is used to store per-view width/height info at the tree nodes.
95 typedef struct _NodeData NodeData;
101 /* Height and width of this node */
105 /* boolean indicating whether the lines below this node are in need of validation.
106 * However, width/height should always represent the current total width and
107 * max height for lines below this node; the valid flag indicates whether the
108 * width/height on the lines needs recomputing, not whether the totals
116 * The data structure below keeps summary information about one tag as part
117 * of the tag information in a node.
120 typedef struct Summary {
121 GtkTextTagInfo *info; /* Handle for tag. */
122 int toggle_count; /* Number of transitions into or
123 * out of this tag that occur in
124 * the subtree rooted at this node. */
125 struct Summary *next; /* Next in list of all tags for same
126 * node, or NULL if at end of list. */
130 * The data structure below defines a node in the B-tree.
133 struct _GtkTextBTreeNode {
134 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
135 * this is the root. */
136 GtkTextBTreeNode *next; /* Next in list of siblings with the
137 * same parent node, or NULL for end
139 Summary *summary; /* First in malloc-ed list of info
140 * about tags in this subtree (NULL if
141 * no tag info in the subtree). */
142 int level; /* Level of this node in the B-tree.
143 * 0 refers to the bottom of the tree
144 * (children are lines, not nodes). */
145 union { /* First in linked list of children. */
146 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
147 GtkTextLine *line; /* Used if level == 0. */
149 int num_children; /* Number of children of this node. */
150 int num_lines; /* Total number of lines (leaves) in
151 * the subtree rooted here. */
152 int num_chars; /* Number of chars below here */
159 * Used to store the list of views in our btree
162 typedef struct _BTreeView BTreeView;
166 GtkTextLayout *layout;
172 * And the tree itself
175 struct _GtkTextBTree {
176 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
177 GtkTextTagTable *table;
178 GHashTable *mark_table;
180 GtkTextMark *insert_mark;
181 GtkTextMark *selection_bound_mark;
182 GtkTextBuffer *buffer;
185 guint tag_changed_handler;
187 /* Incremented when a segment with a byte size > 0
188 * is added to or removed from the tree (i.e. the
189 * length of a line may have changed, and lines may
190 * have been added or removed). This invalidates
191 * all outstanding iterators.
193 guint chars_changed_stamp;
194 /* Incremented when any segments are added or deleted;
195 * this makes outstanding iterators recalculate their
196 * pointed-to segment and segment offset.
198 guint segments_changed_stamp;
200 /* Cache the last line in the buffer */
201 GtkTextLine *last_line;
202 guint last_line_stamp;
204 /* Cache the next-to-last line in the buffer,
205 * containing the end iterator
207 GtkTextLine *end_iter_line;
208 GtkTextLineSegment *end_iter_segment;
209 int end_iter_segment_byte_index;
210 int end_iter_segment_char_offset;
211 guint end_iter_line_stamp;
212 guint end_iter_segment_stamp;
214 GHashTable *child_anchor_table;
219 * Upper and lower bounds on how many children a node may have:
220 * rebalance when either of these limits is exceeded. MAX_CHILDREN
221 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
224 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
225 shallow. It appears to be faster to locate a particular line number
226 if the tree is narrow and deep, since it is more finely sorted. I
227 guess this may increase memory use though, and make it slower to
228 walk the tree in order, or locate a particular byte index (which
229 is done by walking the tree in order).
231 There's basically a tradeoff here. However I'm thinking we want to
232 add pixels, byte counts, and char counts to the tree nodes,
233 at that point narrow and deep should speed up all operations,
234 not just the line number searches.
238 #define MAX_CHILDREN 12
239 #define MIN_CHILDREN 6
241 #define MAX_CHILDREN 6
242 #define MIN_CHILDREN 3
249 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
251 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
252 GtkTextBTreeNode *node);
253 static GtkTextLine * get_last_line (GtkTextBTree *tree);
254 static void post_insert_fixup (GtkTextBTree *tree,
255 GtkTextLine *insert_line,
256 gint char_count_delta,
257 gint line_count_delta);
258 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
259 GtkTextTagInfo *info,
261 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
264 static void segments_changed (GtkTextBTree *tree);
265 static void chars_changed (GtkTextBTree *tree);
266 static void summary_list_destroy (Summary *summary);
267 static GtkTextLine *gtk_text_line_new (void);
268 static void gtk_text_line_destroy (GtkTextBTree *tree,
270 static void gtk_text_line_set_parent (GtkTextLine *line,
271 GtkTextBTreeNode *node);
272 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
276 static NodeData *node_data_new (gpointer view_id);
277 static void node_data_destroy (NodeData *nd);
278 static void node_data_list_destroy (NodeData *nd);
279 static NodeData *node_data_find (NodeData *nd,
282 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
283 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
284 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
286 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
288 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
290 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
293 static void gtk_text_btree_node_remove_view (BTreeView *view,
294 GtkTextBTreeNode *node,
296 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
297 GtkTextBTreeNode *node);
298 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
299 GtkTextBTreeNode *node);
300 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
302 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
304 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
308 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
309 GtkTextBTreeNode *node2);
310 static void get_tree_bounds (GtkTextBTree *tree,
313 static void tag_changed_cb (GtkTextTagTable *table,
315 gboolean size_changed,
317 static void cleanup_line (GtkTextLine *line);
318 static void recompute_node_counts (GtkTextBTree *tree,
319 GtkTextBTreeNode *node);
320 static void inc_count (GtkTextTag *tag,
322 TagInfo *tagInfoPtr);
324 static void summary_destroy (Summary *summary);
326 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
327 const GtkTextIter *iter);
328 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
329 GtkTextLineSegment *seg,
333 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
335 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
337 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
340 static void redisplay_region (GtkTextBTree *tree,
341 const GtkTextIter *start,
342 const GtkTextIter *end);
344 /* Inline thingies */
347 segments_changed (GtkTextBTree *tree)
349 tree->segments_changed_stamp += 1;
353 chars_changed (GtkTextBTree *tree)
355 tree->chars_changed_stamp += 1;
363 _gtk_text_btree_new (GtkTextTagTable *table,
364 GtkTextBuffer *buffer)
367 GtkTextBTreeNode *root_node;
368 GtkTextLine *line, *line2;
370 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
371 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
374 * The tree will initially have two empty lines. The second line
375 * isn't actually part of the tree's contents, but its presence
376 * makes several operations easier. The tree will have one GtkTextBTreeNode,
377 * which is also the root of the tree.
380 /* Create the root node. */
382 root_node = gtk_text_btree_node_new ();
384 line = gtk_text_line_new ();
385 line2 = gtk_text_line_new ();
387 root_node->parent = NULL;
388 root_node->next = NULL;
389 root_node->summary = NULL;
390 root_node->level = 0;
391 root_node->children.line = line;
392 root_node->num_children = 2;
393 root_node->num_lines = 2;
394 root_node->num_chars = 2;
396 line->parent = root_node;
399 line->segments = _gtk_char_segment_new ("\n", 1);
401 line2->parent = root_node;
403 line2->segments = _gtk_char_segment_new ("\n", 1);
405 /* Create the tree itself */
407 tree = g_new0(GtkTextBTree, 1);
408 tree->root_node = root_node;
412 /* Set these to values that are unlikely to be found
413 * in random memory garbage, and also avoid
414 * duplicates between tree instances.
416 tree->chars_changed_stamp = g_random_int ();
417 tree->segments_changed_stamp = g_random_int ();
419 tree->last_line_stamp = tree->chars_changed_stamp - 1;
420 tree->last_line = NULL;
422 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
423 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
424 tree->end_iter_line = NULL;
425 tree->end_iter_segment_byte_index = 0;
426 tree->end_iter_segment_char_offset = 0;
428 g_object_ref (G_OBJECT (tree->table));
430 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
432 G_CALLBACK (tag_changed_cb),
435 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
436 tree->child_anchor_table = NULL;
438 /* We don't ref the buffer, since the buffer owns us;
439 * we'd have some circularity issues. The buffer always
440 * lasts longer than the BTree
442 tree->buffer = buffer;
446 GtkTextLineSegment *seg;
448 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
451 tree->insert_mark = _gtk_text_btree_set_mark (tree,
458 seg = tree->insert_mark->segment;
460 seg->body.mark.not_deleteable = TRUE;
461 seg->body.mark.visible = TRUE;
463 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
470 seg = tree->selection_bound_mark->segment;
472 seg->body.mark.not_deleteable = TRUE;
474 g_object_ref (G_OBJECT (tree->insert_mark));
475 g_object_ref (G_OBJECT (tree->selection_bound_mark));
484 _gtk_text_btree_ref (GtkTextBTree *tree)
486 g_return_if_fail (tree != NULL);
487 g_return_if_fail (tree->refcount > 0);
493 _gtk_text_btree_unref (GtkTextBTree *tree)
495 g_return_if_fail (tree != NULL);
496 g_return_if_fail (tree->refcount > 0);
500 if (tree->refcount == 0)
502 g_signal_handler_disconnect (G_OBJECT (tree->table),
503 tree->tag_changed_handler);
505 g_object_unref (G_OBJECT (tree->table));
508 gtk_text_btree_node_destroy (tree, tree->root_node);
509 tree->root_node = NULL;
511 g_assert (g_hash_table_size (tree->mark_table) == 0);
512 g_hash_table_destroy (tree->mark_table);
513 tree->mark_table = NULL;
514 if (tree->child_anchor_table != NULL)
516 g_hash_table_destroy (tree->child_anchor_table);
517 tree->child_anchor_table = NULL;
520 g_object_unref (G_OBJECT (tree->insert_mark));
521 tree->insert_mark = NULL;
522 g_object_unref (G_OBJECT (tree->selection_bound_mark));
523 tree->selection_bound_mark = NULL;
530 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
536 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
538 return tree->chars_changed_stamp;
542 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
544 return tree->segments_changed_stamp;
548 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
550 g_return_if_fail (tree != NULL);
551 segments_changed (tree);
555 * Indexable segment mutation
559 _gtk_text_btree_delete (GtkTextIter *start,
562 GtkTextLineSegment *prev_seg; /* The segment just before the start
563 * of the deletion range. */
564 GtkTextLineSegment *last_seg; /* The segment just after the end
565 * of the deletion range. */
566 GtkTextLineSegment *seg, *next;
567 GtkTextLine *curline;
568 GtkTextBTreeNode *curnode, *node;
570 GtkTextLine *start_line;
571 GtkTextLine *end_line;
572 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
573 gint start_byte_offset;
575 g_return_if_fail (start != NULL);
576 g_return_if_fail (end != NULL);
577 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
578 _gtk_text_iter_get_btree (end));
580 gtk_text_iter_order (start, end);
582 tree = _gtk_text_iter_get_btree (start);
584 if (gtk_debug_flags & GTK_DEBUG_TEXT)
585 _gtk_text_btree_check (tree);
587 /* Broadcast the need for redisplay before we break the iterators */
588 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
589 _gtk_text_btree_invalidate_region (tree, start, end);
591 /* Save the byte offset so we can reset the iterators */
592 start_byte_offset = gtk_text_iter_get_line_index (start);
594 start_line = _gtk_text_iter_get_text_line (start);
595 end_line = _gtk_text_iter_get_text_line (end);
598 * Split the start and end segments, so we have a place
599 * to insert our new text.
601 * Tricky point: split at end first; otherwise the split
602 * at end may invalidate seg and/or prev_seg. This allows
603 * us to avoid invalidating segments for start.
606 last_seg = gtk_text_line_segment_split (end);
607 if (last_seg != NULL)
608 last_seg = last_seg->next;
610 last_seg = end_line->segments;
612 prev_seg = gtk_text_line_segment_split (start);
613 if (prev_seg != NULL)
615 seg = prev_seg->next;
616 prev_seg->next = last_seg;
620 seg = start_line->segments;
621 start_line->segments = last_seg;
624 /* notify iterators that their segments need recomputation,
625 just for robustness. */
626 segments_changed (tree);
629 * Delete all of the segments between prev_seg and last_seg.
632 curline = start_line;
633 curnode = curline->parent;
634 while (seg != last_seg)
640 GtkTextLine *nextline;
643 * We just ran off the end of a line. First find the
644 * next line, then go back to the old line and delete it
645 * (unless it's the starting line for the range).
648 nextline = _gtk_text_line_next (curline);
649 if (curline != start_line)
651 if (curnode == start_line->parent)
652 start_line->next = curline->next;
654 curnode->children.line = curline->next;
656 for (node = curnode; node != NULL;
659 /* Don't update node->num_chars, because
660 * that was done when we deleted the segments.
662 node->num_lines -= 1;
665 curnode->num_children -= 1;
666 curline->next = deleted_lines;
667 deleted_lines = curline;
671 seg = curline->segments;
674 * If the GtkTextBTreeNode is empty then delete it and its parents,
675 * recursively upwards until a non-empty GtkTextBTreeNode is found.
678 while (curnode->num_children == 0)
680 GtkTextBTreeNode *parent;
682 parent = curnode->parent;
683 if (parent->children.node == curnode)
685 parent->children.node = curnode->next;
689 GtkTextBTreeNode *prevnode = parent->children.node;
690 while (prevnode->next != curnode)
692 prevnode = prevnode->next;
694 prevnode->next = curnode->next;
696 parent->num_children--;
697 gtk_text_btree_node_free_empty (tree, curnode);
700 curnode = curline->parent;
705 char_count = seg->char_count;
707 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
710 * This segment refuses to die. Move it to prev_seg and
711 * advance prev_seg if the segment has left gravity.
714 if (prev_seg == NULL)
716 seg->next = start_line->segments;
717 start_line->segments = seg;
721 seg->next = prev_seg->next;
722 prev_seg->next = seg;
724 if (seg->type->leftGravity)
731 /* Segment is gone. Decrement the char count of the node and
733 for (node = curnode; node != NULL;
736 node->num_chars -= char_count;
744 * If the beginning and end of the deletion range are in different
745 * lines, join the two lines together and discard the ending line.
748 if (start_line != end_line)
751 GtkTextBTreeNode *ancestor_node;
752 GtkTextLine *prevline;
755 /* last_seg was appended to start_line up at the top of this function */
757 for (seg = last_seg; seg != NULL;
760 chars_moved += seg->char_count;
761 if (seg->type->lineChangeFunc != NULL)
763 (*seg->type->lineChangeFunc)(seg, end_line);
767 for (node = start_line->parent; node != NULL;
770 node->num_chars += chars_moved;
773 curnode = end_line->parent;
774 for (node = curnode; node != NULL;
777 node->num_chars -= chars_moved;
780 curnode->num_children--;
781 prevline = curnode->children.line;
782 if (prevline == end_line)
784 curnode->children.line = end_line->next;
788 while (prevline->next != end_line)
790 prevline = prevline->next;
792 prevline->next = end_line->next;
794 end_line->next = deleted_lines;
795 deleted_lines = end_line;
797 /* We now fix up the per-view aggregates. We add all the height and
798 * width for the deleted lines to the start line, so that when revalidation
799 * occurs, the correct change in size is seen.
801 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
808 gint deleted_width = 0;
809 gint deleted_height = 0;
811 line = deleted_lines;
814 GtkTextLine *next_line = line->next;
815 ld = _gtk_text_line_get_data (line, view->view_id);
819 deleted_width = MAX (deleted_width, ld->width);
820 deleted_height += ld->height;
824 gtk_text_line_destroy (tree, line);
829 if (deleted_width > 0 || deleted_height > 0)
831 ld = _gtk_text_line_get_data (start_line, view->view_id);
835 /* This means that start_line has never been validated.
836 * We don't really want to do the validation here but
837 * we do need to store our temporary sizes. So we
838 * create the line data and assume a line w/h of 0.
840 ld = _gtk_text_line_data_new (view->layout, start_line);
841 _gtk_text_line_add_data (start_line, ld);
847 ld->width = MAX (deleted_width, ld->width);
848 ld->height += deleted_height;
852 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
853 if (ancestor_node->parent)
854 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
859 /* avoid dangling pointer */
860 deleted_lines = NULL;
862 gtk_text_btree_rebalance (tree, curnode);
866 * Cleanup the segments in the new line.
869 cleanup_line (start_line);
872 * Lastly, rebalance the first GtkTextBTreeNode of the range.
875 gtk_text_btree_rebalance (tree, start_line->parent);
877 /* Notify outstanding iterators that they
879 chars_changed (tree);
880 segments_changed (tree);
882 if (gtk_debug_flags & GTK_DEBUG_TEXT)
883 _gtk_text_btree_check (tree);
885 /* Re-initialize our iterators */
886 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
891 _gtk_text_btree_insert (GtkTextIter *iter,
895 GtkTextLineSegment *prev_seg; /* The segment just before the first
896 * new segment (NULL means new segment
897 * is at beginning of line). */
898 GtkTextLineSegment *cur_seg; /* Current segment; new characters
899 * are inserted just after this one.
900 * NULL means insert at beginning of
902 GtkTextLine *line; /* Current line (new segments are
903 * added to this line). */
904 GtkTextLineSegment *seg;
905 GtkTextLine *newline;
906 int chunk_len; /* # characters in current chunk. */
907 gint sol; /* start of line */
908 gint eol; /* Pointer to character just after last
909 * one in current chunk.
911 gint delim; /* index of paragraph delimiter */
912 int line_count_delta; /* Counts change to total number of
916 int char_count_delta; /* change to number of chars */
918 gint start_byte_index;
919 GtkTextLine *start_line;
921 g_return_if_fail (text != NULL);
922 g_return_if_fail (iter != NULL);
927 /* extract iterator info */
928 tree = _gtk_text_iter_get_btree (iter);
929 line = _gtk_text_iter_get_text_line (iter);
932 start_byte_index = gtk_text_iter_get_line_index (iter);
934 /* Get our insertion segment split. Note this assumes line allows
935 * char insertions, which isn't true of the "last" line. But iter
936 * should not be on that line, as we assert here.
938 g_assert (!_gtk_text_line_is_last (line, tree));
939 prev_seg = gtk_text_line_segment_split (iter);
942 /* Invalidate all iterators */
943 chars_changed (tree);
944 segments_changed (tree);
947 * Chop the text up into lines and create a new segment for
948 * each line, plus a new line for the leftovers from the
954 line_count_delta = 0;
955 char_count_delta = 0;
960 pango_find_paragraph_boundary (text + sol,
965 /* make these relative to the start of the text */
969 g_assert (eol >= sol);
970 g_assert (delim >= sol);
971 g_assert (eol >= delim);
973 g_assert (eol <= len);
975 chunk_len = eol - sol;
977 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
978 seg = _gtk_char_segment_new (&text[sol], chunk_len);
980 char_count_delta += seg->char_count;
984 seg->next = line->segments;
985 line->segments = seg;
989 seg->next = cur_seg->next;
995 /* chunk didn't end with a paragraph separator */
996 g_assert (eol == len);
1001 * The chunk ended with a newline, so create a new GtkTextLine
1002 * and move the remainder of the old line to it.
1005 newline = gtk_text_line_new ();
1006 gtk_text_line_set_parent (newline, line->parent);
1007 newline->next = line->next;
1008 line->next = newline;
1009 newline->segments = seg->next;
1017 * Cleanup the starting line for the insertion, plus the ending
1018 * line if it's different.
1021 cleanup_line (start_line);
1022 if (line != start_line)
1024 cleanup_line (line);
1027 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1029 /* Invalidate our region, and reset the iterator the user
1030 passed in to point to the end of the inserted text. */
1036 _gtk_text_btree_get_iter_at_line (tree,
1042 /* We could almost certainly be more efficient here
1043 by saving the information from the insertion loop
1045 gtk_text_iter_forward_chars (&end, char_count_delta);
1047 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1048 _gtk_text_btree_invalidate_region (tree,
1052 /* Convenience for the user */
1058 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1059 GtkTextLineSegment *seg)
1063 GtkTextLineSegment *prevPtr;
1066 gint start_byte_offset;
1068 line = _gtk_text_iter_get_text_line (iter);
1069 tree = _gtk_text_iter_get_btree (iter);
1070 start_byte_offset = gtk_text_iter_get_line_index (iter);
1072 prevPtr = gtk_text_line_segment_split (iter);
1073 if (prevPtr == NULL)
1075 seg->next = line->segments;
1076 line->segments = seg;
1080 seg->next = prevPtr->next;
1081 prevPtr->next = seg;
1084 post_insert_fixup (tree, line, 0, seg->char_count);
1086 chars_changed (tree);
1087 segments_changed (tree);
1089 /* reset *iter for the user, and invalidate tree nodes */
1091 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1094 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1096 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1097 _gtk_text_btree_invalidate_region (tree, &start, iter);
1101 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1104 GtkTextLineSegment *seg;
1106 seg = _gtk_pixbuf_segment_new (pixbuf);
1108 insert_pixbuf_or_widget_segment (iter, seg);
1112 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1113 GtkTextChildAnchor *anchor)
1115 GtkTextLineSegment *seg;
1118 if (anchor->segment != NULL)
1120 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1124 seg = _gtk_widget_segment_new (anchor);
1126 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1127 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1129 insert_pixbuf_or_widget_segment (iter, seg);
1131 if (tree->child_anchor_table == NULL)
1132 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1134 g_hash_table_insert (tree->child_anchor_table,
1135 seg->body.child.obj,
1136 seg->body.child.obj);
1140 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1142 GtkTextLineSegment *seg;
1144 seg = anchor->segment;
1146 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1155 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1156 GtkTextBTreeNode *node, gint y, gint *line_top,
1157 GtkTextLine *last_line)
1161 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1162 _gtk_text_btree_check (tree);
1164 if (node->level == 0)
1168 line = node->children.line;
1170 while (line != NULL && line != last_line)
1172 GtkTextLineData *ld;
1174 ld = _gtk_text_line_get_data (line, view->view_id);
1178 if (y < (current_y + (ld ? ld->height : 0)))
1181 current_y += ld->height;
1182 *line_top += ld->height;
1191 GtkTextBTreeNode *child;
1193 child = node->children.node;
1195 while (child != NULL)
1200 gtk_text_btree_node_get_size (child, view->view_id,
1203 if (y < (current_y + height))
1204 return find_line_by_y (tree, view, child,
1205 y - current_y, line_top,
1208 current_y += height;
1209 *line_top += height;
1211 child = child->next;
1219 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1226 GtkTextLine *last_line;
1229 view = gtk_text_btree_get_view (tree, view_id);
1230 g_return_val_if_fail (view != NULL, NULL);
1232 last_line = get_last_line (tree);
1234 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1238 *line_top_out = line_top;
1244 find_line_top_in_line_list (GtkTextBTree *tree,
1247 GtkTextLine *target_line,
1250 while (line != NULL)
1252 GtkTextLineData *ld;
1254 if (line == target_line)
1257 ld = _gtk_text_line_get_data (line, view->view_id);
1264 g_assert_not_reached (); /* If we get here, our
1265 target line didn't exist
1266 under its parent node */
1271 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1272 GtkTextLine *target_line,
1279 GtkTextBTreeNode *node;
1281 view = gtk_text_btree_get_view (tree, view_id);
1283 g_return_val_if_fail (view != NULL, 0);
1286 node = target_line->parent;
1287 while (node != NULL)
1289 nodes = g_slist_prepend (nodes, node);
1290 node = node->parent;
1294 while (iter != NULL)
1298 if (node->level == 0)
1300 g_slist_free (nodes);
1301 return find_line_top_in_line_list (tree, view,
1302 node->children.line,
1307 GtkTextBTreeNode *child;
1308 GtkTextBTreeNode *target_node;
1310 g_assert (iter->next != NULL); /* not at level 0 */
1311 target_node = iter->next->data;
1313 child = node->children.node;
1315 while (child != NULL)
1320 if (child == target_node)
1324 gtk_text_btree_node_get_size (child, view->view_id,
1328 child = child->next;
1330 g_assert (child != NULL); /* should have broken out before we
1334 iter = g_slist_next (iter);
1337 g_assert_not_reached (); /* we return when we find the target line */
1342 _gtk_text_btree_add_view (GtkTextBTree *tree,
1343 GtkTextLayout *layout)
1346 GtkTextLine *last_line;
1347 GtkTextLineData *line_data;
1349 g_return_if_fail (tree != NULL);
1351 view = g_new (BTreeView, 1);
1353 view->view_id = layout;
1354 view->layout = layout;
1356 view->next = tree->views;
1361 g_assert (tree->views->prev == NULL);
1362 tree->views->prev = view;
1367 /* The last line in the buffer has identity values for the per-view
1368 * data so that we can avoid special case checks for it in a large
1371 last_line = get_last_line (tree);
1373 line_data = g_new (GtkTextLineData, 1);
1374 line_data->view_id = layout;
1375 line_data->next = NULL;
1376 line_data->width = 0;
1377 line_data->height = 0;
1378 line_data->valid = TRUE;
1380 _gtk_text_line_add_data (last_line, line_data);
1384 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1388 GtkTextLine *last_line;
1389 GtkTextLineData *line_data;
1391 g_return_if_fail (tree != NULL);
1395 while (view != NULL)
1397 if (view->view_id == view_id)
1403 g_return_if_fail (view != NULL);
1406 view->next->prev = view->prev;
1409 view->prev->next = view->next;
1411 if (view == tree->views)
1412 tree->views = view->next;
1414 /* Remove the line data for the last line which we added ourselves.
1415 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1417 last_line = get_last_line (tree);
1418 line_data = _gtk_text_line_remove_data (last_line, view_id);
1421 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1423 view->layout = (gpointer) 0xdeadbeef;
1424 view->view_id = (gpointer) 0xdeadbeef;
1430 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1431 const GtkTextIter *start,
1432 const GtkTextIter *end)
1438 while (view != NULL)
1440 gtk_text_layout_invalidate (view->layout, start, end);
1447 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1452 g_return_if_fail (tree != NULL);
1453 g_return_if_fail (view_id != NULL);
1455 gtk_text_btree_node_get_size (tree->root_node, view_id,
1470 iter_stack_new (void)
1473 stack = g_new (IterStack, 1);
1474 stack->iters = NULL;
1481 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1484 if (stack->count > stack->alloced)
1486 stack->alloced = stack->count*2;
1487 stack->iters = g_realloc (stack->iters,
1488 stack->alloced*sizeof (GtkTextIter));
1490 stack->iters[stack->count-1] = *iter;
1494 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1496 if (stack->count == 0)
1501 *iter = stack->iters[stack->count];
1507 iter_stack_free (IterStack *stack)
1509 g_free (stack->iters);
1514 iter_stack_invert (IterStack *stack)
1516 if (stack->count > 0)
1519 guint j = stack->count - 1;
1524 tmp = stack->iters[i];
1525 stack->iters[i] = stack->iters[j];
1526 stack->iters[j] = tmp;
1535 queue_tag_redisplay (GtkTextBTree *tree,
1537 const GtkTextIter *start,
1538 const GtkTextIter *end)
1540 if (_gtk_text_tag_affects_size (tag))
1542 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1543 _gtk_text_btree_invalidate_region (tree, start, end);
1545 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1547 /* We only need to queue a redraw, not a relayout */
1548 redisplay_region (tree, start, end);
1551 /* We don't need to do anything if the tag doesn't affect display */
1555 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1556 const GtkTextIter *end_orig,
1560 GtkTextLineSegment *seg, *prev;
1561 GtkTextLine *cleanupline;
1562 gboolean toggled_on;
1563 GtkTextLine *start_line;
1564 GtkTextLine *end_line;
1566 GtkTextIter start, end;
1569 GtkTextTagInfo *info;
1571 g_return_if_fail (start_orig != NULL);
1572 g_return_if_fail (end_orig != NULL);
1573 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1574 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1575 _gtk_text_iter_get_btree (end_orig));
1576 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1579 printf ("%s tag %s from %d to %d\n",
1580 add ? "Adding" : "Removing",
1582 gtk_text_buffer_get_offset (start_orig),
1583 gtk_text_buffer_get_offset (end_orig));
1586 if (gtk_text_iter_equal (start_orig, end_orig))
1589 start = *start_orig;
1592 gtk_text_iter_order (&start, &end);
1594 tree = _gtk_text_iter_get_btree (&start);
1596 queue_tag_redisplay (tree, tag, &start, &end);
1598 info = gtk_text_btree_get_tag_info (tree, tag);
1600 start_line = _gtk_text_iter_get_text_line (&start);
1601 end_line = _gtk_text_iter_get_text_line (&end);
1603 /* Find all tag toggles in the region; we are going to delete them.
1604 We need to find them in advance, because
1605 forward_find_tag_toggle () won't work once we start playing around
1607 stack = iter_stack_new ();
1610 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1611 * which is deliberate - we don't want to delete a toggle at the
1614 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1616 if (gtk_text_iter_compare (&iter, &end) >= 0)
1619 iter_stack_push (stack, &iter);
1622 /* We need to traverse the toggles in order. */
1623 iter_stack_invert (stack);
1626 * See whether the tag is present at the start of the range. If
1627 * the state doesn't already match what we want then add a toggle
1631 toggled_on = gtk_text_iter_has_tag (&start, tag);
1632 if ( (add && !toggled_on) ||
1633 (!add && toggled_on) )
1635 /* This could create a second toggle at the start position;
1636 cleanup_line () will remove it if so. */
1637 seg = _gtk_toggle_segment_new (info, add);
1639 prev = gtk_text_line_segment_split (&start);
1642 seg->next = start_line->segments;
1643 start_line->segments = seg;
1647 seg->next = prev->next;
1651 /* cleanup_line adds the new toggle to the node counts. */
1653 printf ("added toggle at start\n");
1655 /* we should probably call segments_changed, but in theory
1656 any still-cached segments in the iters we are about to
1657 use are still valid, since they're in front
1663 * Scan the range of characters and delete any internal tag
1664 * transitions. Keep track of what the old state was at the end
1665 * of the range, and add a toggle there if it's needed.
1669 cleanupline = start_line;
1670 while (iter_stack_pop (stack, &iter))
1672 GtkTextLineSegment *indexable_seg;
1675 line = _gtk_text_iter_get_text_line (&iter);
1676 seg = _gtk_text_iter_get_any_segment (&iter);
1677 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1679 g_assert (seg != NULL);
1680 g_assert (indexable_seg != NULL);
1681 g_assert (seg != indexable_seg);
1683 prev = line->segments;
1685 /* Find the segment that actually toggles this tag. */
1686 while (seg != indexable_seg)
1688 g_assert (seg != NULL);
1689 g_assert (indexable_seg != NULL);
1690 g_assert (seg != indexable_seg);
1692 if ( (seg->type == >k_text_toggle_on_type ||
1693 seg->type == >k_text_toggle_off_type) &&
1694 (seg->body.toggle.info == info) )
1700 g_assert (seg != NULL);
1701 g_assert (indexable_seg != NULL);
1703 g_assert (seg != indexable_seg); /* If this happens, then
1704 forward_to_tag_toggle was
1706 g_assert (seg->body.toggle.info->tag == tag);
1708 /* If this happens, when previously tagging we didn't merge
1709 overlapping tags. */
1710 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1711 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1713 toggled_on = !toggled_on;
1716 printf ("deleting %s toggle\n",
1717 seg->type == >k_text_toggle_on_type ? "on" : "off");
1719 /* Remove toggle segment from the list. */
1722 line->segments = seg->next;
1726 while (prev->next != seg)
1730 prev->next = seg->next;
1733 /* Inform iterators we've hosed them. This actually reflects a
1734 bit of inefficiency; if you have the same tag toggled on and
1735 off a lot in a single line, we keep having the rescan from
1736 the front of the line. Of course we have to do that to get
1737 "prev" anyway, but here we are doing it an additional
1739 segments_changed (tree);
1741 /* Update node counts */
1742 if (seg->body.toggle.inNodeCounts)
1744 _gtk_change_node_toggle_count (line->parent,
1746 seg->body.toggle.inNodeCounts = FALSE;
1751 /* We only clean up lines when we're done with them, saves some
1752 gratuitous line-segment-traversals */
1754 if (cleanupline != line)
1756 cleanup_line (cleanupline);
1761 iter_stack_free (stack);
1763 /* toggled_on now reflects the toggle state _just before_ the
1764 end iterator. The end iterator could already have a toggle
1765 on or a toggle off. */
1766 if ( (add && !toggled_on) ||
1767 (!add && toggled_on) )
1769 /* This could create a second toggle at the start position;
1770 cleanup_line () will remove it if so. */
1772 seg = _gtk_toggle_segment_new (info, !add);
1774 prev = gtk_text_line_segment_split (&end);
1777 seg->next = end_line->segments;
1778 end_line->segments = seg;
1782 seg->next = prev->next;
1785 /* cleanup_line adds the new toggle to the node counts. */
1786 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1788 printf ("added toggle at end\n");
1793 * Cleanup cleanupline and the last line of the range, if
1794 * these are different.
1797 cleanup_line (cleanupline);
1798 if (cleanupline != end_line)
1800 cleanup_line (end_line);
1803 segments_changed (tree);
1805 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1806 _gtk_text_btree_check (tree);
1815 get_line_internal (GtkTextBTree *tree,
1817 gint *real_line_number,
1818 gboolean include_last)
1820 GtkTextBTreeNode *node;
1825 line_count = _gtk_text_btree_line_count (tree);
1829 if (line_number < 0)
1831 line_number = line_count;
1833 else if (line_number > line_count)
1835 line_number = line_count;
1838 if (real_line_number)
1839 *real_line_number = line_number;
1841 node = tree->root_node;
1842 lines_left = line_number;
1845 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1849 while (node->level != 0)
1851 for (node = node->children.node;
1852 node->num_lines <= lines_left;
1858 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1861 lines_left -= node->num_lines;
1866 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1869 for (line = node->children.line; lines_left > 0;
1875 g_error ("gtk_text_btree_find_line ran out of lines");
1884 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1887 _gtk_text_btree_get_line (tree,
1888 _gtk_text_btree_line_count (tree) - 1,
1893 _gtk_text_btree_get_line (GtkTextBTree *tree,
1895 gint *real_line_number)
1897 return get_line_internal (tree, line_number, real_line_number, TRUE);
1901 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1903 gint *real_line_number)
1905 return get_line_internal (tree, line_number, real_line_number, FALSE);
1909 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1911 gint *line_start_index,
1912 gint *real_char_index)
1914 GtkTextBTreeNode *node;
1916 GtkTextLineSegment *seg;
1921 node = tree->root_node;
1923 /* Clamp to valid indexes (-1 is magic for "highest index"),
1924 * node->num_chars includes the two newlines that aren't really
1927 if (char_index < 0 || char_index >= (node->num_chars - 1))
1929 char_index = node->num_chars - 2;
1932 *real_char_index = char_index;
1935 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1939 chars_left = char_index;
1940 while (node->level != 0)
1942 for (node = node->children.node;
1943 chars_left >= node->num_chars;
1946 chars_left -= node->num_chars;
1948 g_assert (chars_left >= 0);
1952 if (chars_left == 0)
1954 /* Start of a line */
1956 *line_start_index = char_index;
1957 return node->children.line;
1961 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1967 for (line = node->children.line; line != NULL; line = line->next)
1969 seg = line->segments;
1972 if (chars_in_line + seg->char_count > chars_left)
1973 goto found; /* found our line/segment */
1975 chars_in_line += seg->char_count;
1980 chars_left -= chars_in_line;
1988 g_assert (line != NULL); /* hosage, ran out of lines */
1989 g_assert (seg != NULL);
1991 *line_start_index = char_index - chars_left;
1996 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1999 GtkTextBTreeNode *node;
2000 GtkTextLine *siblingline;
2001 GtkTextLineSegment *seg;
2002 int src, dst, index;
2008 #define NUM_TAG_INFOS 10
2010 line = _gtk_text_iter_get_text_line (iter);
2011 tree = _gtk_text_iter_get_btree (iter);
2012 byte_index = gtk_text_iter_get_line_index (iter);
2014 tagInfo.numTags = 0;
2015 tagInfo.arraySize = NUM_TAG_INFOS;
2016 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2017 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2020 * Record tag toggles within the line of indexPtr but preceding
2021 * indexPtr. Note that if this loop segfaults, your
2022 * byte_index probably points past the sum of all
2023 * seg->byte_count */
2025 for (index = 0, seg = line->segments;
2026 (index + seg->byte_count) <= byte_index;
2027 index += seg->byte_count, seg = seg->next)
2029 if ((seg->type == >k_text_toggle_on_type)
2030 || (seg->type == >k_text_toggle_off_type))
2032 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2037 * Record toggles for tags in lines that are predecessors of
2038 * line but under the same level-0 GtkTextBTreeNode.
2041 for (siblingline = line->parent->children.line;
2042 siblingline != line;
2043 siblingline = siblingline->next)
2045 for (seg = siblingline->segments; seg != NULL;
2048 if ((seg->type == >k_text_toggle_on_type)
2049 || (seg->type == >k_text_toggle_off_type))
2051 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2057 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2058 * toggles for all siblings that precede that GtkTextBTreeNode.
2061 for (node = line->parent; node->parent != NULL;
2062 node = node->parent)
2064 GtkTextBTreeNode *siblingPtr;
2067 for (siblingPtr = node->parent->children.node;
2068 siblingPtr != node; siblingPtr = siblingPtr->next)
2070 for (summary = siblingPtr->summary; summary != NULL;
2071 summary = summary->next)
2073 if (summary->toggle_count & 1)
2075 inc_count (summary->info->tag, summary->toggle_count,
2083 * Go through the tag information and squash out all of the tags
2084 * that have even toggle counts (these tags exist before the point
2085 * of interest, but not at the desired character itself).
2088 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2090 if (tagInfo.counts[src] & 1)
2092 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2093 tagInfo.tags[dst] = tagInfo.tags[src];
2099 g_free (tagInfo.counts);
2102 g_free (tagInfo.tags);
2105 return tagInfo.tags;
2109 copy_segment (GString *string,
2110 gboolean include_hidden,
2111 gboolean include_nonchars,
2112 const GtkTextIter *start,
2113 const GtkTextIter *end)
2115 GtkTextLineSegment *end_seg;
2116 GtkTextLineSegment *seg;
2118 if (gtk_text_iter_equal (start, end))
2121 seg = _gtk_text_iter_get_indexable_segment (start);
2122 end_seg = _gtk_text_iter_get_indexable_segment (end);
2124 if (seg->type == >k_text_char_type)
2126 gboolean copy = TRUE;
2127 gint copy_bytes = 0;
2128 gint copy_start = 0;
2130 /* Don't copy if we're invisible; segments are invisible/not
2131 as a whole, no need to check each char */
2132 if (!include_hidden &&
2133 _gtk_text_btree_char_is_invisible (start))
2136 /* printf (" <invisible>\n"); */
2139 copy_start = _gtk_text_iter_get_segment_byte (start);
2143 /* End is in the same segment; need to copy fewer bytes. */
2144 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2146 copy_bytes = end_byte - copy_start;
2149 copy_bytes = seg->byte_count - copy_start;
2151 g_assert (copy_bytes != 0); /* Due to iter equality check at
2152 front of this function. */
2156 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2158 g_string_append_len (string,
2159 seg->body.chars + copy_start,
2163 /* printf (" :%s\n", string->str); */
2165 else if (seg->type == >k_text_pixbuf_type ||
2166 seg->type == >k_text_child_type)
2168 gboolean copy = TRUE;
2170 if (!include_nonchars)
2174 else if (!include_hidden &&
2175 _gtk_text_btree_char_is_invisible (start))
2182 g_string_append_len (string,
2183 gtk_text_unknown_char_utf8,
2191 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2192 const GtkTextIter *end_orig,
2193 gboolean include_hidden,
2194 gboolean include_nonchars)
2196 GtkTextLineSegment *seg;
2197 GtkTextLineSegment *end_seg;
2205 g_return_val_if_fail (start_orig != NULL, NULL);
2206 g_return_val_if_fail (end_orig != NULL, NULL);
2207 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2208 _gtk_text_iter_get_btree (end_orig), NULL);
2210 start = *start_orig;
2213 gtk_text_iter_order (&start, &end);
2215 retval = g_string_new ("");
2217 tree = _gtk_text_iter_get_btree (&start);
2219 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2221 seg = _gtk_text_iter_get_indexable_segment (&iter);
2222 while (seg != end_seg)
2224 copy_segment (retval, include_hidden, include_nonchars,
2227 _gtk_text_iter_forward_indexable_segment (&iter);
2229 seg = _gtk_text_iter_get_indexable_segment (&iter);
2232 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2235 g_string_free (retval, FALSE);
2240 _gtk_text_btree_line_count (GtkTextBTree *tree)
2242 /* Subtract bogus line at the end; we return a count
2244 return tree->root_node->num_lines - 1;
2248 _gtk_text_btree_char_count (GtkTextBTree *tree)
2250 /* Exclude newline in bogus last line and the
2251 * one in the last line that is after the end iterator
2253 return tree->root_node->num_chars - 2;
2256 #define LOTSA_TAGS 1000
2258 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2260 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2262 int deftagCnts[LOTSA_TAGS];
2263 int *tagCnts = deftagCnts;
2264 GtkTextTag *deftags[LOTSA_TAGS];
2265 GtkTextTag **tags = deftags;
2267 GtkTextBTreeNode *node;
2268 GtkTextLine *siblingline;
2269 GtkTextLineSegment *seg;
2276 line = _gtk_text_iter_get_text_line (iter);
2277 tree = _gtk_text_iter_get_btree (iter);
2278 byte_index = gtk_text_iter_get_line_index (iter);
2280 numTags = gtk_text_tag_table_get_size (tree->table);
2282 /* almost always avoid malloc, so stay out of system calls */
2283 if (LOTSA_TAGS < numTags)
2285 tagCnts = g_new (int, numTags);
2286 tags = g_new (GtkTextTag*, numTags);
2289 for (i=0; i<numTags; i++)
2295 * Record tag toggles within the line of indexPtr but preceding
2299 for (index = 0, seg = line->segments;
2300 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2301 index += seg->byte_count, seg = seg->next)
2303 if ((seg->type == >k_text_toggle_on_type)
2304 || (seg->type == >k_text_toggle_off_type))
2306 tag = seg->body.toggle.info->tag;
2307 if (tag->invisible_set && tag->values->invisible)
2309 tags[tag->priority] = tag;
2310 tagCnts[tag->priority]++;
2316 * Record toggles for tags in lines that are predecessors of
2317 * line but under the same level-0 GtkTextBTreeNode.
2320 for (siblingline = line->parent->children.line;
2321 siblingline != line;
2322 siblingline = siblingline->next)
2324 for (seg = siblingline->segments; seg != NULL;
2327 if ((seg->type == >k_text_toggle_on_type)
2328 || (seg->type == >k_text_toggle_off_type))
2330 tag = seg->body.toggle.info->tag;
2331 if (tag->invisible_set && tag->values->invisible)
2333 tags[tag->priority] = tag;
2334 tagCnts[tag->priority]++;
2341 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2342 * for all siblings that precede that GtkTextBTreeNode.
2345 for (node = line->parent; node->parent != NULL;
2346 node = node->parent)
2348 GtkTextBTreeNode *siblingPtr;
2351 for (siblingPtr = node->parent->children.node;
2352 siblingPtr != node; siblingPtr = siblingPtr->next)
2354 for (summary = siblingPtr->summary; summary != NULL;
2355 summary = summary->next)
2357 if (summary->toggle_count & 1)
2359 tag = summary->info->tag;
2360 if (tag->invisible_set && tag->values->invisible)
2362 tags[tag->priority] = tag;
2363 tagCnts[tag->priority] += summary->toggle_count;
2371 * Now traverse from highest priority to lowest,
2372 * take invisible value from first odd count (= on)
2375 for (i = numTags-1; i >=0; i--)
2379 /* FIXME not sure this should be if 0 */
2381 #ifndef ALWAYS_SHOW_SELECTION
2382 /* who would make the selection invisible? */
2383 if ((tag == tkxt->seltag)
2384 && !(tkxt->flags & GOT_FOCUS))
2390 invisible = tags[i]->values->invisible;
2395 if (LOTSA_TAGS < numTags)
2410 redisplay_region (GtkTextBTree *tree,
2411 const GtkTextIter *start,
2412 const GtkTextIter *end)
2415 GtkTextLine *start_line, *end_line;
2417 if (gtk_text_iter_compare (start, end) > 0)
2419 const GtkTextIter *tmp = start;
2424 start_line = _gtk_text_iter_get_text_line (start);
2425 end_line = _gtk_text_iter_get_text_line (end);
2428 while (view != NULL)
2430 gint start_y, end_y;
2431 GtkTextLineData *ld;
2433 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2435 if (end_line == start_line)
2438 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2440 ld = _gtk_text_line_get_data (end_line, view->view_id);
2442 end_y += ld->height;
2444 gtk_text_layout_changed (view->layout, start_y,
2453 redisplay_mark (GtkTextLineSegment *mark)
2458 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2460 mark->body.mark.obj);
2463 gtk_text_iter_forward_char (&end);
2465 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2466 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2471 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2473 if (!mark->body.mark.visible)
2476 redisplay_mark (mark);
2480 ensure_not_off_end (GtkTextBTree *tree,
2481 GtkTextLineSegment *mark,
2484 if (gtk_text_iter_get_line (iter) ==
2485 _gtk_text_btree_line_count (tree))
2486 gtk_text_iter_backward_char (iter);
2489 static GtkTextLineSegment*
2490 real_set_mark (GtkTextBTree *tree,
2491 GtkTextMark *existing_mark,
2493 gboolean left_gravity,
2494 const GtkTextIter *where,
2495 gboolean should_exist,
2496 gboolean redraw_selections)
2498 GtkTextLineSegment *mark;
2501 g_return_val_if_fail (tree != NULL, NULL);
2502 g_return_val_if_fail (where != NULL, NULL);
2503 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2506 mark = existing_mark->segment;
2507 else if (name != NULL)
2508 mark = g_hash_table_lookup (tree->mark_table,
2513 if (should_exist && mark == NULL)
2515 g_warning ("No mark `%s' exists!", name);
2519 /* OK if !should_exist and it does already exist, in that case
2525 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2526 _gtk_text_iter_check (&iter);
2530 if (redraw_selections &&
2531 (mark == tree->insert_mark->segment ||
2532 mark == tree->selection_bound_mark->segment))
2534 GtkTextIter old_pos;
2536 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2537 mark->body.mark.obj);
2538 redisplay_region (tree, &old_pos, where);
2542 * don't let visible marks be after the final newline of the
2546 if (mark->body.mark.visible)
2548 ensure_not_off_end (tree, mark, &iter);
2551 /* Redraw the mark's old location. */
2552 redisplay_mark_if_visible (mark);
2554 /* Unlink mark from its current location.
2555 This could hose our iterator... */
2556 gtk_text_btree_unlink_segment (tree, mark,
2557 mark->body.mark.line);
2558 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2559 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2561 segments_changed (tree); /* make sure the iterator recomputes its
2566 mark = _gtk_mark_segment_new (tree,
2570 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2572 if (mark->body.mark.name)
2573 g_hash_table_insert (tree->mark_table,
2574 mark->body.mark.name,
2578 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2579 _gtk_text_iter_check (&iter);
2581 /* Link mark into new location */
2582 gtk_text_btree_link_segment (mark, &iter);
2584 /* Invalidate some iterators. */
2585 segments_changed (tree);
2588 * update the screen at the mark's new location.
2591 redisplay_mark_if_visible (mark);
2593 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2594 _gtk_text_iter_check (&iter);
2596 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2597 _gtk_text_btree_check (tree);
2604 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2605 GtkTextMark *existing_mark,
2607 gboolean left_gravity,
2608 const GtkTextIter *iter,
2609 gboolean should_exist)
2611 GtkTextLineSegment *seg;
2613 seg = real_set_mark (tree, existing_mark,
2614 name, left_gravity, iter, should_exist,
2617 return seg ? seg->body.mark.obj : NULL;
2621 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2625 GtkTextIter tmp_start, tmp_end;
2627 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2629 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2630 tree->selection_bound_mark);
2632 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2644 gtk_text_iter_order (&tmp_start, &tmp_end);
2657 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2658 const GtkTextIter *iter)
2660 GtkTextIter start, end;
2662 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2663 redisplay_region (tree, &start, &end);
2665 /* Move insert AND selection_bound before we redisplay */
2666 real_set_mark (tree, tree->insert_mark,
2667 "insert", FALSE, iter, TRUE, FALSE);
2668 real_set_mark (tree, tree->selection_bound_mark,
2669 "selection_bound", FALSE, iter, TRUE, FALSE);
2673 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2678 g_return_if_fail (tree != NULL);
2679 g_return_if_fail (name != NULL);
2681 mark = g_hash_table_lookup (tree->mark_table,
2684 _gtk_text_btree_remove_mark (tree, mark);
2688 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2689 GtkTextLineSegment *segment)
2692 if (segment->body.mark.name)
2693 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2695 segment->body.mark.tree = NULL;
2696 segment->body.mark.line = NULL;
2698 /* Remove the ref on the mark, which frees segment as a side effect
2699 * if this is the last reference.
2701 g_object_unref (G_OBJECT (segment->body.mark.obj));
2705 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2708 GtkTextLineSegment *segment;
2710 g_return_if_fail (mark != NULL);
2711 g_return_if_fail (tree != NULL);
2713 segment = mark->segment;
2715 if (segment->body.mark.not_deleteable)
2717 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2721 /* This calls cleanup_line and segments_changed */
2722 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2724 _gtk_text_btree_release_mark_segment (tree, segment);
2728 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2729 GtkTextMark *segment)
2731 return segment == tree->insert_mark;
2735 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2736 GtkTextMark *segment)
2738 return segment == tree->selection_bound_mark;
2742 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2745 GtkTextLineSegment *seg;
2747 g_return_val_if_fail (tree != NULL, NULL);
2748 g_return_val_if_fail (name != NULL, NULL);
2750 seg = g_hash_table_lookup (tree->mark_table, name);
2752 return seg ? seg->body.mark.obj : NULL;
2756 * gtk_text_mark_set_visible:
2757 * @mark: a #GtkTextMark
2758 * @setting: visibility of mark
2760 * Sets the visibility of @mark; the insertion point is normally
2761 * visible, i.e. you can see it as a vertical bar. Also, the text
2762 * widget uses a visible mark to indicate where a drop will occur when
2763 * dragging-and-dropping text. Most other marks are not visible.
2764 * Marks are not visible by default.
2768 gtk_text_mark_set_visible (GtkTextMark *mark,
2771 GtkTextLineSegment *seg;
2773 g_return_if_fail (mark != NULL);
2775 seg = mark->segment;
2777 if (seg->body.mark.visible == setting)
2781 seg->body.mark.visible = setting;
2783 redisplay_mark (seg);
2788 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2791 GtkTextBTreeNode *node;
2792 GtkTextTagInfo *info;
2794 g_return_val_if_fail (tree != NULL, NULL);
2798 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2803 if (info->tag_root == NULL)
2806 node = info->tag_root;
2808 /* We know the tag root has instances of the given
2811 continue_outer_loop:
2812 g_assert (node != NULL);
2813 while (node->level > 0)
2815 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2816 node = node->children.node;
2817 while (node != NULL)
2819 if (gtk_text_btree_node_has_tag (node, tag))
2820 goto continue_outer_loop;
2824 g_assert (node != NULL);
2827 g_assert (node != NULL); /* The tag summaries said some node had
2830 g_assert (node->level == 0);
2832 return node->children.line;
2836 /* Looking for any tag at all (tag == NULL).
2837 Unfortunately this can't be done in a simple and efficient way
2838 right now; so I'm just going to return the
2839 first line in the btree. FIXME */
2840 return _gtk_text_btree_get_line (tree, 0, NULL);
2845 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2848 GtkTextBTreeNode *node;
2849 GtkTextBTreeNode *last_node;
2851 GtkTextTagInfo *info;
2853 g_return_val_if_fail (tree != NULL, NULL);
2857 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2859 if (info->tag_root == NULL)
2862 node = info->tag_root;
2863 /* We know the tag root has instances of the given
2866 while (node->level > 0)
2868 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2870 node = node->children.node;
2871 while (node != NULL)
2873 if (gtk_text_btree_node_has_tag (node, tag))
2881 g_assert (node != NULL); /* The tag summaries said some node had
2884 g_assert (node->level == 0);
2886 /* Find the last line in this node */
2887 line = node->children.line;
2888 while (line->next != NULL)
2895 /* This search can't be done efficiently at the moment,
2896 at least not without complexity.
2897 So, we just return the last line.
2899 return _gtk_text_btree_get_end_iter_line (tree);
2909 _gtk_text_line_get_number (GtkTextLine *line)
2912 GtkTextBTreeNode *node, *parent, *node2;
2916 * First count how many lines precede this one in its level-0
2920 node = line->parent;
2922 for (line2 = node->children.line; line2 != line;
2923 line2 = line2->next)
2927 g_error ("gtk_text_btree_line_number couldn't find line");
2933 * Now work up through the levels of the tree one at a time,
2934 * counting how many lines are in GtkTextBTreeNodes preceding the current
2938 for (parent = node->parent ; parent != NULL;
2939 node = parent, parent = parent->parent)
2941 for (node2 = parent->children.node; node2 != node;
2942 node2 = node2->next)
2946 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2948 index += node2->num_lines;
2954 static GtkTextLineSegment*
2955 find_toggle_segment_before_char (GtkTextLine *line,
2959 GtkTextLineSegment *seg;
2960 GtkTextLineSegment *toggle_seg;
2965 seg = line->segments;
2966 while ( (index + seg->char_count) <= char_in_line )
2968 if (((seg->type == >k_text_toggle_on_type)
2969 || (seg->type == >k_text_toggle_off_type))
2970 && (seg->body.toggle.info->tag == tag))
2973 index += seg->char_count;
2980 static GtkTextLineSegment*
2981 find_toggle_segment_before_byte (GtkTextLine *line,
2985 GtkTextLineSegment *seg;
2986 GtkTextLineSegment *toggle_seg;
2991 seg = line->segments;
2992 while ( (index + seg->byte_count) <= byte_in_line )
2994 if (((seg->type == >k_text_toggle_on_type)
2995 || (seg->type == >k_text_toggle_off_type))
2996 && (seg->body.toggle.info->tag == tag))
2999 index += seg->byte_count;
3007 find_toggle_outside_current_line (GtkTextLine *line,
3011 GtkTextBTreeNode *node;
3012 GtkTextLine *sibling_line;
3013 GtkTextLineSegment *seg;
3014 GtkTextLineSegment *toggle_seg;
3016 GtkTextTagInfo *info = NULL;
3019 * No toggle in this line. Look for toggles for the tag in lines
3020 * that are predecessors of line but under the same
3021 * level-0 GtkTextBTreeNode.
3024 sibling_line = line->parent->children.line;
3025 while (sibling_line != line)
3027 seg = sibling_line->segments;
3030 if (((seg->type == >k_text_toggle_on_type)
3031 || (seg->type == >k_text_toggle_off_type))
3032 && (seg->body.toggle.info->tag == tag))
3038 sibling_line = sibling_line->next;
3041 if (toggle_seg != NULL)
3042 return (toggle_seg->type == >k_text_toggle_on_type);
3045 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3046 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3047 * siblings that precede that GtkTextBTreeNode.
3050 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3056 node = line->parent;
3057 while (node->parent != NULL)
3059 GtkTextBTreeNode *sibling_node;
3061 sibling_node = node->parent->children.node;
3062 while (sibling_node != node)
3066 summary = sibling_node->summary;
3067 while (summary != NULL)
3069 if (summary->info == info)
3070 toggles += summary->toggle_count;
3072 summary = summary->next;
3075 sibling_node = sibling_node->next;
3078 if (node == info->tag_root)
3081 node = node->parent;
3085 * An odd number of toggles means that the tag is present at the
3089 return (toggles & 1) != 0;
3092 /* FIXME this function is far too slow, for no good reason. */
3094 _gtk_text_line_char_has_tag (GtkTextLine *line,
3099 GtkTextLineSegment *toggle_seg;
3101 g_return_val_if_fail (line != NULL, FALSE);
3104 * Check for toggles for the tag in the line but before
3105 * the char. If there is one, its type indicates whether or
3106 * not the character is tagged.
3109 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3111 if (toggle_seg != NULL)
3112 return (toggle_seg->type == >k_text_toggle_on_type);
3114 return find_toggle_outside_current_line (line, tree, tag);
3118 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3123 GtkTextLineSegment *toggle_seg;
3125 g_return_val_if_fail (line != NULL, FALSE);
3128 * Check for toggles for the tag in the line but before
3129 * the char. If there is one, its type indicates whether or
3130 * not the character is tagged.
3133 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3135 if (toggle_seg != NULL)
3136 return (toggle_seg->type == >k_text_toggle_on_type);
3138 return find_toggle_outside_current_line (line, tree, tag);
3142 _gtk_text_line_is_last (GtkTextLine *line,
3145 return line == get_last_line (tree);
3149 ensure_end_iter_line (GtkTextBTree *tree)
3151 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3156 /* n_lines is without the magic line at the end */
3157 n_lines = _gtk_text_btree_line_count (tree);
3159 g_assert (n_lines >= 1);
3161 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3163 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3168 ensure_end_iter_segment (GtkTextBTree *tree)
3170 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3172 GtkTextLineSegment *seg;
3173 GtkTextLineSegment *last_with_chars;
3175 ensure_end_iter_line (tree);
3177 last_with_chars = NULL;
3179 seg = tree->end_iter_line->segments;
3182 if (seg->char_count > 0)
3183 last_with_chars = seg;
3187 tree->end_iter_segment = last_with_chars;
3189 /* We know the last char in the last line is '\n' */
3190 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3191 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3193 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3195 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3196 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3201 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3204 ensure_end_iter_line (tree);
3206 return line == tree->end_iter_line;
3210 _gtk_text_btree_is_end (GtkTextBTree *tree,
3212 GtkTextLineSegment *seg,
3216 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3218 /* Do this first to avoid walking segments in most cases */
3219 if (!_gtk_text_line_contains_end_iter (line, tree))
3222 ensure_end_iter_segment (tree);
3224 if (seg != tree->end_iter_segment)
3227 if (byte_index >= 0)
3228 return byte_index == tree->end_iter_segment_byte_index;
3230 return char_offset == tree->end_iter_segment_char_offset;
3234 _gtk_text_line_next (GtkTextLine *line)
3236 GtkTextBTreeNode *node;
3238 if (line->next != NULL)
3243 * This was the last line associated with the particular parent
3244 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3245 * then search down from that GtkTextBTreeNode to find the first
3249 node = line->parent;
3250 while (node != NULL && node->next == NULL)
3251 node = node->parent;
3257 while (node->level > 0)
3259 node = node->children.node;
3262 g_assert (node->children.line != line);
3264 return node->children.line;
3269 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3273 next = _gtk_text_line_next (line);
3275 /* If we were on the end iter line, we can't go to
3278 if (next && next->next == NULL && /* these checks are optimization only */
3279 _gtk_text_line_next (next) == NULL)
3286 _gtk_text_line_previous (GtkTextLine *line)
3288 GtkTextBTreeNode *node;
3289 GtkTextBTreeNode *node2;
3293 * Find the line under this GtkTextBTreeNode just before the starting line.
3295 prev = line->parent->children.line; /* First line at leaf */
3296 while (prev != line)
3298 if (prev->next == line)
3304 g_error ("gtk_text_btree_previous_line ran out of lines");
3308 * This was the first line associated with the particular parent
3309 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3310 * then search down from that GtkTextBTreeNode to find its last line.
3312 for (node = line->parent; ; node = node->parent)
3314 if (node == NULL || node->parent == NULL)
3316 else if (node != node->parent->children.node)
3320 for (node2 = node->parent->children.node; ;
3321 node2 = node2->children.node)
3323 while (node2->next != node)
3324 node2 = node2->next;
3326 if (node2->level == 0)
3332 for (prev = node2->children.line ; ; prev = prev->next)
3334 if (prev->next == NULL)
3338 g_assert_not_reached ();
3344 _gtk_text_line_data_new (GtkTextLayout *layout,
3347 GtkTextLineData *line_data;
3349 line_data = g_new (GtkTextLineData, 1);
3351 line_data->view_id = layout;
3352 line_data->next = NULL;
3353 line_data->width = 0;
3354 line_data->height = 0;
3355 line_data->valid = FALSE;
3361 _gtk_text_line_add_data (GtkTextLine *line,
3362 GtkTextLineData *data)
3364 g_return_if_fail (line != NULL);
3365 g_return_if_fail (data != NULL);
3366 g_return_if_fail (data->view_id != NULL);
3370 data->next = line->views;
3380 _gtk_text_line_remove_data (GtkTextLine *line,
3383 GtkTextLineData *prev;
3384 GtkTextLineData *iter;
3386 g_return_val_if_fail (line != NULL, NULL);
3387 g_return_val_if_fail (view_id != NULL, NULL);
3391 while (iter != NULL)
3393 if (iter->view_id == view_id)
3402 prev->next = iter->next;
3404 line->views = iter->next;
3413 _gtk_text_line_get_data (GtkTextLine *line,
3416 GtkTextLineData *iter;
3418 g_return_val_if_fail (line != NULL, NULL);
3419 g_return_val_if_fail (view_id != NULL, NULL);
3422 while (iter != NULL)
3424 if (iter->view_id == view_id)
3433 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3434 GtkTextLineData *ld)
3436 /* For now this is totally unoptimized. FIXME?
3438 We could probably optimize the case where the width removed
3439 is less than the max width for the parent node,
3440 and the case where the height is unchanged when we re-wrap.
3443 g_return_if_fail (ld != NULL);
3446 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3450 _gtk_text_line_char_count (GtkTextLine *line)
3452 GtkTextLineSegment *seg;
3456 seg = line->segments;
3459 size += seg->char_count;
3466 _gtk_text_line_byte_count (GtkTextLine *line)
3468 GtkTextLineSegment *seg;
3472 seg = line->segments;
3475 size += seg->byte_count;
3483 _gtk_text_line_char_index (GtkTextLine *target_line)
3485 GSList *node_stack = NULL;
3486 GtkTextBTreeNode *iter;
3490 /* Push all our parent nodes onto a stack */
3491 iter = target_line->parent;
3493 g_assert (iter != NULL);
3495 while (iter != NULL)
3497 node_stack = g_slist_prepend (node_stack, iter);
3499 iter = iter->parent;
3502 /* Check that we have the root node on top of the stack. */
3503 g_assert (node_stack != NULL &&
3504 node_stack->data != NULL &&
3505 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3507 /* Add up chars in all nodes before the nodes in our stack.
3511 iter = node_stack->data;
3512 while (iter != NULL)
3514 GtkTextBTreeNode *child_iter;
3515 GtkTextBTreeNode *next_node;
3517 next_node = node_stack->next ?
3518 node_stack->next->data : NULL;
3519 node_stack = g_slist_remove (node_stack, node_stack->data);
3521 if (iter->level == 0)
3523 /* stack should be empty when we're on the last node */
3524 g_assert (node_stack == NULL);
3525 break; /* Our children are now lines */
3528 g_assert (next_node != NULL);
3529 g_assert (iter != NULL);
3530 g_assert (next_node->parent == iter);
3532 /* Add up chars before us in the tree */
3533 child_iter = iter->children.node;
3534 while (child_iter != next_node)
3536 g_assert (child_iter != NULL);
3538 num_chars += child_iter->num_chars;
3540 child_iter = child_iter->next;
3546 g_assert (iter != NULL);
3547 g_assert (iter == target_line->parent);
3549 /* Since we don't store char counts in lines, only in segments, we
3550 have to iterate over the lines adding up segment char counts
3551 until we find our line. */
3552 line = iter->children.line;
3553 while (line != target_line)
3555 g_assert (line != NULL);
3557 num_chars += _gtk_text_line_char_count (line);
3562 g_assert (line == target_line);
3568 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3572 GtkTextLineSegment *seg;
3575 g_return_val_if_fail (line != NULL, NULL);
3577 offset = byte_offset;
3578 seg = line->segments;
3580 while (offset >= seg->byte_count)
3582 g_assert (seg != NULL); /* means an invalid byte index */
3583 offset -= seg->byte_count;
3588 *seg_offset = offset;
3594 _gtk_text_line_char_to_segment (GtkTextLine *line,
3598 GtkTextLineSegment *seg;
3601 g_return_val_if_fail (line != NULL, NULL);
3603 offset = char_offset;
3604 seg = line->segments;
3606 while (offset >= seg->char_count)
3608 g_assert (seg != NULL); /* means an invalid char index */
3609 offset -= seg->char_count;
3614 *seg_offset = offset;
3620 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3624 GtkTextLineSegment *seg;
3627 g_return_val_if_fail (line != NULL, NULL);
3629 offset = byte_offset;
3630 seg = line->segments;
3632 while (offset > 0 && offset >= seg->byte_count)
3634 g_assert (seg != NULL); /* means an invalid byte index */
3635 offset -= seg->byte_count;
3640 *seg_offset = offset;
3646 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3650 GtkTextLineSegment *seg;
3653 g_return_val_if_fail (line != NULL, NULL);
3655 offset = char_offset;
3656 seg = line->segments;
3658 while (offset > 0 && offset >= seg->char_count)
3660 g_assert (seg != NULL); /* means an invalid byte index */
3661 offset -= seg->char_count;
3666 *seg_offset = offset;
3672 _gtk_text_line_byte_to_char (GtkTextLine *line,
3676 GtkTextLineSegment *seg;
3678 g_return_val_if_fail (line != NULL, 0);
3679 g_return_val_if_fail (byte_offset >= 0, 0);
3682 seg = line->segments;
3683 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3684 the next segment) */
3686 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3688 byte_offset -= seg->byte_count;
3689 char_offset += seg->char_count;
3694 g_assert (seg != NULL);
3696 /* Now byte_offset is the offset into the current segment,
3697 and char_offset is the start of the current segment.
3698 Optimize the case where no chars use > 1 byte */
3699 if (seg->byte_count == seg->char_count)
3700 return char_offset + byte_offset;
3703 if (seg->type == >k_text_char_type)
3704 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3707 g_assert (seg->char_count == 1);
3708 g_assert (byte_offset == 0);
3716 _gtk_text_line_char_to_byte (GtkTextLine *line,
3719 g_warning ("FIXME not implemented");
3724 /* FIXME sync with char_locate (or figure out a clean
3725 way to merge the two functions) */
3727 _gtk_text_line_byte_locate (GtkTextLine *line,
3729 GtkTextLineSegment **segment,
3730 GtkTextLineSegment **any_segment,
3731 gint *seg_byte_offset,
3732 gint *line_byte_offset)
3734 GtkTextLineSegment *seg;
3735 GtkTextLineSegment *after_prev_indexable;
3736 GtkTextLineSegment *after_last_indexable;
3737 GtkTextLineSegment *last_indexable;
3741 g_return_val_if_fail (line != NULL, FALSE);
3742 g_return_val_if_fail (byte_offset >= 0, FALSE);
3745 *any_segment = NULL;
3748 offset = byte_offset;
3750 last_indexable = NULL;
3751 after_last_indexable = line->segments;
3752 after_prev_indexable = line->segments;
3753 seg = line->segments;
3755 /* The loop ends when we're inside a segment;
3756 last_indexable refers to the last segment
3757 we passed entirely. */
3758 while (seg && offset >= seg->byte_count)
3760 if (seg->char_count > 0)
3762 offset -= seg->byte_count;
3763 bytes_in_line += seg->byte_count;
3764 last_indexable = seg;
3765 after_prev_indexable = after_last_indexable;
3766 after_last_indexable = last_indexable->next;
3774 /* We went off the end of the line */
3776 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3783 if (after_last_indexable != NULL)
3784 *any_segment = after_last_indexable;
3786 *any_segment = *segment;
3789 /* Override any_segment if we're in the middle of a segment. */
3791 *any_segment = *segment;
3793 *seg_byte_offset = offset;
3795 g_assert (*segment != NULL);
3796 g_assert (*any_segment != NULL);
3797 g_assert (*seg_byte_offset < (*segment)->byte_count);
3799 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3804 /* FIXME sync with byte_locate (or figure out a clean
3805 way to merge the two functions) */
3807 _gtk_text_line_char_locate (GtkTextLine *line,
3809 GtkTextLineSegment **segment,
3810 GtkTextLineSegment **any_segment,
3811 gint *seg_char_offset,
3812 gint *line_char_offset)
3814 GtkTextLineSegment *seg;
3815 GtkTextLineSegment *after_prev_indexable;
3816 GtkTextLineSegment *after_last_indexable;
3817 GtkTextLineSegment *last_indexable;
3821 g_return_val_if_fail (line != NULL, FALSE);
3822 g_return_val_if_fail (char_offset >= 0, FALSE);
3825 *any_segment = NULL;
3828 offset = char_offset;
3830 last_indexable = NULL;
3831 after_last_indexable = line->segments;
3832 after_prev_indexable = line->segments;
3833 seg = line->segments;
3835 /* The loop ends when we're inside a segment;
3836 last_indexable refers to the last segment
3837 we passed entirely. */
3838 while (seg && offset >= seg->char_count)
3840 if (seg->char_count > 0)
3842 offset -= seg->char_count;
3843 chars_in_line += seg->char_count;
3844 last_indexable = seg;
3845 after_prev_indexable = after_last_indexable;
3846 after_last_indexable = last_indexable->next;
3854 /* end of the line */
3856 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3863 if (after_last_indexable != NULL)
3864 *any_segment = after_last_indexable;
3866 *any_segment = *segment;
3869 /* Override any_segment if we're in the middle of a segment. */
3871 *any_segment = *segment;
3873 *seg_char_offset = offset;
3875 g_assert (*segment != NULL);
3876 g_assert (*any_segment != NULL);
3877 g_assert (*seg_char_offset < (*segment)->char_count);
3879 *line_char_offset = chars_in_line + *seg_char_offset;
3885 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3887 gint *line_char_offset,
3888 gint *seg_char_offset)
3890 GtkTextLineSegment *seg;
3893 g_return_if_fail (line != NULL);
3894 g_return_if_fail (byte_offset >= 0);
3896 *line_char_offset = 0;
3898 offset = byte_offset;
3899 seg = line->segments;
3901 while (offset >= seg->byte_count)
3903 offset -= seg->byte_count;
3904 *line_char_offset += seg->char_count;
3906 g_assert (seg != NULL); /* means an invalid char offset */
3909 g_assert (seg->char_count > 0); /* indexable. */
3911 /* offset is now the number of bytes into the current segment we
3912 * want to go. Count chars into the current segment.
3915 if (seg->type == >k_text_char_type)
3917 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3919 g_assert (*seg_char_offset < seg->char_count);
3921 *line_char_offset += *seg_char_offset;
3925 g_assert (offset == 0);
3926 *seg_char_offset = 0;
3931 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3933 gint *line_byte_offset,
3934 gint *seg_byte_offset)
3936 GtkTextLineSegment *seg;
3939 g_return_if_fail (line != NULL);
3940 g_return_if_fail (char_offset >= 0);
3942 *line_byte_offset = 0;
3944 offset = char_offset;
3945 seg = line->segments;
3947 while (offset >= seg->char_count)
3949 offset -= seg->char_count;
3950 *line_byte_offset += seg->byte_count;
3952 g_assert (seg != NULL); /* means an invalid char offset */
3955 g_assert (seg->char_count > 0); /* indexable. */
3957 /* offset is now the number of chars into the current segment we
3958 want to go. Count bytes into the current segment. */
3960 if (seg->type == >k_text_char_type)
3962 *seg_byte_offset = 0;
3966 const char * start = seg->body.chars + *seg_byte_offset;
3968 bytes = g_utf8_next_char (start) - start;
3969 *seg_byte_offset += bytes;
3973 g_assert (*seg_byte_offset < seg->byte_count);
3975 *line_byte_offset += *seg_byte_offset;
3979 g_assert (offset == 0);
3980 *seg_byte_offset = 0;
3985 node_compare (GtkTextBTreeNode *lhs,
3986 GtkTextBTreeNode *rhs)
3988 GtkTextBTreeNode *iter;
3989 GtkTextBTreeNode *node;
3990 GtkTextBTreeNode *common_parent;
3991 GtkTextBTreeNode *parent_of_lower;
3992 GtkTextBTreeNode *parent_of_higher;
3993 gboolean lhs_is_lower;
3994 GtkTextBTreeNode *lower;
3995 GtkTextBTreeNode *higher;
3997 /* This function assumes that lhs and rhs are not underneath each
4004 if (lhs->level < rhs->level)
4006 lhs_is_lower = TRUE;
4012 lhs_is_lower = FALSE;
4017 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4018 * of the common parent we used to reach the common parent; the
4019 * ordering of these child nodes in the child list is the ordering
4023 /* Get on the same level (may be on same level already) */
4025 while (node->level < higher->level)
4026 node = node->parent;
4028 g_assert (node->level == higher->level);
4030 g_assert (node != higher); /* Happens if lower is underneath higher */
4032 /* Go up until we have two children with a common parent.
4034 parent_of_lower = node;
4035 parent_of_higher = higher;
4037 while (parent_of_lower->parent != parent_of_higher->parent)
4039 parent_of_lower = parent_of_lower->parent;
4040 parent_of_higher = parent_of_higher->parent;
4043 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4045 common_parent = parent_of_lower->parent;
4047 g_assert (common_parent != NULL);
4049 /* See which is first in the list of common_parent's children */
4050 iter = common_parent->children.node;
4051 while (iter != NULL)
4053 if (iter == parent_of_higher)
4055 /* higher is less than lower */
4058 return 1; /* lhs > rhs */
4062 else if (iter == parent_of_lower)
4064 /* lower is less than higher */
4067 return -1; /* lhs < rhs */
4075 g_assert_not_reached ();
4079 /* remember that tag == NULL means "any tag" */
4081 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4085 GtkTextBTreeNode *node;
4086 GtkTextTagInfo *info;
4087 gboolean below_tag_root;
4089 g_return_val_if_fail (line != NULL, NULL);
4091 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4092 _gtk_text_btree_check (tree);
4096 /* Right now we can only offer linear-search if the user wants
4097 * to know about any tag toggle at all.
4099 return _gtk_text_line_next_excluding_last (line);
4102 /* Our tag summaries only have node precision, not line
4103 * precision. This means that if any line under a node could contain a
4104 * tag, then any of the others could also contain a tag.
4106 * In the future we could have some mechanism to keep track of how
4107 * many toggles we've found under a node so far, since we have a
4108 * count of toggles under the node. But for now I'm going with KISS.
4111 /* return same-node line, if any. */
4115 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4119 if (info->tag_root == NULL)
4122 if (info->tag_root == line->parent)
4123 return NULL; /* we were at the last line under the tag root */
4125 /* We need to go up out of this node, and on to the next one with
4126 toggles for the target tag. If we're below the tag root, we need to
4127 find the next node below the tag root that has tag summaries. If
4128 we're not below the tag root, we need to see if the tag root is
4129 after us in the tree, and if so, return the first line underneath
4132 node = line->parent;
4133 below_tag_root = FALSE;
4134 while (node != NULL)
4136 if (node == info->tag_root)
4138 below_tag_root = TRUE;
4142 node = node->parent;
4147 node = line->parent;
4148 while (node != info->tag_root)
4150 if (node->next == NULL)
4151 node = node->parent;
4156 if (gtk_text_btree_node_has_tag (node, tag))
4166 ordering = node_compare (line->parent, info->tag_root);
4170 /* Tag root is ahead of us, so search there. */
4171 node = info->tag_root;
4176 /* Tag root is after us, so no more lines that
4177 * could contain the tag.
4182 g_assert_not_reached ();
4187 g_assert (node != NULL);
4189 /* We have to find the first sub-node of this node that contains
4193 while (node->level > 0)
4195 g_assert (node != NULL); /* If this fails, it likely means an
4196 incorrect tag summary led us on a
4197 wild goose chase down this branch of
4199 node = node->children.node;
4200 while (node != NULL)
4202 if (gtk_text_btree_node_has_tag (node, tag))
4208 g_assert (node != NULL);
4209 g_assert (node->level == 0);
4211 return node->children.line;
4215 prev_line_under_node (GtkTextBTreeNode *node,
4220 prev = node->children.line;
4226 while (prev->next != line)
4236 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4240 GtkTextBTreeNode *node;
4241 GtkTextBTreeNode *found_node = NULL;
4242 GtkTextTagInfo *info;
4243 gboolean below_tag_root;
4245 GtkTextBTreeNode *line_ancestor;
4246 GtkTextBTreeNode *line_ancestor_parent;
4248 /* See next_could_contain_tag () for more extensive comments
4249 * on what's going on here.
4252 g_return_val_if_fail (line != NULL, NULL);
4254 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4255 _gtk_text_btree_check (tree);
4259 /* Right now we can only offer linear-search if the user wants
4260 * to know about any tag toggle at all.
4262 return _gtk_text_line_previous (line);
4265 /* Return same-node line, if any. */
4266 prev = prev_line_under_node (line->parent, line);
4270 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4274 if (info->tag_root == NULL)
4277 if (info->tag_root == line->parent)
4278 return NULL; /* we were at the first line under the tag root */
4280 /* Are we below the tag root */
4281 node = line->parent;
4282 below_tag_root = FALSE;
4283 while (node != NULL)
4285 if (node == info->tag_root)
4287 below_tag_root = TRUE;
4291 node = node->parent;
4296 /* Look for a previous node under this tag root that has our
4300 /* this assertion holds because line->parent is not the
4301 * tag root, we are below the tag root, and the tag
4304 g_assert (line->parent->parent != NULL);
4306 line_ancestor = line->parent;
4307 line_ancestor_parent = line->parent->parent;
4309 node = line_ancestor_parent->children.node;
4310 while (node != line_ancestor &&
4311 line_ancestor != info->tag_root)
4313 GSList *child_nodes = NULL;
4316 /* Create reverse-order list of nodes before
4319 while (node != line_ancestor
4322 child_nodes = g_slist_prepend (child_nodes, node);
4327 /* Try to find a node with our tag on it in the list */
4331 GtkTextBTreeNode *this_node = tmp->data;
4333 g_assert (this_node != line_ancestor);
4335 if (gtk_text_btree_node_has_tag (this_node, tag))
4337 found_node = this_node;
4338 g_slist_free (child_nodes);
4342 tmp = g_slist_next (tmp);
4345 g_slist_free (child_nodes);
4347 /* Didn't find anything on this level; go up one level. */
4348 line_ancestor = line_ancestor_parent;
4349 line_ancestor_parent = line_ancestor->parent;
4351 node = line_ancestor_parent->children.node;
4361 ordering = node_compare (line->parent, info->tag_root);
4365 /* Tag root is ahead of us, so no more lines
4372 /* Tag root is after us, so grab last tagged
4373 * line underneath the tag root.
4375 found_node = info->tag_root;
4379 g_assert_not_reached ();
4384 g_assert (found_node != NULL);
4386 /* We have to find the last sub-node of this node that contains
4391 while (node->level > 0)
4393 GSList *child_nodes = NULL;
4395 g_assert (node != NULL); /* If this fails, it likely means an
4396 incorrect tag summary led us on a
4397 wild goose chase down this branch of
4400 node = node->children.node;
4401 while (node != NULL)
4403 child_nodes = g_slist_prepend (child_nodes, node);
4407 node = NULL; /* detect failure to find a child node. */
4410 while (iter != NULL)
4412 if (gtk_text_btree_node_has_tag (iter->data, tag))
4414 /* recurse into this node. */
4419 iter = g_slist_next (iter);
4422 g_slist_free (child_nodes);
4424 g_assert (node != NULL);
4427 g_assert (node != NULL);
4428 g_assert (node->level == 0);
4430 /* this assertion is correct, but slow. */
4431 /* g_assert (node_compare (node, line->parent) < 0); */
4433 /* Return last line in this node. */
4435 prev = node->children.line;
4443 * Non-public function implementations
4447 summary_list_destroy (Summary *summary)
4450 while (summary != NULL)
4452 next = summary->next;
4453 summary_destroy (summary);
4459 get_last_line (GtkTextBTree *tree)
4461 if (tree->last_line_stamp != tree->chars_changed_stamp)
4467 n_lines = _gtk_text_btree_line_count (tree);
4469 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4471 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4473 tree->last_line_stamp = tree->chars_changed_stamp;
4474 tree->last_line = line;
4477 return tree->last_line;
4485 gtk_text_line_new (void)
4489 line = g_new0(GtkTextLine, 1);
4495 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4497 GtkTextLineData *ld;
4498 GtkTextLineData *next;
4500 g_return_if_fail (line != NULL);
4507 view = gtk_text_btree_get_view (tree, ld->view_id);
4509 g_assert (view != NULL);
4512 gtk_text_layout_free_line_data (view->layout, line, ld);
4521 gtk_text_line_set_parent (GtkTextLine *line,
4522 GtkTextBTreeNode *node)
4524 if (line->parent == node)
4526 line->parent = node;
4527 gtk_text_btree_node_invalidate_upward (node, NULL);
4531 cleanup_line (GtkTextLine *line)
4533 GtkTextLineSegment *seg, **prev_p;
4537 * Make a pass over all of the segments in the line, giving each
4538 * a chance to clean itself up. This could potentially change
4539 * the structure of the line, e.g. by merging two segments
4540 * together or having two segments cancel themselves; if so,
4541 * then repeat the whole process again, since the first structure
4542 * change might make other structure changes possible. Repeat
4543 * until eventually there are no changes.
4550 for (prev_p = &line->segments, seg = *prev_p;
4552 prev_p = &(*prev_p)->next, seg = *prev_p)
4554 if (seg->type->cleanupFunc != NULL)
4556 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4569 node_data_new (gpointer view_id)
4573 nd = g_new (NodeData, 1);
4575 nd->view_id = view_id;
4585 node_data_destroy (NodeData *nd)
4591 node_data_list_destroy (NodeData *nd)
4597 while (iter != NULL)
4600 node_data_destroy (iter);
4606 node_data_find (NodeData *nd, gpointer view_id)
4610 if (nd->view_id == view_id)
4618 summary_destroy (Summary *summary)
4620 /* Fill with error-triggering garbage */
4621 summary->info = (void*)0x1;
4622 summary->toggle_count = 567;
4623 summary->next = (void*)0x1;
4627 static GtkTextBTreeNode*
4628 gtk_text_btree_node_new (void)
4630 GtkTextBTreeNode *node;
4632 node = g_new (GtkTextBTreeNode, 1);
4634 node->node_data = NULL;
4640 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4641 GtkTextTagInfo *info,
4646 summary = node->summary;
4647 while (summary != NULL)
4649 if (summary->info == info)
4651 summary->toggle_count += adjust;
4655 summary = summary->next;
4658 if (summary == NULL)
4660 /* didn't find a summary for our tag. */
4661 g_return_if_fail (adjust > 0);
4662 summary = g_new (Summary, 1);
4663 summary->info = info;
4664 summary->toggle_count = adjust;
4665 summary->next = node->summary;
4666 node->summary = summary;
4670 /* Note that the tag root and above do not have summaries
4671 for the tag; only nodes below the tag root have
4674 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4678 summary = node->summary;
4679 while (summary != NULL)
4682 summary->info->tag == tag)
4685 summary = summary->next;
4691 /* Add node and all children to the damage region. */
4693 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4697 nd = node->node_data;
4704 if (node->level == 0)
4708 line = node->children.line;
4709 while (line != NULL)
4711 GtkTextLineData *ld;
4725 GtkTextBTreeNode *child;
4727 child = node->children.node;
4729 while (child != NULL)
4731 gtk_text_btree_node_invalidate_downward (child);
4733 child = child->next;
4739 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4741 GtkTextBTreeNode *iter;
4744 while (iter != NULL)
4750 nd = node_data_find (iter->node_data, view_id);
4752 if (nd == NULL || !nd->valid)
4753 break; /* Once a node is invalid, we know its parents are as well. */
4759 gboolean should_continue = FALSE;
4761 nd = iter->node_data;
4766 should_continue = TRUE;
4773 if (!should_continue)
4774 break; /* This node was totally invalidated, so are its
4778 iter = iter->parent;
4784 * _gtk_text_btree_is_valid:
4785 * @tree: a #GtkTextBTree
4786 * @view_id: ID for the view
4788 * Check to see if the entire #GtkTextBTree is valid or not for
4791 * Return value: %TRUE if the entire #GtkTextBTree is valid
4794 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4798 g_return_val_if_fail (tree != NULL, FALSE);
4800 nd = node_data_find (tree->root_node->node_data, view_id);
4801 return (nd && nd->valid);
4804 typedef struct _ValidateState ValidateState;
4806 struct _ValidateState
4808 gint remaining_pixels;
4809 gboolean in_validation;
4816 gtk_text_btree_node_validate (BTreeView *view,
4817 GtkTextBTreeNode *node,
4819 ValidateState *state)
4821 gint node_valid = TRUE;
4822 gint node_width = 0;
4823 gint node_height = 0;
4825 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4826 g_return_if_fail (!nd->valid);
4828 if (node->level == 0)
4830 GtkTextLine *line = node->children.line;
4831 GtkTextLineData *ld;
4833 /* Iterate over leading valid lines */
4834 while (line != NULL)
4836 ld = _gtk_text_line_get_data (line, view_id);
4838 if (!ld || !ld->valid)
4840 else if (state->in_validation)
4842 state->in_validation = FALSE;
4847 state->y += ld->height;
4848 node_width = MAX (ld->width, node_width);
4849 node_height += ld->height;
4855 state->in_validation = TRUE;
4857 /* Iterate over invalid lines */
4858 while (line != NULL)
4860 ld = _gtk_text_line_get_data (line, view_id);
4862 if (ld && ld->valid)
4867 state->old_height += ld->height;
4868 ld = gtk_text_layout_wrap (view->layout, line, ld);
4869 state->new_height += ld->height;
4871 node_width = MAX (ld->width, node_width);
4872 node_height += ld->height;
4874 state->remaining_pixels -= ld->height;
4875 if (state->remaining_pixels <= 0)
4885 /* Iterate over the remaining lines */
4886 while (line != NULL)
4888 ld = _gtk_text_line_get_data (line, view_id);
4889 state->in_validation = FALSE;
4891 if (!ld || !ld->valid)
4896 node_width = MAX (ld->width, node_width);
4897 node_height += ld->height;
4905 GtkTextBTreeNode *child;
4908 child = node->children.node;
4910 /* Iterate over leading valid nodes */
4913 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4915 if (!child_nd->valid)
4917 else if (state->in_validation)
4919 state->in_validation = FALSE;
4924 state->y += child_nd->height;
4925 node_width = MAX (node_width, child_nd->width);
4926 node_height += child_nd->height;
4929 child = child->next;
4932 /* Iterate over invalid nodes */
4935 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4937 if (child_nd->valid)
4941 gtk_text_btree_node_validate (view, child, view_id, state);
4943 if (!child_nd->valid)
4945 node_width = MAX (node_width, child_nd->width);
4946 node_height += child_nd->height;
4948 if (!state->in_validation || state->remaining_pixels <= 0)
4950 child = child->next;
4955 child = child->next;
4958 /* Iterate over the remaining lines */
4961 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4962 state->in_validation = FALSE;
4964 if (!child_nd->valid)
4967 node_width = MAX (child_nd->width, node_width);
4968 node_height += child_nd->height;
4970 child = child->next;
4974 nd->width = node_width;
4975 nd->height = node_height;
4976 nd->valid = node_valid;
4980 * _gtk_text_btree_validate:
4981 * @tree: a #GtkTextBTree
4983 * @max_pixels: the maximum number of pixels to validate. (No more
4984 * than one paragraph beyond this limit will be validated)
4985 * @y: location to store starting y coordinate of validated region
4986 * @old_height: location to store old height of validated region
4987 * @new_height: location to store new height of validated region
4989 * Validate a single contiguous invalid region of a #GtkTextBTree for
4992 * Return value: %TRUE if a region has been validated, %FALSE if the
4993 * entire tree was already valid.
4996 _gtk_text_btree_validate (GtkTextBTree *tree,
5005 g_return_val_if_fail (tree != NULL, FALSE);
5007 view = gtk_text_btree_get_view (tree, view_id);
5008 g_return_val_if_fail (view != NULL, FALSE);
5010 if (!_gtk_text_btree_is_valid (tree, view_id))
5012 ValidateState state;
5014 state.remaining_pixels = max_pixels;
5015 state.in_validation = FALSE;
5017 state.old_height = 0;
5018 state.new_height = 0;
5020 gtk_text_btree_node_validate (view,
5027 *old_height = state.old_height;
5029 *new_height = state.new_height;
5031 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5032 _gtk_text_btree_check (tree);
5041 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5045 gboolean *valid_out)
5049 gboolean valid = TRUE;
5051 if (node->level == 0)
5053 GtkTextLine *line = node->children.line;
5055 while (line != NULL)
5057 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5059 if (!ld || !ld->valid)
5064 width = MAX (ld->width, width);
5065 height += ld->height;
5073 GtkTextBTreeNode *child = node->children.node;
5077 NodeData *child_nd = node_data_find (child->node_data, view_id);
5079 if (!child_nd || !child_nd->valid)
5084 width = MAX (child_nd->width, width);
5085 height += child_nd->height;
5088 child = child->next;
5093 *height_out = height;
5098 /* Recompute the validity and size of the view data for a given
5099 * view at this node from the immediate children of the node
5102 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5105 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5110 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5111 &width, &height, &valid);
5113 nd->height = height;
5120 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5125 gtk_text_btree_node_check_valid (node, view_id);
5126 node = node->parent;
5131 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5134 if (node->level == 0)
5136 return gtk_text_btree_node_check_valid (node, view_id);
5140 GtkTextBTreeNode *child = node->children.node;
5142 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5150 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5152 if (!child_nd->valid)
5154 nd->width = MAX (child_nd->width, nd->width);
5155 nd->height += child_nd->height;
5157 child = child->next;
5166 * _gtk_text_btree_validate_line:
5167 * @tree: a #GtkTextBTree
5168 * @line: line to validate
5169 * @view_id: view ID for the view to validate
5171 * Revalidate a single line of the btree for the given view, propagate
5172 * results up through the entire tree.
5175 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5179 GtkTextLineData *ld;
5182 g_return_if_fail (tree != NULL);
5183 g_return_if_fail (line != NULL);
5185 view = gtk_text_btree_get_view (tree, view_id);
5186 g_return_if_fail (view != NULL);
5188 ld = _gtk_text_line_get_data (line, view_id);
5189 if (!ld || !ld->valid)
5191 ld = gtk_text_layout_wrap (view->layout, line, ld);
5193 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5198 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5200 if (node->level == 0)
5204 line = node->children.line;
5205 while (line != NULL)
5207 GtkTextLineData *ld;
5209 ld = _gtk_text_line_remove_data (line, view_id);
5212 gtk_text_layout_free_line_data (view->layout, line, ld);
5219 GtkTextBTreeNode *child;
5221 child = node->children.node;
5223 while (child != NULL)
5226 gtk_text_btree_node_remove_view (view, child, view_id);
5228 child = child->next;
5232 gtk_text_btree_node_remove_data (node, view_id);
5236 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5238 if (node->level == 0)
5241 GtkTextLineSegment *seg;
5243 while (node->children.line != NULL)
5245 line = node->children.line;
5246 node->children.line = line->next;
5247 while (line->segments != NULL)
5249 seg = line->segments;
5250 line->segments = seg->next;
5252 (*seg->type->deleteFunc) (seg, line, TRUE);
5254 gtk_text_line_destroy (tree, line);
5259 GtkTextBTreeNode *childPtr;
5261 while (node->children.node != NULL)
5263 childPtr = node->children.node;
5264 node->children.node = childPtr->next;
5265 gtk_text_btree_node_destroy (tree, childPtr);
5269 gtk_text_btree_node_free_empty (tree, node);
5273 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5274 GtkTextBTreeNode *node)
5276 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5277 (node->level == 0 && node->children.line == NULL));
5279 summary_list_destroy (node->summary);
5280 node_data_list_destroy (node->node_data);
5285 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5289 nd = node->node_data;
5292 if (nd->view_id == view_id)
5300 nd = node_data_new (view_id);
5302 if (node->node_data)
5303 nd->next = node->node_data;
5305 node->node_data = nd;
5312 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5318 nd = node->node_data;
5321 if (nd->view_id == view_id)
5332 prev->next = nd->next;
5334 if (node->node_data == nd)
5335 node->node_data = nd->next;
5339 node_data_destroy (nd);
5343 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5344 gint *width, gint *height)
5348 g_return_if_fail (width != NULL);
5349 g_return_if_fail (height != NULL);
5351 nd = gtk_text_btree_node_ensure_data (node, view_id);
5356 *height = nd->height;
5359 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5360 * here isn't quite right, since for a lot of operations we want to
5361 * know which children of the common parent correspond to the two nodes
5362 * (e.g., when computing the order of two iters)
5364 static GtkTextBTreeNode *
5365 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5366 GtkTextBTreeNode *node2)
5368 while (node1->level < node2->level)
5369 node1 = node1->parent;
5370 while (node2->level < node1->level)
5371 node2 = node2->parent;
5372 while (node1 != node2)
5374 node1 = node1->parent;
5375 node2 = node2->parent;
5386 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5391 while (view != NULL)
5393 if (view->view_id == view_id)
5402 get_tree_bounds (GtkTextBTree *tree,
5406 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5407 _gtk_text_btree_get_end_iter (tree, end);
5411 tag_changed_cb (GtkTextTagTable *table,
5413 gboolean size_changed,
5418 /* We need to queue a relayout on all regions that are tagged with
5425 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5427 /* Must be a last toggle if there was a first one. */
5428 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5429 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5430 _gtk_text_btree_invalidate_region (tree,
5437 /* We only need to queue a redraw, not a relayout */
5442 while (view != NULL)
5446 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5447 gtk_text_layout_changed (view->layout, 0, height, height);
5455 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5458 /* Remove the tag from the tree */
5463 get_tree_bounds (tree, &start, &end);
5465 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5466 gtk_text_btree_remove_tag_info (tree, tag);
5470 /* Rebalance the out-of-whack node "node" */
5472 gtk_text_btree_rebalance (GtkTextBTree *tree,
5473 GtkTextBTreeNode *node)
5476 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5477 * up through the tree one GtkTextBTreeNode at a time until the root
5478 * GtkTextBTreeNode has been processed.
5481 while (node != NULL)
5483 GtkTextBTreeNode *new_node, *child;
5488 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5489 * then split off all but the first MIN_CHILDREN into a separate
5490 * GtkTextBTreeNode following the original one. Then repeat until the
5491 * GtkTextBTreeNode has a decent size.
5494 if (node->num_children > MAX_CHILDREN)
5499 * If the GtkTextBTreeNode being split is the root
5500 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5504 if (node->parent == NULL)
5506 new_node = gtk_text_btree_node_new ();
5507 new_node->parent = NULL;
5508 new_node->next = NULL;
5509 new_node->summary = NULL;
5510 new_node->level = node->level + 1;
5511 new_node->children.node = node;
5512 recompute_node_counts (tree, new_node);
5513 tree->root_node = new_node;
5515 new_node = gtk_text_btree_node_new ();
5516 new_node->parent = node->parent;
5517 new_node->next = node->next;
5518 node->next = new_node;
5519 new_node->summary = NULL;
5520 new_node->level = node->level;
5521 new_node->num_children = node->num_children - MIN_CHILDREN;
5522 if (node->level == 0)
5524 for (i = MIN_CHILDREN-1,
5525 line = node->children.line;
5526 i > 0; i--, line = line->next)
5528 /* Empty loop body. */
5530 new_node->children.line = line->next;
5535 for (i = MIN_CHILDREN-1,
5536 child = node->children.node;
5537 i > 0; i--, child = child->next)
5539 /* Empty loop body. */
5541 new_node->children.node = child->next;
5544 recompute_node_counts (tree, node);
5545 node->parent->num_children++;
5547 if (node->num_children <= MAX_CHILDREN)
5549 recompute_node_counts (tree, node);
5555 while (node->num_children < MIN_CHILDREN)
5557 GtkTextBTreeNode *other;
5558 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5559 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5560 int total_children, first_children, i;
5563 * Too few children for this GtkTextBTreeNode. If this is the root then,
5564 * it's OK for it to have less than MIN_CHILDREN children
5565 * as long as it's got at least two. If it has only one
5566 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5567 * the tree and use its child as the new root.
5570 if (node->parent == NULL)
5572 if ((node->num_children == 1) && (node->level > 0))
5574 tree->root_node = node->children.node;
5575 tree->root_node->parent = NULL;
5577 node->children.node = NULL;
5578 gtk_text_btree_node_free_empty (tree, node);
5584 * Not the root. Make sure that there are siblings to
5588 if (node->parent->num_children < 2)
5590 gtk_text_btree_rebalance (tree, node->parent);
5595 * Find a sibling neighbor to borrow from, and arrange for
5596 * node to be the earlier of the pair.
5599 if (node->next == NULL)
5601 for (other = node->parent->children.node;
5602 other->next != node;
5603 other = other->next)
5605 /* Empty loop body. */
5612 * We're going to either merge the two siblings together
5613 * into one GtkTextBTreeNode or redivide the children among them to
5614 * balance their loads. As preparation, join their two
5615 * child lists into a single list and remember the half-way
5616 * point in the list.
5619 total_children = node->num_children + other->num_children;
5620 first_children = total_children/2;
5621 if (node->children.node == NULL)
5623 node->children = other->children;
5624 other->children.node = NULL;
5625 other->children.line = NULL;
5627 if (node->level == 0)
5631 for (line = node->children.line, i = 1;
5633 line = line->next, i++)
5635 if (i == first_children)
5640 line->next = other->children.line;
5641 while (i <= first_children)
5650 GtkTextBTreeNode *child;
5652 for (child = node->children.node, i = 1;
5653 child->next != NULL;
5654 child = child->next, i++)
5656 if (i <= first_children)
5658 if (i == first_children)
5660 halfwaynode = child;
5664 child->next = other->children.node;
5665 while (i <= first_children)
5667 halfwaynode = child;
5668 child = child->next;
5674 * If the two siblings can simply be merged together, do it.
5677 if (total_children <= MAX_CHILDREN)
5679 recompute_node_counts (tree, node);
5680 node->next = other->next;
5681 node->parent->num_children--;
5683 other->children.node = NULL;
5684 other->children.line = NULL;
5685 gtk_text_btree_node_free_empty (tree, other);
5690 * The siblings can't be merged, so just divide their
5691 * children evenly between them.
5694 if (node->level == 0)
5696 other->children.line = halfwayline->next;
5697 halfwayline->next = NULL;
5701 other->children.node = halfwaynode->next;
5702 halfwaynode->next = NULL;
5705 recompute_node_counts (tree, node);
5706 recompute_node_counts (tree, other);
5709 node = node->parent;
5714 post_insert_fixup (GtkTextBTree *tree,
5716 gint line_count_delta,
5717 gint char_count_delta)
5720 GtkTextBTreeNode *node;
5723 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5724 * point, then rebalance the tree if necessary.
5727 for (node = line->parent ; node != NULL;
5728 node = node->parent)
5730 node->num_lines += line_count_delta;
5731 node->num_chars += char_count_delta;
5733 node = line->parent;
5734 node->num_children += line_count_delta;
5736 if (node->num_children > MAX_CHILDREN)
5738 gtk_text_btree_rebalance (tree, node);
5741 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5742 _gtk_text_btree_check (tree);
5745 static GtkTextTagInfo*
5746 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5749 GtkTextTagInfo *info;
5753 list = tree->tag_infos;
5754 while (list != NULL)
5757 if (info->tag == tag)
5760 list = g_slist_next (list);
5766 static GtkTextTagInfo*
5767 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5770 GtkTextTagInfo *info;
5772 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5776 /* didn't find it, create. */
5778 info = g_new (GtkTextTagInfo, 1);
5781 g_object_ref (G_OBJECT (tag));
5782 info->tag_root = NULL;
5783 info->toggle_count = 0;
5785 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5788 g_print ("Created tag info %p for tag %s(%p)\n",
5789 info, info->tag->name ? info->tag->name : "anon",
5798 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5801 GtkTextTagInfo *info;
5806 list = tree->tag_infos;
5807 while (list != NULL)
5810 if (info->tag == tag)
5813 g_print ("Removing tag info %p for tag %s(%p)\n",
5814 info, info->tag->name ? info->tag->name : "anon",
5820 prev->next = list->next;
5824 tree->tag_infos = list->next;
5827 g_slist_free (list);
5829 g_object_unref (G_OBJECT (info->tag));
5836 list = g_slist_next (list);
5841 recompute_level_zero_counts (GtkTextBTreeNode *node)
5844 GtkTextLineSegment *seg;
5846 g_assert (node->level == 0);
5848 line = node->children.line;
5849 while (line != NULL)
5851 node->num_children++;
5854 if (line->parent != node)
5855 gtk_text_line_set_parent (line, node);
5857 seg = line->segments;
5861 node->num_chars += seg->char_count;
5863 if (((seg->type != >k_text_toggle_on_type)
5864 && (seg->type != >k_text_toggle_off_type))
5865 || !(seg->body.toggle.inNodeCounts))
5871 GtkTextTagInfo *info;
5873 info = seg->body.toggle.info;
5875 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5886 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5889 GtkTextBTreeNode *child;
5891 g_assert (node->level > 0);
5893 child = node->children.node;
5894 while (child != NULL)
5896 node->num_children += 1;
5897 node->num_lines += child->num_lines;
5898 node->num_chars += child->num_chars;
5900 if (child->parent != node)
5902 child->parent = node;
5903 gtk_text_btree_node_invalidate_upward (node, NULL);
5906 summary = child->summary;
5907 while (summary != NULL)
5909 gtk_text_btree_node_adjust_toggle_count (node,
5911 summary->toggle_count);
5913 summary = summary->next;
5916 child = child->next;
5921 *----------------------------------------------------------------------
5923 * recompute_node_counts --
5925 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5926 * (tags, child information, etc.) by scanning the information in
5927 * its descendants. This procedure is called during rebalancing
5928 * when a GtkTextBTreeNode's child structure has changed.
5934 * The tag counts for node are modified to reflect its current
5935 * child structure, as are its num_children, num_lines, num_chars fields.
5936 * Also, all of the childrens' parent fields are made to point
5939 *----------------------------------------------------------------------
5943 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5946 Summary *summary, *summary2;
5949 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5950 * the existing Summary records (most of them will probably be reused).
5953 summary = node->summary;
5954 while (summary != NULL)
5956 summary->toggle_count = 0;
5957 summary = summary->next;
5960 node->num_children = 0;
5961 node->num_lines = 0;
5962 node->num_chars = 0;
5965 * Scan through the children, adding the childrens' tag counts into
5966 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5970 if (node->level == 0)
5971 recompute_level_zero_counts (node);
5973 recompute_level_nonzero_counts (node);
5978 gtk_text_btree_node_check_valid (node, view->view_id);
5983 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5984 * records that still have a zero count, or that have all the toggles.
5985 * The GtkTextBTreeNode with the children that account for all the tags toggles
5986 * have no summary information, and they become the tag_root for the tag.
5990 for (summary = node->summary; summary != NULL; )
5992 if (summary->toggle_count > 0 &&
5993 summary->toggle_count < summary->info->toggle_count)
5995 if (node->level == summary->info->tag_root->level)
5998 * The tag's root GtkTextBTreeNode split and some toggles left.
5999 * The tag root must move up a level.
6001 summary->info->tag_root = node->parent;
6004 summary = summary->next;
6007 if (summary->toggle_count == summary->info->toggle_count)
6010 * A GtkTextBTreeNode merge has collected all the toggles under
6011 * one GtkTextBTreeNode. Push the root down to this level.
6013 summary->info->tag_root = node;
6015 if (summary2 != NULL)
6017 summary2->next = summary->next;
6018 summary_destroy (summary);
6019 summary = summary2->next;
6023 node->summary = summary->next;
6024 summary_destroy (summary);
6025 summary = node->summary;
6031 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6032 GtkTextTagInfo *info,
6033 gint delta) /* may be negative */
6035 Summary *summary, *prevPtr;
6036 GtkTextBTreeNode *node2Ptr;
6037 int rootLevel; /* Level of original tag root */
6039 info->toggle_count += delta;
6041 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6043 info->tag_root = node;
6048 * Note the level of the existing root for the tag so we can detect
6049 * if it needs to be moved because of the toggle count change.
6052 rootLevel = info->tag_root->level;
6055 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6056 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6060 for ( ; node != info->tag_root; node = node->parent)
6063 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6064 * perhaps all we have to do is adjust its count.
6067 for (prevPtr = NULL, summary = node->summary;
6069 prevPtr = summary, summary = summary->next)
6071 if (summary->info == info)
6076 if (summary != NULL)
6078 summary->toggle_count += delta;
6079 if (summary->toggle_count > 0 &&
6080 summary->toggle_count < info->toggle_count)
6084 if (summary->toggle_count != 0)
6087 * Should never find a GtkTextBTreeNode with max toggle count at this
6088 * point (there shouldn't have been a summary entry in the
6092 g_error ("%s: bad toggle count (%d) max (%d)",
6093 G_STRLOC, summary->toggle_count, info->toggle_count);
6097 * Zero toggle count; must remove this tag from the list.
6100 if (prevPtr == NULL)
6102 node->summary = summary->next;
6106 prevPtr->next = summary->next;
6108 summary_destroy (summary);
6113 * This tag isn't currently in the summary information list.
6116 if (rootLevel == node->level)
6120 * The old tag root is at the same level in the tree as this
6121 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6122 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6123 * as well as the old root (if not, we'll move it up again
6124 * the next time through the loop). To push it up one level
6125 * we copy the original toggle count into the summary
6126 * information at the old root and change the root to its
6127 * parent GtkTextBTreeNode.
6130 GtkTextBTreeNode *rootnode = info->tag_root;
6131 summary = (Summary *) g_malloc (sizeof (Summary));
6132 summary->info = info;
6133 summary->toggle_count = info->toggle_count - delta;
6134 summary->next = rootnode->summary;
6135 rootnode->summary = summary;
6136 rootnode = rootnode->parent;
6137 rootLevel = rootnode->level;
6138 info->tag_root = rootnode;
6140 summary = (Summary *) g_malloc (sizeof (Summary));
6141 summary->info = info;
6142 summary->toggle_count = delta;
6143 summary->next = node->summary;
6144 node->summary = summary;
6149 * If we've decremented the toggle count, then it may be necessary
6150 * to push the tag root down one or more levels.
6157 if (info->toggle_count == 0)
6159 info->tag_root = (GtkTextBTreeNode *) NULL;
6162 node = info->tag_root;
6163 while (node->level > 0)
6166 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6167 * toggles. If so, push the root down one level.
6170 for (node2Ptr = node->children.node;
6171 node2Ptr != (GtkTextBTreeNode *)NULL ;
6172 node2Ptr = node2Ptr->next)
6174 for (prevPtr = NULL, summary = node2Ptr->summary;
6176 prevPtr = summary, summary = summary->next)
6178 if (summary->info == info)
6183 if (summary == NULL)
6187 if (summary->toggle_count != info->toggle_count)
6190 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6197 * This GtkTextBTreeNode has all the toggles, so push down the root.
6200 if (prevPtr == NULL)
6202 node2Ptr->summary = summary->next;
6206 prevPtr->next = summary->next;
6208 summary_destroy (summary);
6209 info->tag_root = node2Ptr;
6212 node = info->tag_root;
6217 *----------------------------------------------------------------------
6221 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6222 * increments the count for a particular tag, adding a new
6223 * entry for that tag if there wasn't one previously.
6229 * The information at *tagInfoPtr may be modified, and the arrays
6230 * may be reallocated to make them larger.
6232 *----------------------------------------------------------------------
6236 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6241 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6242 count > 0; tag_p++, count--)
6246 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6252 * There isn't currently an entry for this tag, so we have to
6253 * make a new one. If the arrays are full, then enlarge the
6257 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6259 GtkTextTag **newTags;
6260 int *newCounts, newSize;
6262 newSize = 2*tagInfoPtr->arraySize;
6263 newTags = (GtkTextTag **) g_malloc ((unsigned)
6264 (newSize*sizeof (GtkTextTag *)));
6265 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6266 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6267 g_free ((char *) tagInfoPtr->tags);
6268 tagInfoPtr->tags = newTags;
6269 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6270 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6271 tagInfoPtr->arraySize *sizeof (int));
6272 g_free ((char *) tagInfoPtr->counts);
6273 tagInfoPtr->counts = newCounts;
6274 tagInfoPtr->arraySize = newSize;
6277 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6278 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6279 tagInfoPtr->numTags++;
6283 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6284 const GtkTextIter *iter)
6286 GtkTextLineSegment *prev;
6290 line = _gtk_text_iter_get_text_line (iter);
6291 tree = _gtk_text_iter_get_btree (iter);
6293 prev = gtk_text_line_segment_split (iter);
6296 seg->next = line->segments;
6297 line->segments = seg;
6301 seg->next = prev->next;
6304 cleanup_line (line);
6305 segments_changed (tree);
6307 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6308 _gtk_text_btree_check (tree);
6312 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6313 GtkTextLineSegment *seg,
6316 GtkTextLineSegment *prev;
6318 if (line->segments == seg)
6320 line->segments = seg->next;
6324 for (prev = line->segments; prev->next != seg;
6327 /* Empty loop body. */
6329 prev->next = seg->next;
6331 cleanup_line (line);
6332 segments_changed (tree);
6336 * This is here because it requires BTree internals, it logically
6337 * belongs in gtktextsegment.c
6342 *--------------------------------------------------------------
6344 * _gtk_toggle_segment_check_func --
6346 * This procedure is invoked to perform consistency checks
6347 * on toggle segments.
6353 * If a consistency problem is found the procedure g_errors.
6355 *--------------------------------------------------------------
6359 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6365 if (segPtr->byte_count != 0)
6367 g_error ("toggle_segment_check_func: segment had non-zero size");
6369 if (!segPtr->body.toggle.inNodeCounts)
6371 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6373 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6374 for (summary = line->parent->summary; ;
6375 summary = summary->next)
6377 if (summary == NULL)
6381 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6388 if (summary->info == segPtr->body.toggle.info)
6392 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6404 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6405 GtkTextBTreeNode *node,
6415 while (view != NULL)
6417 if (view->view_id == nd->view_id)
6424 g_error ("Node has data for a view %p no longer attached to the tree",
6427 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6428 &width, &height, &valid);
6430 /* valid aggregate not checked the same as width/height, because on
6431 * btree rebalance we can have invalid nodes where all lines below
6432 * them are actually valid, due to moving lines around between
6435 * The guarantee is that if there are invalid lines the node is
6436 * invalid - we don't guarantee that if the node is invalid there
6437 * are invalid lines.
6440 if (nd->width != width ||
6441 nd->height != height ||
6442 (nd->valid && !valid))
6444 g_error ("Node aggregates for view %p are invalid:\n"
6445 "Are (%d,%d,%s), should be (%d,%d,%s)",
6447 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6448 width, height, valid ? "TRUE" : "FALSE");
6453 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6454 GtkTextBTreeNode *node)
6456 GtkTextBTreeNode *childnode;
6457 Summary *summary, *summary2;
6459 GtkTextLineSegment *segPtr;
6460 int num_children, num_lines, num_chars, toggle_count, min_children;
6461 GtkTextLineData *ld;
6464 if (node->parent != NULL)
6466 min_children = MIN_CHILDREN;
6468 else if (node->level > 0)
6475 if ((node->num_children < min_children)
6476 || (node->num_children > MAX_CHILDREN))
6478 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6479 node->num_children);
6482 nd = node->node_data;
6485 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6492 if (node->level == 0)
6494 for (line = node->children.line; line != NULL;
6497 if (line->parent != node)
6499 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6501 if (line->segments == NULL)
6503 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6509 /* Just ensuring we don't segv while doing this loop */
6514 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6516 if (segPtr->type->checkFunc != NULL)
6518 (*segPtr->type->checkFunc)(segPtr, line);
6520 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6521 && (segPtr->next != NULL)
6522 && (segPtr->next->byte_count == 0)
6523 && (segPtr->next->type->leftGravity))
6525 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6527 if ((segPtr->next == NULL)
6528 && (segPtr->type != >k_text_char_type))
6530 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6533 num_chars += segPtr->char_count;
6542 for (childnode = node->children.node; childnode != NULL;
6543 childnode = childnode->next)
6545 if (childnode->parent != node)
6547 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6549 if (childnode->level != (node->level-1))
6551 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6552 node->level, childnode->level);
6554 gtk_text_btree_node_check_consistency (tree, childnode);
6555 for (summary = childnode->summary; summary != NULL;
6556 summary = summary->next)
6558 for (summary2 = node->summary; ;
6559 summary2 = summary2->next)
6561 if (summary2 == NULL)
6563 if (summary->info->tag_root == node)
6567 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6568 summary->info->tag->name,
6569 "present in parent summaries");
6571 if (summary->info == summary2->info)
6578 num_lines += childnode->num_lines;
6579 num_chars += childnode->num_chars;
6582 if (num_children != node->num_children)
6584 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6585 num_children, node->num_children);
6587 if (num_lines != node->num_lines)
6589 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6590 num_lines, node->num_lines);
6592 if (num_chars != node->num_chars)
6594 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6595 num_chars, node->num_chars);
6598 for (summary = node->summary; summary != NULL;
6599 summary = summary->next)
6601 if (summary->info->toggle_count == summary->toggle_count)
6603 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6604 summary->info->tag->name);
6607 if (node->level == 0)
6609 for (line = node->children.line; line != NULL;
6612 for (segPtr = line->segments; segPtr != NULL;
6613 segPtr = segPtr->next)
6615 if ((segPtr->type != >k_text_toggle_on_type)
6616 && (segPtr->type != >k_text_toggle_off_type))
6620 if (segPtr->body.toggle.info == summary->info)
6622 if (!segPtr->body.toggle.inNodeCounts)
6623 g_error ("Toggle segment not in the node counts");
6632 for (childnode = node->children.node;
6634 childnode = childnode->next)
6636 for (summary2 = childnode->summary;
6638 summary2 = summary2->next)
6640 if (summary2->info == summary->info)
6642 toggle_count += summary2->toggle_count;
6647 if (toggle_count != summary->toggle_count)
6649 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6650 toggle_count, summary->toggle_count);
6652 for (summary2 = summary->next; summary2 != NULL;
6653 summary2 = summary2->next)
6655 if (summary2->info == summary->info)
6657 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6658 summary->info->tag->name);
6665 listify_foreach (GtkTextTag *tag, gpointer user_data)
6667 GSList** listp = user_data;
6669 *listp = g_slist_prepend (*listp, tag);
6673 list_of_tags (GtkTextTagTable *table)
6675 GSList *list = NULL;
6677 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6683 _gtk_text_btree_check (GtkTextBTree *tree)
6686 GtkTextBTreeNode *node;
6688 GtkTextLineSegment *seg;
6690 GSList *taglist = NULL;
6692 GtkTextTagInfo *info;
6695 * Make sure that the tag toggle counts and the tag root pointers are OK.
6697 for (taglist = list_of_tags (tree->table);
6698 taglist != NULL ; taglist = taglist->next)
6700 tag = taglist->data;
6701 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6704 node = info->tag_root;
6707 if (info->toggle_count != 0)
6709 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6710 tag->name, info->toggle_count);
6712 continue; /* no ranges for the tag */
6714 else if (info->toggle_count == 0)
6716 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6719 else if (info->toggle_count & 1)
6721 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6722 tag->name, info->toggle_count);
6724 for (summary = node->summary; summary != NULL;
6725 summary = summary->next)
6727 if (summary->info->tag == tag)
6729 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6733 if (node->level > 0)
6735 for (node = node->children.node ; node != NULL ;
6738 for (summary = node->summary; summary != NULL;
6739 summary = summary->next)
6741 if (summary->info->tag == tag)
6743 count += summary->toggle_count;
6750 GtkTextLineSegmentClass * last = NULL;
6752 for (line = node->children.line ; line != NULL ;
6755 for (seg = line->segments; seg != NULL;
6758 if ((seg->type == >k_text_toggle_on_type ||
6759 seg->type == >k_text_toggle_off_type) &&
6760 seg->body.toggle.info->tag == tag)
6762 if (last == seg->type)
6763 g_error ("Two consecutive toggles on or off weren't merged");
6764 if (!seg->body.toggle.inNodeCounts)
6765 g_error ("Toggle segment not in the node counts");
6774 if (count != info->toggle_count)
6776 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6777 info->toggle_count, tag->name, count);
6782 g_slist_free (taglist);
6786 * Call a recursive procedure to do the main body of checks.
6789 node = tree->root_node;
6790 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6793 * Make sure that there are at least two lines in the text and
6794 * that the last line has no characters except a newline.
6797 if (node->num_lines < 2)
6799 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6801 if (node->num_chars < 2)
6803 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6805 while (node->level > 0)
6807 node = node->children.node;
6808 while (node->next != NULL)
6813 line = node->children.line;
6814 while (line->next != NULL)
6818 seg = line->segments;
6819 while ((seg->type == >k_text_toggle_off_type)
6820 || (seg->type == >k_text_right_mark_type)
6821 || (seg->type == >k_text_left_mark_type))
6824 * It's OK to toggle a tag off in the last line, but
6825 * not to start a new range. It's also OK to have marks
6831 if (seg->type != >k_text_char_type)
6833 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6835 if (seg->next != NULL)
6837 g_error ("_gtk_text_btree_check: last line has too many segments");
6839 if (seg->byte_count != 1)
6841 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6844 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6846 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6851 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6852 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6853 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6854 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6857 _gtk_text_btree_spew (GtkTextBTree *tree)
6862 printf ("%d lines in tree %p\n",
6863 _gtk_text_btree_line_count (tree), tree);
6865 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6867 while (line != NULL)
6869 _gtk_text_btree_spew_line (tree, line);
6870 line = _gtk_text_line_next (line);
6873 printf ("=================== Tag information\n");
6878 list = tree->tag_infos;
6880 while (list != NULL)
6882 GtkTextTagInfo *info;
6886 printf (" tag `%s': root at %p, toggle count %d\n",
6887 info->tag->name, info->tag_root, info->toggle_count);
6889 list = g_slist_next (list);
6892 if (tree->tag_infos == NULL)
6894 printf (" (no tags in the tree)\n");
6898 printf ("=================== Tree nodes\n");
6901 _gtk_text_btree_spew_node (tree->root_node, 0);
6906 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6909 GtkTextLineSegment *seg;
6911 spaces = g_strnfill (indent, ' ');
6913 printf ("%sline %p chars %d bytes %d\n",
6915 _gtk_text_line_char_count (line),
6916 _gtk_text_line_byte_count (line));
6918 seg = line->segments;
6921 if (seg->type == >k_text_char_type)
6923 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6928 if (*s == '\n' || *s == '\r')
6932 printf ("%s chars `%s'...\n", spaces, str);
6935 else if (seg->type == >k_text_right_mark_type)
6937 printf ("%s right mark `%s' visible: %d\n",
6939 seg->body.mark.name,
6940 seg->body.mark.visible);
6942 else if (seg->type == >k_text_left_mark_type)
6944 printf ("%s left mark `%s' visible: %d\n",
6946 seg->body.mark.name,
6947 seg->body.mark.visible);
6949 else if (seg->type == >k_text_toggle_on_type ||
6950 seg->type == >k_text_toggle_off_type)
6952 printf ("%s tag `%s' %s\n",
6953 spaces, seg->body.toggle.info->tag->name,
6954 seg->type == >k_text_toggle_off_type ? "off" : "on");
6964 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6967 GtkTextBTreeNode *iter;
6970 spaces = g_strnfill (indent, ' ');
6972 printf ("%snode %p level %d children %d lines %d chars %d\n",
6973 spaces, node, node->level,
6974 node->num_children, node->num_lines, node->num_chars);
6979 printf ("%s %d toggles of `%s' below this node\n",
6980 spaces, s->toggle_count, s->info->tag->name);
6986 if (node->level > 0)
6988 iter = node->children.node;
6989 while (iter != NULL)
6991 _gtk_text_btree_spew_node (iter, indent + 2);
6998 GtkTextLine *line = node->children.line;
6999 while (line != NULL)
7001 _gtk_text_btree_spew_line_short (line, indent + 2);
7009 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7011 GtkTextLineSegment * seg;
7013 printf ("%4d| line: %p parent: %p next: %p\n",
7014 _gtk_text_line_get_number (line), line, line->parent, line->next);
7016 seg = line->segments;
7020 _gtk_text_btree_spew_segment (tree, seg);
7026 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7028 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7029 seg, seg->type->name, seg->byte_count, seg->char_count);
7031 if (seg->type == >k_text_char_type)
7033 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7034 printf (" `%s'\n", str);
7037 else if (seg->type == >k_text_right_mark_type)
7039 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7040 seg->body.mark.name,
7041 seg->body.mark.visible,
7042 seg->body.mark.not_deleteable);
7044 else if (seg->type == >k_text_left_mark_type)
7046 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7047 seg->body.mark.name,
7048 seg->body.mark.visible,
7049 seg->body.mark.not_deleteable);
7051 else if (seg->type == >k_text_toggle_on_type ||
7052 seg->type == >k_text_toggle_off_type)
7054 printf (" tag `%s' priority %d\n",
7055 seg->body.toggle.info->tag->name,
7056 seg->body.toggle.info->tag->priority);