4 * This file contains code that manages the B-tree representation
5 * of text for the text buffer and implements character and
6 * toggle segment types.
8 * Copyright (c) 1992-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10 * Copyright (c) 2000 Red Hat, Inc.
11 * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
13 * This software is copyrighted by the Regents of the University of
14 * California, Sun Microsystems, Inc., and other parties. The
15 * following terms apply to all files associated with the software
16 * unless explicitly disclaimed in individual files.
18 * The authors hereby grant permission to use, copy, modify,
19 * distribute, and license this software and its documentation for any
20 * purpose, provided that existing copyright notices are retained in
21 * all copies and that this notice is included verbatim in any
22 * distributions. No written agreement, license, or royalty fee is
23 * required for any of the authorized uses. Modifications to this
24 * software may be copyrighted by their authors and need not follow
25 * the licensing terms described here, provided that the new terms are
26 * clearly indicated on the first page of each file where they apply.
28 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
29 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
30 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
31 * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
32 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
35 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
37 * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
38 * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
39 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 * GOVERNMENT USE: If you are acquiring this software on behalf of the
42 * U.S. government, the Government shall have only "Restricted Rights"
43 * in the software and related documentation as defined in the Federal
44 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
45 * are acquiring the software on behalf of the Department of Defense,
46 * the software shall be classified as "Commercial Computer Software"
47 * and the Government shall have only "Restricted Rights" as defined
48 * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
49 * foregoing, the authors grant the U.S. Government and others acting
50 * in its behalf permission to use and distribute the software in
51 * accordance with the terms specified in this license.
55 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
56 #include "gtktextbtree.h"
60 #include "gtksignal.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
74 * The structure below is used to pass information between
75 * _gtk_text_btree_get_tags and inc_count:
78 typedef struct TagInfo {
79 int numTags; /* Number of tags for which there
80 * is currently information in
82 int arraySize; /* Number of entries allocated for
84 GtkTextTag **tags; /* Array of tags seen so far.
86 int *counts; /* Toggle count (so far) for each
87 * entry in tags. Malloc-ed. */
92 * This is used to store per-view width/height info at the tree nodes.
95 typedef struct _NodeData NodeData;
101 /* Height and width of this node */
105 /* boolean indicating whether the lines below this node are in need of validation.
106 * However, width/height should always represent the current total width and
107 * max height for lines below this node; the valid flag indicates whether the
108 * width/height on the lines needs recomputing, not whether the totals
116 * The data structure below keeps summary information about one tag as part
117 * of the tag information in a node.
120 typedef struct Summary {
121 GtkTextTagInfo *info; /* Handle for tag. */
122 int toggle_count; /* Number of transitions into or
123 * out of this tag that occur in
124 * the subtree rooted at this node. */
125 struct Summary *next; /* Next in list of all tags for same
126 * node, or NULL if at end of list. */
130 * The data structure below defines a node in the B-tree.
133 struct _GtkTextBTreeNode {
134 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
135 * this is the root. */
136 GtkTextBTreeNode *next; /* Next in list of siblings with the
137 * same parent node, or NULL for end
139 Summary *summary; /* First in malloc-ed list of info
140 * about tags in this subtree (NULL if
141 * no tag info in the subtree). */
142 int level; /* Level of this node in the B-tree.
143 * 0 refers to the bottom of the tree
144 * (children are lines, not nodes). */
145 union { /* First in linked list of children. */
146 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
147 GtkTextLine *line; /* Used if level == 0. */
149 int num_children; /* Number of children of this node. */
150 int num_lines; /* Total number of lines (leaves) in
151 * the subtree rooted here. */
152 int num_chars; /* Number of chars below here */
159 * Used to store the list of views in our btree
162 typedef struct _BTreeView BTreeView;
166 GtkTextLayout *layout;
172 * And the tree itself
175 struct _GtkTextBTree {
176 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
177 GtkTextTagTable *table;
178 GHashTable *mark_table;
180 GtkTextMark *insert_mark;
181 GtkTextMark *selection_bound_mark;
182 GtkTextBuffer *buffer;
185 guint tag_changed_handler;
187 /* Incremented when a segment with a byte size > 0
188 * is added to or removed from the tree (i.e. the
189 * length of a line may have changed, and lines may
190 * have been added or removed). This invalidates
191 * all outstanding iterators.
193 guint chars_changed_stamp;
194 /* Incremented when any segments are added or deleted;
195 * this makes outstanding iterators recalculate their
196 * pointed-to segment and segment offset.
198 guint segments_changed_stamp;
200 /* Cache the last line in the buffer */
201 GtkTextLine *last_line;
202 guint last_line_stamp;
204 /* Cache the next-to-last line in the buffer,
205 * containing the end iterator
207 GtkTextLine *end_iter_line;
208 GtkTextLineSegment *end_iter_segment;
209 int end_iter_segment_byte_index;
210 int end_iter_segment_char_offset;
211 guint end_iter_line_stamp;
212 guint end_iter_segment_stamp;
214 GHashTable *child_anchor_table;
219 * Upper and lower bounds on how many children a node may have:
220 * rebalance when either of these limits is exceeded. MAX_CHILDREN
221 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
224 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
225 shallow. It appears to be faster to locate a particular line number
226 if the tree is narrow and deep, since it is more finely sorted. I
227 guess this may increase memory use though, and make it slower to
228 walk the tree in order, or locate a particular byte index (which
229 is done by walking the tree in order).
231 There's basically a tradeoff here. However I'm thinking we want to
232 add pixels, byte counts, and char counts to the tree nodes,
233 at that point narrow and deep should speed up all operations,
234 not just the line number searches.
238 #define MAX_CHILDREN 12
239 #define MIN_CHILDREN 6
241 #define MAX_CHILDREN 6
242 #define MIN_CHILDREN 3
249 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
251 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
252 GtkTextBTreeNode *node);
253 static GtkTextLine * get_last_line (GtkTextBTree *tree);
254 static void post_insert_fixup (GtkTextBTree *tree,
255 GtkTextLine *insert_line,
256 gint char_count_delta,
257 gint line_count_delta);
258 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
259 GtkTextTagInfo *info,
261 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
264 static void segments_changed (GtkTextBTree *tree);
265 static void chars_changed (GtkTextBTree *tree);
266 static void summary_list_destroy (Summary *summary);
267 static GtkTextLine *gtk_text_line_new (void);
268 static void gtk_text_line_destroy (GtkTextBTree *tree,
270 static void gtk_text_line_set_parent (GtkTextLine *line,
271 GtkTextBTreeNode *node);
272 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
276 static NodeData *node_data_new (gpointer view_id);
277 static void node_data_destroy (NodeData *nd);
278 static void node_data_list_destroy (NodeData *nd);
279 static NodeData *node_data_find (NodeData *nd,
282 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
283 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
284 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
286 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
288 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
290 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
293 static void gtk_text_btree_node_remove_view (BTreeView *view,
294 GtkTextBTreeNode *node,
296 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
297 GtkTextBTreeNode *node);
298 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
299 GtkTextBTreeNode *node);
300 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
302 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
304 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
308 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
309 GtkTextBTreeNode *node2);
310 static void get_tree_bounds (GtkTextBTree *tree,
313 static void tag_changed_cb (GtkTextTagTable *table,
315 gboolean size_changed,
317 static void cleanup_line (GtkTextLine *line);
318 static void recompute_node_counts (GtkTextBTree *tree,
319 GtkTextBTreeNode *node);
320 static void inc_count (GtkTextTag *tag,
322 TagInfo *tagInfoPtr);
324 static void summary_destroy (Summary *summary);
326 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
327 const GtkTextIter *iter);
328 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
329 GtkTextLineSegment *seg,
333 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
335 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
337 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
340 static void redisplay_region (GtkTextBTree *tree,
341 const GtkTextIter *start,
342 const GtkTextIter *end);
344 /* Inline thingies */
347 segments_changed (GtkTextBTree *tree)
349 tree->segments_changed_stamp += 1;
353 chars_changed (GtkTextBTree *tree)
355 tree->chars_changed_stamp += 1;
363 _gtk_text_btree_new (GtkTextTagTable *table,
364 GtkTextBuffer *buffer)
367 GtkTextBTreeNode *root_node;
368 GtkTextLine *line, *line2;
370 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
371 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
374 * The tree will initially have two empty lines. The second line
375 * isn't actually part of the tree's contents, but its presence
376 * makes several operations easier. The tree will have one GtkTextBTreeNode,
377 * which is also the root of the tree.
380 /* Create the root node. */
382 root_node = gtk_text_btree_node_new ();
384 line = gtk_text_line_new ();
385 line2 = gtk_text_line_new ();
387 root_node->parent = NULL;
388 root_node->next = NULL;
389 root_node->summary = NULL;
390 root_node->level = 0;
391 root_node->children.line = line;
392 root_node->num_children = 2;
393 root_node->num_lines = 2;
394 root_node->num_chars = 2;
396 line->parent = root_node;
399 line->segments = _gtk_char_segment_new ("\n", 1);
401 line2->parent = root_node;
403 line2->segments = _gtk_char_segment_new ("\n", 1);
405 /* Create the tree itself */
407 tree = g_new0(GtkTextBTree, 1);
408 tree->root_node = root_node;
412 /* Set these to values that are unlikely to be found
413 * in random memory garbage, and also avoid
414 * duplicates between tree instances.
416 tree->chars_changed_stamp = g_random_int ();
417 tree->segments_changed_stamp = g_random_int ();
419 tree->last_line_stamp = tree->chars_changed_stamp - 1;
420 tree->last_line = NULL;
422 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
423 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
424 tree->end_iter_line = NULL;
425 tree->end_iter_segment_byte_index = 0;
426 tree->end_iter_segment_char_offset = 0;
428 g_object_ref (G_OBJECT (tree->table));
430 tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
432 G_CALLBACK (tag_changed_cb),
435 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
436 tree->child_anchor_table = NULL;
438 /* We don't ref the buffer, since the buffer owns us;
439 * we'd have some circularity issues. The buffer always
440 * lasts longer than the BTree
442 tree->buffer = buffer;
446 GtkTextLineSegment *seg;
448 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
451 tree->insert_mark = _gtk_text_btree_set_mark (tree,
458 seg = tree->insert_mark->segment;
460 seg->body.mark.not_deleteable = TRUE;
461 seg->body.mark.visible = TRUE;
463 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
470 seg = tree->selection_bound_mark->segment;
472 seg->body.mark.not_deleteable = TRUE;
474 g_object_ref (G_OBJECT (tree->insert_mark));
475 g_object_ref (G_OBJECT (tree->selection_bound_mark));
484 _gtk_text_btree_ref (GtkTextBTree *tree)
486 g_return_if_fail (tree != NULL);
487 g_return_if_fail (tree->refcount > 0);
493 _gtk_text_btree_unref (GtkTextBTree *tree)
495 g_return_if_fail (tree != NULL);
496 g_return_if_fail (tree->refcount > 0);
500 if (tree->refcount == 0)
502 g_signal_handler_disconnect (G_OBJECT (tree->table),
503 tree->tag_changed_handler);
505 g_object_unref (G_OBJECT (tree->table));
508 gtk_text_btree_node_destroy (tree, tree->root_node);
509 tree->root_node = NULL;
511 g_assert (g_hash_table_size (tree->mark_table) == 0);
512 g_hash_table_destroy (tree->mark_table);
513 tree->mark_table = NULL;
515 g_object_unref (G_OBJECT (tree->insert_mark));
516 tree->insert_mark = NULL;
517 g_object_unref (G_OBJECT (tree->selection_bound_mark));
518 tree->selection_bound_mark = NULL;
525 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
531 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
533 return tree->chars_changed_stamp;
537 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
539 return tree->segments_changed_stamp;
543 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
545 g_return_if_fail (tree != NULL);
546 segments_changed (tree);
550 * Indexable segment mutation
554 _gtk_text_btree_delete (GtkTextIter *start,
557 GtkTextLineSegment *prev_seg; /* The segment just before the start
558 * of the deletion range. */
559 GtkTextLineSegment *last_seg; /* The segment just after the end
560 * of the deletion range. */
561 GtkTextLineSegment *seg, *next;
562 GtkTextLine *curline;
563 GtkTextBTreeNode *curnode, *node;
565 GtkTextLine *start_line;
566 GtkTextLine *end_line;
567 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
568 gint start_byte_offset;
570 g_return_if_fail (start != NULL);
571 g_return_if_fail (end != NULL);
572 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
573 _gtk_text_iter_get_btree (end));
575 gtk_text_iter_order (start, end);
577 tree = _gtk_text_iter_get_btree (start);
579 if (gtk_debug_flags & GTK_DEBUG_TEXT)
580 _gtk_text_btree_check (tree);
582 /* Broadcast the need for redisplay before we break the iterators */
583 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
584 _gtk_text_btree_invalidate_region (tree, start, end);
586 /* Save the byte offset so we can reset the iterators */
587 start_byte_offset = gtk_text_iter_get_line_index (start);
589 start_line = _gtk_text_iter_get_text_line (start);
590 end_line = _gtk_text_iter_get_text_line (end);
593 * Split the start and end segments, so we have a place
594 * to insert our new text.
596 * Tricky point: split at end first; otherwise the split
597 * at end may invalidate seg and/or prev_seg. This allows
598 * us to avoid invalidating segments for start.
601 last_seg = gtk_text_line_segment_split (end);
602 if (last_seg != NULL)
603 last_seg = last_seg->next;
605 last_seg = end_line->segments;
607 prev_seg = gtk_text_line_segment_split (start);
608 if (prev_seg != NULL)
610 seg = prev_seg->next;
611 prev_seg->next = last_seg;
615 seg = start_line->segments;
616 start_line->segments = last_seg;
619 /* notify iterators that their segments need recomputation,
620 just for robustness. */
621 segments_changed (tree);
624 * Delete all of the segments between prev_seg and last_seg.
627 curline = start_line;
628 curnode = curline->parent;
629 while (seg != last_seg)
635 GtkTextLine *nextline;
638 * We just ran off the end of a line. First find the
639 * next line, then go back to the old line and delete it
640 * (unless it's the starting line for the range).
643 nextline = _gtk_text_line_next (curline);
644 if (curline != start_line)
646 if (curnode == start_line->parent)
647 start_line->next = curline->next;
649 curnode->children.line = curline->next;
651 for (node = curnode; node != NULL;
654 /* Don't update node->num_chars, because
655 * that was done when we deleted the segments.
657 node->num_lines -= 1;
660 curnode->num_children -= 1;
661 curline->next = deleted_lines;
662 deleted_lines = curline;
666 seg = curline->segments;
669 * If the GtkTextBTreeNode is empty then delete it and its parents,
670 * recursively upwards until a non-empty GtkTextBTreeNode is found.
673 while (curnode->num_children == 0)
675 GtkTextBTreeNode *parent;
677 parent = curnode->parent;
678 if (parent->children.node == curnode)
680 parent->children.node = curnode->next;
684 GtkTextBTreeNode *prevnode = parent->children.node;
685 while (prevnode->next != curnode)
687 prevnode = prevnode->next;
689 prevnode->next = curnode->next;
691 parent->num_children--;
692 gtk_text_btree_node_free_empty (tree, curnode);
695 curnode = curline->parent;
700 char_count = seg->char_count;
702 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
705 * This segment refuses to die. Move it to prev_seg and
706 * advance prev_seg if the segment has left gravity.
709 if (prev_seg == NULL)
711 seg->next = start_line->segments;
712 start_line->segments = seg;
716 seg->next = prev_seg->next;
717 prev_seg->next = seg;
719 if (seg->type->leftGravity)
726 /* Segment is gone. Decrement the char count of the node and
728 for (node = curnode; node != NULL;
731 node->num_chars -= char_count;
739 * If the beginning and end of the deletion range are in different
740 * lines, join the two lines together and discard the ending line.
743 if (start_line != end_line)
746 GtkTextBTreeNode *ancestor_node;
747 GtkTextLine *prevline;
750 /* last_seg was appended to start_line up at the top of this function */
752 for (seg = last_seg; seg != NULL;
755 chars_moved += seg->char_count;
756 if (seg->type->lineChangeFunc != NULL)
758 (*seg->type->lineChangeFunc)(seg, end_line);
762 for (node = start_line->parent; node != NULL;
765 node->num_chars += chars_moved;
768 curnode = end_line->parent;
769 for (node = curnode; node != NULL;
772 node->num_chars -= chars_moved;
775 curnode->num_children--;
776 prevline = curnode->children.line;
777 if (prevline == end_line)
779 curnode->children.line = end_line->next;
783 while (prevline->next != end_line)
785 prevline = prevline->next;
787 prevline->next = end_line->next;
789 end_line->next = deleted_lines;
790 deleted_lines = end_line;
792 /* We now fix up the per-view aggregates. We add all the height and
793 * width for the deleted lines to the start line, so that when revalidation
794 * occurs, the correct change in size is seen.
796 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
803 gint deleted_width = 0;
804 gint deleted_height = 0;
806 line = deleted_lines;
809 GtkTextLine *next_line = line->next;
810 ld = _gtk_text_line_get_data (line, view->view_id);
814 deleted_width = MAX (deleted_width, ld->width);
815 deleted_height += ld->height;
819 gtk_text_line_destroy (tree, line);
824 if (deleted_width > 0 || deleted_height > 0)
826 ld = _gtk_text_line_get_data (start_line, view->view_id);
830 /* This means that start_line has never been validated.
831 * We don't really want to do the validation here but
832 * we do need to store our temporary sizes. So we
833 * create the line data and assume a line w/h of 0.
835 ld = _gtk_text_line_data_new (view->layout, start_line);
836 _gtk_text_line_add_data (start_line, ld);
842 ld->width = MAX (deleted_width, ld->width);
843 ld->height += deleted_height;
847 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
848 if (ancestor_node->parent)
849 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
854 /* avoid dangling pointer */
855 deleted_lines = NULL;
857 gtk_text_btree_rebalance (tree, curnode);
861 * Cleanup the segments in the new line.
864 cleanup_line (start_line);
867 * Lastly, rebalance the first GtkTextBTreeNode of the range.
870 gtk_text_btree_rebalance (tree, start_line->parent);
872 /* Notify outstanding iterators that they
874 chars_changed (tree);
875 segments_changed (tree);
877 if (gtk_debug_flags & GTK_DEBUG_TEXT)
878 _gtk_text_btree_check (tree);
880 /* Re-initialize our iterators */
881 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
886 _gtk_text_btree_insert (GtkTextIter *iter,
890 GtkTextLineSegment *prev_seg; /* The segment just before the first
891 * new segment (NULL means new segment
892 * is at beginning of line). */
893 GtkTextLineSegment *cur_seg; /* Current segment; new characters
894 * are inserted just after this one.
895 * NULL means insert at beginning of
897 GtkTextLine *line; /* Current line (new segments are
898 * added to this line). */
899 GtkTextLineSegment *seg;
900 GtkTextLine *newline;
901 int chunk_len; /* # characters in current chunk. */
902 gint sol; /* start of line */
903 gint eol; /* Pointer to character just after last
904 * one in current chunk.
906 gint delim; /* index of paragraph delimiter */
907 int line_count_delta; /* Counts change to total number of
911 int char_count_delta; /* change to number of chars */
913 gint start_byte_index;
914 GtkTextLine *start_line;
916 g_return_if_fail (text != NULL);
917 g_return_if_fail (iter != NULL);
922 /* extract iterator info */
923 tree = _gtk_text_iter_get_btree (iter);
924 line = _gtk_text_iter_get_text_line (iter);
927 start_byte_index = gtk_text_iter_get_line_index (iter);
929 /* Get our insertion segment split. Note this assumes line allows
930 * char insertions, which isn't true of the "last" line. But iter
931 * should not be on that line, as we assert here.
933 g_assert (!_gtk_text_line_is_last (line, tree));
934 prev_seg = gtk_text_line_segment_split (iter);
937 /* Invalidate all iterators */
938 chars_changed (tree);
939 segments_changed (tree);
942 * Chop the text up into lines and create a new segment for
943 * each line, plus a new line for the leftovers from the
949 line_count_delta = 0;
950 char_count_delta = 0;
955 pango_find_paragraph_boundary (text + sol,
960 /* make these relative to the start of the text */
964 g_assert (eol >= sol);
965 g_assert (delim >= sol);
966 g_assert (eol >= delim);
968 g_assert (eol <= len);
970 chunk_len = eol - sol;
972 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
973 seg = _gtk_char_segment_new (&text[sol], chunk_len);
975 char_count_delta += seg->char_count;
979 seg->next = line->segments;
980 line->segments = seg;
984 seg->next = cur_seg->next;
990 /* chunk didn't end with a paragraph separator */
991 g_assert (eol == len);
996 * The chunk ended with a newline, so create a new GtkTextLine
997 * and move the remainder of the old line to it.
1000 newline = gtk_text_line_new ();
1001 gtk_text_line_set_parent (newline, line->parent);
1002 newline->next = line->next;
1003 line->next = newline;
1004 newline->segments = seg->next;
1012 * Cleanup the starting line for the insertion, plus the ending
1013 * line if it's different.
1016 cleanup_line (start_line);
1017 if (line != start_line)
1019 cleanup_line (line);
1022 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1024 /* Invalidate our region, and reset the iterator the user
1025 passed in to point to the end of the inserted text. */
1031 _gtk_text_btree_get_iter_at_line (tree,
1037 /* We could almost certainly be more efficient here
1038 by saving the information from the insertion loop
1040 gtk_text_iter_forward_chars (&end, char_count_delta);
1042 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1043 _gtk_text_btree_invalidate_region (tree,
1047 /* Convenience for the user */
1053 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1054 GtkTextLineSegment *seg)
1058 GtkTextLineSegment *prevPtr;
1061 gint start_byte_offset;
1063 line = _gtk_text_iter_get_text_line (iter);
1064 tree = _gtk_text_iter_get_btree (iter);
1065 start_byte_offset = gtk_text_iter_get_line_index (iter);
1067 prevPtr = gtk_text_line_segment_split (iter);
1068 if (prevPtr == NULL)
1070 seg->next = line->segments;
1071 line->segments = seg;
1075 seg->next = prevPtr->next;
1076 prevPtr->next = seg;
1079 post_insert_fixup (tree, line, 0, seg->char_count);
1081 chars_changed (tree);
1082 segments_changed (tree);
1084 /* reset *iter for the user, and invalidate tree nodes */
1086 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1089 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1091 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1092 _gtk_text_btree_invalidate_region (tree, &start, iter);
1096 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1099 GtkTextLineSegment *seg;
1101 seg = _gtk_pixbuf_segment_new (pixbuf);
1103 insert_pixbuf_or_widget_segment (iter, seg);
1107 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1108 GtkTextChildAnchor *anchor)
1110 GtkTextLineSegment *seg;
1113 if (anchor->segment != NULL)
1115 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1119 seg = _gtk_widget_segment_new (anchor);
1121 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1122 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1124 insert_pixbuf_or_widget_segment (iter, seg);
1126 if (tree->child_anchor_table == NULL)
1127 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1129 g_hash_table_insert (tree->child_anchor_table,
1130 seg->body.child.obj,
1131 seg->body.child.obj);
1135 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1137 GtkTextLineSegment *seg;
1139 seg = anchor->segment;
1141 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1150 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1151 GtkTextBTreeNode *node, gint y, gint *line_top,
1152 GtkTextLine *last_line)
1156 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1157 _gtk_text_btree_check (tree);
1159 if (node->level == 0)
1163 line = node->children.line;
1165 while (line != NULL && line != last_line)
1167 GtkTextLineData *ld;
1169 ld = _gtk_text_line_get_data (line, view->view_id);
1173 if (y < (current_y + (ld ? ld->height : 0)))
1176 current_y += ld->height;
1177 *line_top += ld->height;
1186 GtkTextBTreeNode *child;
1188 child = node->children.node;
1190 while (child != NULL)
1195 gtk_text_btree_node_get_size (child, view->view_id,
1198 if (y < (current_y + height))
1199 return find_line_by_y (tree, view, child,
1200 y - current_y, line_top,
1203 current_y += height;
1204 *line_top += height;
1206 child = child->next;
1214 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1221 GtkTextLine *last_line;
1224 view = gtk_text_btree_get_view (tree, view_id);
1225 g_return_val_if_fail (view != NULL, NULL);
1227 last_line = get_last_line (tree);
1229 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1233 *line_top_out = line_top;
1239 find_line_top_in_line_list (GtkTextBTree *tree,
1242 GtkTextLine *target_line,
1245 while (line != NULL)
1247 GtkTextLineData *ld;
1249 if (line == target_line)
1252 ld = _gtk_text_line_get_data (line, view->view_id);
1259 g_assert_not_reached (); /* If we get here, our
1260 target line didn't exist
1261 under its parent node */
1266 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1267 GtkTextLine *target_line,
1274 GtkTextBTreeNode *node;
1276 view = gtk_text_btree_get_view (tree, view_id);
1278 g_return_val_if_fail (view != NULL, 0);
1281 node = target_line->parent;
1282 while (node != NULL)
1284 nodes = g_slist_prepend (nodes, node);
1285 node = node->parent;
1289 while (iter != NULL)
1293 if (node->level == 0)
1295 g_slist_free (nodes);
1296 return find_line_top_in_line_list (tree, view,
1297 node->children.line,
1302 GtkTextBTreeNode *child;
1303 GtkTextBTreeNode *target_node;
1305 g_assert (iter->next != NULL); /* not at level 0 */
1306 target_node = iter->next->data;
1308 child = node->children.node;
1310 while (child != NULL)
1315 if (child == target_node)
1319 gtk_text_btree_node_get_size (child, view->view_id,
1323 child = child->next;
1325 g_assert (child != NULL); /* should have broken out before we
1329 iter = g_slist_next (iter);
1332 g_assert_not_reached (); /* we return when we find the target line */
1337 _gtk_text_btree_add_view (GtkTextBTree *tree,
1338 GtkTextLayout *layout)
1341 GtkTextLine *last_line;
1342 GtkTextLineData *line_data;
1344 g_return_if_fail (tree != NULL);
1346 view = g_new (BTreeView, 1);
1348 view->view_id = layout;
1349 view->layout = layout;
1351 view->next = tree->views;
1356 g_assert (tree->views->prev == NULL);
1357 tree->views->prev = view;
1362 /* The last line in the buffer has identity values for the per-view
1363 * data so that we can avoid special case checks for it in a large
1366 last_line = get_last_line (tree);
1368 line_data = g_new (GtkTextLineData, 1);
1369 line_data->view_id = layout;
1370 line_data->next = NULL;
1371 line_data->width = 0;
1372 line_data->height = 0;
1373 line_data->valid = TRUE;
1375 _gtk_text_line_add_data (last_line, line_data);
1379 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1383 GtkTextLine *last_line;
1384 GtkTextLineData *line_data;
1386 g_return_if_fail (tree != NULL);
1390 while (view != NULL)
1392 if (view->view_id == view_id)
1398 g_return_if_fail (view != NULL);
1401 view->next->prev = view->prev;
1404 view->prev->next = view->next;
1406 if (view == tree->views)
1407 tree->views = view->next;
1409 /* Remove the line data for the last line which we added ourselves.
1410 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1412 last_line = get_last_line (tree);
1413 line_data = _gtk_text_line_remove_data (last_line, view_id);
1416 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1418 view->layout = (gpointer) 0xdeadbeef;
1419 view->view_id = (gpointer) 0xdeadbeef;
1425 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1426 const GtkTextIter *start,
1427 const GtkTextIter *end)
1433 while (view != NULL)
1435 gtk_text_layout_invalidate (view->layout, start, end);
1442 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1447 g_return_if_fail (tree != NULL);
1448 g_return_if_fail (view_id != NULL);
1450 gtk_text_btree_node_get_size (tree->root_node, view_id,
1465 iter_stack_new (void)
1468 stack = g_new (IterStack, 1);
1469 stack->iters = NULL;
1476 iter_stack_push (IterStack *stack, const GtkTextIter *iter)
1479 if (stack->count > stack->alloced)
1481 stack->alloced = stack->count*2;
1482 stack->iters = g_realloc (stack->iters,
1483 stack->alloced*sizeof (GtkTextIter));
1485 stack->iters[stack->count-1] = *iter;
1489 iter_stack_pop (IterStack *stack, GtkTextIter *iter)
1491 if (stack->count == 0)
1496 *iter = stack->iters[stack->count];
1502 iter_stack_free (IterStack *stack)
1504 g_free (stack->iters);
1509 iter_stack_invert (IterStack *stack)
1511 if (stack->count > 0)
1514 guint j = stack->count - 1;
1519 tmp = stack->iters[i];
1520 stack->iters[i] = stack->iters[j];
1521 stack->iters[j] = tmp;
1530 queue_tag_redisplay (GtkTextBTree *tree,
1532 const GtkTextIter *start,
1533 const GtkTextIter *end)
1535 if (_gtk_text_tag_affects_size (tag))
1537 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1538 _gtk_text_btree_invalidate_region (tree, start, end);
1540 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1542 /* We only need to queue a redraw, not a relayout */
1543 redisplay_region (tree, start, end);
1546 /* We don't need to do anything if the tag doesn't affect display */
1550 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1551 const GtkTextIter *end_orig,
1555 GtkTextLineSegment *seg, *prev;
1556 GtkTextLine *cleanupline;
1557 gboolean toggled_on;
1558 GtkTextLine *start_line;
1559 GtkTextLine *end_line;
1561 GtkTextIter start, end;
1564 GtkTextTagInfo *info;
1566 g_return_if_fail (start_orig != NULL);
1567 g_return_if_fail (end_orig != NULL);
1568 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1569 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1570 _gtk_text_iter_get_btree (end_orig));
1571 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1574 printf ("%s tag %s from %d to %d\n",
1575 add ? "Adding" : "Removing",
1577 gtk_text_buffer_get_offset (start_orig),
1578 gtk_text_buffer_get_offset (end_orig));
1581 if (gtk_text_iter_equal (start_orig, end_orig))
1584 start = *start_orig;
1587 gtk_text_iter_order (&start, &end);
1589 tree = _gtk_text_iter_get_btree (&start);
1591 queue_tag_redisplay (tree, tag, &start, &end);
1593 info = gtk_text_btree_get_tag_info (tree, tag);
1595 start_line = _gtk_text_iter_get_text_line (&start);
1596 end_line = _gtk_text_iter_get_text_line (&end);
1598 /* Find all tag toggles in the region; we are going to delete them.
1599 We need to find them in advance, because
1600 forward_find_tag_toggle () won't work once we start playing around
1602 stack = iter_stack_new ();
1605 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1606 * which is deliberate - we don't want to delete a toggle at the
1609 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1611 if (gtk_text_iter_compare (&iter, &end) >= 0)
1614 iter_stack_push (stack, &iter);
1617 /* We need to traverse the toggles in order. */
1618 iter_stack_invert (stack);
1621 * See whether the tag is present at the start of the range. If
1622 * the state doesn't already match what we want then add a toggle
1626 toggled_on = gtk_text_iter_has_tag (&start, tag);
1627 if ( (add && !toggled_on) ||
1628 (!add && toggled_on) )
1630 /* This could create a second toggle at the start position;
1631 cleanup_line () will remove it if so. */
1632 seg = _gtk_toggle_segment_new (info, add);
1634 prev = gtk_text_line_segment_split (&start);
1637 seg->next = start_line->segments;
1638 start_line->segments = seg;
1642 seg->next = prev->next;
1646 /* cleanup_line adds the new toggle to the node counts. */
1648 printf ("added toggle at start\n");
1650 /* we should probably call segments_changed, but in theory
1651 any still-cached segments in the iters we are about to
1652 use are still valid, since they're in front
1658 * Scan the range of characters and delete any internal tag
1659 * transitions. Keep track of what the old state was at the end
1660 * of the range, and add a toggle there if it's needed.
1664 cleanupline = start_line;
1665 while (iter_stack_pop (stack, &iter))
1667 GtkTextLineSegment *indexable_seg;
1670 line = _gtk_text_iter_get_text_line (&iter);
1671 seg = _gtk_text_iter_get_any_segment (&iter);
1672 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1674 g_assert (seg != NULL);
1675 g_assert (indexable_seg != NULL);
1676 g_assert (seg != indexable_seg);
1678 prev = line->segments;
1680 /* Find the segment that actually toggles this tag. */
1681 while (seg != indexable_seg)
1683 g_assert (seg != NULL);
1684 g_assert (indexable_seg != NULL);
1685 g_assert (seg != indexable_seg);
1687 if ( (seg->type == >k_text_toggle_on_type ||
1688 seg->type == >k_text_toggle_off_type) &&
1689 (seg->body.toggle.info == info) )
1695 g_assert (seg != NULL);
1696 g_assert (indexable_seg != NULL);
1698 g_assert (seg != indexable_seg); /* If this happens, then
1699 forward_to_tag_toggle was
1701 g_assert (seg->body.toggle.info->tag == tag);
1703 /* If this happens, when previously tagging we didn't merge
1704 overlapping tags. */
1705 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1706 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1708 toggled_on = !toggled_on;
1711 printf ("deleting %s toggle\n",
1712 seg->type == >k_text_toggle_on_type ? "on" : "off");
1714 /* Remove toggle segment from the list. */
1717 line->segments = seg->next;
1721 while (prev->next != seg)
1725 prev->next = seg->next;
1728 /* Inform iterators we've hosed them. This actually reflects a
1729 bit of inefficiency; if you have the same tag toggled on and
1730 off a lot in a single line, we keep having the rescan from
1731 the front of the line. Of course we have to do that to get
1732 "prev" anyway, but here we are doing it an additional
1734 segments_changed (tree);
1736 /* Update node counts */
1737 if (seg->body.toggle.inNodeCounts)
1739 _gtk_change_node_toggle_count (line->parent,
1741 seg->body.toggle.inNodeCounts = FALSE;
1746 /* We only clean up lines when we're done with them, saves some
1747 gratuitous line-segment-traversals */
1749 if (cleanupline != line)
1751 cleanup_line (cleanupline);
1756 iter_stack_free (stack);
1758 /* toggled_on now reflects the toggle state _just before_ the
1759 end iterator. The end iterator could already have a toggle
1760 on or a toggle off. */
1761 if ( (add && !toggled_on) ||
1762 (!add && toggled_on) )
1764 /* This could create a second toggle at the start position;
1765 cleanup_line () will remove it if so. */
1767 seg = _gtk_toggle_segment_new (info, !add);
1769 prev = gtk_text_line_segment_split (&end);
1772 seg->next = end_line->segments;
1773 end_line->segments = seg;
1777 seg->next = prev->next;
1780 /* cleanup_line adds the new toggle to the node counts. */
1781 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1783 printf ("added toggle at end\n");
1788 * Cleanup cleanupline and the last line of the range, if
1789 * these are different.
1792 cleanup_line (cleanupline);
1793 if (cleanupline != end_line)
1795 cleanup_line (end_line);
1798 segments_changed (tree);
1800 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1801 _gtk_text_btree_check (tree);
1810 get_line_internal (GtkTextBTree *tree,
1812 gint *real_line_number,
1813 gboolean include_last)
1815 GtkTextBTreeNode *node;
1820 line_count = _gtk_text_btree_line_count (tree);
1824 if (line_number < 0)
1826 line_number = line_count;
1828 else if (line_number > line_count)
1830 line_number = line_count;
1833 if (real_line_number)
1834 *real_line_number = line_number;
1836 node = tree->root_node;
1837 lines_left = line_number;
1840 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1844 while (node->level != 0)
1846 for (node = node->children.node;
1847 node->num_lines <= lines_left;
1853 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
1856 lines_left -= node->num_lines;
1861 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1864 for (line = node->children.line; lines_left > 0;
1870 g_error ("gtk_text_btree_find_line ran out of lines");
1879 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
1882 _gtk_text_btree_get_line (tree,
1883 _gtk_text_btree_line_count (tree) - 1,
1888 _gtk_text_btree_get_line (GtkTextBTree *tree,
1890 gint *real_line_number)
1892 return get_line_internal (tree, line_number, real_line_number, TRUE);
1896 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
1898 gint *real_line_number)
1900 return get_line_internal (tree, line_number, real_line_number, FALSE);
1904 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
1906 gint *line_start_index,
1907 gint *real_char_index)
1909 GtkTextBTreeNode *node;
1911 GtkTextLineSegment *seg;
1916 node = tree->root_node;
1918 /* Clamp to valid indexes (-1 is magic for "highest index"),
1919 * node->num_chars includes the two newlines that aren't really
1922 if (char_index < 0 || char_index >= (node->num_chars - 1))
1924 char_index = node->num_chars - 2;
1927 *real_char_index = char_index;
1930 * Work down through levels of the tree until a GtkTextBTreeNode is found at
1934 chars_left = char_index;
1935 while (node->level != 0)
1937 for (node = node->children.node;
1938 chars_left >= node->num_chars;
1941 chars_left -= node->num_chars;
1943 g_assert (chars_left >= 0);
1947 if (chars_left == 0)
1949 /* Start of a line */
1951 *line_start_index = char_index;
1952 return node->children.line;
1956 * Work through the lines attached to the level-0 GtkTextBTreeNode.
1962 for (line = node->children.line; line != NULL; line = line->next)
1964 seg = line->segments;
1967 if (chars_in_line + seg->char_count > chars_left)
1968 goto found; /* found our line/segment */
1970 chars_in_line += seg->char_count;
1975 chars_left -= chars_in_line;
1983 g_assert (line != NULL); /* hosage, ran out of lines */
1984 g_assert (seg != NULL);
1986 *line_start_index = char_index - chars_left;
1991 _gtk_text_btree_get_tags (const GtkTextIter *iter,
1994 GtkTextBTreeNode *node;
1995 GtkTextLine *siblingline;
1996 GtkTextLineSegment *seg;
1997 int src, dst, index;
2003 #define NUM_TAG_INFOS 10
2005 line = _gtk_text_iter_get_text_line (iter);
2006 tree = _gtk_text_iter_get_btree (iter);
2007 byte_index = gtk_text_iter_get_line_index (iter);
2009 tagInfo.numTags = 0;
2010 tagInfo.arraySize = NUM_TAG_INFOS;
2011 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2012 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2015 * Record tag toggles within the line of indexPtr but preceding
2016 * indexPtr. Note that if this loop segfaults, your
2017 * byte_index probably points past the sum of all
2018 * seg->byte_count */
2020 for (index = 0, seg = line->segments;
2021 (index + seg->byte_count) <= byte_index;
2022 index += seg->byte_count, seg = seg->next)
2024 if ((seg->type == >k_text_toggle_on_type)
2025 || (seg->type == >k_text_toggle_off_type))
2027 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2032 * Record toggles for tags in lines that are predecessors of
2033 * line but under the same level-0 GtkTextBTreeNode.
2036 for (siblingline = line->parent->children.line;
2037 siblingline != line;
2038 siblingline = siblingline->next)
2040 for (seg = siblingline->segments; seg != NULL;
2043 if ((seg->type == >k_text_toggle_on_type)
2044 || (seg->type == >k_text_toggle_off_type))
2046 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2052 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2053 * toggles for all siblings that precede that GtkTextBTreeNode.
2056 for (node = line->parent; node->parent != NULL;
2057 node = node->parent)
2059 GtkTextBTreeNode *siblingPtr;
2062 for (siblingPtr = node->parent->children.node;
2063 siblingPtr != node; siblingPtr = siblingPtr->next)
2065 for (summary = siblingPtr->summary; summary != NULL;
2066 summary = summary->next)
2068 if (summary->toggle_count & 1)
2070 inc_count (summary->info->tag, summary->toggle_count,
2078 * Go through the tag information and squash out all of the tags
2079 * that have even toggle counts (these tags exist before the point
2080 * of interest, but not at the desired character itself).
2083 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2085 if (tagInfo.counts[src] & 1)
2087 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2088 tagInfo.tags[dst] = tagInfo.tags[src];
2094 g_free (tagInfo.counts);
2097 g_free (tagInfo.tags);
2100 return tagInfo.tags;
2104 copy_segment (GString *string,
2105 gboolean include_hidden,
2106 gboolean include_nonchars,
2107 const GtkTextIter *start,
2108 const GtkTextIter *end)
2110 GtkTextLineSegment *end_seg;
2111 GtkTextLineSegment *seg;
2113 if (gtk_text_iter_equal (start, end))
2116 seg = _gtk_text_iter_get_indexable_segment (start);
2117 end_seg = _gtk_text_iter_get_indexable_segment (end);
2119 if (seg->type == >k_text_char_type)
2121 gboolean copy = TRUE;
2122 gint copy_bytes = 0;
2123 gint copy_start = 0;
2125 /* Don't copy if we're invisible; segments are invisible/not
2126 as a whole, no need to check each char */
2127 if (!include_hidden &&
2128 _gtk_text_btree_char_is_invisible (start))
2131 /* printf (" <invisible>\n"); */
2134 copy_start = _gtk_text_iter_get_segment_byte (start);
2138 /* End is in the same segment; need to copy fewer bytes. */
2139 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2141 copy_bytes = end_byte - copy_start;
2144 copy_bytes = seg->byte_count - copy_start;
2146 g_assert (copy_bytes != 0); /* Due to iter equality check at
2147 front of this function. */
2151 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2153 g_string_append_len (string,
2154 seg->body.chars + copy_start,
2158 /* printf (" :%s\n", string->str); */
2160 else if (seg->type == >k_text_pixbuf_type ||
2161 seg->type == >k_text_child_type)
2163 gboolean copy = TRUE;
2165 if (!include_nonchars)
2169 else if (!include_hidden &&
2170 _gtk_text_btree_char_is_invisible (start))
2177 g_string_append_len (string,
2178 gtk_text_unknown_char_utf8,
2186 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2187 const GtkTextIter *end_orig,
2188 gboolean include_hidden,
2189 gboolean include_nonchars)
2191 GtkTextLineSegment *seg;
2192 GtkTextLineSegment *end_seg;
2200 g_return_val_if_fail (start_orig != NULL, NULL);
2201 g_return_val_if_fail (end_orig != NULL, NULL);
2202 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2203 _gtk_text_iter_get_btree (end_orig), NULL);
2205 start = *start_orig;
2208 gtk_text_iter_order (&start, &end);
2210 retval = g_string_new ("");
2212 tree = _gtk_text_iter_get_btree (&start);
2214 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2216 seg = _gtk_text_iter_get_indexable_segment (&iter);
2217 while (seg != end_seg)
2219 copy_segment (retval, include_hidden, include_nonchars,
2222 _gtk_text_iter_forward_indexable_segment (&iter);
2224 seg = _gtk_text_iter_get_indexable_segment (&iter);
2227 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2230 g_string_free (retval, FALSE);
2235 _gtk_text_btree_line_count (GtkTextBTree *tree)
2237 /* Subtract bogus line at the end; we return a count
2239 return tree->root_node->num_lines - 1;
2243 _gtk_text_btree_char_count (GtkTextBTree *tree)
2245 /* Exclude newline in bogus last line and the
2246 * one in the last line that is after the end iterator
2248 return tree->root_node->num_chars - 2;
2251 #define LOTSA_TAGS 1000
2253 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2255 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2257 int deftagCnts[LOTSA_TAGS];
2258 int *tagCnts = deftagCnts;
2259 GtkTextTag *deftags[LOTSA_TAGS];
2260 GtkTextTag **tags = deftags;
2262 GtkTextBTreeNode *node;
2263 GtkTextLine *siblingline;
2264 GtkTextLineSegment *seg;
2271 line = _gtk_text_iter_get_text_line (iter);
2272 tree = _gtk_text_iter_get_btree (iter);
2273 byte_index = gtk_text_iter_get_line_index (iter);
2275 numTags = gtk_text_tag_table_get_size (tree->table);
2277 /* almost always avoid malloc, so stay out of system calls */
2278 if (LOTSA_TAGS < numTags)
2280 tagCnts = g_new (int, numTags);
2281 tags = g_new (GtkTextTag*, numTags);
2284 for (i=0; i<numTags; i++)
2290 * Record tag toggles within the line of indexPtr but preceding
2294 for (index = 0, seg = line->segments;
2295 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2296 index += seg->byte_count, seg = seg->next)
2298 if ((seg->type == >k_text_toggle_on_type)
2299 || (seg->type == >k_text_toggle_off_type))
2301 tag = seg->body.toggle.info->tag;
2302 if (tag->invisible_set && tag->values->invisible)
2304 tags[tag->priority] = tag;
2305 tagCnts[tag->priority]++;
2311 * Record toggles for tags in lines that are predecessors of
2312 * line but under the same level-0 GtkTextBTreeNode.
2315 for (siblingline = line->parent->children.line;
2316 siblingline != line;
2317 siblingline = siblingline->next)
2319 for (seg = siblingline->segments; seg != NULL;
2322 if ((seg->type == >k_text_toggle_on_type)
2323 || (seg->type == >k_text_toggle_off_type))
2325 tag = seg->body.toggle.info->tag;
2326 if (tag->invisible_set && tag->values->invisible)
2328 tags[tag->priority] = tag;
2329 tagCnts[tag->priority]++;
2336 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2337 * for all siblings that precede that GtkTextBTreeNode.
2340 for (node = line->parent; node->parent != NULL;
2341 node = node->parent)
2343 GtkTextBTreeNode *siblingPtr;
2346 for (siblingPtr = node->parent->children.node;
2347 siblingPtr != node; siblingPtr = siblingPtr->next)
2349 for (summary = siblingPtr->summary; summary != NULL;
2350 summary = summary->next)
2352 if (summary->toggle_count & 1)
2354 tag = summary->info->tag;
2355 if (tag->invisible_set && tag->values->invisible)
2357 tags[tag->priority] = tag;
2358 tagCnts[tag->priority] += summary->toggle_count;
2366 * Now traverse from highest priority to lowest,
2367 * take invisible value from first odd count (= on)
2370 for (i = numTags-1; i >=0; i--)
2374 /* FIXME not sure this should be if 0 */
2376 #ifndef ALWAYS_SHOW_SELECTION
2377 /* who would make the selection invisible? */
2378 if ((tag == tkxt->seltag)
2379 && !(tkxt->flags & GOT_FOCUS))
2385 invisible = tags[i]->values->invisible;
2390 if (LOTSA_TAGS < numTags)
2405 redisplay_region (GtkTextBTree *tree,
2406 const GtkTextIter *start,
2407 const GtkTextIter *end)
2410 GtkTextLine *start_line, *end_line;
2412 if (gtk_text_iter_compare (start, end) > 0)
2414 const GtkTextIter *tmp = start;
2419 start_line = _gtk_text_iter_get_text_line (start);
2420 end_line = _gtk_text_iter_get_text_line (end);
2423 while (view != NULL)
2425 gint start_y, end_y;
2426 GtkTextLineData *ld;
2428 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2430 if (end_line == start_line)
2433 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2435 ld = _gtk_text_line_get_data (end_line, view->view_id);
2437 end_y += ld->height;
2439 gtk_text_layout_changed (view->layout, start_y,
2448 redisplay_mark (GtkTextLineSegment *mark)
2453 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2455 mark->body.mark.obj);
2458 gtk_text_iter_forward_char (&end);
2460 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2461 _gtk_text_btree_invalidate_region (mark->body.mark.tree,
2466 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2468 if (!mark->body.mark.visible)
2471 redisplay_mark (mark);
2475 ensure_not_off_end (GtkTextBTree *tree,
2476 GtkTextLineSegment *mark,
2479 if (gtk_text_iter_get_line (iter) ==
2480 _gtk_text_btree_line_count (tree))
2481 gtk_text_iter_backward_char (iter);
2484 static GtkTextLineSegment*
2485 real_set_mark (GtkTextBTree *tree,
2486 GtkTextMark *existing_mark,
2488 gboolean left_gravity,
2489 const GtkTextIter *where,
2490 gboolean should_exist,
2491 gboolean redraw_selections)
2493 GtkTextLineSegment *mark;
2496 g_return_val_if_fail (tree != NULL, NULL);
2497 g_return_val_if_fail (where != NULL, NULL);
2498 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2501 mark = existing_mark->segment;
2502 else if (name != NULL)
2503 mark = g_hash_table_lookup (tree->mark_table,
2508 if (should_exist && mark == NULL)
2510 g_warning ("No mark `%s' exists!", name);
2514 /* OK if !should_exist and it does already exist, in that case
2520 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2521 _gtk_text_iter_check (&iter);
2525 if (redraw_selections &&
2526 (mark == tree->insert_mark->segment ||
2527 mark == tree->selection_bound_mark->segment))
2529 GtkTextIter old_pos;
2531 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2532 mark->body.mark.obj);
2533 redisplay_region (tree, &old_pos, where);
2537 * don't let visible marks be after the final newline of the
2541 if (mark->body.mark.visible)
2543 ensure_not_off_end (tree, mark, &iter);
2546 /* Redraw the mark's old location. */
2547 redisplay_mark_if_visible (mark);
2549 /* Unlink mark from its current location.
2550 This could hose our iterator... */
2551 gtk_text_btree_unlink_segment (tree, mark,
2552 mark->body.mark.line);
2553 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2554 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2556 segments_changed (tree); /* make sure the iterator recomputes its
2561 mark = _gtk_mark_segment_new (tree,
2565 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2567 if (mark->body.mark.name)
2568 g_hash_table_insert (tree->mark_table,
2569 mark->body.mark.name,
2573 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2574 _gtk_text_iter_check (&iter);
2576 /* Link mark into new location */
2577 gtk_text_btree_link_segment (mark, &iter);
2579 /* Invalidate some iterators. */
2580 segments_changed (tree);
2583 * update the screen at the mark's new location.
2586 redisplay_mark_if_visible (mark);
2588 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2589 _gtk_text_iter_check (&iter);
2591 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2592 _gtk_text_btree_check (tree);
2599 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2600 GtkTextMark *existing_mark,
2602 gboolean left_gravity,
2603 const GtkTextIter *iter,
2604 gboolean should_exist)
2606 GtkTextLineSegment *seg;
2608 seg = real_set_mark (tree, existing_mark,
2609 name, left_gravity, iter, should_exist,
2612 return seg ? seg->body.mark.obj : NULL;
2616 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2620 GtkTextIter tmp_start, tmp_end;
2622 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2624 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2625 tree->selection_bound_mark);
2627 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2639 gtk_text_iter_order (&tmp_start, &tmp_end);
2652 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2653 const GtkTextIter *iter)
2655 GtkTextIter start, end;
2657 if (_gtk_text_btree_get_selection_bounds (tree, &start, &end))
2658 redisplay_region (tree, &start, &end);
2660 /* Move insert AND selection_bound before we redisplay */
2661 real_set_mark (tree, tree->insert_mark,
2662 "insert", FALSE, iter, TRUE, FALSE);
2663 real_set_mark (tree, tree->selection_bound_mark,
2664 "selection_bound", FALSE, iter, TRUE, FALSE);
2668 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2673 g_return_if_fail (tree != NULL);
2674 g_return_if_fail (name != NULL);
2676 mark = g_hash_table_lookup (tree->mark_table,
2679 _gtk_text_btree_remove_mark (tree, mark);
2683 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2684 GtkTextLineSegment *segment)
2687 if (segment->body.mark.name)
2688 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2690 segment->body.mark.tree = NULL;
2691 segment->body.mark.line = NULL;
2693 /* Remove the ref on the mark, which frees segment as a side effect
2694 * if this is the last reference.
2696 g_object_unref (G_OBJECT (segment->body.mark.obj));
2700 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2703 GtkTextLineSegment *segment;
2705 g_return_if_fail (mark != NULL);
2706 g_return_if_fail (tree != NULL);
2708 segment = mark->segment;
2710 if (segment->body.mark.not_deleteable)
2712 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2716 /* This calls cleanup_line and segments_changed */
2717 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2719 _gtk_text_btree_release_mark_segment (tree, segment);
2723 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2724 GtkTextMark *segment)
2726 return segment == tree->insert_mark;
2730 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2731 GtkTextMark *segment)
2733 return segment == tree->selection_bound_mark;
2737 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2740 GtkTextLineSegment *seg;
2742 g_return_val_if_fail (tree != NULL, NULL);
2743 g_return_val_if_fail (name != NULL, NULL);
2745 seg = g_hash_table_lookup (tree->mark_table, name);
2747 return seg ? seg->body.mark.obj : NULL;
2751 * gtk_text_mark_set_visible:
2752 * @mark: a #GtkTextMark
2753 * @setting: visibility of mark
2755 * Sets the visibility of @mark; the insertion point is normally
2756 * visible, i.e. you can see it as a vertical bar. Also, the text
2757 * widget uses a visible mark to indicate where a drop will occur when
2758 * dragging-and-dropping text. Most other marks are not visible.
2759 * Marks are not visible by default.
2763 gtk_text_mark_set_visible (GtkTextMark *mark,
2766 GtkTextLineSegment *seg;
2768 g_return_if_fail (mark != NULL);
2770 seg = mark->segment;
2772 if (seg->body.mark.visible == setting)
2776 seg->body.mark.visible = setting;
2778 redisplay_mark (seg);
2783 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
2786 GtkTextBTreeNode *node;
2787 GtkTextTagInfo *info;
2789 g_return_val_if_fail (tree != NULL, NULL);
2793 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2798 if (info->tag_root == NULL)
2801 node = info->tag_root;
2803 /* We know the tag root has instances of the given
2806 continue_outer_loop:
2807 g_assert (node != NULL);
2808 while (node->level > 0)
2810 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2811 node = node->children.node;
2812 while (node != NULL)
2814 if (gtk_text_btree_node_has_tag (node, tag))
2815 goto continue_outer_loop;
2819 g_assert (node != NULL);
2822 g_assert (node != NULL); /* The tag summaries said some node had
2825 g_assert (node->level == 0);
2827 return node->children.line;
2831 /* Looking for any tag at all (tag == NULL).
2832 Unfortunately this can't be done in a simple and efficient way
2833 right now; so I'm just going to return the
2834 first line in the btree. FIXME */
2835 return _gtk_text_btree_get_line (tree, 0, NULL);
2840 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
2843 GtkTextBTreeNode *node;
2844 GtkTextBTreeNode *last_node;
2846 GtkTextTagInfo *info;
2848 g_return_val_if_fail (tree != NULL, NULL);
2852 info = gtk_text_btree_get_existing_tag_info (tree, tag);
2854 if (info->tag_root == NULL)
2857 node = info->tag_root;
2858 /* We know the tag root has instances of the given
2861 while (node->level > 0)
2863 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
2865 node = node->children.node;
2866 while (node != NULL)
2868 if (gtk_text_btree_node_has_tag (node, tag))
2876 g_assert (node != NULL); /* The tag summaries said some node had
2879 g_assert (node->level == 0);
2881 /* Find the last line in this node */
2882 line = node->children.line;
2883 while (line->next != NULL)
2890 /* This search can't be done efficiently at the moment,
2891 at least not without complexity.
2892 So, we just return the last line.
2894 return _gtk_text_btree_get_end_iter_line (tree);
2904 _gtk_text_line_get_number (GtkTextLine *line)
2907 GtkTextBTreeNode *node, *parent, *node2;
2911 * First count how many lines precede this one in its level-0
2915 node = line->parent;
2917 for (line2 = node->children.line; line2 != line;
2918 line2 = line2->next)
2922 g_error ("gtk_text_btree_line_number couldn't find line");
2928 * Now work up through the levels of the tree one at a time,
2929 * counting how many lines are in GtkTextBTreeNodes preceding the current
2933 for (parent = node->parent ; parent != NULL;
2934 node = parent, parent = parent->parent)
2936 for (node2 = parent->children.node; node2 != node;
2937 node2 = node2->next)
2941 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
2943 index += node2->num_lines;
2949 static GtkTextLineSegment*
2950 find_toggle_segment_before_char (GtkTextLine *line,
2954 GtkTextLineSegment *seg;
2955 GtkTextLineSegment *toggle_seg;
2960 seg = line->segments;
2961 while ( (index + seg->char_count) <= char_in_line )
2963 if (((seg->type == >k_text_toggle_on_type)
2964 || (seg->type == >k_text_toggle_off_type))
2965 && (seg->body.toggle.info->tag == tag))
2968 index += seg->char_count;
2975 static GtkTextLineSegment*
2976 find_toggle_segment_before_byte (GtkTextLine *line,
2980 GtkTextLineSegment *seg;
2981 GtkTextLineSegment *toggle_seg;
2986 seg = line->segments;
2987 while ( (index + seg->byte_count) <= byte_in_line )
2989 if (((seg->type == >k_text_toggle_on_type)
2990 || (seg->type == >k_text_toggle_off_type))
2991 && (seg->body.toggle.info->tag == tag))
2994 index += seg->byte_count;
3002 find_toggle_outside_current_line (GtkTextLine *line,
3006 GtkTextBTreeNode *node;
3007 GtkTextLine *sibling_line;
3008 GtkTextLineSegment *seg;
3009 GtkTextLineSegment *toggle_seg;
3011 GtkTextTagInfo *info = NULL;
3014 * No toggle in this line. Look for toggles for the tag in lines
3015 * that are predecessors of line but under the same
3016 * level-0 GtkTextBTreeNode.
3019 sibling_line = line->parent->children.line;
3020 while (sibling_line != line)
3022 seg = sibling_line->segments;
3025 if (((seg->type == >k_text_toggle_on_type)
3026 || (seg->type == >k_text_toggle_off_type))
3027 && (seg->body.toggle.info->tag == tag))
3033 sibling_line = sibling_line->next;
3036 if (toggle_seg != NULL)
3037 return (toggle_seg->type == >k_text_toggle_on_type);
3040 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3041 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3042 * siblings that precede that GtkTextBTreeNode.
3045 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3051 node = line->parent;
3052 while (node->parent != NULL)
3054 GtkTextBTreeNode *sibling_node;
3056 sibling_node = node->parent->children.node;
3057 while (sibling_node != node)
3061 summary = sibling_node->summary;
3062 while (summary != NULL)
3064 if (summary->info == info)
3065 toggles += summary->toggle_count;
3067 summary = summary->next;
3070 sibling_node = sibling_node->next;
3073 if (node == info->tag_root)
3076 node = node->parent;
3080 * An odd number of toggles means that the tag is present at the
3084 return (toggles & 1) != 0;
3087 /* FIXME this function is far too slow, for no good reason. */
3089 _gtk_text_line_char_has_tag (GtkTextLine *line,
3094 GtkTextLineSegment *toggle_seg;
3096 g_return_val_if_fail (line != NULL, FALSE);
3099 * Check for toggles for the tag in the line but before
3100 * the char. If there is one, its type indicates whether or
3101 * not the character is tagged.
3104 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3106 if (toggle_seg != NULL)
3107 return (toggle_seg->type == >k_text_toggle_on_type);
3109 return find_toggle_outside_current_line (line, tree, tag);
3113 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3118 GtkTextLineSegment *toggle_seg;
3120 g_return_val_if_fail (line != NULL, FALSE);
3123 * Check for toggles for the tag in the line but before
3124 * the char. If there is one, its type indicates whether or
3125 * not the character is tagged.
3128 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3130 if (toggle_seg != NULL)
3131 return (toggle_seg->type == >k_text_toggle_on_type);
3133 return find_toggle_outside_current_line (line, tree, tag);
3137 _gtk_text_line_is_last (GtkTextLine *line,
3140 return line == get_last_line (tree);
3144 ensure_end_iter_line (GtkTextBTree *tree)
3146 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3151 /* n_lines is without the magic line at the end */
3152 n_lines = _gtk_text_btree_line_count (tree);
3154 g_assert (n_lines >= 1);
3156 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3158 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3163 ensure_end_iter_segment (GtkTextBTree *tree)
3165 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3167 GtkTextLineSegment *seg;
3168 GtkTextLineSegment *last_with_chars;
3170 ensure_end_iter_line (tree);
3172 last_with_chars = NULL;
3174 seg = tree->end_iter_line->segments;
3177 if (seg->char_count > 0)
3178 last_with_chars = seg;
3182 tree->end_iter_segment = last_with_chars;
3184 /* We know the last char in the last line is '\n' */
3185 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3186 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3188 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3190 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3191 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3196 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3199 ensure_end_iter_line (tree);
3201 return line == tree->end_iter_line;
3205 _gtk_text_btree_is_end (GtkTextBTree *tree,
3207 GtkTextLineSegment *seg,
3211 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3213 /* Do this first to avoid walking segments in most cases */
3214 if (!_gtk_text_line_contains_end_iter (line, tree))
3217 ensure_end_iter_segment (tree);
3219 if (seg != tree->end_iter_segment)
3222 if (byte_index >= 0)
3223 return byte_index == tree->end_iter_segment_byte_index;
3225 return char_offset == tree->end_iter_segment_char_offset;
3229 _gtk_text_line_next (GtkTextLine *line)
3231 GtkTextBTreeNode *node;
3233 if (line->next != NULL)
3238 * This was the last line associated with the particular parent
3239 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3240 * then search down from that GtkTextBTreeNode to find the first
3244 node = line->parent;
3245 while (node != NULL && node->next == NULL)
3246 node = node->parent;
3252 while (node->level > 0)
3254 node = node->children.node;
3257 g_assert (node->children.line != line);
3259 return node->children.line;
3264 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3268 next = _gtk_text_line_next (line);
3270 /* If we were on the end iter line, we can't go to
3273 if (next && next->next == NULL && /* these checks are optimization only */
3274 _gtk_text_line_next (next) == NULL)
3281 _gtk_text_line_previous (GtkTextLine *line)
3283 GtkTextBTreeNode *node;
3284 GtkTextBTreeNode *node2;
3288 * Find the line under this GtkTextBTreeNode just before the starting line.
3290 prev = line->parent->children.line; /* First line at leaf */
3291 while (prev != line)
3293 if (prev->next == line)
3299 g_error ("gtk_text_btree_previous_line ran out of lines");
3303 * This was the first line associated with the particular parent
3304 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3305 * then search down from that GtkTextBTreeNode to find its last line.
3307 for (node = line->parent; ; node = node->parent)
3309 if (node == NULL || node->parent == NULL)
3311 else if (node != node->parent->children.node)
3315 for (node2 = node->parent->children.node; ;
3316 node2 = node2->children.node)
3318 while (node2->next != node)
3319 node2 = node2->next;
3321 if (node2->level == 0)
3327 for (prev = node2->children.line ; ; prev = prev->next)
3329 if (prev->next == NULL)
3333 g_assert_not_reached ();
3339 _gtk_text_line_data_new (GtkTextLayout *layout,
3342 GtkTextLineData *line_data;
3344 line_data = g_new (GtkTextLineData, 1);
3346 line_data->view_id = layout;
3347 line_data->next = NULL;
3348 line_data->width = 0;
3349 line_data->height = 0;
3350 line_data->valid = FALSE;
3356 _gtk_text_line_add_data (GtkTextLine *line,
3357 GtkTextLineData *data)
3359 g_return_if_fail (line != NULL);
3360 g_return_if_fail (data != NULL);
3361 g_return_if_fail (data->view_id != NULL);
3365 data->next = line->views;
3375 _gtk_text_line_remove_data (GtkTextLine *line,
3378 GtkTextLineData *prev;
3379 GtkTextLineData *iter;
3381 g_return_val_if_fail (line != NULL, NULL);
3382 g_return_val_if_fail (view_id != NULL, NULL);
3386 while (iter != NULL)
3388 if (iter->view_id == view_id)
3397 prev->next = iter->next;
3399 line->views = iter->next;
3408 _gtk_text_line_get_data (GtkTextLine *line,
3411 GtkTextLineData *iter;
3413 g_return_val_if_fail (line != NULL, NULL);
3414 g_return_val_if_fail (view_id != NULL, NULL);
3417 while (iter != NULL)
3419 if (iter->view_id == view_id)
3428 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3429 GtkTextLineData *ld)
3431 /* For now this is totally unoptimized. FIXME?
3433 We could probably optimize the case where the width removed
3434 is less than the max width for the parent node,
3435 and the case where the height is unchanged when we re-wrap.
3438 g_return_if_fail (ld != NULL);
3441 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3445 _gtk_text_line_char_count (GtkTextLine *line)
3447 GtkTextLineSegment *seg;
3451 seg = line->segments;
3454 size += seg->char_count;
3461 _gtk_text_line_byte_count (GtkTextLine *line)
3463 GtkTextLineSegment *seg;
3467 seg = line->segments;
3470 size += seg->byte_count;
3478 _gtk_text_line_char_index (GtkTextLine *target_line)
3480 GSList *node_stack = NULL;
3481 GtkTextBTreeNode *iter;
3485 /* Push all our parent nodes onto a stack */
3486 iter = target_line->parent;
3488 g_assert (iter != NULL);
3490 while (iter != NULL)
3492 node_stack = g_slist_prepend (node_stack, iter);
3494 iter = iter->parent;
3497 /* Check that we have the root node on top of the stack. */
3498 g_assert (node_stack != NULL &&
3499 node_stack->data != NULL &&
3500 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3502 /* Add up chars in all nodes before the nodes in our stack.
3506 iter = node_stack->data;
3507 while (iter != NULL)
3509 GtkTextBTreeNode *child_iter;
3510 GtkTextBTreeNode *next_node;
3512 next_node = node_stack->next ?
3513 node_stack->next->data : NULL;
3514 node_stack = g_slist_remove (node_stack, node_stack->data);
3516 if (iter->level == 0)
3518 /* stack should be empty when we're on the last node */
3519 g_assert (node_stack == NULL);
3520 break; /* Our children are now lines */
3523 g_assert (next_node != NULL);
3524 g_assert (iter != NULL);
3525 g_assert (next_node->parent == iter);
3527 /* Add up chars before us in the tree */
3528 child_iter = iter->children.node;
3529 while (child_iter != next_node)
3531 g_assert (child_iter != NULL);
3533 num_chars += child_iter->num_chars;
3535 child_iter = child_iter->next;
3541 g_assert (iter != NULL);
3542 g_assert (iter == target_line->parent);
3544 /* Since we don't store char counts in lines, only in segments, we
3545 have to iterate over the lines adding up segment char counts
3546 until we find our line. */
3547 line = iter->children.line;
3548 while (line != target_line)
3550 g_assert (line != NULL);
3552 num_chars += _gtk_text_line_char_count (line);
3557 g_assert (line == target_line);
3563 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3567 GtkTextLineSegment *seg;
3570 g_return_val_if_fail (line != NULL, NULL);
3572 offset = byte_offset;
3573 seg = line->segments;
3575 while (offset >= seg->byte_count)
3577 g_assert (seg != NULL); /* means an invalid byte index */
3578 offset -= seg->byte_count;
3583 *seg_offset = offset;
3589 _gtk_text_line_char_to_segment (GtkTextLine *line,
3593 GtkTextLineSegment *seg;
3596 g_return_val_if_fail (line != NULL, NULL);
3598 offset = char_offset;
3599 seg = line->segments;
3601 while (offset >= seg->char_count)
3603 g_assert (seg != NULL); /* means an invalid char index */
3604 offset -= seg->char_count;
3609 *seg_offset = offset;
3615 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3619 GtkTextLineSegment *seg;
3622 g_return_val_if_fail (line != NULL, NULL);
3624 offset = byte_offset;
3625 seg = line->segments;
3627 while (offset > 0 && offset >= seg->byte_count)
3629 g_assert (seg != NULL); /* means an invalid byte index */
3630 offset -= seg->byte_count;
3635 *seg_offset = offset;
3641 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3645 GtkTextLineSegment *seg;
3648 g_return_val_if_fail (line != NULL, NULL);
3650 offset = char_offset;
3651 seg = line->segments;
3653 while (offset > 0 && offset >= seg->char_count)
3655 g_assert (seg != NULL); /* means an invalid byte index */
3656 offset -= seg->char_count;
3661 *seg_offset = offset;
3667 _gtk_text_line_byte_to_char (GtkTextLine *line,
3671 GtkTextLineSegment *seg;
3673 g_return_val_if_fail (line != NULL, 0);
3674 g_return_val_if_fail (byte_offset >= 0, 0);
3677 seg = line->segments;
3678 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3679 the next segment) */
3681 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3683 byte_offset -= seg->byte_count;
3684 char_offset += seg->char_count;
3689 g_assert (seg != NULL);
3691 /* Now byte_offset is the offset into the current segment,
3692 and char_offset is the start of the current segment.
3693 Optimize the case where no chars use > 1 byte */
3694 if (seg->byte_count == seg->char_count)
3695 return char_offset + byte_offset;
3698 if (seg->type == >k_text_char_type)
3699 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3702 g_assert (seg->char_count == 1);
3703 g_assert (byte_offset == 0);
3711 _gtk_text_line_char_to_byte (GtkTextLine *line,
3714 g_warning ("FIXME not implemented");
3719 /* FIXME sync with char_locate (or figure out a clean
3720 way to merge the two functions) */
3722 _gtk_text_line_byte_locate (GtkTextLine *line,
3724 GtkTextLineSegment **segment,
3725 GtkTextLineSegment **any_segment,
3726 gint *seg_byte_offset,
3727 gint *line_byte_offset)
3729 GtkTextLineSegment *seg;
3730 GtkTextLineSegment *after_prev_indexable;
3731 GtkTextLineSegment *after_last_indexable;
3732 GtkTextLineSegment *last_indexable;
3736 g_return_val_if_fail (line != NULL, FALSE);
3737 g_return_val_if_fail (byte_offset >= 0, FALSE);
3740 *any_segment = NULL;
3743 offset = byte_offset;
3745 last_indexable = NULL;
3746 after_last_indexable = line->segments;
3747 after_prev_indexable = line->segments;
3748 seg = line->segments;
3750 /* The loop ends when we're inside a segment;
3751 last_indexable refers to the last segment
3752 we passed entirely. */
3753 while (seg && offset >= seg->byte_count)
3755 if (seg->char_count > 0)
3757 offset -= seg->byte_count;
3758 bytes_in_line += seg->byte_count;
3759 last_indexable = seg;
3760 after_prev_indexable = after_last_indexable;
3761 after_last_indexable = last_indexable->next;
3769 /* We went off the end of the line */
3771 g_warning ("%s: byte index off the end of the line", G_STRLOC);
3778 if (after_last_indexable != NULL)
3779 *any_segment = after_last_indexable;
3781 *any_segment = *segment;
3784 /* Override any_segment if we're in the middle of a segment. */
3786 *any_segment = *segment;
3788 *seg_byte_offset = offset;
3790 g_assert (*segment != NULL);
3791 g_assert (*any_segment != NULL);
3792 g_assert (*seg_byte_offset < (*segment)->byte_count);
3794 *line_byte_offset = bytes_in_line + *seg_byte_offset;
3799 /* FIXME sync with byte_locate (or figure out a clean
3800 way to merge the two functions) */
3802 _gtk_text_line_char_locate (GtkTextLine *line,
3804 GtkTextLineSegment **segment,
3805 GtkTextLineSegment **any_segment,
3806 gint *seg_char_offset,
3807 gint *line_char_offset)
3809 GtkTextLineSegment *seg;
3810 GtkTextLineSegment *after_prev_indexable;
3811 GtkTextLineSegment *after_last_indexable;
3812 GtkTextLineSegment *last_indexable;
3816 g_return_val_if_fail (line != NULL, FALSE);
3817 g_return_val_if_fail (char_offset >= 0, FALSE);
3820 *any_segment = NULL;
3823 offset = char_offset;
3825 last_indexable = NULL;
3826 after_last_indexable = line->segments;
3827 after_prev_indexable = line->segments;
3828 seg = line->segments;
3830 /* The loop ends when we're inside a segment;
3831 last_indexable refers to the last segment
3832 we passed entirely. */
3833 while (seg && offset >= seg->char_count)
3835 if (seg->char_count > 0)
3837 offset -= seg->char_count;
3838 chars_in_line += seg->char_count;
3839 last_indexable = seg;
3840 after_prev_indexable = after_last_indexable;
3841 after_last_indexable = last_indexable->next;
3849 /* end of the line */
3851 g_warning ("%s: char offset off the end of the line", G_STRLOC);
3858 if (after_last_indexable != NULL)
3859 *any_segment = after_last_indexable;
3861 *any_segment = *segment;
3864 /* Override any_segment if we're in the middle of a segment. */
3866 *any_segment = *segment;
3868 *seg_char_offset = offset;
3870 g_assert (*segment != NULL);
3871 g_assert (*any_segment != NULL);
3872 g_assert (*seg_char_offset < (*segment)->char_count);
3874 *line_char_offset = chars_in_line + *seg_char_offset;
3880 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
3882 gint *line_char_offset,
3883 gint *seg_char_offset)
3885 GtkTextLineSegment *seg;
3888 g_return_if_fail (line != NULL);
3889 g_return_if_fail (byte_offset >= 0);
3891 *line_char_offset = 0;
3893 offset = byte_offset;
3894 seg = line->segments;
3896 while (offset >= seg->byte_count)
3898 offset -= seg->byte_count;
3899 *line_char_offset += seg->char_count;
3901 g_assert (seg != NULL); /* means an invalid char offset */
3904 g_assert (seg->char_count > 0); /* indexable. */
3906 /* offset is now the number of bytes into the current segment we
3907 * want to go. Count chars into the current segment.
3910 if (seg->type == >k_text_char_type)
3912 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
3914 g_assert (*seg_char_offset < seg->char_count);
3916 *line_char_offset += *seg_char_offset;
3920 g_assert (offset == 0);
3921 *seg_char_offset = 0;
3926 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
3928 gint *line_byte_offset,
3929 gint *seg_byte_offset)
3931 GtkTextLineSegment *seg;
3934 g_return_if_fail (line != NULL);
3935 g_return_if_fail (char_offset >= 0);
3937 *line_byte_offset = 0;
3939 offset = char_offset;
3940 seg = line->segments;
3942 while (offset >= seg->char_count)
3944 offset -= seg->char_count;
3945 *line_byte_offset += seg->byte_count;
3947 g_assert (seg != NULL); /* means an invalid char offset */
3950 g_assert (seg->char_count > 0); /* indexable. */
3952 /* offset is now the number of chars into the current segment we
3953 want to go. Count bytes into the current segment. */
3955 if (seg->type == >k_text_char_type)
3957 *seg_byte_offset = 0;
3961 const char * start = seg->body.chars + *seg_byte_offset;
3963 bytes = g_utf8_next_char (start) - start;
3964 *seg_byte_offset += bytes;
3968 g_assert (*seg_byte_offset < seg->byte_count);
3970 *line_byte_offset += *seg_byte_offset;
3974 g_assert (offset == 0);
3975 *seg_byte_offset = 0;
3980 node_compare (GtkTextBTreeNode *lhs,
3981 GtkTextBTreeNode *rhs)
3983 GtkTextBTreeNode *iter;
3984 GtkTextBTreeNode *node;
3985 GtkTextBTreeNode *common_parent;
3986 GtkTextBTreeNode *parent_of_lower;
3987 GtkTextBTreeNode *parent_of_higher;
3988 gboolean lhs_is_lower;
3989 GtkTextBTreeNode *lower;
3990 GtkTextBTreeNode *higher;
3992 /* This function assumes that lhs and rhs are not underneath each
3999 if (lhs->level < rhs->level)
4001 lhs_is_lower = TRUE;
4007 lhs_is_lower = FALSE;
4012 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4013 * of the common parent we used to reach the common parent; the
4014 * ordering of these child nodes in the child list is the ordering
4018 /* Get on the same level (may be on same level already) */
4020 while (node->level < higher->level)
4021 node = node->parent;
4023 g_assert (node->level == higher->level);
4025 g_assert (node != higher); /* Happens if lower is underneath higher */
4027 /* Go up until we have two children with a common parent.
4029 parent_of_lower = node;
4030 parent_of_higher = higher;
4032 while (parent_of_lower->parent != parent_of_higher->parent)
4034 parent_of_lower = parent_of_lower->parent;
4035 parent_of_higher = parent_of_higher->parent;
4038 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4040 common_parent = parent_of_lower->parent;
4042 g_assert (common_parent != NULL);
4044 /* See which is first in the list of common_parent's children */
4045 iter = common_parent->children.node;
4046 while (iter != NULL)
4048 if (iter == parent_of_higher)
4050 /* higher is less than lower */
4053 return 1; /* lhs > rhs */
4057 else if (iter == parent_of_lower)
4059 /* lower is less than higher */
4062 return -1; /* lhs < rhs */
4070 g_assert_not_reached ();
4074 /* remember that tag == NULL means "any tag" */
4076 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4080 GtkTextBTreeNode *node;
4081 GtkTextTagInfo *info;
4082 gboolean below_tag_root;
4084 g_return_val_if_fail (line != NULL, NULL);
4086 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4087 _gtk_text_btree_check (tree);
4091 /* Right now we can only offer linear-search if the user wants
4092 * to know about any tag toggle at all.
4094 return _gtk_text_line_next_excluding_last (line);
4097 /* Our tag summaries only have node precision, not line
4098 * precision. This means that if any line under a node could contain a
4099 * tag, then any of the others could also contain a tag.
4101 * In the future we could have some mechanism to keep track of how
4102 * many toggles we've found under a node so far, since we have a
4103 * count of toggles under the node. But for now I'm going with KISS.
4106 /* return same-node line, if any. */
4110 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4114 if (info->tag_root == NULL)
4117 if (info->tag_root == line->parent)
4118 return NULL; /* we were at the last line under the tag root */
4120 /* We need to go up out of this node, and on to the next one with
4121 toggles for the target tag. If we're below the tag root, we need to
4122 find the next node below the tag root that has tag summaries. If
4123 we're not below the tag root, we need to see if the tag root is
4124 after us in the tree, and if so, return the first line underneath
4127 node = line->parent;
4128 below_tag_root = FALSE;
4129 while (node != NULL)
4131 if (node == info->tag_root)
4133 below_tag_root = TRUE;
4137 node = node->parent;
4142 node = line->parent;
4143 while (node != info->tag_root)
4145 if (node->next == NULL)
4146 node = node->parent;
4151 if (gtk_text_btree_node_has_tag (node, tag))
4161 ordering = node_compare (line->parent, info->tag_root);
4165 /* Tag root is ahead of us, so search there. */
4166 node = info->tag_root;
4171 /* Tag root is after us, so no more lines that
4172 * could contain the tag.
4177 g_assert_not_reached ();
4182 g_assert (node != NULL);
4184 /* We have to find the first sub-node of this node that contains
4188 while (node->level > 0)
4190 g_assert (node != NULL); /* If this fails, it likely means an
4191 incorrect tag summary led us on a
4192 wild goose chase down this branch of
4194 node = node->children.node;
4195 while (node != NULL)
4197 if (gtk_text_btree_node_has_tag (node, tag))
4203 g_assert (node != NULL);
4204 g_assert (node->level == 0);
4206 return node->children.line;
4210 prev_line_under_node (GtkTextBTreeNode *node,
4215 prev = node->children.line;
4221 while (prev->next != line)
4231 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4235 GtkTextBTreeNode *node;
4236 GtkTextBTreeNode *found_node = NULL;
4237 GtkTextTagInfo *info;
4238 gboolean below_tag_root;
4240 GtkTextBTreeNode *line_ancestor;
4241 GtkTextBTreeNode *line_ancestor_parent;
4243 /* See next_could_contain_tag () for more extensive comments
4244 * on what's going on here.
4247 g_return_val_if_fail (line != NULL, NULL);
4249 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4250 _gtk_text_btree_check (tree);
4254 /* Right now we can only offer linear-search if the user wants
4255 * to know about any tag toggle at all.
4257 return _gtk_text_line_previous (line);
4260 /* Return same-node line, if any. */
4261 prev = prev_line_under_node (line->parent, line);
4265 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4269 if (info->tag_root == NULL)
4272 if (info->tag_root == line->parent)
4273 return NULL; /* we were at the first line under the tag root */
4275 /* Are we below the tag root */
4276 node = line->parent;
4277 below_tag_root = FALSE;
4278 while (node != NULL)
4280 if (node == info->tag_root)
4282 below_tag_root = TRUE;
4286 node = node->parent;
4291 /* Look for a previous node under this tag root that has our
4295 /* this assertion holds because line->parent is not the
4296 * tag root, we are below the tag root, and the tag
4299 g_assert (line->parent->parent != NULL);
4301 line_ancestor = line->parent;
4302 line_ancestor_parent = line->parent->parent;
4304 node = line_ancestor_parent->children.node;
4305 while (node != line_ancestor &&
4306 line_ancestor != info->tag_root)
4308 GSList *child_nodes = NULL;
4311 /* Create reverse-order list of nodes before
4314 while (node != line_ancestor
4317 child_nodes = g_slist_prepend (child_nodes, node);
4322 /* Try to find a node with our tag on it in the list */
4326 GtkTextBTreeNode *this_node = tmp->data;
4328 g_assert (this_node != line_ancestor);
4330 if (gtk_text_btree_node_has_tag (this_node, tag))
4332 found_node = this_node;
4333 g_slist_free (child_nodes);
4337 tmp = g_slist_next (tmp);
4340 g_slist_free (child_nodes);
4342 /* Didn't find anything on this level; go up one level. */
4343 line_ancestor = line_ancestor_parent;
4344 line_ancestor_parent = line_ancestor->parent;
4346 node = line_ancestor_parent->children.node;
4356 ordering = node_compare (line->parent, info->tag_root);
4360 /* Tag root is ahead of us, so no more lines
4367 /* Tag root is after us, so grab last tagged
4368 * line underneath the tag root.
4370 found_node = info->tag_root;
4374 g_assert_not_reached ();
4379 g_assert (found_node != NULL);
4381 /* We have to find the last sub-node of this node that contains
4386 while (node->level > 0)
4388 GSList *child_nodes = NULL;
4390 g_assert (node != NULL); /* If this fails, it likely means an
4391 incorrect tag summary led us on a
4392 wild goose chase down this branch of
4395 node = node->children.node;
4396 while (node != NULL)
4398 child_nodes = g_slist_prepend (child_nodes, node);
4402 node = NULL; /* detect failure to find a child node. */
4405 while (iter != NULL)
4407 if (gtk_text_btree_node_has_tag (iter->data, tag))
4409 /* recurse into this node. */
4414 iter = g_slist_next (iter);
4417 g_slist_free (child_nodes);
4419 g_assert (node != NULL);
4422 g_assert (node != NULL);
4423 g_assert (node->level == 0);
4425 /* this assertion is correct, but slow. */
4426 /* g_assert (node_compare (node, line->parent) < 0); */
4428 /* Return last line in this node. */
4430 prev = node->children.line;
4438 * Non-public function implementations
4442 summary_list_destroy (Summary *summary)
4445 while (summary != NULL)
4447 next = summary->next;
4448 summary_destroy (summary);
4454 get_last_line (GtkTextBTree *tree)
4456 if (tree->last_line_stamp != tree->chars_changed_stamp)
4462 n_lines = _gtk_text_btree_line_count (tree);
4464 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4466 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4468 tree->last_line_stamp = tree->chars_changed_stamp;
4469 tree->last_line = line;
4472 return tree->last_line;
4480 gtk_text_line_new (void)
4484 line = g_new0(GtkTextLine, 1);
4490 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4492 GtkTextLineData *ld;
4493 GtkTextLineData *next;
4495 g_return_if_fail (line != NULL);
4502 view = gtk_text_btree_get_view (tree, ld->view_id);
4504 g_assert (view != NULL);
4507 gtk_text_layout_free_line_data (view->layout, line, ld);
4516 gtk_text_line_set_parent (GtkTextLine *line,
4517 GtkTextBTreeNode *node)
4519 if (line->parent == node)
4521 line->parent = node;
4522 gtk_text_btree_node_invalidate_upward (node, NULL);
4526 cleanup_line (GtkTextLine *line)
4528 GtkTextLineSegment *seg, **prev_p;
4532 * Make a pass over all of the segments in the line, giving each
4533 * a chance to clean itself up. This could potentially change
4534 * the structure of the line, e.g. by merging two segments
4535 * together or having two segments cancel themselves; if so,
4536 * then repeat the whole process again, since the first structure
4537 * change might make other structure changes possible. Repeat
4538 * until eventually there are no changes.
4545 for (prev_p = &line->segments, seg = *prev_p;
4547 prev_p = &(*prev_p)->next, seg = *prev_p)
4549 if (seg->type->cleanupFunc != NULL)
4551 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4564 node_data_new (gpointer view_id)
4568 nd = g_new (NodeData, 1);
4570 nd->view_id = view_id;
4580 node_data_destroy (NodeData *nd)
4586 node_data_list_destroy (NodeData *nd)
4592 while (iter != NULL)
4595 node_data_destroy (iter);
4601 node_data_find (NodeData *nd, gpointer view_id)
4605 if (nd->view_id == view_id)
4613 summary_destroy (Summary *summary)
4615 /* Fill with error-triggering garbage */
4616 summary->info = (void*)0x1;
4617 summary->toggle_count = 567;
4618 summary->next = (void*)0x1;
4622 static GtkTextBTreeNode*
4623 gtk_text_btree_node_new (void)
4625 GtkTextBTreeNode *node;
4627 node = g_new (GtkTextBTreeNode, 1);
4629 node->node_data = NULL;
4635 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4636 GtkTextTagInfo *info,
4641 summary = node->summary;
4642 while (summary != NULL)
4644 if (summary->info == info)
4646 summary->toggle_count += adjust;
4650 summary = summary->next;
4653 if (summary == NULL)
4655 /* didn't find a summary for our tag. */
4656 g_return_if_fail (adjust > 0);
4657 summary = g_new (Summary, 1);
4658 summary->info = info;
4659 summary->toggle_count = adjust;
4660 summary->next = node->summary;
4661 node->summary = summary;
4665 /* Note that the tag root and above do not have summaries
4666 for the tag; only nodes below the tag root have
4669 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4673 summary = node->summary;
4674 while (summary != NULL)
4677 summary->info->tag == tag)
4680 summary = summary->next;
4686 /* Add node and all children to the damage region. */
4688 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4692 nd = node->node_data;
4699 if (node->level == 0)
4703 line = node->children.line;
4704 while (line != NULL)
4706 GtkTextLineData *ld;
4720 GtkTextBTreeNode *child;
4722 child = node->children.node;
4724 while (child != NULL)
4726 gtk_text_btree_node_invalidate_downward (child);
4728 child = child->next;
4734 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4736 GtkTextBTreeNode *iter;
4739 while (iter != NULL)
4745 nd = node_data_find (iter->node_data, view_id);
4747 if (nd == NULL || !nd->valid)
4748 break; /* Once a node is invalid, we know its parents are as well. */
4754 gboolean should_continue = FALSE;
4756 nd = iter->node_data;
4761 should_continue = TRUE;
4768 if (!should_continue)
4769 break; /* This node was totally invalidated, so are its
4773 iter = iter->parent;
4779 * _gtk_text_btree_is_valid:
4780 * @tree: a #GtkTextBTree
4781 * @view_id: ID for the view
4783 * Check to see if the entire #GtkTextBTree is valid or not for
4786 * Return value: %TRUE if the entire #GtkTextBTree is valid
4789 _gtk_text_btree_is_valid (GtkTextBTree *tree,
4793 g_return_val_if_fail (tree != NULL, FALSE);
4795 nd = node_data_find (tree->root_node->node_data, view_id);
4796 return (nd && nd->valid);
4799 typedef struct _ValidateState ValidateState;
4801 struct _ValidateState
4803 gint remaining_pixels;
4804 gboolean in_validation;
4811 gtk_text_btree_node_validate (BTreeView *view,
4812 GtkTextBTreeNode *node,
4814 ValidateState *state)
4816 gint node_valid = TRUE;
4817 gint node_width = 0;
4818 gint node_height = 0;
4820 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
4821 g_return_if_fail (!nd->valid);
4823 if (node->level == 0)
4825 GtkTextLine *line = node->children.line;
4826 GtkTextLineData *ld;
4828 /* Iterate over leading valid lines */
4829 while (line != NULL)
4831 ld = _gtk_text_line_get_data (line, view_id);
4833 if (!ld || !ld->valid)
4835 else if (state->in_validation)
4837 state->in_validation = FALSE;
4842 state->y += ld->height;
4843 node_width = MAX (ld->width, node_width);
4844 node_height += ld->height;
4850 state->in_validation = TRUE;
4852 /* Iterate over invalid lines */
4853 while (line != NULL)
4855 ld = _gtk_text_line_get_data (line, view_id);
4857 if (ld && ld->valid)
4862 state->old_height += ld->height;
4863 ld = gtk_text_layout_wrap (view->layout, line, ld);
4864 state->new_height += ld->height;
4866 node_width = MAX (ld->width, node_width);
4867 node_height += ld->height;
4869 state->remaining_pixels -= ld->height;
4870 if (state->remaining_pixels <= 0)
4880 /* Iterate over the remaining lines */
4881 while (line != NULL)
4883 ld = _gtk_text_line_get_data (line, view_id);
4884 state->in_validation = FALSE;
4886 if (!ld || !ld->valid)
4891 node_width = MAX (ld->width, node_width);
4892 node_height += ld->height;
4900 GtkTextBTreeNode *child;
4903 child = node->children.node;
4905 /* Iterate over leading valid nodes */
4908 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4910 if (!child_nd->valid)
4912 else if (state->in_validation)
4914 state->in_validation = FALSE;
4919 state->y += child_nd->height;
4920 node_width = MAX (node_width, child_nd->width);
4921 node_height += child_nd->height;
4924 child = child->next;
4927 /* Iterate over invalid nodes */
4930 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4932 if (child_nd->valid)
4936 gtk_text_btree_node_validate (view, child, view_id, state);
4938 if (!child_nd->valid)
4940 node_width = MAX (node_width, child_nd->width);
4941 node_height += child_nd->height;
4943 if (!state->in_validation || state->remaining_pixels <= 0)
4945 child = child->next;
4950 child = child->next;
4953 /* Iterate over the remaining lines */
4956 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
4957 state->in_validation = FALSE;
4959 if (!child_nd->valid)
4962 node_width = MAX (child_nd->width, node_width);
4963 node_height += child_nd->height;
4965 child = child->next;
4969 nd->width = node_width;
4970 nd->height = node_height;
4971 nd->valid = node_valid;
4975 * _gtk_text_btree_validate:
4976 * @tree: a #GtkTextBTree
4978 * @max_pixels: the maximum number of pixels to validate. (No more
4979 * than one paragraph beyond this limit will be validated)
4980 * @y: location to store starting y coordinate of validated region
4981 * @old_height: location to store old height of validated region
4982 * @new_height: location to store new height of validated region
4984 * Validate a single contiguous invalid region of a #GtkTextBTree for
4987 * Return value: %TRUE if a region has been validated, %FALSE if the
4988 * entire tree was already valid.
4991 _gtk_text_btree_validate (GtkTextBTree *tree,
5000 g_return_val_if_fail (tree != NULL, FALSE);
5002 view = gtk_text_btree_get_view (tree, view_id);
5003 g_return_val_if_fail (view != NULL, FALSE);
5005 if (!_gtk_text_btree_is_valid (tree, view_id))
5007 ValidateState state;
5009 state.remaining_pixels = max_pixels;
5010 state.in_validation = FALSE;
5012 state.old_height = 0;
5013 state.new_height = 0;
5015 gtk_text_btree_node_validate (view,
5022 *old_height = state.old_height;
5024 *new_height = state.new_height;
5026 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5027 _gtk_text_btree_check (tree);
5036 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5040 gboolean *valid_out)
5044 gboolean valid = TRUE;
5046 if (node->level == 0)
5048 GtkTextLine *line = node->children.line;
5050 while (line != NULL)
5052 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5054 if (!ld || !ld->valid)
5059 width = MAX (ld->width, width);
5060 height += ld->height;
5068 GtkTextBTreeNode *child = node->children.node;
5072 NodeData *child_nd = node_data_find (child->node_data, view_id);
5074 if (!child_nd || !child_nd->valid)
5079 width = MAX (child_nd->width, width);
5080 height += child_nd->height;
5083 child = child->next;
5088 *height_out = height;
5093 /* Recompute the validity and size of the view data for a given
5094 * view at this node from the immediate children of the node
5097 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5100 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5105 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5106 &width, &height, &valid);
5108 nd->height = height;
5115 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5120 gtk_text_btree_node_check_valid (node, view_id);
5121 node = node->parent;
5126 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5129 if (node->level == 0)
5131 return gtk_text_btree_node_check_valid (node, view_id);
5135 GtkTextBTreeNode *child = node->children.node;
5137 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5145 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5147 if (!child_nd->valid)
5149 nd->width = MAX (child_nd->width, nd->width);
5150 nd->height += child_nd->height;
5152 child = child->next;
5161 * _gtk_text_btree_validate_line:
5162 * @tree: a #GtkTextBTree
5163 * @line: line to validate
5164 * @view_id: view ID for the view to validate
5166 * Revalidate a single line of the btree for the given view, propagate
5167 * results up through the entire tree.
5170 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5174 GtkTextLineData *ld;
5177 g_return_if_fail (tree != NULL);
5178 g_return_if_fail (line != NULL);
5180 view = gtk_text_btree_get_view (tree, view_id);
5181 g_return_if_fail (view != NULL);
5183 ld = _gtk_text_line_get_data (line, view_id);
5184 if (!ld || !ld->valid)
5186 ld = gtk_text_layout_wrap (view->layout, line, ld);
5188 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5193 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5195 if (node->level == 0)
5199 line = node->children.line;
5200 while (line != NULL)
5202 GtkTextLineData *ld;
5204 ld = _gtk_text_line_remove_data (line, view_id);
5207 gtk_text_layout_free_line_data (view->layout, line, ld);
5214 GtkTextBTreeNode *child;
5216 child = node->children.node;
5218 while (child != NULL)
5221 gtk_text_btree_node_remove_view (view, child, view_id);
5223 child = child->next;
5227 gtk_text_btree_node_remove_data (node, view_id);
5231 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5233 if (node->level == 0)
5236 GtkTextLineSegment *seg;
5238 while (node->children.line != NULL)
5240 line = node->children.line;
5241 node->children.line = line->next;
5242 while (line->segments != NULL)
5244 seg = line->segments;
5245 line->segments = seg->next;
5247 (*seg->type->deleteFunc) (seg, line, TRUE);
5249 gtk_text_line_destroy (tree, line);
5254 GtkTextBTreeNode *childPtr;
5256 while (node->children.node != NULL)
5258 childPtr = node->children.node;
5259 node->children.node = childPtr->next;
5260 gtk_text_btree_node_destroy (tree, childPtr);
5264 gtk_text_btree_node_free_empty (tree, node);
5268 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5269 GtkTextBTreeNode *node)
5271 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5272 (node->level == 0 && node->children.line == NULL));
5274 summary_list_destroy (node->summary);
5275 node_data_list_destroy (node->node_data);
5280 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5284 nd = node->node_data;
5287 if (nd->view_id == view_id)
5295 nd = node_data_new (view_id);
5297 if (node->node_data)
5298 nd->next = node->node_data;
5300 node->node_data = nd;
5307 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5313 nd = node->node_data;
5316 if (nd->view_id == view_id)
5327 prev->next = nd->next;
5329 if (node->node_data == nd)
5330 node->node_data = nd->next;
5334 node_data_destroy (nd);
5338 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5339 gint *width, gint *height)
5343 g_return_if_fail (width != NULL);
5344 g_return_if_fail (height != NULL);
5346 nd = gtk_text_btree_node_ensure_data (node, view_id);
5351 *height = nd->height;
5354 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5355 * here isn't quite right, since for a lot of operations we want to
5356 * know which children of the common parent correspond to the two nodes
5357 * (e.g., when computing the order of two iters)
5359 static GtkTextBTreeNode *
5360 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5361 GtkTextBTreeNode *node2)
5363 while (node1->level < node2->level)
5364 node1 = node1->parent;
5365 while (node2->level < node1->level)
5366 node2 = node2->parent;
5367 while (node1 != node2)
5369 node1 = node1->parent;
5370 node2 = node2->parent;
5381 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5386 while (view != NULL)
5388 if (view->view_id == view_id)
5397 get_tree_bounds (GtkTextBTree *tree,
5401 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5402 _gtk_text_btree_get_end_iter (tree, end);
5406 tag_changed_cb (GtkTextTagTable *table,
5408 gboolean size_changed,
5413 /* We need to queue a relayout on all regions that are tagged with
5420 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5422 /* Must be a last toggle if there was a first one. */
5423 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5424 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5425 _gtk_text_btree_invalidate_region (tree,
5432 /* We only need to queue a redraw, not a relayout */
5437 while (view != NULL)
5441 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5442 gtk_text_layout_changed (view->layout, 0, height, height);
5450 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5453 /* Remove the tag from the tree */
5458 get_tree_bounds (tree, &start, &end);
5460 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5461 gtk_text_btree_remove_tag_info (tree, tag);
5465 /* Rebalance the out-of-whack node "node" */
5467 gtk_text_btree_rebalance (GtkTextBTree *tree,
5468 GtkTextBTreeNode *node)
5471 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5472 * up through the tree one GtkTextBTreeNode at a time until the root
5473 * GtkTextBTreeNode has been processed.
5476 while (node != NULL)
5478 GtkTextBTreeNode *new_node, *child;
5483 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5484 * then split off all but the first MIN_CHILDREN into a separate
5485 * GtkTextBTreeNode following the original one. Then repeat until the
5486 * GtkTextBTreeNode has a decent size.
5489 if (node->num_children > MAX_CHILDREN)
5494 * If the GtkTextBTreeNode being split is the root
5495 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5499 if (node->parent == NULL)
5501 new_node = gtk_text_btree_node_new ();
5502 new_node->parent = NULL;
5503 new_node->next = NULL;
5504 new_node->summary = NULL;
5505 new_node->level = node->level + 1;
5506 new_node->children.node = node;
5507 recompute_node_counts (tree, new_node);
5508 tree->root_node = new_node;
5510 new_node = gtk_text_btree_node_new ();
5511 new_node->parent = node->parent;
5512 new_node->next = node->next;
5513 node->next = new_node;
5514 new_node->summary = NULL;
5515 new_node->level = node->level;
5516 new_node->num_children = node->num_children - MIN_CHILDREN;
5517 if (node->level == 0)
5519 for (i = MIN_CHILDREN-1,
5520 line = node->children.line;
5521 i > 0; i--, line = line->next)
5523 /* Empty loop body. */
5525 new_node->children.line = line->next;
5530 for (i = MIN_CHILDREN-1,
5531 child = node->children.node;
5532 i > 0; i--, child = child->next)
5534 /* Empty loop body. */
5536 new_node->children.node = child->next;
5539 recompute_node_counts (tree, node);
5540 node->parent->num_children++;
5542 if (node->num_children <= MAX_CHILDREN)
5544 recompute_node_counts (tree, node);
5550 while (node->num_children < MIN_CHILDREN)
5552 GtkTextBTreeNode *other;
5553 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5554 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5555 int total_children, first_children, i;
5558 * Too few children for this GtkTextBTreeNode. If this is the root then,
5559 * it's OK for it to have less than MIN_CHILDREN children
5560 * as long as it's got at least two. If it has only one
5561 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5562 * the tree and use its child as the new root.
5565 if (node->parent == NULL)
5567 if ((node->num_children == 1) && (node->level > 0))
5569 tree->root_node = node->children.node;
5570 tree->root_node->parent = NULL;
5572 node->children.node = NULL;
5573 gtk_text_btree_node_free_empty (tree, node);
5579 * Not the root. Make sure that there are siblings to
5583 if (node->parent->num_children < 2)
5585 gtk_text_btree_rebalance (tree, node->parent);
5590 * Find a sibling neighbor to borrow from, and arrange for
5591 * node to be the earlier of the pair.
5594 if (node->next == NULL)
5596 for (other = node->parent->children.node;
5597 other->next != node;
5598 other = other->next)
5600 /* Empty loop body. */
5607 * We're going to either merge the two siblings together
5608 * into one GtkTextBTreeNode or redivide the children among them to
5609 * balance their loads. As preparation, join their two
5610 * child lists into a single list and remember the half-way
5611 * point in the list.
5614 total_children = node->num_children + other->num_children;
5615 first_children = total_children/2;
5616 if (node->children.node == NULL)
5618 node->children = other->children;
5619 other->children.node = NULL;
5620 other->children.line = NULL;
5622 if (node->level == 0)
5626 for (line = node->children.line, i = 1;
5628 line = line->next, i++)
5630 if (i == first_children)
5635 line->next = other->children.line;
5636 while (i <= first_children)
5645 GtkTextBTreeNode *child;
5647 for (child = node->children.node, i = 1;
5648 child->next != NULL;
5649 child = child->next, i++)
5651 if (i <= first_children)
5653 if (i == first_children)
5655 halfwaynode = child;
5659 child->next = other->children.node;
5660 while (i <= first_children)
5662 halfwaynode = child;
5663 child = child->next;
5669 * If the two siblings can simply be merged together, do it.
5672 if (total_children <= MAX_CHILDREN)
5674 recompute_node_counts (tree, node);
5675 node->next = other->next;
5676 node->parent->num_children--;
5678 other->children.node = NULL;
5679 other->children.line = NULL;
5680 gtk_text_btree_node_free_empty (tree, other);
5685 * The siblings can't be merged, so just divide their
5686 * children evenly between them.
5689 if (node->level == 0)
5691 other->children.line = halfwayline->next;
5692 halfwayline->next = NULL;
5696 other->children.node = halfwaynode->next;
5697 halfwaynode->next = NULL;
5700 recompute_node_counts (tree, node);
5701 recompute_node_counts (tree, other);
5704 node = node->parent;
5709 post_insert_fixup (GtkTextBTree *tree,
5711 gint line_count_delta,
5712 gint char_count_delta)
5715 GtkTextBTreeNode *node;
5718 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5719 * point, then rebalance the tree if necessary.
5722 for (node = line->parent ; node != NULL;
5723 node = node->parent)
5725 node->num_lines += line_count_delta;
5726 node->num_chars += char_count_delta;
5728 node = line->parent;
5729 node->num_children += line_count_delta;
5731 if (node->num_children > MAX_CHILDREN)
5733 gtk_text_btree_rebalance (tree, node);
5736 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5737 _gtk_text_btree_check (tree);
5740 static GtkTextTagInfo*
5741 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5744 GtkTextTagInfo *info;
5748 list = tree->tag_infos;
5749 while (list != NULL)
5752 if (info->tag == tag)
5755 list = g_slist_next (list);
5761 static GtkTextTagInfo*
5762 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
5765 GtkTextTagInfo *info;
5767 info = gtk_text_btree_get_existing_tag_info (tree, tag);
5771 /* didn't find it, create. */
5773 info = g_new (GtkTextTagInfo, 1);
5776 g_object_ref (G_OBJECT (tag));
5777 info->tag_root = NULL;
5778 info->toggle_count = 0;
5780 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
5783 g_print ("Created tag info %p for tag %s(%p)\n",
5784 info, info->tag->name ? info->tag->name : "anon",
5793 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
5796 GtkTextTagInfo *info;
5801 list = tree->tag_infos;
5802 while (list != NULL)
5805 if (info->tag == tag)
5808 g_print ("Removing tag info %p for tag %s(%p)\n",
5809 info, info->tag->name ? info->tag->name : "anon",
5815 prev->next = list->next;
5819 tree->tag_infos = list->next;
5822 g_slist_free (list);
5824 g_object_unref (G_OBJECT (info->tag));
5831 list = g_slist_next (list);
5836 recompute_level_zero_counts (GtkTextBTreeNode *node)
5839 GtkTextLineSegment *seg;
5841 g_assert (node->level == 0);
5843 line = node->children.line;
5844 while (line != NULL)
5846 node->num_children++;
5849 if (line->parent != node)
5850 gtk_text_line_set_parent (line, node);
5852 seg = line->segments;
5856 node->num_chars += seg->char_count;
5858 if (((seg->type != >k_text_toggle_on_type)
5859 && (seg->type != >k_text_toggle_off_type))
5860 || !(seg->body.toggle.inNodeCounts))
5866 GtkTextTagInfo *info;
5868 info = seg->body.toggle.info;
5870 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
5881 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
5884 GtkTextBTreeNode *child;
5886 g_assert (node->level > 0);
5888 child = node->children.node;
5889 while (child != NULL)
5891 node->num_children += 1;
5892 node->num_lines += child->num_lines;
5893 node->num_chars += child->num_chars;
5895 if (child->parent != node)
5897 child->parent = node;
5898 gtk_text_btree_node_invalidate_upward (node, NULL);
5901 summary = child->summary;
5902 while (summary != NULL)
5904 gtk_text_btree_node_adjust_toggle_count (node,
5906 summary->toggle_count);
5908 summary = summary->next;
5911 child = child->next;
5916 *----------------------------------------------------------------------
5918 * recompute_node_counts --
5920 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
5921 * (tags, child information, etc.) by scanning the information in
5922 * its descendants. This procedure is called during rebalancing
5923 * when a GtkTextBTreeNode's child structure has changed.
5929 * The tag counts for node are modified to reflect its current
5930 * child structure, as are its num_children, num_lines, num_chars fields.
5931 * Also, all of the childrens' parent fields are made to point
5934 *----------------------------------------------------------------------
5938 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
5941 Summary *summary, *summary2;
5944 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
5945 * the existing Summary records (most of them will probably be reused).
5948 summary = node->summary;
5949 while (summary != NULL)
5951 summary->toggle_count = 0;
5952 summary = summary->next;
5955 node->num_children = 0;
5956 node->num_lines = 0;
5957 node->num_chars = 0;
5960 * Scan through the children, adding the childrens' tag counts into
5961 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
5965 if (node->level == 0)
5966 recompute_level_zero_counts (node);
5968 recompute_level_nonzero_counts (node);
5973 gtk_text_btree_node_check_valid (node, view->view_id);
5978 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
5979 * records that still have a zero count, or that have all the toggles.
5980 * The GtkTextBTreeNode with the children that account for all the tags toggles
5981 * have no summary information, and they become the tag_root for the tag.
5985 for (summary = node->summary; summary != NULL; )
5987 if (summary->toggle_count > 0 &&
5988 summary->toggle_count < summary->info->toggle_count)
5990 if (node->level == summary->info->tag_root->level)
5993 * The tag's root GtkTextBTreeNode split and some toggles left.
5994 * The tag root must move up a level.
5996 summary->info->tag_root = node->parent;
5999 summary = summary->next;
6002 if (summary->toggle_count == summary->info->toggle_count)
6005 * A GtkTextBTreeNode merge has collected all the toggles under
6006 * one GtkTextBTreeNode. Push the root down to this level.
6008 summary->info->tag_root = node;
6010 if (summary2 != NULL)
6012 summary2->next = summary->next;
6013 summary_destroy (summary);
6014 summary = summary2->next;
6018 node->summary = summary->next;
6019 summary_destroy (summary);
6020 summary = node->summary;
6026 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6027 GtkTextTagInfo *info,
6028 gint delta) /* may be negative */
6030 Summary *summary, *prevPtr;
6031 GtkTextBTreeNode *node2Ptr;
6032 int rootLevel; /* Level of original tag root */
6034 info->toggle_count += delta;
6036 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6038 info->tag_root = node;
6043 * Note the level of the existing root for the tag so we can detect
6044 * if it needs to be moved because of the toggle count change.
6047 rootLevel = info->tag_root->level;
6050 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6051 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6055 for ( ; node != info->tag_root; node = node->parent)
6058 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6059 * perhaps all we have to do is adjust its count.
6062 for (prevPtr = NULL, summary = node->summary;
6064 prevPtr = summary, summary = summary->next)
6066 if (summary->info == info)
6071 if (summary != NULL)
6073 summary->toggle_count += delta;
6074 if (summary->toggle_count > 0 &&
6075 summary->toggle_count < info->toggle_count)
6079 if (summary->toggle_count != 0)
6082 * Should never find a GtkTextBTreeNode with max toggle count at this
6083 * point (there shouldn't have been a summary entry in the
6087 g_error ("%s: bad toggle count (%d) max (%d)",
6088 G_STRLOC, summary->toggle_count, info->toggle_count);
6092 * Zero toggle count; must remove this tag from the list.
6095 if (prevPtr == NULL)
6097 node->summary = summary->next;
6101 prevPtr->next = summary->next;
6103 summary_destroy (summary);
6108 * This tag isn't currently in the summary information list.
6111 if (rootLevel == node->level)
6115 * The old tag root is at the same level in the tree as this
6116 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6117 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6118 * as well as the old root (if not, we'll move it up again
6119 * the next time through the loop). To push it up one level
6120 * we copy the original toggle count into the summary
6121 * information at the old root and change the root to its
6122 * parent GtkTextBTreeNode.
6125 GtkTextBTreeNode *rootnode = info->tag_root;
6126 summary = (Summary *) g_malloc (sizeof (Summary));
6127 summary->info = info;
6128 summary->toggle_count = info->toggle_count - delta;
6129 summary->next = rootnode->summary;
6130 rootnode->summary = summary;
6131 rootnode = rootnode->parent;
6132 rootLevel = rootnode->level;
6133 info->tag_root = rootnode;
6135 summary = (Summary *) g_malloc (sizeof (Summary));
6136 summary->info = info;
6137 summary->toggle_count = delta;
6138 summary->next = node->summary;
6139 node->summary = summary;
6144 * If we've decremented the toggle count, then it may be necessary
6145 * to push the tag root down one or more levels.
6152 if (info->toggle_count == 0)
6154 info->tag_root = (GtkTextBTreeNode *) NULL;
6157 node = info->tag_root;
6158 while (node->level > 0)
6161 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6162 * toggles. If so, push the root down one level.
6165 for (node2Ptr = node->children.node;
6166 node2Ptr != (GtkTextBTreeNode *)NULL ;
6167 node2Ptr = node2Ptr->next)
6169 for (prevPtr = NULL, summary = node2Ptr->summary;
6171 prevPtr = summary, summary = summary->next)
6173 if (summary->info == info)
6178 if (summary == NULL)
6182 if (summary->toggle_count != info->toggle_count)
6185 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6192 * This GtkTextBTreeNode has all the toggles, so push down the root.
6195 if (prevPtr == NULL)
6197 node2Ptr->summary = summary->next;
6201 prevPtr->next = summary->next;
6203 summary_destroy (summary);
6204 info->tag_root = node2Ptr;
6207 node = info->tag_root;
6212 *----------------------------------------------------------------------
6216 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6217 * increments the count for a particular tag, adding a new
6218 * entry for that tag if there wasn't one previously.
6224 * The information at *tagInfoPtr may be modified, and the arrays
6225 * may be reallocated to make them larger.
6227 *----------------------------------------------------------------------
6231 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6236 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6237 count > 0; tag_p++, count--)
6241 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6247 * There isn't currently an entry for this tag, so we have to
6248 * make a new one. If the arrays are full, then enlarge the
6252 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6254 GtkTextTag **newTags;
6255 int *newCounts, newSize;
6257 newSize = 2*tagInfoPtr->arraySize;
6258 newTags = (GtkTextTag **) g_malloc ((unsigned)
6259 (newSize*sizeof (GtkTextTag *)));
6260 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6261 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6262 g_free ((char *) tagInfoPtr->tags);
6263 tagInfoPtr->tags = newTags;
6264 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6265 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6266 tagInfoPtr->arraySize *sizeof (int));
6267 g_free ((char *) tagInfoPtr->counts);
6268 tagInfoPtr->counts = newCounts;
6269 tagInfoPtr->arraySize = newSize;
6272 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6273 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6274 tagInfoPtr->numTags++;
6278 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6279 const GtkTextIter *iter)
6281 GtkTextLineSegment *prev;
6285 line = _gtk_text_iter_get_text_line (iter);
6286 tree = _gtk_text_iter_get_btree (iter);
6288 prev = gtk_text_line_segment_split (iter);
6291 seg->next = line->segments;
6292 line->segments = seg;
6296 seg->next = prev->next;
6299 cleanup_line (line);
6300 segments_changed (tree);
6302 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6303 _gtk_text_btree_check (tree);
6307 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6308 GtkTextLineSegment *seg,
6311 GtkTextLineSegment *prev;
6313 if (line->segments == seg)
6315 line->segments = seg->next;
6319 for (prev = line->segments; prev->next != seg;
6322 /* Empty loop body. */
6324 prev->next = seg->next;
6326 cleanup_line (line);
6327 segments_changed (tree);
6331 * This is here because it requires BTree internals, it logically
6332 * belongs in gtktextsegment.c
6337 *--------------------------------------------------------------
6339 * _gtk_toggle_segment_check_func --
6341 * This procedure is invoked to perform consistency checks
6342 * on toggle segments.
6348 * If a consistency problem is found the procedure g_errors.
6350 *--------------------------------------------------------------
6354 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6360 if (segPtr->byte_count != 0)
6362 g_error ("toggle_segment_check_func: segment had non-zero size");
6364 if (!segPtr->body.toggle.inNodeCounts)
6366 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6368 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6369 for (summary = line->parent->summary; ;
6370 summary = summary->next)
6372 if (summary == NULL)
6376 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6383 if (summary->info == segPtr->body.toggle.info)
6387 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6399 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6400 GtkTextBTreeNode *node,
6410 while (view != NULL)
6412 if (view->view_id == nd->view_id)
6419 g_error ("Node has data for a view %p no longer attached to the tree",
6422 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6423 &width, &height, &valid);
6425 /* valid aggregate not checked the same as width/height, because on
6426 * btree rebalance we can have invalid nodes where all lines below
6427 * them are actually valid, due to moving lines around between
6430 * The guarantee is that if there are invalid lines the node is
6431 * invalid - we don't guarantee that if the node is invalid there
6432 * are invalid lines.
6435 if (nd->width != width ||
6436 nd->height != height ||
6437 (nd->valid && !valid))
6439 g_error ("Node aggregates for view %p are invalid:\n"
6440 "Are (%d,%d,%s), should be (%d,%d,%s)",
6442 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6443 width, height, valid ? "TRUE" : "FALSE");
6448 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6449 GtkTextBTreeNode *node)
6451 GtkTextBTreeNode *childnode;
6452 Summary *summary, *summary2;
6454 GtkTextLineSegment *segPtr;
6455 int num_children, num_lines, num_chars, toggle_count, min_children;
6456 GtkTextLineData *ld;
6459 if (node->parent != NULL)
6461 min_children = MIN_CHILDREN;
6463 else if (node->level > 0)
6470 if ((node->num_children < min_children)
6471 || (node->num_children > MAX_CHILDREN))
6473 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6474 node->num_children);
6477 nd = node->node_data;
6480 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6487 if (node->level == 0)
6489 for (line = node->children.line; line != NULL;
6492 if (line->parent != node)
6494 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6496 if (line->segments == NULL)
6498 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6504 /* Just ensuring we don't segv while doing this loop */
6509 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6511 if (segPtr->type->checkFunc != NULL)
6513 (*segPtr->type->checkFunc)(segPtr, line);
6515 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6516 && (segPtr->next != NULL)
6517 && (segPtr->next->byte_count == 0)
6518 && (segPtr->next->type->leftGravity))
6520 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6522 if ((segPtr->next == NULL)
6523 && (segPtr->type != >k_text_char_type))
6525 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6528 num_chars += segPtr->char_count;
6537 for (childnode = node->children.node; childnode != NULL;
6538 childnode = childnode->next)
6540 if (childnode->parent != node)
6542 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6544 if (childnode->level != (node->level-1))
6546 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6547 node->level, childnode->level);
6549 gtk_text_btree_node_check_consistency (tree, childnode);
6550 for (summary = childnode->summary; summary != NULL;
6551 summary = summary->next)
6553 for (summary2 = node->summary; ;
6554 summary2 = summary2->next)
6556 if (summary2 == NULL)
6558 if (summary->info->tag_root == node)
6562 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6563 summary->info->tag->name,
6564 "present in parent summaries");
6566 if (summary->info == summary2->info)
6573 num_lines += childnode->num_lines;
6574 num_chars += childnode->num_chars;
6577 if (num_children != node->num_children)
6579 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6580 num_children, node->num_children);
6582 if (num_lines != node->num_lines)
6584 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6585 num_lines, node->num_lines);
6587 if (num_chars != node->num_chars)
6589 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6590 num_chars, node->num_chars);
6593 for (summary = node->summary; summary != NULL;
6594 summary = summary->next)
6596 if (summary->info->toggle_count == summary->toggle_count)
6598 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6599 summary->info->tag->name);
6602 if (node->level == 0)
6604 for (line = node->children.line; line != NULL;
6607 for (segPtr = line->segments; segPtr != NULL;
6608 segPtr = segPtr->next)
6610 if ((segPtr->type != >k_text_toggle_on_type)
6611 && (segPtr->type != >k_text_toggle_off_type))
6615 if (segPtr->body.toggle.info == summary->info)
6617 if (!segPtr->body.toggle.inNodeCounts)
6618 g_error ("Toggle segment not in the node counts");
6627 for (childnode = node->children.node;
6629 childnode = childnode->next)
6631 for (summary2 = childnode->summary;
6633 summary2 = summary2->next)
6635 if (summary2->info == summary->info)
6637 toggle_count += summary2->toggle_count;
6642 if (toggle_count != summary->toggle_count)
6644 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6645 toggle_count, summary->toggle_count);
6647 for (summary2 = summary->next; summary2 != NULL;
6648 summary2 = summary2->next)
6650 if (summary2->info == summary->info)
6652 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6653 summary->info->tag->name);
6660 listify_foreach (GtkTextTag *tag, gpointer user_data)
6662 GSList** listp = user_data;
6664 *listp = g_slist_prepend (*listp, tag);
6668 list_of_tags (GtkTextTagTable *table)
6670 GSList *list = NULL;
6672 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6678 _gtk_text_btree_check (GtkTextBTree *tree)
6681 GtkTextBTreeNode *node;
6683 GtkTextLineSegment *seg;
6685 GSList *taglist = NULL;
6687 GtkTextTagInfo *info;
6690 * Make sure that the tag toggle counts and the tag root pointers are OK.
6692 for (taglist = list_of_tags (tree->table);
6693 taglist != NULL ; taglist = taglist->next)
6695 tag = taglist->data;
6696 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6699 node = info->tag_root;
6702 if (info->toggle_count != 0)
6704 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6705 tag->name, info->toggle_count);
6707 continue; /* no ranges for the tag */
6709 else if (info->toggle_count == 0)
6711 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6714 else if (info->toggle_count & 1)
6716 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6717 tag->name, info->toggle_count);
6719 for (summary = node->summary; summary != NULL;
6720 summary = summary->next)
6722 if (summary->info->tag == tag)
6724 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6728 if (node->level > 0)
6730 for (node = node->children.node ; node != NULL ;
6733 for (summary = node->summary; summary != NULL;
6734 summary = summary->next)
6736 if (summary->info->tag == tag)
6738 count += summary->toggle_count;
6745 GtkTextLineSegmentClass * last = NULL;
6747 for (line = node->children.line ; line != NULL ;
6750 for (seg = line->segments; seg != NULL;
6753 if ((seg->type == >k_text_toggle_on_type ||
6754 seg->type == >k_text_toggle_off_type) &&
6755 seg->body.toggle.info->tag == tag)
6757 if (last == seg->type)
6758 g_error ("Two consecutive toggles on or off weren't merged");
6759 if (!seg->body.toggle.inNodeCounts)
6760 g_error ("Toggle segment not in the node counts");
6769 if (count != info->toggle_count)
6771 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6772 info->toggle_count, tag->name, count);
6777 g_slist_free (taglist);
6781 * Call a recursive procedure to do the main body of checks.
6784 node = tree->root_node;
6785 gtk_text_btree_node_check_consistency (tree, tree->root_node);
6788 * Make sure that there are at least two lines in the text and
6789 * that the last line has no characters except a newline.
6792 if (node->num_lines < 2)
6794 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
6796 if (node->num_chars < 2)
6798 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
6800 while (node->level > 0)
6802 node = node->children.node;
6803 while (node->next != NULL)
6808 line = node->children.line;
6809 while (line->next != NULL)
6813 seg = line->segments;
6814 while ((seg->type == >k_text_toggle_off_type)
6815 || (seg->type == >k_text_right_mark_type)
6816 || (seg->type == >k_text_left_mark_type))
6819 * It's OK to toggle a tag off in the last line, but
6820 * not to start a new range. It's also OK to have marks
6826 if (seg->type != >k_text_char_type)
6828 g_error ("_gtk_text_btree_check: last line has bogus segment type");
6830 if (seg->next != NULL)
6832 g_error ("_gtk_text_btree_check: last line has too many segments");
6834 if (seg->byte_count != 1)
6836 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
6839 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
6841 g_error ("_gtk_text_btree_check: last line had bad value: %s",
6846 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
6847 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
6848 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
6849 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
6852 _gtk_text_btree_spew (GtkTextBTree *tree)
6857 printf ("%d lines in tree %p\n",
6858 _gtk_text_btree_line_count (tree), tree);
6860 line = _gtk_text_btree_get_line (tree, 0, &real_line);
6862 while (line != NULL)
6864 _gtk_text_btree_spew_line (tree, line);
6865 line = _gtk_text_line_next (line);
6868 printf ("=================== Tag information\n");
6873 list = tree->tag_infos;
6875 while (list != NULL)
6877 GtkTextTagInfo *info;
6881 printf (" tag `%s': root at %p, toggle count %d\n",
6882 info->tag->name, info->tag_root, info->toggle_count);
6884 list = g_slist_next (list);
6887 if (tree->tag_infos == NULL)
6889 printf (" (no tags in the tree)\n");
6893 printf ("=================== Tree nodes\n");
6896 _gtk_text_btree_spew_node (tree->root_node, 0);
6901 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
6904 GtkTextLineSegment *seg;
6906 spaces = g_strnfill (indent, ' ');
6908 printf ("%sline %p chars %d bytes %d\n",
6910 _gtk_text_line_char_count (line),
6911 _gtk_text_line_byte_count (line));
6913 seg = line->segments;
6916 if (seg->type == >k_text_char_type)
6918 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
6923 if (*s == '\n' || *s == '\r')
6927 printf ("%s chars `%s'...\n", spaces, str);
6930 else if (seg->type == >k_text_right_mark_type)
6932 printf ("%s right mark `%s' visible: %d\n",
6934 seg->body.mark.name,
6935 seg->body.mark.visible);
6937 else if (seg->type == >k_text_left_mark_type)
6939 printf ("%s left mark `%s' visible: %d\n",
6941 seg->body.mark.name,
6942 seg->body.mark.visible);
6944 else if (seg->type == >k_text_toggle_on_type ||
6945 seg->type == >k_text_toggle_off_type)
6947 printf ("%s tag `%s' %s\n",
6948 spaces, seg->body.toggle.info->tag->name,
6949 seg->type == >k_text_toggle_off_type ? "off" : "on");
6959 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
6962 GtkTextBTreeNode *iter;
6965 spaces = g_strnfill (indent, ' ');
6967 printf ("%snode %p level %d children %d lines %d chars %d\n",
6968 spaces, node, node->level,
6969 node->num_children, node->num_lines, node->num_chars);
6974 printf ("%s %d toggles of `%s' below this node\n",
6975 spaces, s->toggle_count, s->info->tag->name);
6981 if (node->level > 0)
6983 iter = node->children.node;
6984 while (iter != NULL)
6986 _gtk_text_btree_spew_node (iter, indent + 2);
6993 GtkTextLine *line = node->children.line;
6994 while (line != NULL)
6996 _gtk_text_btree_spew_line_short (line, indent + 2);
7004 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7006 GtkTextLineSegment * seg;
7008 printf ("%4d| line: %p parent: %p next: %p\n",
7009 _gtk_text_line_get_number (line), line, line->parent, line->next);
7011 seg = line->segments;
7015 _gtk_text_btree_spew_segment (tree, seg);
7021 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7023 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7024 seg, seg->type->name, seg->byte_count, seg->char_count);
7026 if (seg->type == >k_text_char_type)
7028 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7029 printf (" `%s'\n", str);
7032 else if (seg->type == >k_text_right_mark_type)
7034 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7035 seg->body.mark.name,
7036 seg->body.mark.visible,
7037 seg->body.mark.not_deleteable);
7039 else if (seg->type == >k_text_left_mark_type)
7041 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7042 seg->body.mark.name,
7043 seg->body.mark.visible,
7044 seg->body.mark.not_deleteable);
7046 else if (seg->type == >k_text_toggle_on_type ||
7047 seg->type == >k_text_toggle_off_type)
7049 printf (" tag `%s' priority %d\n",
7050 seg->body.toggle.info->tag->name,
7051 seg->body.toggle.info->tag->priority);