4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
60 #include "gtktexttag.h"
61 #include "gtktexttagtable.h"
62 #include "gtktextlayout.h"
63 #include "gtktextiterprivate.h"
65 #include "gtktextmarkprivate.h"
73 * The structure below is used to pass information between
74 * _gtk_text_btree_get_tags and inc_count:
77 typedef struct TagInfo {
78 int numTags; /* Number of tags for which there
79 * is currently information in
81 int arraySize; /* Number of entries allocated for
83 GtkTextTag **tags; /* Array of tags seen so far.
85 int *counts; /* Toggle count (so far) for each
86 * entry in tags. Malloc-ed. */
91 * This is used to store per-view width/height info at the tree nodes.
94 typedef struct _NodeData NodeData;
100 /* Height and width of this node */
102 signed int width : 24;
104 /* boolean indicating whether the lines below this node are in need of validation.
105 * However, width/height should always represent the current total width and
106 * max height for lines below this node; the valid flag indicates whether the
107 * width/height on the lines needs recomputing, not whether the totals
110 guint valid : 8; /* Actually a boolean */
115 * The data structure below keeps summary information about one tag as part
116 * of the tag information in a node.
119 typedef struct Summary {
120 GtkTextTagInfo *info; /* Handle for tag. */
121 int toggle_count; /* Number of transitions into or
122 * out of this tag that occur in
123 * the subtree rooted at this node. */
124 struct Summary *next; /* Next in list of all tags for same
125 * node, or NULL if at end of list. */
129 * The data structure below defines a node in the B-tree.
132 struct _GtkTextBTreeNode {
133 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
134 * this is the root. */
135 GtkTextBTreeNode *next; /* Next in list of siblings with the
136 * same parent node, or NULL for end
138 Summary *summary; /* First in malloc-ed list of info
139 * about tags in this subtree (NULL if
140 * no tag info in the subtree). */
141 int level; /* Level of this node in the B-tree.
142 * 0 refers to the bottom of the tree
143 * (children are lines, not nodes). */
144 union { /* First in linked list of children. */
145 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
146 GtkTextLine *line; /* Used if level == 0. */
148 int num_children; /* Number of children of this node. */
149 int num_lines; /* Total number of lines (leaves) in
150 * the subtree rooted here. */
151 int num_chars; /* Number of chars below here */
158 * Used to store the list of views in our btree
161 typedef struct _BTreeView BTreeView;
165 GtkTextLayout *layout;
171 * And the tree itself
174 struct _GtkTextBTree {
175 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
176 GtkTextTagTable *table;
177 GHashTable *mark_table;
179 GtkTextMark *insert_mark;
180 GtkTextMark *selection_bound_mark;
181 GtkTextBuffer *buffer;
184 gulong tag_changed_handler;
186 /* Incremented when a segment with a byte size > 0
187 * is added to or removed from the tree (i.e. the
188 * length of a line may have changed, and lines may
189 * have been added or removed). This invalidates
190 * all outstanding iterators.
192 guint chars_changed_stamp;
193 /* Incremented when any segments are added or deleted;
194 * this makes outstanding iterators recalculate their
195 * pointed-to segment and segment offset.
197 guint segments_changed_stamp;
199 /* Cache the last line in the buffer */
200 GtkTextLine *last_line;
201 guint last_line_stamp;
203 /* Cache the next-to-last line in the buffer,
204 * containing the end iterator
206 GtkTextLine *end_iter_line;
207 GtkTextLineSegment *end_iter_segment;
208 int end_iter_segment_byte_index;
209 int end_iter_segment_char_offset;
210 guint end_iter_line_stamp;
211 guint end_iter_segment_stamp;
213 GHashTable *child_anchor_table;
218 * Upper and lower bounds on how many children a node may have:
219 * rebalance when either of these limits is exceeded. MAX_CHILDREN
220 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
223 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
224 shallow. It appears to be faster to locate a particular line number
225 if the tree is narrow and deep, since it is more finely sorted. I
226 guess this may increase memory use though, and make it slower to
227 walk the tree in order, or locate a particular byte index (which
228 is done by walking the tree in order).
230 There's basically a tradeoff here. However I'm thinking we want to
231 add pixels, byte counts, and char counts to the tree nodes,
232 at that point narrow and deep should speed up all operations,
233 not just the line number searches.
237 #define MAX_CHILDREN 12
238 #define MIN_CHILDREN 6
240 #define MAX_CHILDREN 6
241 #define MIN_CHILDREN 3
248 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
250 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
251 GtkTextBTreeNode *node);
252 static GtkTextLine * get_last_line (GtkTextBTree *tree);
253 static void post_insert_fixup (GtkTextBTree *tree,
254 GtkTextLine *insert_line,
255 gint char_count_delta,
256 gint line_count_delta);
257 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
258 GtkTextTagInfo *info,
260 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
263 static void segments_changed (GtkTextBTree *tree);
264 static void chars_changed (GtkTextBTree *tree);
265 static void summary_list_destroy (Summary *summary);
266 static GtkTextLine *gtk_text_line_new (void);
267 static void gtk_text_line_destroy (GtkTextBTree *tree,
269 static void gtk_text_line_set_parent (GtkTextLine *line,
270 GtkTextBTreeNode *node);
271 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
275 static NodeData *node_data_new (gpointer view_id);
276 static void node_data_destroy (NodeData *nd);
277 static void node_data_list_destroy (NodeData *nd);
278 static NodeData *node_data_find (NodeData *nd,
281 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
282 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
283 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
285 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
289 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
292 static void gtk_text_btree_node_remove_view (BTreeView *view,
293 GtkTextBTreeNode *node,
295 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
296 GtkTextBTreeNode *node);
297 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
301 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
307 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
308 GtkTextBTreeNode *node2);
309 static void get_tree_bounds (GtkTextBTree *tree,
312 static void tag_changed_cb (GtkTextTagTable *table,
314 gboolean size_changed,
316 static void cleanup_line (GtkTextLine *line);
317 static void recompute_node_counts (GtkTextBTree *tree,
318 GtkTextBTreeNode *node);
319 static void inc_count (GtkTextTag *tag,
321 TagInfo *tagInfoPtr);
323 static void summary_destroy (Summary *summary);
325 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
326 const GtkTextIter *iter);
327 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
328 GtkTextLineSegment *seg,
332 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
334 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
336 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
339 static void redisplay_region (GtkTextBTree *tree,
340 const GtkTextIter *start,
341 const GtkTextIter *end);
343 /* Inline thingies */
346 segments_changed (GtkTextBTree *tree)
348 tree->segments_changed_stamp += 1;
352 chars_changed (GtkTextBTree *tree)
354 tree->chars_changed_stamp += 1;
362 _gtk_text_btree_new (GtkTextTagTable *table,
363 GtkTextBuffer *buffer)
366 GtkTextBTreeNode *root_node;
367 GtkTextLine *line, *line2;
369 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
370 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
373 * The tree will initially have two empty lines. The second line
374 * isn't actually part of the tree's contents, but its presence
375 * makes several operations easier. The tree will have one GtkTextBTreeNode,
376 * which is also the root of the tree.
379 /* Create the root node. */
381 root_node = gtk_text_btree_node_new ();
383 line = gtk_text_line_new ();
384 line2 = gtk_text_line_new ();
386 root_node->parent = NULL;
387 root_node->next = NULL;
388 root_node->summary = NULL;
389 root_node->level = 0;
390 root_node->children.line = line;
391 root_node->num_children = 2;
392 root_node->num_lines = 2;
393 root_node->num_chars = 2;
395 line->parent = root_node;
398 line->segments = _gtk_char_segment_new ("\n", 1);
400 line2->parent = root_node;
402 line2->segments = _gtk_char_segment_new ("\n", 1);
404 /* Create the tree itself */
406 tree = g_new0(GtkTextBTree, 1);
407 tree->root_node = root_node;
411 /* Set these to values that are unlikely to be found
412 * in random memory garbage, and also avoid
413 * duplicates between tree instances.
415 tree->chars_changed_stamp = g_random_int ();
416 tree->segments_changed_stamp = g_random_int ();
418 tree->last_line_stamp = tree->chars_changed_stamp - 1;
419 tree->last_line = NULL;
421 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
422 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
423 tree->end_iter_line = NULL;
424 tree->end_iter_segment_byte_index = 0;
425 tree->end_iter_segment_char_offset = 0;
427 g_object_ref (tree->table);
429 tree->tag_changed_handler = g_signal_connect (tree->table,
431 G_CALLBACK (tag_changed_cb),
434 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
435 tree->child_anchor_table = NULL;
437 /* We don't ref the buffer, since the buffer owns us;
438 * we'd have some circularity issues. The buffer always
439 * lasts longer than the BTree
441 tree->buffer = buffer;
445 GtkTextLineSegment *seg;
447 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
450 tree->insert_mark = _gtk_text_btree_set_mark (tree,
457 seg = tree->insert_mark->segment;
459 seg->body.mark.not_deleteable = TRUE;
460 seg->body.mark.visible = TRUE;
462 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
469 seg = tree->selection_bound_mark->segment;
471 seg->body.mark.not_deleteable = TRUE;
473 g_object_ref (tree->insert_mark);
474 g_object_ref (tree->selection_bound_mark);
483 _gtk_text_btree_ref (GtkTextBTree *tree)
485 g_return_if_fail (tree != NULL);
486 g_return_if_fail (tree->refcount > 0);
492 _gtk_text_btree_unref (GtkTextBTree *tree)
494 g_return_if_fail (tree != NULL);
495 g_return_if_fail (tree->refcount > 0);
499 if (tree->refcount == 0)
501 g_signal_handler_disconnect (tree->table,
502 tree->tag_changed_handler);
504 g_object_unref (tree->table);
507 gtk_text_btree_node_destroy (tree, tree->root_node);
508 tree->root_node = NULL;
510 g_assert (g_hash_table_size (tree->mark_table) == 0);
511 g_hash_table_destroy (tree->mark_table);
512 tree->mark_table = NULL;
513 if (tree->child_anchor_table != NULL)
515 g_hash_table_destroy (tree->child_anchor_table);
516 tree->child_anchor_table = NULL;
519 g_object_unref (tree->insert_mark);
520 tree->insert_mark = NULL;
521 g_object_unref (tree->selection_bound_mark);
522 tree->selection_bound_mark = NULL;
529 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
535 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
537 return tree->chars_changed_stamp;
541 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
543 return tree->segments_changed_stamp;
547 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
549 g_return_if_fail (tree != NULL);
550 segments_changed (tree);
554 * Indexable segment mutation
558 _gtk_text_btree_delete (GtkTextIter *start,
561 GtkTextLineSegment *prev_seg; /* The segment just before the start
562 * of the deletion range. */
563 GtkTextLineSegment *last_seg; /* The segment just after the end
564 * of the deletion range. */
565 GtkTextLineSegment *seg, *next;
566 GtkTextLine *curline;
567 GtkTextBTreeNode *curnode, *node;
569 GtkTextLine *start_line;
570 GtkTextLine *end_line;
571 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
572 gint start_byte_offset;
574 g_return_if_fail (start != NULL);
575 g_return_if_fail (end != NULL);
576 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
577 _gtk_text_iter_get_btree (end));
579 gtk_text_iter_order (start, end);
581 tree = _gtk_text_iter_get_btree (start);
583 if (gtk_debug_flags & GTK_DEBUG_TEXT)
584 _gtk_text_btree_check (tree);
586 /* Broadcast the need for redisplay before we break the iterators */
587 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
588 _gtk_text_btree_invalidate_region (tree, start, end);
590 /* Save the byte offset so we can reset the iterators */
591 start_byte_offset = gtk_text_iter_get_line_index (start);
593 start_line = _gtk_text_iter_get_text_line (start);
594 end_line = _gtk_text_iter_get_text_line (end);
597 * Split the start and end segments, so we have a place
598 * to insert our new text.
600 * Tricky point: split at end first; otherwise the split
601 * at end may invalidate seg and/or prev_seg. This allows
602 * us to avoid invalidating segments for start.
605 last_seg = gtk_text_line_segment_split (end);
606 if (last_seg != NULL)
607 last_seg = last_seg->next;
609 last_seg = end_line->segments;
611 prev_seg = gtk_text_line_segment_split (start);
612 if (prev_seg != NULL)
614 seg = prev_seg->next;
615 prev_seg->next = last_seg;
619 seg = start_line->segments;
620 start_line->segments = last_seg;
623 /* notify iterators that their segments need recomputation,
624 just for robustness. */
625 segments_changed (tree);
628 * Delete all of the segments between prev_seg and last_seg.
631 curline = start_line;
632 curnode = curline->parent;
633 while (seg != last_seg)
639 GtkTextLine *nextline;
642 * We just ran off the end of a line. First find the
643 * next line, then go back to the old line and delete it
644 * (unless it's the starting line for the range).
647 nextline = _gtk_text_line_next (curline);
648 if (curline != start_line)
650 if (curnode == start_line->parent)
651 start_line->next = curline->next;
653 curnode->children.line = curline->next;
655 for (node = curnode; node != NULL;
658 /* Don't update node->num_chars, because
659 * that was done when we deleted the segments.
661 node->num_lines -= 1;
664 curnode->num_children -= 1;
665 curline->next = deleted_lines;
666 deleted_lines = curline;
670 seg = curline->segments;
673 * If the GtkTextBTreeNode is empty then delete it and its parents,
674 * recursively upwards until a non-empty GtkTextBTreeNode is found.
677 while (curnode->num_children == 0)
679 GtkTextBTreeNode *parent;
681 parent = curnode->parent;
682 if (parent->children.node == curnode)
684 parent->children.node = curnode->next;
688 GtkTextBTreeNode *prevnode = parent->children.node;
689 while (prevnode->next != curnode)
691 prevnode = prevnode->next;
693 prevnode->next = curnode->next;
695 parent->num_children--;
696 gtk_text_btree_node_free_empty (tree, curnode);
699 curnode = curline->parent;
704 char_count = seg->char_count;
706 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
709 * This segment refuses to die. Move it to prev_seg and
710 * advance prev_seg if the segment has left gravity.
713 if (prev_seg == NULL)
715 seg->next = start_line->segments;
716 start_line->segments = seg;
720 seg->next = prev_seg->next;
721 prev_seg->next = seg;
723 if (seg->type->leftGravity)
730 /* Segment is gone. Decrement the char count of the node and
732 for (node = curnode; node != NULL;
735 node->num_chars -= char_count;
743 * If the beginning and end of the deletion range are in different
744 * lines, join the two lines together and discard the ending line.
747 if (start_line != end_line)
750 GtkTextBTreeNode *ancestor_node;
751 GtkTextLine *prevline;
754 /* last_seg was appended to start_line up at the top of this function */
756 for (seg = last_seg; seg != NULL;
759 chars_moved += seg->char_count;
760 if (seg->type->lineChangeFunc != NULL)
762 (*seg->type->lineChangeFunc)(seg, end_line);
766 for (node = start_line->parent; node != NULL;
769 node->num_chars += chars_moved;
772 curnode = end_line->parent;
773 for (node = curnode; node != NULL;
776 node->num_chars -= chars_moved;
779 curnode->num_children--;
780 prevline = curnode->children.line;
781 if (prevline == end_line)
783 curnode->children.line = end_line->next;
787 while (prevline->next != end_line)
789 prevline = prevline->next;
791 prevline->next = end_line->next;
793 end_line->next = deleted_lines;
794 deleted_lines = end_line;
796 /* We now fix up the per-view aggregates. We add all the height and
797 * width for the deleted lines to the start line, so that when revalidation
798 * occurs, the correct change in size is seen.
800 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
807 gint deleted_width = 0;
808 gint deleted_height = 0;
810 line = deleted_lines;
813 GtkTextLine *next_line = line->next;
814 ld = _gtk_text_line_get_data (line, view->view_id);
818 deleted_width = MAX (deleted_width, ld->width);
819 deleted_height += ld->height;
823 gtk_text_line_destroy (tree, line);
828 if (deleted_width > 0 || deleted_height > 0)
830 ld = _gtk_text_line_get_data (start_line, view->view_id);
834 /* This means that start_line has never been validated.
835 * We don't really want to do the validation here but
836 * we do need to store our temporary sizes. So we
837 * create the line data and assume a line w/h of 0.
839 ld = _gtk_text_line_data_new (view->layout, start_line);
840 _gtk_text_line_add_data (start_line, ld);
846 ld->width = MAX (deleted_width, ld->width);
847 ld->height += deleted_height;
851 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
852 if (ancestor_node->parent)
853 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
858 /* avoid dangling pointer */
859 deleted_lines = NULL;
861 gtk_text_btree_rebalance (tree, curnode);
865 * Cleanup the segments in the new line.
868 cleanup_line (start_line);
871 * Lastly, rebalance the first GtkTextBTreeNode of the range.
874 gtk_text_btree_rebalance (tree, start_line->parent);
876 /* Notify outstanding iterators that they
878 chars_changed (tree);
879 segments_changed (tree);
881 if (gtk_debug_flags & GTK_DEBUG_TEXT)
882 _gtk_text_btree_check (tree);
884 /* Re-initialize our iterators */
885 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
890 _gtk_text_btree_insert (GtkTextIter *iter,
894 GtkTextLineSegment *prev_seg; /* The segment just before the first
895 * new segment (NULL means new segment
896 * is at beginning of line). */
897 GtkTextLineSegment *cur_seg; /* Current segment; new characters
898 * are inserted just after this one.
899 * NULL means insert at beginning of
901 GtkTextLine *line; /* Current line (new segments are
902 * added to this line). */
903 GtkTextLineSegment *seg;
904 GtkTextLine *newline;
905 int chunk_len; /* # characters in current chunk. */
906 gint sol; /* start of line */
907 gint eol; /* Pointer to character just after last
908 * one in current chunk.
910 gint delim; /* index of paragraph delimiter */
911 int line_count_delta; /* Counts change to total number of
915 int char_count_delta; /* change to number of chars */
917 gint start_byte_index;
918 GtkTextLine *start_line;
920 g_return_if_fail (text != NULL);
921 g_return_if_fail (iter != NULL);
926 /* extract iterator info */
927 tree = _gtk_text_iter_get_btree (iter);
928 line = _gtk_text_iter_get_text_line (iter);
931 start_byte_index = gtk_text_iter_get_line_index (iter);
933 /* Get our insertion segment split. Note this assumes line allows
934 * char insertions, which isn't true of the "last" line. But iter
935 * should not be on that line, as we assert here.
937 g_assert (!_gtk_text_line_is_last (line, tree));
938 prev_seg = gtk_text_line_segment_split (iter);
941 /* Invalidate all iterators */
942 chars_changed (tree);
943 segments_changed (tree);
946 * Chop the text up into lines and create a new segment for
947 * each line, plus a new line for the leftovers from the
953 line_count_delta = 0;
954 char_count_delta = 0;
959 pango_find_paragraph_boundary (text + sol,
964 /* make these relative to the start of the text */
968 g_assert (eol >= sol);
969 g_assert (delim >= sol);
970 g_assert (eol >= delim);
972 g_assert (eol <= len);
974 chunk_len = eol - sol;
976 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
977 seg = _gtk_char_segment_new (&text[sol], chunk_len);
979 char_count_delta += seg->char_count;
983 seg->next = line->segments;
984 line->segments = seg;
988 seg->next = cur_seg->next;
994 /* chunk didn't end with a paragraph separator */
995 g_assert (eol == len);
1000 * The chunk ended with a newline, so create a new GtkTextLine
1001 * and move the remainder of the old line to it.
1004 newline = gtk_text_line_new ();
1005 gtk_text_line_set_parent (newline, line->parent);
1006 newline->next = line->next;
1007 line->next = newline;
1008 newline->segments = seg->next;
1016 * Cleanup the starting line for the insertion, plus the ending
1017 * line if it's different.
1020 cleanup_line (start_line);
1021 if (line != start_line)
1023 cleanup_line (line);
1026 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1028 /* Invalidate our region, and reset the iterator the user
1029 passed in to point to the end of the inserted text. */
1035 _gtk_text_btree_get_iter_at_line (tree,
1041 /* We could almost certainly be more efficient here
1042 by saving the information from the insertion loop
1044 gtk_text_iter_forward_chars (&end, char_count_delta);
1046 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1047 _gtk_text_btree_invalidate_region (tree,
1051 /* Convenience for the user */
1057 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1058 GtkTextLineSegment *seg)
1062 GtkTextLineSegment *prevPtr;
1065 gint start_byte_offset;
1067 line = _gtk_text_iter_get_text_line (iter);
1068 tree = _gtk_text_iter_get_btree (iter);
1069 start_byte_offset = gtk_text_iter_get_line_index (iter);
1071 prevPtr = gtk_text_line_segment_split (iter);
1072 if (prevPtr == NULL)
1074 seg->next = line->segments;
1075 line->segments = seg;
1079 seg->next = prevPtr->next;
1080 prevPtr->next = seg;
1083 post_insert_fixup (tree, line, 0, seg->char_count);
1085 chars_changed (tree);
1086 segments_changed (tree);
1088 /* reset *iter for the user, and invalidate tree nodes */
1090 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1093 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1095 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1096 _gtk_text_btree_invalidate_region (tree, &start, iter);
1100 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1103 GtkTextLineSegment *seg;
1105 seg = _gtk_pixbuf_segment_new (pixbuf);
1107 insert_pixbuf_or_widget_segment (iter, seg);
1111 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1112 GtkTextChildAnchor *anchor)
1114 GtkTextLineSegment *seg;
1117 if (anchor->segment != NULL)
1119 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1123 seg = _gtk_widget_segment_new (anchor);
1125 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1126 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1128 insert_pixbuf_or_widget_segment (iter, seg);
1130 if (tree->child_anchor_table == NULL)
1131 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1133 g_hash_table_insert (tree->child_anchor_table,
1134 seg->body.child.obj,
1135 seg->body.child.obj);
1139 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1141 GtkTextLineSegment *seg;
1143 seg = anchor->segment;
1145 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1154 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1155 GtkTextBTreeNode *node, gint y, gint *line_top,
1156 GtkTextLine *last_line)
1160 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1161 _gtk_text_btree_check (tree);
1163 if (node->level == 0)
1167 line = node->children.line;
1169 while (line != NULL && line != last_line)
1171 GtkTextLineData *ld;
1173 ld = _gtk_text_line_get_data (line, view->view_id);
1177 if (y < (current_y + (ld ? ld->height : 0)))
1180 current_y += ld->height;
1181 *line_top += ld->height;
1190 GtkTextBTreeNode *child;
1192 child = node->children.node;
1194 while (child != NULL)
1199 gtk_text_btree_node_get_size (child, view->view_id,
1202 if (y < (current_y + height))
1203 return find_line_by_y (tree, view, child,
1204 y - current_y, line_top,
1207 current_y += height;
1208 *line_top += height;
1210 child = child->next;
1218 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1225 GtkTextLine *last_line;
1228 view = gtk_text_btree_get_view (tree, view_id);
1229 g_return_val_if_fail (view != NULL, NULL);
1231 last_line = get_last_line (tree);
1233 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1237 *line_top_out = line_top;
1243 find_line_top_in_line_list (GtkTextBTree *tree,
1246 GtkTextLine *target_line,
1249 while (line != NULL)
1251 GtkTextLineData *ld;
1253 if (line == target_line)
1256 ld = _gtk_text_line_get_data (line, view->view_id);
1263 g_assert_not_reached (); /* If we get here, our
1264 target line didn't exist
1265 under its parent node */
1270 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1271 GtkTextLine *target_line,
1278 GtkTextBTreeNode *node;
1280 view = gtk_text_btree_get_view (tree, view_id);
1282 g_return_val_if_fail (view != NULL, 0);
1285 node = target_line->parent;
1286 while (node != NULL)
1288 nodes = g_slist_prepend (nodes, node);
1289 node = node->parent;
1293 while (iter != NULL)
1297 if (node->level == 0)
1299 g_slist_free (nodes);
1300 return find_line_top_in_line_list (tree, view,
1301 node->children.line,
1306 GtkTextBTreeNode *child;
1307 GtkTextBTreeNode *target_node;
1309 g_assert (iter->next != NULL); /* not at level 0 */
1310 target_node = iter->next->data;
1312 child = node->children.node;
1314 while (child != NULL)
1319 if (child == target_node)
1323 gtk_text_btree_node_get_size (child, view->view_id,
1327 child = child->next;
1329 g_assert (child != NULL); /* should have broken out before we
1333 iter = g_slist_next (iter);
1336 g_assert_not_reached (); /* we return when we find the target line */
1341 _gtk_text_btree_add_view (GtkTextBTree *tree,
1342 GtkTextLayout *layout)
1345 GtkTextLine *last_line;
1346 GtkTextLineData *line_data;
1348 g_return_if_fail (tree != NULL);
1350 view = g_new (BTreeView, 1);
1352 view->view_id = layout;
1353 view->layout = layout;
1355 view->next = tree->views;
1360 g_assert (tree->views->prev == NULL);
1361 tree->views->prev = view;
1366 /* The last line in the buffer has identity values for the per-view
1367 * data so that we can avoid special case checks for it in a large
1370 last_line = get_last_line (tree);
1372 line_data = g_new (GtkTextLineData, 1);
1373 line_data->view_id = layout;
1374 line_data->next = NULL;
1375 line_data->width = 0;
1376 line_data->height = 0;
1377 line_data->valid = TRUE;
1379 _gtk_text_line_add_data (last_line, line_data);
1383 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1387 GtkTextLine *last_line;
1388 GtkTextLineData *line_data;
1390 g_return_if_fail (tree != NULL);
1394 while (view != NULL)
1396 if (view->view_id == view_id)
1402 g_return_if_fail (view != NULL);
1405 view->next->prev = view->prev;
1408 view->prev->next = view->next;
1410 if (view == tree->views)
1411 tree->views = view->next;
1413 /* Remove the line data for the last line which we added ourselves.
1414 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1416 last_line = get_last_line (tree);
1417 line_data = _gtk_text_line_remove_data (last_line, view_id);
1420 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1422 view->layout = (gpointer) 0xdeadbeef;
1423 view->view_id = (gpointer) 0xdeadbeef;
1429 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1430 const GtkTextIter *start,
1431 const GtkTextIter *end)
1437 while (view != NULL)
1439 gtk_text_layout_invalidate (view->layout, start, end);
1446 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1451 g_return_if_fail (tree != NULL);
1452 g_return_if_fail (view_id != NULL);
1454 gtk_text_btree_node_get_size (tree->root_node, view_id,
1469 iter_stack_new (void)
1472 stack = g_new (IterStack, 1);
1473 stack->iters = NULL;
1480 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1483 if (stack->count > stack->alloced)
1485 stack->alloced = stack->count*2;
1486 stack->iters = g_realloc (stack->iters,
1487 stack->alloced*sizeof (GtkTextIter));
1489 stack->iters[stack->count-1] = *iter;
1493 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1495 if (stack->count == 0)
1500 *iter = stack->iters[stack->count];
1506 iter_stack_free (IterStack *stack)
1508 g_free (stack->iters);
1513 iter_stack_invert (IterStack *stack)
1515 if (stack->count > 0)
1518 guint j = stack->count - 1;
1523 tmp = stack->iters[i];
1524 stack->iters[i] = stack->iters[j];
1525 stack->iters[j] = tmp;
1534 queue_tag_redisplay (GtkTextBTree *tree,
1536 const GtkTextIter *start,
1537 const GtkTextIter *end)
1539 if (_gtk_text_tag_affects_size (tag))
1541 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1542 _gtk_text_btree_invalidate_region (tree, start, end);
1544 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1546 /* We only need to queue a redraw, not a relayout */
1547 redisplay_region (tree, start, end);
1550 /* We don't need to do anything if the tag doesn't affect display */
1554 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1555 const GtkTextIter *end_orig,
1559 GtkTextLineSegment *seg, *prev;
1560 GtkTextLine *cleanupline;
1561 gboolean toggled_on;
1562 GtkTextLine *start_line;
1563 GtkTextLine *end_line;
1565 GtkTextIter start, end;
1568 GtkTextTagInfo *info;
1570 g_return_if_fail (start_orig != NULL);
1571 g_return_if_fail (end_orig != NULL);
1572 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1573 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1574 _gtk_text_iter_get_btree (end_orig));
1575 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1578 printf ("%s tag %s from %d to %d\n",
1579 add ? "Adding" : "Removing",
1581 gtk_text_buffer_get_offset (start_orig),
1582 gtk_text_buffer_get_offset (end_orig));
1585 if (gtk_text_iter_equal (start_orig, end_orig))
1588 start = *start_orig;
1591 gtk_text_iter_order (&start, &end);
1593 tree = _gtk_text_iter_get_btree (&start);
1595 queue_tag_redisplay (tree, tag, &start, &end);
1597 info = gtk_text_btree_get_tag_info (tree, tag);
1599 start_line = _gtk_text_iter_get_text_line (&start);
1600 end_line = _gtk_text_iter_get_text_line (&end);
1602 /* Find all tag toggles in the region; we are going to delete them.
1603 We need to find them in advance, because
1604 forward_find_tag_toggle () won't work once we start playing around
1606 stack = iter_stack_new ();
1609 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1610 * which is deliberate - we don't want to delete a toggle at the
1613 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1615 if (gtk_text_iter_compare (&iter, &end) >= 0)
1618 iter_stack_push (stack, &iter);
1621 /* We need to traverse the toggles in order. */
1622 iter_stack_invert (stack);
1625 * See whether the tag is present at the start of the range. If
1626 * the state doesn't already match what we want then add a toggle
1630 toggled_on = gtk_text_iter_has_tag (&start, tag);
1631 if ( (add && !toggled_on) ||
1632 (!add && toggled_on) )
1634 /* This could create a second toggle at the start position;
1635 cleanup_line () will remove it if so. */
1636 seg = _gtk_toggle_segment_new (info, add);
1638 prev = gtk_text_line_segment_split (&start);
1641 seg->next = start_line->segments;
1642 start_line->segments = seg;
1646 seg->next = prev->next;
1650 /* cleanup_line adds the new toggle to the node counts. */
1652 printf ("added toggle at start\n");
1654 /* we should probably call segments_changed, but in theory
1655 any still-cached segments in the iters we are about to
1656 use are still valid, since they're in front
1662 * Scan the range of characters and delete any internal tag
1663 * transitions. Keep track of what the old state was at the end
1664 * of the range, and add a toggle there if it's needed.
1668 cleanupline = start_line;
1669 while (iter_stack_pop (stack, &iter))
1671 GtkTextLineSegment *indexable_seg;
1674 line = _gtk_text_iter_get_text_line (&iter);
1675 seg = _gtk_text_iter_get_any_segment (&iter);
1676 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1678 g_assert (seg != NULL);
1679 g_assert (indexable_seg != NULL);
1680 g_assert (seg != indexable_seg);
1682 prev = line->segments;
1684 /* Find the segment that actually toggles this tag. */
1685 while (seg != indexable_seg)
1687 g_assert (seg != NULL);
1688 g_assert (indexable_seg != NULL);
1689 g_assert (seg != indexable_seg);
1691 if ( (seg->type == >k_text_toggle_on_type ||
1692 seg->type == >k_text_toggle_off_type) &&
1693 (seg->body.toggle.info == info) )
1699 g_assert (seg != NULL);
1700 g_assert (indexable_seg != NULL);
1702 g_assert (seg != indexable_seg); /* If this happens, then
1703 forward_to_tag_toggle was
1705 g_assert (seg->body.toggle.info->tag == tag);
1707 /* If this happens, when previously tagging we didn't merge
1708 overlapping tags. */
1709 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1710 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1712 toggled_on = !toggled_on;
1715 printf ("deleting %s toggle\n",
1716 seg->type == >k_text_toggle_on_type ? "on" : "off");
1718 /* Remove toggle segment from the list. */
1721 line->segments = seg->next;
1725 while (prev->next != seg)
1729 prev->next = seg->next;
1732 /* Inform iterators we've hosed them. This actually reflects a
1733 bit of inefficiency; if you have the same tag toggled on and
1734 off a lot in a single line, we keep having the rescan from
1735 the front of the line. Of course we have to do that to get
1736 "prev" anyway, but here we are doing it an additional
1738 segments_changed (tree);
1740 /* Update node counts */
1741 if (seg->body.toggle.inNodeCounts)
1743 _gtk_change_node_toggle_count (line->parent,
1745 seg->body.toggle.inNodeCounts = FALSE;
1750 /* We only clean up lines when we're done with them, saves some
1751 gratuitous line-segment-traversals */
1753 if (cleanupline != line)
1755 cleanup_line (cleanupline);
1760 iter_stack_free (stack);
1762 /* toggled_on now reflects the toggle state _just before_ the
1763 end iterator. The end iterator could already have a toggle
1764 on or a toggle off. */
1765 if ( (add && !toggled_on) ||
1766 (!add && toggled_on) )
1768 /* This could create a second toggle at the start position;
1769 cleanup_line () will remove it if so. */
1771 seg = _gtk_toggle_segment_new (info, !add);
1773 prev = gtk_text_line_segment_split (&end);
1776 seg->next = end_line->segments;
1777 end_line->segments = seg;
1781 seg->next = prev->next;
1784 /* cleanup_line adds the new toggle to the node counts. */
1785 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1787 printf ("added toggle at end\n");
1792 * Cleanup cleanupline and the last line of the range, if
1793 * these are different.
1796 cleanup_line (cleanupline);
1797 if (cleanupline != end_line)
1799 cleanup_line (end_line);
1802 segments_changed (tree);
1804 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1805 _gtk_text_btree_check (tree);
1814 get_line_internal (GtkTextBTree *tree,
1816 gint *real_line_number,
1817 gboolean include_last)
1819 GtkTextBTreeNode *node;
1824 line_count = _gtk_text_btree_line_count (tree);
1828 if (line_number < 0)
1830 line_number = line_count;
1832 else if (line_number > line_count)
1834 line_number = line_count;
1837 if (real_line_number)
1838 *real_line_number = line_number;
1840 node = tree->root_node;
1841 lines_left = line_number;
1844 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1848 while (node->level != 0)
1850 for (node = node->children.node;
1851 node->num_lines <= lines_left;
1857 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1860 lines_left -= node->num_lines;
1865 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1868 for (line = node->children.line; lines_left > 0;
1874 g_error ("gtk_text_btree_find_line ran out of lines");
1883 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1886 _gtk_text_btree_get_line (tree,
1887 _gtk_text_btree_line_count (tree) - 1,
1892 _gtk_text_btree_get_line (GtkTextBTree *tree,
1894 gint *real_line_number)
1896 return get_line_internal (tree, line_number, real_line_number, TRUE);
1900 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1902 gint *real_line_number)
1904 return get_line_internal (tree, line_number, real_line_number, FALSE);
1908 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1910 gint *line_start_index,
1911 gint *real_char_index)
1913 GtkTextBTreeNode *node;
1915 GtkTextLineSegment *seg;
1920 node = tree->root_node;
1922 /* Clamp to valid indexes (-1 is magic for "highest index"),
1923 * node->num_chars includes the two newlines that aren't really
1926 if (char_index < 0 || char_index >= (node->num_chars - 1))
1928 char_index = node->num_chars - 2;
1931 *real_char_index = char_index;
1934 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1938 chars_left = char_index;
1939 while (node->level != 0)
1941 for (node = node->children.node;
1942 chars_left >= node->num_chars;
1945 chars_left -= node->num_chars;
1947 g_assert (chars_left >= 0);
1951 if (chars_left == 0)
1953 /* Start of a line */
1955 *line_start_index = char_index;
1956 return node->children.line;
1960 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1966 for (line = node->children.line; line != NULL; line = line->next)
1968 seg = line->segments;
1971 if (chars_in_line + seg->char_count > chars_left)
1972 goto found; /* found our line/segment */
1974 chars_in_line += seg->char_count;
1979 chars_left -= chars_in_line;
1987 g_assert (line != NULL); /* hosage, ran out of lines */
1988 g_assert (seg != NULL);
1990 *line_start_index = char_index - chars_left;
1995 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1998 GtkTextBTreeNode *node;
1999 GtkTextLine *siblingline;
2000 GtkTextLineSegment *seg;
2001 int src, dst, index;
2007 #define NUM_TAG_INFOS 10
2009 line = _gtk_text_iter_get_text_line (iter);
2010 tree = _gtk_text_iter_get_btree (iter);
2011 byte_index = gtk_text_iter_get_line_index (iter);
2013 tagInfo.numTags = 0;
2014 tagInfo.arraySize = NUM_TAG_INFOS;
2015 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2016 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2019 * Record tag toggles within the line of indexPtr but preceding
2020 * indexPtr. Note that if this loop segfaults, your
2021 * byte_index probably points past the sum of all
2022 * seg->byte_count */
2024 for (index = 0, seg = line->segments;
2025 (index + seg->byte_count) <= byte_index;
2026 index += seg->byte_count, seg = seg->next)
2028 if ((seg->type == >k_text_toggle_on_type)
2029 || (seg->type == >k_text_toggle_off_type))
2031 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2036 * Record toggles for tags in lines that are predecessors of
2037 * line but under the same level-0 GtkTextBTreeNode.
2040 for (siblingline = line->parent->children.line;
2041 siblingline != line;
2042 siblingline = siblingline->next)
2044 for (seg = siblingline->segments; seg != NULL;
2047 if ((seg->type == >k_text_toggle_on_type)
2048 || (seg->type == >k_text_toggle_off_type))
2050 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2056 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2057 * toggles for all siblings that precede that GtkTextBTreeNode.
2060 for (node = line->parent; node->parent != NULL;
2061 node = node->parent)
2063 GtkTextBTreeNode *siblingPtr;
2066 for (siblingPtr = node->parent->children.node;
2067 siblingPtr != node; siblingPtr = siblingPtr->next)
2069 for (summary = siblingPtr->summary; summary != NULL;
2070 summary = summary->next)
2072 if (summary->toggle_count & 1)
2074 inc_count (summary->info->tag, summary->toggle_count,
2082 * Go through the tag information and squash out all of the tags
2083 * that have even toggle counts (these tags exist before the point
2084 * of interest, but not at the desired character itself).
2087 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2089 if (tagInfo.counts[src] & 1)
2091 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2092 tagInfo.tags[dst] = tagInfo.tags[src];
2098 g_free (tagInfo.counts);
2101 g_free (tagInfo.tags);
2104 return tagInfo.tags;
2108 copy_segment (GString *string,
2109 gboolean include_hidden,
2110 gboolean include_nonchars,
2111 const GtkTextIter *start,
2112 const GtkTextIter *end)
2114 GtkTextLineSegment *end_seg;
2115 GtkTextLineSegment *seg;
2117 if (gtk_text_iter_equal (start, end))
2120 seg = _gtk_text_iter_get_indexable_segment (start);
2121 end_seg = _gtk_text_iter_get_indexable_segment (end);
2123 if (seg->type == >k_text_char_type)
2125 gboolean copy = TRUE;
2126 gint copy_bytes = 0;
2127 gint copy_start = 0;
2129 /* Don't copy if we're invisible; segments are invisible/not
2130 as a whole, no need to check each char */
2131 if (!include_hidden &&
2132 _gtk_text_btree_char_is_invisible (start))
2135 /* printf (" <invisible>\n"); */
2138 copy_start = _gtk_text_iter_get_segment_byte (start);
2142 /* End is in the same segment; need to copy fewer bytes. */
2143 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2145 copy_bytes = end_byte - copy_start;
2148 copy_bytes = seg->byte_count - copy_start;
2150 g_assert (copy_bytes != 0); /* Due to iter equality check at
2151 front of this function. */
2155 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2157 g_string_append_len (string,
2158 seg->body.chars + copy_start,
2162 /* printf (" :%s\n", string->str); */
2164 else if (seg->type == >k_text_pixbuf_type ||
2165 seg->type == >k_text_child_type)
2167 gboolean copy = TRUE;
2169 if (!include_nonchars)
2173 else if (!include_hidden &&
2174 _gtk_text_btree_char_is_invisible (start))
2181 g_string_append_len (string,
2182 gtk_text_unknown_char_utf8,
2190 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2191 const GtkTextIter *end_orig,
2192 gboolean include_hidden,
2193 gboolean include_nonchars)
2195 GtkTextLineSegment *seg;
2196 GtkTextLineSegment *end_seg;
2204 g_return_val_if_fail (start_orig != NULL, NULL);
2205 g_return_val_if_fail (end_orig != NULL, NULL);
2206 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2207 _gtk_text_iter_get_btree (end_orig), NULL);
2209 start = *start_orig;
2212 gtk_text_iter_order (&start, &end);
2214 retval = g_string_new ("");
2216 tree = _gtk_text_iter_get_btree (&start);
2218 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2220 seg = _gtk_text_iter_get_indexable_segment (&iter);
2221 while (seg != end_seg)
2223 copy_segment (retval, include_hidden, include_nonchars,
2226 _gtk_text_iter_forward_indexable_segment (&iter);
2228 seg = _gtk_text_iter_get_indexable_segment (&iter);
2231 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2234 g_string_free (retval, FALSE);
2239 _gtk_text_btree_line_count (GtkTextBTree *tree)
2241 /* Subtract bogus line at the end; we return a count
2243 return tree->root_node->num_lines - 1;
2247 _gtk_text_btree_char_count (GtkTextBTree *tree)
2249 /* Exclude newline in bogus last line and the
2250 * one in the last line that is after the end iterator
2252 return tree->root_node->num_chars - 2;
2255 #define LOTSA_TAGS 1000
2257 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2259 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2261 int deftagCnts[LOTSA_TAGS];
2262 int *tagCnts = deftagCnts;
2263 GtkTextTag *deftags[LOTSA_TAGS];
2264 GtkTextTag **tags = deftags;
2266 GtkTextBTreeNode *node;
2267 GtkTextLine *siblingline;
2268 GtkTextLineSegment *seg;
2275 line = _gtk_text_iter_get_text_line (iter);
2276 tree = _gtk_text_iter_get_btree (iter);
2277 byte_index = gtk_text_iter_get_line_index (iter);
2279 numTags = gtk_text_tag_table_get_size (tree->table);
2281 /* almost always avoid malloc, so stay out of system calls */
2282 if (LOTSA_TAGS < numTags)
2284 tagCnts = g_new (int, numTags);
2285 tags = g_new (GtkTextTag*, numTags);
2288 for (i=0; i<numTags; i++)
2294 * Record tag toggles within the line of indexPtr but preceding
2298 for (index = 0, seg = line->segments;
2299 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2300 index += seg->byte_count, seg = seg->next)
2302 if ((seg->type == >k_text_toggle_on_type)
2303 || (seg->type == >k_text_toggle_off_type))
2305 tag = seg->body.toggle.info->tag;
2306 if (tag->invisible_set && tag->values->invisible)
2308 tags[tag->priority] = tag;
2309 tagCnts[tag->priority]++;
2315 * Record toggles for tags in lines that are predecessors of
2316 * line but under the same level-0 GtkTextBTreeNode.
2319 for (siblingline = line->parent->children.line;
2320 siblingline != line;
2321 siblingline = siblingline->next)
2323 for (seg = siblingline->segments; seg != NULL;
2326 if ((seg->type == >k_text_toggle_on_type)
2327 || (seg->type == >k_text_toggle_off_type))
2329 tag = seg->body.toggle.info->tag;
2330 if (tag->invisible_set && tag->values->invisible)
2332 tags[tag->priority] = tag;
2333 tagCnts[tag->priority]++;
2340 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2341 * for all siblings that precede that GtkTextBTreeNode.
2344 for (node = line->parent; node->parent != NULL;
2345 node = node->parent)
2347 GtkTextBTreeNode *siblingPtr;
2350 for (siblingPtr = node->parent->children.node;
2351 siblingPtr != node; siblingPtr = siblingPtr->next)
2353 for (summary = siblingPtr->summary; summary != NULL;
2354 summary = summary->next)
2356 if (summary->toggle_count & 1)
2358 tag = summary->info->tag;
2359 if (tag->invisible_set && tag->values->invisible)
2361 tags[tag->priority] = tag;
2362 tagCnts[tag->priority] += summary->toggle_count;
2370 * Now traverse from highest priority to lowest,
2371 * take invisible value from first odd count (= on)
2374 for (i = numTags-1; i >=0; i--)
2378 /* FIXME not sure this should be if 0 */
2380 #ifndef ALWAYS_SHOW_SELECTION
2381 /* who would make the selection invisible? */
2382 if ((tag == tkxt->seltag)
2383 && !(tkxt->flags & GOT_FOCUS))
2389 invisible = tags[i]->values->invisible;
2394 if (LOTSA_TAGS < numTags)
2409 redisplay_region (GtkTextBTree *tree,
2410 const GtkTextIter *start,
2411 const GtkTextIter *end)
2414 GtkTextLine *start_line, *end_line;
2416 if (gtk_text_iter_compare (start, end) > 0)
2418 const GtkTextIter *tmp = start;
2423 start_line = _gtk_text_iter_get_text_line (start);
2424 end_line = _gtk_text_iter_get_text_line (end);
2427 while (view != NULL)
2429 gint start_y, end_y;
2430 GtkTextLineData *ld;
2432 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2434 if (end_line == start_line)
2437 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2439 ld = _gtk_text_line_get_data (end_line, view->view_id);
2441 end_y += ld->height;
2443 gtk_text_layout_changed (view->layout, start_y,
2452 redisplay_mark (GtkTextLineSegment *mark)
2457 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2459 mark->body.mark.obj);
2462 gtk_text_iter_forward_char (&end);
2464 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2465 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2470 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2472 if (!mark->body.mark.visible)
2475 redisplay_mark (mark);
2479 ensure_not_off_end (GtkTextBTree *tree,
2480 GtkTextLineSegment *mark,
2483 if (gtk_text_iter_get_line (iter) ==
2484 _gtk_text_btree_line_count (tree))
2485 gtk_text_iter_backward_char (iter);
2488 static GtkTextLineSegment*
2489 real_set_mark (GtkTextBTree *tree,
2490 GtkTextMark *existing_mark,
2492 gboolean left_gravity,
2493 const GtkTextIter *where,
2494 gboolean should_exist,
2495 gboolean redraw_selections)
2497 GtkTextLineSegment *mark;
2500 g_return_val_if_fail (tree != NULL, NULL);
2501 g_return_val_if_fail (where != NULL, NULL);
2502 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2505 mark = existing_mark->segment;
2506 else if (name != NULL)
2507 mark = g_hash_table_lookup (tree->mark_table,
2512 if (should_exist && mark == NULL)
2514 g_warning ("No mark `%s' exists!", name);
2518 /* OK if !should_exist and it does already exist, in that case
2524 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2525 _gtk_text_iter_check (&iter);
2529 if (redraw_selections &&
2530 (mark == tree->insert_mark->segment ||
2531 mark == tree->selection_bound_mark->segment))
2533 GtkTextIter old_pos;
2535 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2536 mark->body.mark.obj);
2537 redisplay_region (tree, &old_pos, where);
2541 * don't let visible marks be after the final newline of the
2545 if (mark->body.mark.visible)
2547 ensure_not_off_end (tree, mark, &iter);
2550 /* Redraw the mark's old location. */
2551 redisplay_mark_if_visible (mark);
2553 /* Unlink mark from its current location.
2554 This could hose our iterator... */
2555 gtk_text_btree_unlink_segment (tree, mark,
2556 mark->body.mark.line);
2557 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2558 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2560 segments_changed (tree); /* make sure the iterator recomputes its
2565 mark = _gtk_mark_segment_new (tree,
2569 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2571 if (mark->body.mark.name)
2572 g_hash_table_insert (tree->mark_table,
2573 mark->body.mark.name,
2577 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2578 _gtk_text_iter_check (&iter);
2580 /* Link mark into new location */
2581 gtk_text_btree_link_segment (mark, &iter);
2583 /* Invalidate some iterators. */
2584 segments_changed (tree);
2587 * update the screen at the mark's new location.
2590 redisplay_mark_if_visible (mark);
2592 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2593 _gtk_text_iter_check (&iter);
2595 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2596 _gtk_text_btree_check (tree);
2603 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2604 GtkTextMark *existing_mark,
2606 gboolean left_gravity,
2607 const GtkTextIter *iter,
2608 gboolean should_exist)
2610 GtkTextLineSegment *seg;
2612 seg = real_set_mark (tree, existing_mark,
2613 name, left_gravity, iter, should_exist,
2616 return seg ? seg->body.mark.obj : NULL;
2620 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2624 GtkTextIter tmp_start, tmp_end;
2626 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2628 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2629 tree->selection_bound_mark);
2631 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2643 gtk_text_iter_order (&tmp_start, &tmp_end);
2656 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2657 const GtkTextIter *iter)
2659 GtkTextIter start, end;
2661 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2662 redisplay_region (tree, &start, &end);
2664 /* Move insert AND selection_bound before we redisplay */
2665 real_set_mark (tree, tree->insert_mark,
2666 "insert", FALSE, iter, TRUE, FALSE);
2667 real_set_mark (tree, tree->selection_bound_mark,
2668 "selection_bound", FALSE, iter, TRUE, FALSE);
2672 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2677 g_return_if_fail (tree != NULL);
2678 g_return_if_fail (name != NULL);
2680 mark = g_hash_table_lookup (tree->mark_table,
2683 _gtk_text_btree_remove_mark (tree, mark);
2687 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2688 GtkTextLineSegment *segment)
2691 if (segment->body.mark.name)
2692 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2694 segment->body.mark.tree = NULL;
2695 segment->body.mark.line = NULL;
2697 /* Remove the ref on the mark, which frees segment as a side effect
2698 * if this is the last reference.
2700 g_object_unref (segment->body.mark.obj);
2704 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2707 GtkTextLineSegment *segment;
2709 g_return_if_fail (mark != NULL);
2710 g_return_if_fail (tree != NULL);
2712 segment = mark->segment;
2714 if (segment->body.mark.not_deleteable)
2716 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2720 /* This calls cleanup_line and segments_changed */
2721 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2723 _gtk_text_btree_release_mark_segment (tree, segment);
2727 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2728 GtkTextMark *segment)
2730 return segment == tree->insert_mark;
2734 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2735 GtkTextMark *segment)
2737 return segment == tree->selection_bound_mark;
2741 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2744 GtkTextLineSegment *seg;
2746 g_return_val_if_fail (tree != NULL, NULL);
2747 g_return_val_if_fail (name != NULL, NULL);
2749 seg = g_hash_table_lookup (tree->mark_table, name);
2751 return seg ? seg->body.mark.obj : NULL;
2755 * gtk_text_mark_set_visible:
2756 * @mark: a #GtkTextMark
2757 * @setting: visibility of mark
2759 * Sets the visibility of @mark; the insertion point is normally
2760 * visible, i.e. you can see it as a vertical bar. Also, the text
2761 * widget uses a visible mark to indicate where a drop will occur when
2762 * dragging-and-dropping text. Most other marks are not visible.
2763 * Marks are not visible by default.
2767 gtk_text_mark_set_visible (GtkTextMark *mark,
2770 GtkTextLineSegment *seg;
2772 g_return_if_fail (mark != NULL);
2774 seg = mark->segment;
2776 if (seg->body.mark.visible == setting)
2780 seg->body.mark.visible = setting;
2782 redisplay_mark (seg);
2787 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2790 GtkTextBTreeNode *node;
2791 GtkTextTagInfo *info;
2793 g_return_val_if_fail (tree != NULL, NULL);
2797 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2802 if (info->tag_root == NULL)
2805 node = info->tag_root;
2807 /* We know the tag root has instances of the given
2810 continue_outer_loop:
2811 g_assert (node != NULL);
2812 while (node->level > 0)
2814 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2815 node = node->children.node;
2816 while (node != NULL)
2818 if (gtk_text_btree_node_has_tag (node, tag))
2819 goto continue_outer_loop;
2823 g_assert (node != NULL);
2826 g_assert (node != NULL); /* The tag summaries said some node had
2829 g_assert (node->level == 0);
2831 return node->children.line;
2835 /* Looking for any tag at all (tag == NULL).
2836 Unfortunately this can't be done in a simple and efficient way
2837 right now; so I'm just going to return the
2838 first line in the btree. FIXME */
2839 return _gtk_text_btree_get_line (tree, 0, NULL);
2844 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2847 GtkTextBTreeNode *node;
2848 GtkTextBTreeNode *last_node;
2850 GtkTextTagInfo *info;
2852 g_return_val_if_fail (tree != NULL, NULL);
2856 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2858 if (info->tag_root == NULL)
2861 node = info->tag_root;
2862 /* We know the tag root has instances of the given
2865 while (node->level > 0)
2867 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2869 node = node->children.node;
2870 while (node != NULL)
2872 if (gtk_text_btree_node_has_tag (node, tag))
2880 g_assert (node != NULL); /* The tag summaries said some node had
2883 g_assert (node->level == 0);
2885 /* Find the last line in this node */
2886 line = node->children.line;
2887 while (line->next != NULL)
2894 /* This search can't be done efficiently at the moment,
2895 at least not without complexity.
2896 So, we just return the last line.
2898 return _gtk_text_btree_get_end_iter_line (tree);
2908 _gtk_text_line_get_number (GtkTextLine *line)
2911 GtkTextBTreeNode *node, *parent, *node2;
2915 * First count how many lines precede this one in its level-0
2919 node = line->parent;
2921 for (line2 = node->children.line; line2 != line;
2922 line2 = line2->next)
2926 g_error ("gtk_text_btree_line_number couldn't find line");
2932 * Now work up through the levels of the tree one at a time,
2933 * counting how many lines are in GtkTextBTreeNodes preceding the current
2937 for (parent = node->parent ; parent != NULL;
2938 node = parent, parent = parent->parent)
2940 for (node2 = parent->children.node; node2 != node;
2941 node2 = node2->next)
2945 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2947 index += node2->num_lines;
2953 static GtkTextLineSegment*
2954 find_toggle_segment_before_char (GtkTextLine *line,
2958 GtkTextLineSegment *seg;
2959 GtkTextLineSegment *toggle_seg;
2964 seg = line->segments;
2965 while ( (index + seg->char_count) <= char_in_line )
2967 if (((seg->type == >k_text_toggle_on_type)
2968 || (seg->type == >k_text_toggle_off_type))
2969 && (seg->body.toggle.info->tag == tag))
2972 index += seg->char_count;
2979 static GtkTextLineSegment*
2980 find_toggle_segment_before_byte (GtkTextLine *line,
2984 GtkTextLineSegment *seg;
2985 GtkTextLineSegment *toggle_seg;
2990 seg = line->segments;
2991 while ( (index + seg->byte_count) <= byte_in_line )
2993 if (((seg->type == >k_text_toggle_on_type)
2994 || (seg->type == >k_text_toggle_off_type))
2995 && (seg->body.toggle.info->tag == tag))
2998 index += seg->byte_count;
3006 find_toggle_outside_current_line (GtkTextLine *line,
3010 GtkTextBTreeNode *node;
3011 GtkTextLine *sibling_line;
3012 GtkTextLineSegment *seg;
3013 GtkTextLineSegment *toggle_seg;
3015 GtkTextTagInfo *info = NULL;
3018 * No toggle in this line. Look for toggles for the tag in lines
3019 * that are predecessors of line but under the same
3020 * level-0 GtkTextBTreeNode.
3023 sibling_line = line->parent->children.line;
3024 while (sibling_line != line)
3026 seg = sibling_line->segments;
3029 if (((seg->type == >k_text_toggle_on_type)
3030 || (seg->type == >k_text_toggle_off_type))
3031 && (seg->body.toggle.info->tag == tag))
3037 sibling_line = sibling_line->next;
3040 if (toggle_seg != NULL)
3041 return (toggle_seg->type == >k_text_toggle_on_type);
3044 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3045 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3046 * siblings that precede that GtkTextBTreeNode.
3049 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3055 node = line->parent;
3056 while (node->parent != NULL)
3058 GtkTextBTreeNode *sibling_node;
3060 sibling_node = node->parent->children.node;
3061 while (sibling_node != node)
3065 summary = sibling_node->summary;
3066 while (summary != NULL)
3068 if (summary->info == info)
3069 toggles += summary->toggle_count;
3071 summary = summary->next;
3074 sibling_node = sibling_node->next;
3077 if (node == info->tag_root)
3080 node = node->parent;
3084 * An odd number of toggles means that the tag is present at the
3088 return (toggles & 1) != 0;
3091 /* FIXME this function is far too slow, for no good reason. */
3093 _gtk_text_line_char_has_tag (GtkTextLine *line,
3098 GtkTextLineSegment *toggle_seg;
3100 g_return_val_if_fail (line != NULL, FALSE);
3103 * Check for toggles for the tag in the line but before
3104 * the char. If there is one, its type indicates whether or
3105 * not the character is tagged.
3108 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3110 if (toggle_seg != NULL)
3111 return (toggle_seg->type == >k_text_toggle_on_type);
3113 return find_toggle_outside_current_line (line, tree, tag);
3117 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3122 GtkTextLineSegment *toggle_seg;
3124 g_return_val_if_fail (line != NULL, FALSE);
3127 * Check for toggles for the tag in the line but before
3128 * the char. If there is one, its type indicates whether or
3129 * not the character is tagged.
3132 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3134 if (toggle_seg != NULL)
3135 return (toggle_seg->type == >k_text_toggle_on_type);
3137 return find_toggle_outside_current_line (line, tree, tag);
3141 _gtk_text_line_is_last (GtkTextLine *line,
3144 return line == get_last_line (tree);
3148 ensure_end_iter_line (GtkTextBTree *tree)
3150 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3155 /* n_lines is without the magic line at the end */
3156 n_lines = _gtk_text_btree_line_count (tree);
3158 g_assert (n_lines >= 1);
3160 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3162 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3167 ensure_end_iter_segment (GtkTextBTree *tree)
3169 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3171 GtkTextLineSegment *seg;
3172 GtkTextLineSegment *last_with_chars;
3174 ensure_end_iter_line (tree);
3176 last_with_chars = NULL;
3178 seg = tree->end_iter_line->segments;
3181 if (seg->char_count > 0)
3182 last_with_chars = seg;
3186 tree->end_iter_segment = last_with_chars;
3188 /* We know the last char in the last line is '\n' */
3189 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3190 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3192 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3194 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3195 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3200 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3203 ensure_end_iter_line (tree);
3205 return line == tree->end_iter_line;
3209 _gtk_text_btree_is_end (GtkTextBTree *tree,
3211 GtkTextLineSegment *seg,
3215 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3217 /* Do this first to avoid walking segments in most cases */
3218 if (!_gtk_text_line_contains_end_iter (line, tree))
3221 ensure_end_iter_segment (tree);
3223 if (seg != tree->end_iter_segment)
3226 if (byte_index >= 0)
3227 return byte_index == tree->end_iter_segment_byte_index;
3229 return char_offset == tree->end_iter_segment_char_offset;
3233 _gtk_text_line_next (GtkTextLine *line)
3235 GtkTextBTreeNode *node;
3237 if (line->next != NULL)
3242 * This was the last line associated with the particular parent
3243 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3244 * then search down from that GtkTextBTreeNode to find the first
3248 node = line->parent;
3249 while (node != NULL && node->next == NULL)
3250 node = node->parent;
3256 while (node->level > 0)
3258 node = node->children.node;
3261 g_assert (node->children.line != line);
3263 return node->children.line;
3268 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3272 next = _gtk_text_line_next (line);
3274 /* If we were on the end iter line, we can't go to
3277 if (next && next->next == NULL && /* these checks are optimization only */
3278 _gtk_text_line_next (next) == NULL)
3285 _gtk_text_line_previous (GtkTextLine *line)
3287 GtkTextBTreeNode *node;
3288 GtkTextBTreeNode *node2;
3292 * Find the line under this GtkTextBTreeNode just before the starting line.
3294 prev = line->parent->children.line; /* First line at leaf */
3295 while (prev != line)
3297 if (prev->next == line)
3303 g_error ("gtk_text_btree_previous_line ran out of lines");
3307 * This was the first line associated with the particular parent
3308 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3309 * then search down from that GtkTextBTreeNode to find its last line.
3311 for (node = line->parent; ; node = node->parent)
3313 if (node == NULL || node->parent == NULL)
3315 else if (node != node->parent->children.node)
3319 for (node2 = node->parent->children.node; ;
3320 node2 = node2->children.node)
3322 while (node2->next != node)
3323 node2 = node2->next;
3325 if (node2->level == 0)
3331 for (prev = node2->children.line ; ; prev = prev->next)
3333 if (prev->next == NULL)
3337 g_assert_not_reached ();
3343 _gtk_text_line_data_new (GtkTextLayout *layout,
3346 GtkTextLineData *line_data;
3348 line_data = g_new (GtkTextLineData, 1);
3350 line_data->view_id = layout;
3351 line_data->next = NULL;
3352 line_data->width = 0;
3353 line_data->height = 0;
3354 line_data->valid = FALSE;
3360 _gtk_text_line_add_data (GtkTextLine *line,
3361 GtkTextLineData *data)
3363 g_return_if_fail (line != NULL);
3364 g_return_if_fail (data != NULL);
3365 g_return_if_fail (data->view_id != NULL);
3369 data->next = line->views;
3379 _gtk_text_line_remove_data (GtkTextLine *line,
3382 GtkTextLineData *prev;
3383 GtkTextLineData *iter;
3385 g_return_val_if_fail (line != NULL, NULL);
3386 g_return_val_if_fail (view_id != NULL, NULL);
3390 while (iter != NULL)
3392 if (iter->view_id == view_id)
3401 prev->next = iter->next;
3403 line->views = iter->next;
3412 _gtk_text_line_get_data (GtkTextLine *line,
3415 GtkTextLineData *iter;
3417 g_return_val_if_fail (line != NULL, NULL);
3418 g_return_val_if_fail (view_id != NULL, NULL);
3421 while (iter != NULL)
3423 if (iter->view_id == view_id)
3432 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3433 GtkTextLineData *ld)
3435 /* For now this is totally unoptimized. FIXME?
3437 We could probably optimize the case where the width removed
3438 is less than the max width for the parent node,
3439 and the case where the height is unchanged when we re-wrap.
3442 g_return_if_fail (ld != NULL);
3445 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3449 _gtk_text_line_char_count (GtkTextLine *line)
3451 GtkTextLineSegment *seg;
3455 seg = line->segments;
3458 size += seg->char_count;
3465 _gtk_text_line_byte_count (GtkTextLine *line)
3467 GtkTextLineSegment *seg;
3471 seg = line->segments;
3474 size += seg->byte_count;
3482 _gtk_text_line_char_index (GtkTextLine *target_line)
3484 GSList *node_stack = NULL;
3485 GtkTextBTreeNode *iter;
3489 /* Push all our parent nodes onto a stack */
3490 iter = target_line->parent;
3492 g_assert (iter != NULL);
3494 while (iter != NULL)
3496 node_stack = g_slist_prepend (node_stack, iter);
3498 iter = iter->parent;
3501 /* Check that we have the root node on top of the stack. */
3502 g_assert (node_stack != NULL &&
3503 node_stack->data != NULL &&
3504 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3506 /* Add up chars in all nodes before the nodes in our stack.
3510 iter = node_stack->data;
3511 while (iter != NULL)
3513 GtkTextBTreeNode *child_iter;
3514 GtkTextBTreeNode *next_node;
3516 next_node = node_stack->next ?
3517 node_stack->next->data : NULL;
3518 node_stack = g_slist_remove (node_stack, node_stack->data);
3520 if (iter->level == 0)
3522 /* stack should be empty when we're on the last node */
3523 g_assert (node_stack == NULL);
3524 break; /* Our children are now lines */
3527 g_assert (next_node != NULL);
3528 g_assert (iter != NULL);
3529 g_assert (next_node->parent == iter);
3531 /* Add up chars before us in the tree */
3532 child_iter = iter->children.node;
3533 while (child_iter != next_node)
3535 g_assert (child_iter != NULL);
3537 num_chars += child_iter->num_chars;
3539 child_iter = child_iter->next;
3545 g_assert (iter != NULL);
3546 g_assert (iter == target_line->parent);
3548 /* Since we don't store char counts in lines, only in segments, we
3549 have to iterate over the lines adding up segment char counts
3550 until we find our line. */
3551 line = iter->children.line;
3552 while (line != target_line)
3554 g_assert (line != NULL);
3556 num_chars += _gtk_text_line_char_count (line);
3561 g_assert (line == target_line);
3567 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3571 GtkTextLineSegment *seg;
3574 g_return_val_if_fail (line != NULL, NULL);
3576 offset = byte_offset;
3577 seg = line->segments;
3579 while (offset >= seg->byte_count)
3581 g_assert (seg != NULL); /* means an invalid byte index */
3582 offset -= seg->byte_count;
3587 *seg_offset = offset;
3593 _gtk_text_line_char_to_segment (GtkTextLine *line,
3597 GtkTextLineSegment *seg;
3600 g_return_val_if_fail (line != NULL, NULL);
3602 offset = char_offset;
3603 seg = line->segments;
3605 while (offset >= seg->char_count)
3607 g_assert (seg != NULL); /* means an invalid char index */
3608 offset -= seg->char_count;
3613 *seg_offset = offset;
3619 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3623 GtkTextLineSegment *seg;
3626 g_return_val_if_fail (line != NULL, NULL);
3628 offset = byte_offset;
3629 seg = line->segments;
3631 while (offset > 0 && offset >= seg->byte_count)
3633 g_assert (seg != NULL); /* means an invalid byte index */
3634 offset -= seg->byte_count;
3639 *seg_offset = offset;
3645 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3649 GtkTextLineSegment *seg;
3652 g_return_val_if_fail (line != NULL, NULL);
3654 offset = char_offset;
3655 seg = line->segments;
3657 while (offset > 0 && offset >= seg->char_count)
3659 g_assert (seg != NULL); /* means an invalid byte index */
3660 offset -= seg->char_count;
3665 *seg_offset = offset;
3671 _gtk_text_line_byte_to_char (GtkTextLine *line,
3675 GtkTextLineSegment *seg;
3677 g_return_val_if_fail (line != NULL, 0);
3678 g_return_val_if_fail (byte_offset >= 0, 0);
3681 seg = line->segments;
3682 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3683 the next segment) */
3685 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3687 byte_offset -= seg->byte_count;
3688 char_offset += seg->char_count;
3693 g_assert (seg != NULL);
3695 /* Now byte_offset is the offset into the current segment,
3696 and char_offset is the start of the current segment.
3697 Optimize the case where no chars use > 1 byte */
3698 if (seg->byte_count == seg->char_count)
3699 return char_offset + byte_offset;
3702 if (seg->type == >k_text_char_type)
3703 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3706 g_assert (seg->char_count == 1);
3707 g_assert (byte_offset == 0);
3715 _gtk_text_line_char_to_byte (GtkTextLine *line,
3718 g_warning ("FIXME not implemented");
3723 /* FIXME sync with char_locate (or figure out a clean
3724 way to merge the two functions) */
3726 _gtk_text_line_byte_locate (GtkTextLine *line,
3728 GtkTextLineSegment **segment,
3729 GtkTextLineSegment **any_segment,
3730 gint *seg_byte_offset,
3731 gint *line_byte_offset)
3733 GtkTextLineSegment *seg;
3734 GtkTextLineSegment *after_prev_indexable;
3735 GtkTextLineSegment *after_last_indexable;
3736 GtkTextLineSegment *last_indexable;
3740 g_return_val_if_fail (line != NULL, FALSE);
3741 g_return_val_if_fail (byte_offset >= 0, FALSE);
3744 *any_segment = NULL;
3747 offset = byte_offset;
3749 last_indexable = NULL;
3750 after_last_indexable = line->segments;
3751 after_prev_indexable = line->segments;
3752 seg = line->segments;
3754 /* The loop ends when we're inside a segment;
3755 last_indexable refers to the last segment
3756 we passed entirely. */
3757 while (seg && offset >= seg->byte_count)
3759 if (seg->char_count > 0)
3761 offset -= seg->byte_count;
3762 bytes_in_line += seg->byte_count;
3763 last_indexable = seg;
3764 after_prev_indexable = after_last_indexable;
3765 after_last_indexable = last_indexable->next;
3773 /* We went off the end of the line */
3775 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3782 if (after_last_indexable != NULL)
3783 *any_segment = after_last_indexable;
3785 *any_segment = *segment;
3788 /* Override any_segment if we're in the middle of a segment. */
3790 *any_segment = *segment;
3792 *seg_byte_offset = offset;
3794 g_assert (*segment != NULL);
3795 g_assert (*any_segment != NULL);
3796 g_assert (*seg_byte_offset < (*segment)->byte_count);
3798 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3803 /* FIXME sync with byte_locate (or figure out a clean
3804 way to merge the two functions) */
3806 _gtk_text_line_char_locate (GtkTextLine *line,
3808 GtkTextLineSegment **segment,
3809 GtkTextLineSegment **any_segment,
3810 gint *seg_char_offset,
3811 gint *line_char_offset)
3813 GtkTextLineSegment *seg;
3814 GtkTextLineSegment *after_prev_indexable;
3815 GtkTextLineSegment *after_last_indexable;
3816 GtkTextLineSegment *last_indexable;
3820 g_return_val_if_fail (line != NULL, FALSE);
3821 g_return_val_if_fail (char_offset >= 0, FALSE);
3824 *any_segment = NULL;
3827 offset = char_offset;
3829 last_indexable = NULL;
3830 after_last_indexable = line->segments;
3831 after_prev_indexable = line->segments;
3832 seg = line->segments;
3834 /* The loop ends when we're inside a segment;
3835 last_indexable refers to the last segment
3836 we passed entirely. */
3837 while (seg && offset >= seg->char_count)
3839 if (seg->char_count > 0)
3841 offset -= seg->char_count;
3842 chars_in_line += seg->char_count;
3843 last_indexable = seg;
3844 after_prev_indexable = after_last_indexable;
3845 after_last_indexable = last_indexable->next;
3853 /* end of the line */
3855 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3862 if (after_last_indexable != NULL)
3863 *any_segment = after_last_indexable;
3865 *any_segment = *segment;
3868 /* Override any_segment if we're in the middle of a segment. */
3870 *any_segment = *segment;
3872 *seg_char_offset = offset;
3874 g_assert (*segment != NULL);
3875 g_assert (*any_segment != NULL);
3876 g_assert (*seg_char_offset < (*segment)->char_count);
3878 *line_char_offset = chars_in_line + *seg_char_offset;
3884 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3886 gint *line_char_offset,
3887 gint *seg_char_offset)
3889 GtkTextLineSegment *seg;
3892 g_return_if_fail (line != NULL);
3893 g_return_if_fail (byte_offset >= 0);
3895 *line_char_offset = 0;
3897 offset = byte_offset;
3898 seg = line->segments;
3900 while (offset >= seg->byte_count)
3902 offset -= seg->byte_count;
3903 *line_char_offset += seg->char_count;
3905 g_assert (seg != NULL); /* means an invalid char offset */
3908 g_assert (seg->char_count > 0); /* indexable. */
3910 /* offset is now the number of bytes into the current segment we
3911 * want to go. Count chars into the current segment.
3914 if (seg->type == >k_text_char_type)
3916 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3918 g_assert (*seg_char_offset < seg->char_count);
3920 *line_char_offset += *seg_char_offset;
3924 g_assert (offset == 0);
3925 *seg_char_offset = 0;
3930 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3932 gint *line_byte_offset,
3933 gint *seg_byte_offset)
3935 GtkTextLineSegment *seg;
3938 g_return_if_fail (line != NULL);
3939 g_return_if_fail (char_offset >= 0);
3941 *line_byte_offset = 0;
3943 offset = char_offset;
3944 seg = line->segments;
3946 while (offset >= seg->char_count)
3948 offset -= seg->char_count;
3949 *line_byte_offset += seg->byte_count;
3951 g_assert (seg != NULL); /* means an invalid char offset */
3954 g_assert (seg->char_count > 0); /* indexable. */
3956 /* offset is now the number of chars into the current segment we
3957 want to go. Count bytes into the current segment. */
3959 if (seg->type == >k_text_char_type)
3961 *seg_byte_offset = 0;
3965 const char * start = seg->body.chars + *seg_byte_offset;
3967 bytes = g_utf8_next_char (start) - start;
3968 *seg_byte_offset += bytes;
3972 g_assert (*seg_byte_offset < seg->byte_count);
3974 *line_byte_offset += *seg_byte_offset;
3978 g_assert (offset == 0);
3979 *seg_byte_offset = 0;
3984 node_compare (GtkTextBTreeNode *lhs,
3985 GtkTextBTreeNode *rhs)
3987 GtkTextBTreeNode *iter;
3988 GtkTextBTreeNode *node;
3989 GtkTextBTreeNode *common_parent;
3990 GtkTextBTreeNode *parent_of_lower;
3991 GtkTextBTreeNode *parent_of_higher;
3992 gboolean lhs_is_lower;
3993 GtkTextBTreeNode *lower;
3994 GtkTextBTreeNode *higher;
3996 /* This function assumes that lhs and rhs are not underneath each
4003 if (lhs->level < rhs->level)
4005 lhs_is_lower = TRUE;
4011 lhs_is_lower = FALSE;
4016 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4017 * of the common parent we used to reach the common parent; the
4018 * ordering of these child nodes in the child list is the ordering
4022 /* Get on the same level (may be on same level already) */
4024 while (node->level < higher->level)
4025 node = node->parent;
4027 g_assert (node->level == higher->level);
4029 g_assert (node != higher); /* Happens if lower is underneath higher */
4031 /* Go up until we have two children with a common parent.
4033 parent_of_lower = node;
4034 parent_of_higher = higher;
4036 while (parent_of_lower->parent != parent_of_higher->parent)
4038 parent_of_lower = parent_of_lower->parent;
4039 parent_of_higher = parent_of_higher->parent;
4042 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4044 common_parent = parent_of_lower->parent;
4046 g_assert (common_parent != NULL);
4048 /* See which is first in the list of common_parent's children */
4049 iter = common_parent->children.node;
4050 while (iter != NULL)
4052 if (iter == parent_of_higher)
4054 /* higher is less than lower */
4057 return 1; /* lhs > rhs */
4061 else if (iter == parent_of_lower)
4063 /* lower is less than higher */
4066 return -1; /* lhs < rhs */
4074 g_assert_not_reached ();
4078 /* remember that tag == NULL means "any tag" */
4080 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4084 GtkTextBTreeNode *node;
4085 GtkTextTagInfo *info;
4086 gboolean below_tag_root;
4088 g_return_val_if_fail (line != NULL, NULL);
4090 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4091 _gtk_text_btree_check (tree);
4095 /* Right now we can only offer linear-search if the user wants
4096 * to know about any tag toggle at all.
4098 return _gtk_text_line_next_excluding_last (line);
4101 /* Our tag summaries only have node precision, not line
4102 * precision. This means that if any line under a node could contain a
4103 * tag, then any of the others could also contain a tag.
4105 * In the future we could have some mechanism to keep track of how
4106 * many toggles we've found under a node so far, since we have a
4107 * count of toggles under the node. But for now I'm going with KISS.
4110 /* return same-node line, if any. */
4114 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4118 if (info->tag_root == NULL)
4121 if (info->tag_root == line->parent)
4122 return NULL; /* we were at the last line under the tag root */
4124 /* We need to go up out of this node, and on to the next one with
4125 toggles for the target tag. If we're below the tag root, we need to
4126 find the next node below the tag root that has tag summaries. If
4127 we're not below the tag root, we need to see if the tag root is
4128 after us in the tree, and if so, return the first line underneath
4131 node = line->parent;
4132 below_tag_root = FALSE;
4133 while (node != NULL)
4135 if (node == info->tag_root)
4137 below_tag_root = TRUE;
4141 node = node->parent;
4146 node = line->parent;
4147 while (node != info->tag_root)
4149 if (node->next == NULL)
4150 node = node->parent;
4155 if (gtk_text_btree_node_has_tag (node, tag))
4165 ordering = node_compare (line->parent, info->tag_root);
4169 /* Tag root is ahead of us, so search there. */
4170 node = info->tag_root;
4175 /* Tag root is after us, so no more lines that
4176 * could contain the tag.
4181 g_assert_not_reached ();
4186 g_assert (node != NULL);
4188 /* We have to find the first sub-node of this node that contains
4192 while (node->level > 0)
4194 g_assert (node != NULL); /* If this fails, it likely means an
4195 incorrect tag summary led us on a
4196 wild goose chase down this branch of
4198 node = node->children.node;
4199 while (node != NULL)
4201 if (gtk_text_btree_node_has_tag (node, tag))
4207 g_assert (node != NULL);
4208 g_assert (node->level == 0);
4210 return node->children.line;
4214 prev_line_under_node (GtkTextBTreeNode *node,
4219 prev = node->children.line;
4225 while (prev->next != line)
4235 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4239 GtkTextBTreeNode *node;
4240 GtkTextBTreeNode *found_node = NULL;
4241 GtkTextTagInfo *info;
4242 gboolean below_tag_root;
4244 GtkTextBTreeNode *line_ancestor;
4245 GtkTextBTreeNode *line_ancestor_parent;
4247 /* See next_could_contain_tag () for more extensive comments
4248 * on what's going on here.
4251 g_return_val_if_fail (line != NULL, NULL);
4253 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4254 _gtk_text_btree_check (tree);
4258 /* Right now we can only offer linear-search if the user wants
4259 * to know about any tag toggle at all.
4261 return _gtk_text_line_previous (line);
4264 /* Return same-node line, if any. */
4265 prev = prev_line_under_node (line->parent, line);
4269 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4273 if (info->tag_root == NULL)
4276 if (info->tag_root == line->parent)
4277 return NULL; /* we were at the first line under the tag root */
4279 /* Are we below the tag root */
4280 node = line->parent;
4281 below_tag_root = FALSE;
4282 while (node != NULL)
4284 if (node == info->tag_root)
4286 below_tag_root = TRUE;
4290 node = node->parent;
4295 /* Look for a previous node under this tag root that has our
4299 /* this assertion holds because line->parent is not the
4300 * tag root, we are below the tag root, and the tag
4303 g_assert (line->parent->parent != NULL);
4305 line_ancestor = line->parent;
4306 line_ancestor_parent = line->parent->parent;
4308 node = line_ancestor_parent->children.node;
4309 while (node != line_ancestor &&
4310 line_ancestor != info->tag_root)
4312 GSList *child_nodes = NULL;
4315 /* Create reverse-order list of nodes before
4318 while (node != line_ancestor
4321 child_nodes = g_slist_prepend (child_nodes, node);
4326 /* Try to find a node with our tag on it in the list */
4330 GtkTextBTreeNode *this_node = tmp->data;
4332 g_assert (this_node != line_ancestor);
4334 if (gtk_text_btree_node_has_tag (this_node, tag))
4336 found_node = this_node;
4337 g_slist_free (child_nodes);
4341 tmp = g_slist_next (tmp);
4344 g_slist_free (child_nodes);
4346 /* Didn't find anything on this level; go up one level. */
4347 line_ancestor = line_ancestor_parent;
4348 line_ancestor_parent = line_ancestor->parent;
4350 node = line_ancestor_parent->children.node;
4360 ordering = node_compare (line->parent, info->tag_root);
4364 /* Tag root is ahead of us, so no more lines
4371 /* Tag root is after us, so grab last tagged
4372 * line underneath the tag root.
4374 found_node = info->tag_root;
4378 g_assert_not_reached ();
4383 g_assert (found_node != NULL);
4385 /* We have to find the last sub-node of this node that contains
4390 while (node->level > 0)
4392 GSList *child_nodes = NULL;
4394 g_assert (node != NULL); /* If this fails, it likely means an
4395 incorrect tag summary led us on a
4396 wild goose chase down this branch of
4399 node = node->children.node;
4400 while (node != NULL)
4402 child_nodes = g_slist_prepend (child_nodes, node);
4406 node = NULL; /* detect failure to find a child node. */
4409 while (iter != NULL)
4411 if (gtk_text_btree_node_has_tag (iter->data, tag))
4413 /* recurse into this node. */
4418 iter = g_slist_next (iter);
4421 g_slist_free (child_nodes);
4423 g_assert (node != NULL);
4426 g_assert (node != NULL);
4427 g_assert (node->level == 0);
4429 /* this assertion is correct, but slow. */
4430 /* g_assert (node_compare (node, line->parent) < 0); */
4432 /* Return last line in this node. */
4434 prev = node->children.line;
4442 * Non-public function implementations
4446 summary_list_destroy (Summary *summary)
4449 while (summary != NULL)
4451 next = summary->next;
4452 summary_destroy (summary);
4458 get_last_line (GtkTextBTree *tree)
4460 if (tree->last_line_stamp != tree->chars_changed_stamp)
4466 n_lines = _gtk_text_btree_line_count (tree);
4468 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4470 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4472 tree->last_line_stamp = tree->chars_changed_stamp;
4473 tree->last_line = line;
4476 return tree->last_line;
4484 gtk_text_line_new (void)
4488 line = g_new0(GtkTextLine, 1);
4494 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4496 GtkTextLineData *ld;
4497 GtkTextLineData *next;
4499 g_return_if_fail (line != NULL);
4506 view = gtk_text_btree_get_view (tree, ld->view_id);
4508 g_assert (view != NULL);
4511 gtk_text_layout_free_line_data (view->layout, line, ld);
4520 gtk_text_line_set_parent (GtkTextLine *line,
4521 GtkTextBTreeNode *node)
4523 if (line->parent == node)
4525 line->parent = node;
4526 gtk_text_btree_node_invalidate_upward (node, NULL);
4530 cleanup_line (GtkTextLine *line)
4532 GtkTextLineSegment *seg, **prev_p;
4536 * Make a pass over all of the segments in the line, giving each
4537 * a chance to clean itself up. This could potentially change
4538 * the structure of the line, e.g. by merging two segments
4539 * together or having two segments cancel themselves; if so,
4540 * then repeat the whole process again, since the first structure
4541 * change might make other structure changes possible. Repeat
4542 * until eventually there are no changes.
4549 for (prev_p = &line->segments, seg = *prev_p;
4551 prev_p = &(*prev_p)->next, seg = *prev_p)
4553 if (seg->type->cleanupFunc != NULL)
4555 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4568 node_data_new (gpointer view_id)
4572 nd = g_new (NodeData, 1);
4574 nd->view_id = view_id;
4584 node_data_destroy (NodeData *nd)
4590 node_data_list_destroy (NodeData *nd)
4596 while (iter != NULL)
4599 node_data_destroy (iter);
4605 node_data_find (NodeData *nd, gpointer view_id)
4609 if (nd->view_id == view_id)
4617 summary_destroy (Summary *summary)
4619 /* Fill with error-triggering garbage */
4620 summary->info = (void*)0x1;
4621 summary->toggle_count = 567;
4622 summary->next = (void*)0x1;
4626 static GtkTextBTreeNode*
4627 gtk_text_btree_node_new (void)
4629 GtkTextBTreeNode *node;
4631 node = g_new (GtkTextBTreeNode, 1);
4633 node->node_data = NULL;
4639 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4640 GtkTextTagInfo *info,
4645 summary = node->summary;
4646 while (summary != NULL)
4648 if (summary->info == info)
4650 summary->toggle_count += adjust;
4654 summary = summary->next;
4657 if (summary == NULL)
4659 /* didn't find a summary for our tag. */
4660 g_return_if_fail (adjust > 0);
4661 summary = g_new (Summary, 1);
4662 summary->info = info;
4663 summary->toggle_count = adjust;
4664 summary->next = node->summary;
4665 node->summary = summary;
4669 /* Note that the tag root and above do not have summaries
4670 for the tag; only nodes below the tag root have
4673 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4677 summary = node->summary;
4678 while (summary != NULL)
4681 summary->info->tag == tag)
4684 summary = summary->next;
4690 /* Add node and all children to the damage region. */
4692 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4696 nd = node->node_data;
4703 if (node->level == 0)
4707 line = node->children.line;
4708 while (line != NULL)
4710 GtkTextLineData *ld;
4724 GtkTextBTreeNode *child;
4726 child = node->children.node;
4728 while (child != NULL)
4730 gtk_text_btree_node_invalidate_downward (child);
4732 child = child->next;
4738 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4740 GtkTextBTreeNode *iter;
4743 while (iter != NULL)
4749 nd = node_data_find (iter->node_data, view_id);
4751 if (nd == NULL || !nd->valid)
4752 break; /* Once a node is invalid, we know its parents are as well. */
4758 gboolean should_continue = FALSE;
4760 nd = iter->node_data;
4765 should_continue = TRUE;
4772 if (!should_continue)
4773 break; /* This node was totally invalidated, so are its
4777 iter = iter->parent;
4783 * _gtk_text_btree_is_valid:
4784 * @tree: a #GtkTextBTree
4785 * @view_id: ID for the view
4787 * Check to see if the entire #GtkTextBTree is valid or not for
4790 * Return value: %TRUE if the entire #GtkTextBTree is valid
4793 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4797 g_return_val_if_fail (tree != NULL, FALSE);
4799 nd = node_data_find (tree->root_node->node_data, view_id);
4800 return (nd && nd->valid);
4803 typedef struct _ValidateState ValidateState;
4805 struct _ValidateState
4807 gint remaining_pixels;
4808 gboolean in_validation;
4815 gtk_text_btree_node_validate (BTreeView *view,
4816 GtkTextBTreeNode *node,
4818 ValidateState *state)
4820 gint node_valid = TRUE;
4821 gint node_width = 0;
4822 gint node_height = 0;
4824 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4825 g_return_if_fail (!nd->valid);
4827 if (node->level == 0)
4829 GtkTextLine *line = node->children.line;
4830 GtkTextLineData *ld;
4832 /* Iterate over leading valid lines */
4833 while (line != NULL)
4835 ld = _gtk_text_line_get_data (line, view_id);
4837 if (!ld || !ld->valid)
4839 else if (state->in_validation)
4841 state->in_validation = FALSE;
4846 state->y += ld->height;
4847 node_width = MAX (ld->width, node_width);
4848 node_height += ld->height;
4854 state->in_validation = TRUE;
4856 /* Iterate over invalid lines */
4857 while (line != NULL)
4859 ld = _gtk_text_line_get_data (line, view_id);
4861 if (ld && ld->valid)
4866 state->old_height += ld->height;
4867 ld = gtk_text_layout_wrap (view->layout, line, ld);
4868 state->new_height += ld->height;
4870 node_width = MAX (ld->width, node_width);
4871 node_height += ld->height;
4873 state->remaining_pixels -= ld->height;
4874 if (state->remaining_pixels <= 0)
4884 /* Iterate over the remaining lines */
4885 while (line != NULL)
4887 ld = _gtk_text_line_get_data (line, view_id);
4888 state->in_validation = FALSE;
4890 if (!ld || !ld->valid)
4895 node_width = MAX (ld->width, node_width);
4896 node_height += ld->height;
4904 GtkTextBTreeNode *child;
4907 child = node->children.node;
4909 /* Iterate over leading valid nodes */
4912 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4914 if (!child_nd->valid)
4916 else if (state->in_validation)
4918 state->in_validation = FALSE;
4923 state->y += child_nd->height;
4924 node_width = MAX (node_width, child_nd->width);
4925 node_height += child_nd->height;
4928 child = child->next;
4931 /* Iterate over invalid nodes */
4934 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4936 if (child_nd->valid)
4940 gtk_text_btree_node_validate (view, child, view_id, state);
4942 if (!child_nd->valid)
4944 node_width = MAX (node_width, child_nd->width);
4945 node_height += child_nd->height;
4947 if (!state->in_validation || state->remaining_pixels <= 0)
4949 child = child->next;
4954 child = child->next;
4957 /* Iterate over the remaining lines */
4960 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4961 state->in_validation = FALSE;
4963 if (!child_nd->valid)
4966 node_width = MAX (child_nd->width, node_width);
4967 node_height += child_nd->height;
4969 child = child->next;
4973 nd->width = node_width;
4974 nd->height = node_height;
4975 nd->valid = node_valid;
4979 * _gtk_text_btree_validate:
4980 * @tree: a #GtkTextBTree
4982 * @max_pixels: the maximum number of pixels to validate. (No more
4983 * than one paragraph beyond this limit will be validated)
4984 * @y: location to store starting y coordinate of validated region
4985 * @old_height: location to store old height of validated region
4986 * @new_height: location to store new height of validated region
4988 * Validate a single contiguous invalid region of a #GtkTextBTree for
4991 * Return value: %TRUE if a region has been validated, %FALSE if the
4992 * entire tree was already valid.
4995 _gtk_text_btree_validate (GtkTextBTree *tree,
5004 g_return_val_if_fail (tree != NULL, FALSE);
5006 view = gtk_text_btree_get_view (tree, view_id);
5007 g_return_val_if_fail (view != NULL, FALSE);
5009 if (!_gtk_text_btree_is_valid (tree, view_id))
5011 ValidateState state;
5013 state.remaining_pixels = max_pixels;
5014 state.in_validation = FALSE;
5016 state.old_height = 0;
5017 state.new_height = 0;
5019 gtk_text_btree_node_validate (view,
5026 *old_height = state.old_height;
5028 *new_height = state.new_height;
5030 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5031 _gtk_text_btree_check (tree);
5040 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5044 gboolean *valid_out)
5048 gboolean valid = TRUE;
5050 if (node->level == 0)
5052 GtkTextLine *line = node->children.line;
5054 while (line != NULL)
5056 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5058 if (!ld || !ld->valid)
5063 width = MAX (ld->width, width);
5064 height += ld->height;
5072 GtkTextBTreeNode *child = node->children.node;
5076 NodeData *child_nd = node_data_find (child->node_data, view_id);
5078 if (!child_nd || !child_nd->valid)
5083 width = MAX (child_nd->width, width);
5084 height += child_nd->height;
5087 child = child->next;
5092 *height_out = height;
5097 /* Recompute the validity and size of the view data for a given
5098 * view at this node from the immediate children of the node
5101 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5104 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5109 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5110 &width, &height, &valid);
5112 nd->height = height;
5119 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5124 gtk_text_btree_node_check_valid (node, view_id);
5125 node = node->parent;
5130 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5133 if (node->level == 0)
5135 return gtk_text_btree_node_check_valid (node, view_id);
5139 GtkTextBTreeNode *child = node->children.node;
5141 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5149 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5151 if (!child_nd->valid)
5153 nd->width = MAX (child_nd->width, nd->width);
5154 nd->height += child_nd->height;
5156 child = child->next;
5165 * _gtk_text_btree_validate_line:
5166 * @tree: a #GtkTextBTree
5167 * @line: line to validate
5168 * @view_id: view ID for the view to validate
5170 * Revalidate a single line of the btree for the given view, propagate
5171 * results up through the entire tree.
5174 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5178 GtkTextLineData *ld;
5181 g_return_if_fail (tree != NULL);
5182 g_return_if_fail (line != NULL);
5184 view = gtk_text_btree_get_view (tree, view_id);
5185 g_return_if_fail (view != NULL);
5187 ld = _gtk_text_line_get_data (line, view_id);
5188 if (!ld || !ld->valid)
5190 ld = gtk_text_layout_wrap (view->layout, line, ld);
5192 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5197 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5199 if (node->level == 0)
5203 line = node->children.line;
5204 while (line != NULL)
5206 GtkTextLineData *ld;
5208 ld = _gtk_text_line_remove_data (line, view_id);
5211 gtk_text_layout_free_line_data (view->layout, line, ld);
5218 GtkTextBTreeNode *child;
5220 child = node->children.node;
5222 while (child != NULL)
5225 gtk_text_btree_node_remove_view (view, child, view_id);
5227 child = child->next;
5231 gtk_text_btree_node_remove_data (node, view_id);
5235 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5237 if (node->level == 0)
5240 GtkTextLineSegment *seg;
5242 while (node->children.line != NULL)
5244 line = node->children.line;
5245 node->children.line = line->next;
5246 while (line->segments != NULL)
5248 seg = line->segments;
5249 line->segments = seg->next;
5251 (*seg->type->deleteFunc) (seg, line, TRUE);
5253 gtk_text_line_destroy (tree, line);
5258 GtkTextBTreeNode *childPtr;
5260 while (node->children.node != NULL)
5262 childPtr = node->children.node;
5263 node->children.node = childPtr->next;
5264 gtk_text_btree_node_destroy (tree, childPtr);
5268 gtk_text_btree_node_free_empty (tree, node);
5272 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5273 GtkTextBTreeNode *node)
5275 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5276 (node->level == 0 && node->children.line == NULL));
5278 summary_list_destroy (node->summary);
5279 node_data_list_destroy (node->node_data);
5284 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5288 nd = node->node_data;
5291 if (nd->view_id == view_id)
5299 nd = node_data_new (view_id);
5301 if (node->node_data)
5302 nd->next = node->node_data;
5304 node->node_data = nd;
5311 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5317 nd = node->node_data;
5320 if (nd->view_id == view_id)
5331 prev->next = nd->next;
5333 if (node->node_data == nd)
5334 node->node_data = nd->next;
5338 node_data_destroy (nd);
5342 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5343 gint *width, gint *height)
5347 g_return_if_fail (width != NULL);
5348 g_return_if_fail (height != NULL);
5350 nd = gtk_text_btree_node_ensure_data (node, view_id);
5355 *height = nd->height;
5358 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5359 * here isn't quite right, since for a lot of operations we want to
5360 * know which children of the common parent correspond to the two nodes
5361 * (e.g., when computing the order of two iters)
5363 static GtkTextBTreeNode *
5364 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5365 GtkTextBTreeNode *node2)
5367 while (node1->level < node2->level)
5368 node1 = node1->parent;
5369 while (node2->level < node1->level)
5370 node2 = node2->parent;
5371 while (node1 != node2)
5373 node1 = node1->parent;
5374 node2 = node2->parent;
5385 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5390 while (view != NULL)
5392 if (view->view_id == view_id)
5401 get_tree_bounds (GtkTextBTree *tree,
5405 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5406 _gtk_text_btree_get_end_iter (tree, end);
5410 tag_changed_cb (GtkTextTagTable *table,
5412 gboolean size_changed,
5417 /* We need to queue a relayout on all regions that are tagged with
5424 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5426 /* Must be a last toggle if there was a first one. */
5427 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5428 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5429 _gtk_text_btree_invalidate_region (tree,
5436 /* We only need to queue a redraw, not a relayout */
5441 while (view != NULL)
5445 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5446 gtk_text_layout_changed (view->layout, 0, height, height);
5454 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5457 /* Remove the tag from the tree */
5462 get_tree_bounds (tree, &start, &end);
5464 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5465 gtk_text_btree_remove_tag_info (tree, tag);
5469 /* Rebalance the out-of-whack node "node" */
5471 gtk_text_btree_rebalance (GtkTextBTree *tree,
5472 GtkTextBTreeNode *node)
5475 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5476 * up through the tree one GtkTextBTreeNode at a time until the root
5477 * GtkTextBTreeNode has been processed.
5480 while (node != NULL)
5482 GtkTextBTreeNode *new_node, *child;
5487 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5488 * then split off all but the first MIN_CHILDREN into a separate
5489 * GtkTextBTreeNode following the original one. Then repeat until the
5490 * GtkTextBTreeNode has a decent size.
5493 if (node->num_children > MAX_CHILDREN)
5498 * If the GtkTextBTreeNode being split is the root
5499 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5503 if (node->parent == NULL)
5505 new_node = gtk_text_btree_node_new ();
5506 new_node->parent = NULL;
5507 new_node->next = NULL;
5508 new_node->summary = NULL;
5509 new_node->level = node->level + 1;
5510 new_node->children.node = node;
5511 recompute_node_counts (tree, new_node);
5512 tree->root_node = new_node;
5514 new_node = gtk_text_btree_node_new ();
5515 new_node->parent = node->parent;
5516 new_node->next = node->next;
5517 node->next = new_node;
5518 new_node->summary = NULL;
5519 new_node->level = node->level;
5520 new_node->num_children = node->num_children - MIN_CHILDREN;
5521 if (node->level == 0)
5523 for (i = MIN_CHILDREN-1,
5524 line = node->children.line;
5525 i > 0; i--, line = line->next)
5527 /* Empty loop body. */
5529 new_node->children.line = line->next;
5534 for (i = MIN_CHILDREN-1,
5535 child = node->children.node;
5536 i > 0; i--, child = child->next)
5538 /* Empty loop body. */
5540 new_node->children.node = child->next;
5543 recompute_node_counts (tree, node);
5544 node->parent->num_children++;
5546 if (node->num_children <= MAX_CHILDREN)
5548 recompute_node_counts (tree, node);
5554 while (node->num_children < MIN_CHILDREN)
5556 GtkTextBTreeNode *other;
5557 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5558 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5559 int total_children, first_children, i;
5562 * Too few children for this GtkTextBTreeNode. If this is the root then,
5563 * it's OK for it to have less than MIN_CHILDREN children
5564 * as long as it's got at least two. If it has only one
5565 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5566 * the tree and use its child as the new root.
5569 if (node->parent == NULL)
5571 if ((node->num_children == 1) && (node->level > 0))
5573 tree->root_node = node->children.node;
5574 tree->root_node->parent = NULL;
5576 node->children.node = NULL;
5577 gtk_text_btree_node_free_empty (tree, node);
5583 * Not the root. Make sure that there are siblings to
5587 if (node->parent->num_children < 2)
5589 gtk_text_btree_rebalance (tree, node->parent);
5594 * Find a sibling neighbor to borrow from, and arrange for
5595 * node to be the earlier of the pair.
5598 if (node->next == NULL)
5600 for (other = node->parent->children.node;
5601 other->next != node;
5602 other = other->next)
5604 /* Empty loop body. */
5611 * We're going to either merge the two siblings together
5612 * into one GtkTextBTreeNode or redivide the children among them to
5613 * balance their loads. As preparation, join their two
5614 * child lists into a single list and remember the half-way
5615 * point in the list.
5618 total_children = node->num_children + other->num_children;
5619 first_children = total_children/2;
5620 if (node->children.node == NULL)
5622 node->children = other->children;
5623 other->children.node = NULL;
5624 other->children.line = NULL;
5626 if (node->level == 0)
5630 for (line = node->children.line, i = 1;
5632 line = line->next, i++)
5634 if (i == first_children)
5639 line->next = other->children.line;
5640 while (i <= first_children)
5649 GtkTextBTreeNode *child;
5651 for (child = node->children.node, i = 1;
5652 child->next != NULL;
5653 child = child->next, i++)
5655 if (i <= first_children)
5657 if (i == first_children)
5659 halfwaynode = child;
5663 child->next = other->children.node;
5664 while (i <= first_children)
5666 halfwaynode = child;
5667 child = child->next;
5673 * If the two siblings can simply be merged together, do it.
5676 if (total_children <= MAX_CHILDREN)
5678 recompute_node_counts (tree, node);
5679 node->next = other->next;
5680 node->parent->num_children--;
5682 other->children.node = NULL;
5683 other->children.line = NULL;
5684 gtk_text_btree_node_free_empty (tree, other);
5689 * The siblings can't be merged, so just divide their
5690 * children evenly between them.
5693 if (node->level == 0)
5695 other->children.line = halfwayline->next;
5696 halfwayline->next = NULL;
5700 other->children.node = halfwaynode->next;
5701 halfwaynode->next = NULL;
5704 recompute_node_counts (tree, node);
5705 recompute_node_counts (tree, other);
5708 node = node->parent;
5713 post_insert_fixup (GtkTextBTree *tree,
5715 gint line_count_delta,
5716 gint char_count_delta)
5719 GtkTextBTreeNode *node;
5722 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5723 * point, then rebalance the tree if necessary.
5726 for (node = line->parent ; node != NULL;
5727 node = node->parent)
5729 node->num_lines += line_count_delta;
5730 node->num_chars += char_count_delta;
5732 node = line->parent;
5733 node->num_children += line_count_delta;
5735 if (node->num_children > MAX_CHILDREN)
5737 gtk_text_btree_rebalance (tree, node);
5740 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5741 _gtk_text_btree_check (tree);
5744 static GtkTextTagInfo*
5745 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5748 GtkTextTagInfo *info;
5752 list = tree->tag_infos;
5753 while (list != NULL)
5756 if (info->tag == tag)
5759 list = g_slist_next (list);
5765 static GtkTextTagInfo*
5766 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5769 GtkTextTagInfo *info;
5771 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5775 /* didn't find it, create. */
5777 info = g_new (GtkTextTagInfo, 1);
5781 info->tag_root = NULL;
5782 info->toggle_count = 0;
5784 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5787 g_print ("Created tag info %p for tag %s(%p)\n",
5788 info, info->tag->name ? info->tag->name : "anon",
5797 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5800 GtkTextTagInfo *info;
5805 list = tree->tag_infos;
5806 while (list != NULL)
5809 if (info->tag == tag)
5812 g_print ("Removing tag info %p for tag %s(%p)\n",
5813 info, info->tag->name ? info->tag->name : "anon",
5819 prev->next = list->next;
5823 tree->tag_infos = list->next;
5826 g_slist_free (list);
5828 g_object_unref (info->tag);
5835 list = g_slist_next (list);
5840 recompute_level_zero_counts (GtkTextBTreeNode *node)
5843 GtkTextLineSegment *seg;
5845 g_assert (node->level == 0);
5847 line = node->children.line;
5848 while (line != NULL)
5850 node->num_children++;
5853 if (line->parent != node)
5854 gtk_text_line_set_parent (line, node);
5856 seg = line->segments;
5860 node->num_chars += seg->char_count;
5862 if (((seg->type != >k_text_toggle_on_type)
5863 && (seg->type != >k_text_toggle_off_type))
5864 || !(seg->body.toggle.inNodeCounts))
5870 GtkTextTagInfo *info;
5872 info = seg->body.toggle.info;
5874 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5885 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5888 GtkTextBTreeNode *child;
5890 g_assert (node->level > 0);
5892 child = node->children.node;
5893 while (child != NULL)
5895 node->num_children += 1;
5896 node->num_lines += child->num_lines;
5897 node->num_chars += child->num_chars;
5899 if (child->parent != node)
5901 child->parent = node;
5902 gtk_text_btree_node_invalidate_upward (node, NULL);
5905 summary = child->summary;
5906 while (summary != NULL)
5908 gtk_text_btree_node_adjust_toggle_count (node,
5910 summary->toggle_count);
5912 summary = summary->next;
5915 child = child->next;
5920 *----------------------------------------------------------------------
5922 * recompute_node_counts --
5924 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5925 * (tags, child information, etc.) by scanning the information in
5926 * its descendants. This procedure is called during rebalancing
5927 * when a GtkTextBTreeNode's child structure has changed.
5933 * The tag counts for node are modified to reflect its current
5934 * child structure, as are its num_children, num_lines, num_chars fields.
5935 * Also, all of the childrens' parent fields are made to point
5938 *----------------------------------------------------------------------
5942 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5945 Summary *summary, *summary2;
5948 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5949 * the existing Summary records (most of them will probably be reused).
5952 summary = node->summary;
5953 while (summary != NULL)
5955 summary->toggle_count = 0;
5956 summary = summary->next;
5959 node->num_children = 0;
5960 node->num_lines = 0;
5961 node->num_chars = 0;
5964 * Scan through the children, adding the childrens' tag counts into
5965 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5969 if (node->level == 0)
5970 recompute_level_zero_counts (node);
5972 recompute_level_nonzero_counts (node);
5977 gtk_text_btree_node_check_valid (node, view->view_id);
5982 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5983 * records that still have a zero count, or that have all the toggles.
5984 * The GtkTextBTreeNode with the children that account for all the tags toggles
5985 * have no summary information, and they become the tag_root for the tag.
5989 for (summary = node->summary; summary != NULL; )
5991 if (summary->toggle_count > 0 &&
5992 summary->toggle_count < summary->info->toggle_count)
5994 if (node->level == summary->info->tag_root->level)
5997 * The tag's root GtkTextBTreeNode split and some toggles left.
5998 * The tag root must move up a level.
6000 summary->info->tag_root = node->parent;
6003 summary = summary->next;
6006 if (summary->toggle_count == summary->info->toggle_count)
6009 * A GtkTextBTreeNode merge has collected all the toggles under
6010 * one GtkTextBTreeNode. Push the root down to this level.
6012 summary->info->tag_root = node;
6014 if (summary2 != NULL)
6016 summary2->next = summary->next;
6017 summary_destroy (summary);
6018 summary = summary2->next;
6022 node->summary = summary->next;
6023 summary_destroy (summary);
6024 summary = node->summary;
6030 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6031 GtkTextTagInfo *info,
6032 gint delta) /* may be negative */
6034 Summary *summary, *prevPtr;
6035 GtkTextBTreeNode *node2Ptr;
6036 int rootLevel; /* Level of original tag root */
6038 info->toggle_count += delta;
6040 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6042 info->tag_root = node;
6047 * Note the level of the existing root for the tag so we can detect
6048 * if it needs to be moved because of the toggle count change.
6051 rootLevel = info->tag_root->level;
6054 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6055 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6059 for ( ; node != info->tag_root; node = node->parent)
6062 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6063 * perhaps all we have to do is adjust its count.
6066 for (prevPtr = NULL, summary = node->summary;
6068 prevPtr = summary, summary = summary->next)
6070 if (summary->info == info)
6075 if (summary != NULL)
6077 summary->toggle_count += delta;
6078 if (summary->toggle_count > 0 &&
6079 summary->toggle_count < info->toggle_count)
6083 if (summary->toggle_count != 0)
6086 * Should never find a GtkTextBTreeNode with max toggle count at this
6087 * point (there shouldn't have been a summary entry in the
6091 g_error ("%s: bad toggle count (%d) max (%d)",
6092 G_STRLOC, summary->toggle_count, info->toggle_count);
6096 * Zero toggle count; must remove this tag from the list.
6099 if (prevPtr == NULL)
6101 node->summary = summary->next;
6105 prevPtr->next = summary->next;
6107 summary_destroy (summary);
6112 * This tag isn't currently in the summary information list.
6115 if (rootLevel == node->level)
6119 * The old tag root is at the same level in the tree as this
6120 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6121 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6122 * as well as the old root (if not, we'll move it up again
6123 * the next time through the loop). To push it up one level
6124 * we copy the original toggle count into the summary
6125 * information at the old root and change the root to its
6126 * parent GtkTextBTreeNode.
6129 GtkTextBTreeNode *rootnode = info->tag_root;
6130 summary = (Summary *) g_malloc (sizeof (Summary));
6131 summary->info = info;
6132 summary->toggle_count = info->toggle_count - delta;
6133 summary->next = rootnode->summary;
6134 rootnode->summary = summary;
6135 rootnode = rootnode->parent;
6136 rootLevel = rootnode->level;
6137 info->tag_root = rootnode;
6139 summary = (Summary *) g_malloc (sizeof (Summary));
6140 summary->info = info;
6141 summary->toggle_count = delta;
6142 summary->next = node->summary;
6143 node->summary = summary;
6148 * If we've decremented the toggle count, then it may be necessary
6149 * to push the tag root down one or more levels.
6156 if (info->toggle_count == 0)
6158 info->tag_root = (GtkTextBTreeNode *) NULL;
6161 node = info->tag_root;
6162 while (node->level > 0)
6165 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6166 * toggles. If so, push the root down one level.
6169 for (node2Ptr = node->children.node;
6170 node2Ptr != (GtkTextBTreeNode *)NULL ;
6171 node2Ptr = node2Ptr->next)
6173 for (prevPtr = NULL, summary = node2Ptr->summary;
6175 prevPtr = summary, summary = summary->next)
6177 if (summary->info == info)
6182 if (summary == NULL)
6186 if (summary->toggle_count != info->toggle_count)
6189 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6196 * This GtkTextBTreeNode has all the toggles, so push down the root.
6199 if (prevPtr == NULL)
6201 node2Ptr->summary = summary->next;
6205 prevPtr->next = summary->next;
6207 summary_destroy (summary);
6208 info->tag_root = node2Ptr;
6211 node = info->tag_root;
6216 *----------------------------------------------------------------------
6220 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6221 * increments the count for a particular tag, adding a new
6222 * entry for that tag if there wasn't one previously.
6228 * The information at *tagInfoPtr may be modified, and the arrays
6229 * may be reallocated to make them larger.
6231 *----------------------------------------------------------------------
6235 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6240 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6241 count > 0; tag_p++, count--)
6245 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6251 * There isn't currently an entry for this tag, so we have to
6252 * make a new one. If the arrays are full, then enlarge the
6256 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6258 GtkTextTag **newTags;
6259 int *newCounts, newSize;
6261 newSize = 2*tagInfoPtr->arraySize;
6262 newTags = (GtkTextTag **) g_malloc ((unsigned)
6263 (newSize*sizeof (GtkTextTag *)));
6264 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6265 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6266 g_free ((char *) tagInfoPtr->tags);
6267 tagInfoPtr->tags = newTags;
6268 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6269 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6270 tagInfoPtr->arraySize *sizeof (int));
6271 g_free ((char *) tagInfoPtr->counts);
6272 tagInfoPtr->counts = newCounts;
6273 tagInfoPtr->arraySize = newSize;
6276 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6277 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6278 tagInfoPtr->numTags++;
6282 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6283 const GtkTextIter *iter)
6285 GtkTextLineSegment *prev;
6289 line = _gtk_text_iter_get_text_line (iter);
6290 tree = _gtk_text_iter_get_btree (iter);
6292 prev = gtk_text_line_segment_split (iter);
6295 seg->next = line->segments;
6296 line->segments = seg;
6300 seg->next = prev->next;
6303 cleanup_line (line);
6304 segments_changed (tree);
6306 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6307 _gtk_text_btree_check (tree);
6311 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6312 GtkTextLineSegment *seg,
6315 GtkTextLineSegment *prev;
6317 if (line->segments == seg)
6319 line->segments = seg->next;
6323 for (prev = line->segments; prev->next != seg;
6326 /* Empty loop body. */
6328 prev->next = seg->next;
6330 cleanup_line (line);
6331 segments_changed (tree);
6335 * This is here because it requires BTree internals, it logically
6336 * belongs in gtktextsegment.c
6341 *--------------------------------------------------------------
6343 * _gtk_toggle_segment_check_func --
6345 * This procedure is invoked to perform consistency checks
6346 * on toggle segments.
6352 * If a consistency problem is found the procedure g_errors.
6354 *--------------------------------------------------------------
6358 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6364 if (segPtr->byte_count != 0)
6366 g_error ("toggle_segment_check_func: segment had non-zero size");
6368 if (!segPtr->body.toggle.inNodeCounts)
6370 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6372 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6373 for (summary = line->parent->summary; ;
6374 summary = summary->next)
6376 if (summary == NULL)
6380 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6387 if (summary->info == segPtr->body.toggle.info)
6391 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6403 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6404 GtkTextBTreeNode *node,
6414 while (view != NULL)
6416 if (view->view_id == nd->view_id)
6423 g_error ("Node has data for a view %p no longer attached to the tree",
6426 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6427 &width, &height, &valid);
6429 /* valid aggregate not checked the same as width/height, because on
6430 * btree rebalance we can have invalid nodes where all lines below
6431 * them are actually valid, due to moving lines around between
6434 * The guarantee is that if there are invalid lines the node is
6435 * invalid - we don't guarantee that if the node is invalid there
6436 * are invalid lines.
6439 if (nd->width != width ||
6440 nd->height != height ||
6441 (nd->valid && !valid))
6443 g_error ("Node aggregates for view %p are invalid:\n"
6444 "Are (%d,%d,%s), should be (%d,%d,%s)",
6446 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6447 width, height, valid ? "TRUE" : "FALSE");
6452 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6453 GtkTextBTreeNode *node)
6455 GtkTextBTreeNode *childnode;
6456 Summary *summary, *summary2;
6458 GtkTextLineSegment *segPtr;
6459 int num_children, num_lines, num_chars, toggle_count, min_children;
6460 GtkTextLineData *ld;
6463 if (node->parent != NULL)
6465 min_children = MIN_CHILDREN;
6467 else if (node->level > 0)
6474 if ((node->num_children < min_children)
6475 || (node->num_children > MAX_CHILDREN))
6477 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6478 node->num_children);
6481 nd = node->node_data;
6484 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6491 if (node->level == 0)
6493 for (line = node->children.line; line != NULL;
6496 if (line->parent != node)
6498 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6500 if (line->segments == NULL)
6502 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6508 /* Just ensuring we don't segv while doing this loop */
6513 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6515 if (segPtr->type->checkFunc != NULL)
6517 (*segPtr->type->checkFunc)(segPtr, line);
6519 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6520 && (segPtr->next != NULL)
6521 && (segPtr->next->byte_count == 0)
6522 && (segPtr->next->type->leftGravity))
6524 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6526 if ((segPtr->next == NULL)
6527 && (segPtr->type != >k_text_char_type))
6529 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6532 num_chars += segPtr->char_count;
6541 for (childnode = node->children.node; childnode != NULL;
6542 childnode = childnode->next)
6544 if (childnode->parent != node)
6546 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6548 if (childnode->level != (node->level-1))
6550 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6551 node->level, childnode->level);
6553 gtk_text_btree_node_check_consistency (tree, childnode);
6554 for (summary = childnode->summary; summary != NULL;
6555 summary = summary->next)
6557 for (summary2 = node->summary; ;
6558 summary2 = summary2->next)
6560 if (summary2 == NULL)
6562 if (summary->info->tag_root == node)
6566 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6567 summary->info->tag->name,
6568 "present in parent summaries");
6570 if (summary->info == summary2->info)
6577 num_lines += childnode->num_lines;
6578 num_chars += childnode->num_chars;
6581 if (num_children != node->num_children)
6583 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6584 num_children, node->num_children);
6586 if (num_lines != node->num_lines)
6588 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6589 num_lines, node->num_lines);
6591 if (num_chars != node->num_chars)
6593 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6594 num_chars, node->num_chars);
6597 for (summary = node->summary; summary != NULL;
6598 summary = summary->next)
6600 if (summary->info->toggle_count == summary->toggle_count)
6602 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6603 summary->info->tag->name);
6606 if (node->level == 0)
6608 for (line = node->children.line; line != NULL;
6611 for (segPtr = line->segments; segPtr != NULL;
6612 segPtr = segPtr->next)
6614 if ((segPtr->type != >k_text_toggle_on_type)
6615 && (segPtr->type != >k_text_toggle_off_type))
6619 if (segPtr->body.toggle.info == summary->info)
6621 if (!segPtr->body.toggle.inNodeCounts)
6622 g_error ("Toggle segment not in the node counts");
6631 for (childnode = node->children.node;
6633 childnode = childnode->next)
6635 for (summary2 = childnode->summary;
6637 summary2 = summary2->next)
6639 if (summary2->info == summary->info)
6641 toggle_count += summary2->toggle_count;
6646 if (toggle_count != summary->toggle_count)
6648 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6649 toggle_count, summary->toggle_count);
6651 for (summary2 = summary->next; summary2 != NULL;
6652 summary2 = summary2->next)
6654 if (summary2->info == summary->info)
6656 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6657 summary->info->tag->name);
6664 listify_foreach (GtkTextTag *tag, gpointer user_data)
6666 GSList** listp = user_data;
6668 *listp = g_slist_prepend (*listp, tag);
6672 list_of_tags (GtkTextTagTable *table)
6674 GSList *list = NULL;
6676 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6682 _gtk_text_btree_check (GtkTextBTree *tree)
6685 GtkTextBTreeNode *node;
6687 GtkTextLineSegment *seg;
6689 GSList *taglist = NULL;
6691 GtkTextTagInfo *info;
6694 * Make sure that the tag toggle counts and the tag root pointers are OK.
6696 for (taglist = list_of_tags (tree->table);
6697 taglist != NULL ; taglist = taglist->next)
6699 tag = taglist->data;
6700 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6703 node = info->tag_root;
6706 if (info->toggle_count != 0)
6708 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6709 tag->name, info->toggle_count);
6711 continue; /* no ranges for the tag */
6713 else if (info->toggle_count == 0)
6715 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6718 else if (info->toggle_count & 1)
6720 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6721 tag->name, info->toggle_count);
6723 for (summary = node->summary; summary != NULL;
6724 summary = summary->next)
6726 if (summary->info->tag == tag)
6728 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6732 if (node->level > 0)
6734 for (node = node->children.node ; node != NULL ;
6737 for (summary = node->summary; summary != NULL;
6738 summary = summary->next)
6740 if (summary->info->tag == tag)
6742 count += summary->toggle_count;
6749 GtkTextLineSegmentClass * last = NULL;
6751 for (line = node->children.line ; line != NULL ;
6754 for (seg = line->segments; seg != NULL;
6757 if ((seg->type == >k_text_toggle_on_type ||
6758 seg->type == >k_text_toggle_off_type) &&
6759 seg->body.toggle.info->tag == tag)
6761 if (last == seg->type)
6762 g_error ("Two consecutive toggles on or off weren't merged");
6763 if (!seg->body.toggle.inNodeCounts)
6764 g_error ("Toggle segment not in the node counts");
6773 if (count != info->toggle_count)
6775 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6776 info->toggle_count, tag->name, count);
6781 g_slist_free (taglist);
6785 * Call a recursive procedure to do the main body of checks.
6788 node = tree->root_node;
6789 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6792 * Make sure that there are at least two lines in the text and
6793 * that the last line has no characters except a newline.
6796 if (node->num_lines < 2)
6798 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6800 if (node->num_chars < 2)
6802 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6804 while (node->level > 0)
6806 node = node->children.node;
6807 while (node->next != NULL)
6812 line = node->children.line;
6813 while (line->next != NULL)
6817 seg = line->segments;
6818 while ((seg->type == >k_text_toggle_off_type)
6819 || (seg->type == >k_text_right_mark_type)
6820 || (seg->type == >k_text_left_mark_type))
6823 * It's OK to toggle a tag off in the last line, but
6824 * not to start a new range. It's also OK to have marks
6830 if (seg->type != >k_text_char_type)
6832 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6834 if (seg->next != NULL)
6836 g_error ("_gtk_text_btree_check: last line has too many segments");
6838 if (seg->byte_count != 1)
6840 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6843 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6845 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6850 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6851 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6852 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6853 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6856 _gtk_text_btree_spew (GtkTextBTree *tree)
6861 printf ("%d lines in tree %p\n",
6862 _gtk_text_btree_line_count (tree), tree);
6864 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6866 while (line != NULL)
6868 _gtk_text_btree_spew_line (tree, line);
6869 line = _gtk_text_line_next (line);
6872 printf ("=================== Tag information\n");
6877 list = tree->tag_infos;
6879 while (list != NULL)
6881 GtkTextTagInfo *info;
6885 printf (" tag `%s': root at %p, toggle count %d\n",
6886 info->tag->name, info->tag_root, info->toggle_count);
6888 list = g_slist_next (list);
6891 if (tree->tag_infos == NULL)
6893 printf (" (no tags in the tree)\n");
6897 printf ("=================== Tree nodes\n");
6900 _gtk_text_btree_spew_node (tree->root_node, 0);
6905 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6908 GtkTextLineSegment *seg;
6910 spaces = g_strnfill (indent, ' ');
6912 printf ("%sline %p chars %d bytes %d\n",
6914 _gtk_text_line_char_count (line),
6915 _gtk_text_line_byte_count (line));
6917 seg = line->segments;
6920 if (seg->type == >k_text_char_type)
6922 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6927 if (*s == '\n' || *s == '\r')
6931 printf ("%s chars `%s'...\n", spaces, str);
6934 else if (seg->type == >k_text_right_mark_type)
6936 printf ("%s right mark `%s' visible: %d\n",
6938 seg->body.mark.name,
6939 seg->body.mark.visible);
6941 else if (seg->type == >k_text_left_mark_type)
6943 printf ("%s left mark `%s' visible: %d\n",
6945 seg->body.mark.name,
6946 seg->body.mark.visible);
6948 else if (seg->type == >k_text_toggle_on_type ||
6949 seg->type == >k_text_toggle_off_type)
6951 printf ("%s tag `%s' %s\n",
6952 spaces, seg->body.toggle.info->tag->name,
6953 seg->type == >k_text_toggle_off_type ? "off" : "on");
6963 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6966 GtkTextBTreeNode *iter;
6969 spaces = g_strnfill (indent, ' ');
6971 printf ("%snode %p level %d children %d lines %d chars %d\n",
6972 spaces, node, node->level,
6973 node->num_children, node->num_lines, node->num_chars);
6978 printf ("%s %d toggles of `%s' below this node\n",
6979 spaces, s->toggle_count, s->info->tag->name);
6985 if (node->level > 0)
6987 iter = node->children.node;
6988 while (iter != NULL)
6990 _gtk_text_btree_spew_node (iter, indent + 2);
6997 GtkTextLine *line = node->children.line;
6998 while (line != NULL)
7000 _gtk_text_btree_spew_line_short (line, indent + 2);
7008 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7010 GtkTextLineSegment * seg;
7012 printf ("%4d| line: %p parent: %p next: %p\n",
7013 _gtk_text_line_get_number (line), line, line->parent, line->next);
7015 seg = line->segments;
7019 _gtk_text_btree_spew_segment (tree, seg);
7025 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7027 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7028 seg, seg->type->name, seg->byte_count, seg->char_count);
7030 if (seg->type == >k_text_char_type)
7032 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7033 printf (" `%s'\n", str);
7036 else if (seg->type == >k_text_right_mark_type)
7038 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7039 seg->body.mark.name,
7040 seg->body.mark.visible,
7041 seg->body.mark.not_deleteable);
7043 else if (seg->type == >k_text_left_mark_type)
7045 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7046 seg->body.mark.name,
7047 seg->body.mark.visible,
7048 seg->body.mark.not_deleteable);
7050 else if (seg->type == >k_text_toggle_on_type ||
7051 seg->type == >k_text_toggle_off_type)
7053 printf (" tag `%s' priority %d\n",
7054 seg->body.toggle.info->tag->name,
7055 seg->body.toggle.info->tag->priority);