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
57 #include "gtktextbtree.h"
61 #include "gtktexttag.h"
62 #include "gtktexttagtable.h"
63 #include "gtktextlayout.h"
64 #include "gtktextiterprivate.h"
66 #include "gtktextmarkprivate.h"
75 * The structure below is used to pass information between
76 * _gtk_text_btree_get_tags and inc_count:
79 typedef struct TagInfo {
80 int numTags; /* Number of tags for which there
81 * is currently information in
83 int arraySize; /* Number of entries allocated for
85 GtkTextTag **tags; /* Array of tags seen so far.
87 int *counts; /* Toggle count (so far) for each
88 * entry in tags. Malloc-ed. */
93 * This is used to store per-view width/height info at the tree nodes.
96 typedef struct _NodeData NodeData;
102 /* Height and width of this node */
104 signed int width : 24;
106 /* boolean indicating whether the lines below this node are in need of validation.
107 * However, width/height should always represent the current total width and
108 * max height for lines below this node; the valid flag indicates whether the
109 * width/height on the lines needs recomputing, not whether the totals
112 guint valid : 8; /* Actually a boolean */
117 * The data structure below keeps summary information about one tag as part
118 * of the tag information in a node.
121 typedef struct Summary {
122 GtkTextTagInfo *info; /* Handle for tag. */
123 int toggle_count; /* Number of transitions into or
124 * out of this tag that occur in
125 * the subtree rooted at this node. */
126 struct Summary *next; /* Next in list of all tags for same
127 * node, or NULL if at end of list. */
131 * The data structure below defines a node in the B-tree.
134 struct _GtkTextBTreeNode {
135 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
136 * this is the root. */
137 GtkTextBTreeNode *next; /* Next in list of siblings with the
138 * same parent node, or NULL for end
140 Summary *summary; /* First in malloc-ed list of info
141 * about tags in this subtree (NULL if
142 * no tag info in the subtree). */
143 int level; /* Level of this node in the B-tree.
144 * 0 refers to the bottom of the tree
145 * (children are lines, not nodes). */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
150 int num_children; /* Number of children of this node. */
151 int num_lines; /* Total number of lines (leaves) in
152 * the subtree rooted here. */
153 int num_chars; /* Number of chars below here */
160 * Used to store the list of views in our btree
163 typedef struct _BTreeView BTreeView;
167 GtkTextLayout *layout;
173 * And the tree itself
176 struct _GtkTextBTree {
177 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
178 GtkTextTagTable *table;
179 GHashTable *mark_table;
181 GtkTextMark *insert_mark;
182 GtkTextMark *selection_bound_mark;
183 GtkTextBuffer *buffer;
186 gulong tag_changed_handler;
188 /* Incremented when a segment with a byte size > 0
189 * is added to or removed from the tree (i.e. the
190 * length of a line may have changed, and lines may
191 * have been added or removed). This invalidates
192 * all outstanding iterators.
194 guint chars_changed_stamp;
195 /* Incremented when any segments are added or deleted;
196 * this makes outstanding iterators recalculate their
197 * pointed-to segment and segment offset.
199 guint segments_changed_stamp;
201 /* Cache the last line in the buffer */
202 GtkTextLine *last_line;
203 guint last_line_stamp;
205 /* Cache the next-to-last line in the buffer,
206 * containing the end iterator
208 GtkTextLine *end_iter_line;
209 GtkTextLineSegment *end_iter_segment;
210 int end_iter_segment_byte_index;
211 int end_iter_segment_char_offset;
212 guint end_iter_line_stamp;
213 guint end_iter_segment_stamp;
215 GHashTable *child_anchor_table;
220 * Upper and lower bounds on how many children a node may have:
221 * rebalance when either of these limits is exceeded. MAX_CHILDREN
222 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
225 /* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
226 shallow. It appears to be faster to locate a particular line number
227 if the tree is narrow and deep, since it is more finely sorted. I
228 guess this may increase memory use though, and make it slower to
229 walk the tree in order, or locate a particular byte index (which
230 is done by walking the tree in order).
232 There's basically a tradeoff here. However I'm thinking we want to
233 add pixels, byte counts, and char counts to the tree nodes,
234 at that point narrow and deep should speed up all operations,
235 not just the line number searches.
239 #define MAX_CHILDREN 12
240 #define MIN_CHILDREN 6
242 #define MAX_CHILDREN 6
243 #define MIN_CHILDREN 3
250 static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
252 static void gtk_text_btree_rebalance (GtkTextBTree *tree,
253 GtkTextBTreeNode *node);
254 static GtkTextLine * get_last_line (GtkTextBTree *tree);
255 static void post_insert_fixup (GtkTextBTree *tree,
256 GtkTextLine *insert_line,
257 gint char_count_delta,
258 gint line_count_delta);
259 static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
260 GtkTextTagInfo *info,
262 static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
265 static void segments_changed (GtkTextBTree *tree);
266 static void chars_changed (GtkTextBTree *tree);
267 static void summary_list_destroy (Summary *summary);
268 static GtkTextLine *gtk_text_line_new (void);
269 static void gtk_text_line_destroy (GtkTextBTree *tree,
271 static void gtk_text_line_set_parent (GtkTextLine *line,
272 GtkTextBTreeNode *node);
273 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
277 static NodeData *node_data_new (gpointer view_id);
278 static void node_data_destroy (NodeData *nd);
279 static void node_data_list_destroy (NodeData *nd);
280 static NodeData *node_data_find (NodeData *nd,
283 static GtkTextBTreeNode *gtk_text_btree_node_new (void);
284 static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
285 static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
287 static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
289 static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
291 static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
294 static void gtk_text_btree_node_remove_view (BTreeView *view,
295 GtkTextBTreeNode *node,
297 static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
298 GtkTextBTreeNode *node);
299 static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
300 GtkTextBTreeNode *node);
301 static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
303 static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
305 static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
309 static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
310 GtkTextBTreeNode *node2);
311 static void get_tree_bounds (GtkTextBTree *tree,
314 static void tag_changed_cb (GtkTextTagTable *table,
316 gboolean size_changed,
318 static void cleanup_line (GtkTextLine *line);
319 static void recompute_node_counts (GtkTextBTree *tree,
320 GtkTextBTreeNode *node);
321 static void inc_count (GtkTextTag *tag,
323 TagInfo *tagInfoPtr);
325 static void summary_destroy (Summary *summary);
327 static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
328 const GtkTextIter *iter);
329 static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
330 GtkTextLineSegment *seg,
334 static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
336 static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
338 static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
341 static void redisplay_region (GtkTextBTree *tree,
342 const GtkTextIter *start,
343 const GtkTextIter *end,
344 gboolean cursors_only);
346 /* Inline thingies */
349 segments_changed (GtkTextBTree *tree)
351 tree->segments_changed_stamp += 1;
355 chars_changed (GtkTextBTree *tree)
357 tree->chars_changed_stamp += 1;
365 _gtk_text_btree_new (GtkTextTagTable *table,
366 GtkTextBuffer *buffer)
369 GtkTextBTreeNode *root_node;
370 GtkTextLine *line, *line2;
372 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
373 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
376 * The tree will initially have two empty lines. The second line
377 * isn't actually part of the tree's contents, but its presence
378 * makes several operations easier. The tree will have one GtkTextBTreeNode,
379 * which is also the root of the tree.
382 /* Create the root node. */
384 root_node = gtk_text_btree_node_new ();
386 line = gtk_text_line_new ();
387 line2 = gtk_text_line_new ();
389 root_node->parent = NULL;
390 root_node->next = NULL;
391 root_node->summary = NULL;
392 root_node->level = 0;
393 root_node->children.line = line;
394 root_node->num_children = 2;
395 root_node->num_lines = 2;
396 root_node->num_chars = 2;
398 line->parent = root_node;
401 line->segments = _gtk_char_segment_new ("\n", 1);
403 line2->parent = root_node;
405 line2->segments = _gtk_char_segment_new ("\n", 1);
407 /* Create the tree itself */
409 tree = g_new0(GtkTextBTree, 1);
410 tree->root_node = root_node;
414 /* Set these to values that are unlikely to be found
415 * in random memory garbage, and also avoid
416 * duplicates between tree instances.
418 tree->chars_changed_stamp = g_random_int ();
419 tree->segments_changed_stamp = g_random_int ();
421 tree->last_line_stamp = tree->chars_changed_stamp - 1;
422 tree->last_line = NULL;
424 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
425 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
426 tree->end_iter_line = NULL;
427 tree->end_iter_segment_byte_index = 0;
428 tree->end_iter_segment_char_offset = 0;
430 g_object_ref (tree->table);
432 tree->tag_changed_handler = g_signal_connect (tree->table,
434 G_CALLBACK (tag_changed_cb),
437 tree->mark_table = g_hash_table_new (g_str_hash, g_str_equal);
438 tree->child_anchor_table = NULL;
440 /* We don't ref the buffer, since the buffer owns us;
441 * we'd have some circularity issues. The buffer always
442 * lasts longer than the BTree
444 tree->buffer = buffer;
448 GtkTextLineSegment *seg;
450 _gtk_text_btree_get_iter_at_line_char (tree, &start, 0, 0);
453 tree->insert_mark = _gtk_text_btree_set_mark (tree,
460 seg = tree->insert_mark->segment;
462 seg->body.mark.not_deleteable = TRUE;
463 seg->body.mark.visible = TRUE;
465 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
472 seg = tree->selection_bound_mark->segment;
474 seg->body.mark.not_deleteable = TRUE;
476 g_object_ref (tree->insert_mark);
477 g_object_ref (tree->selection_bound_mark);
486 _gtk_text_btree_ref (GtkTextBTree *tree)
488 g_return_if_fail (tree != NULL);
489 g_return_if_fail (tree->refcount > 0);
495 _gtk_text_btree_unref (GtkTextBTree *tree)
497 g_return_if_fail (tree != NULL);
498 g_return_if_fail (tree->refcount > 0);
502 if (tree->refcount == 0)
504 g_signal_handler_disconnect (tree->table,
505 tree->tag_changed_handler);
507 g_object_unref (tree->table);
510 gtk_text_btree_node_destroy (tree, tree->root_node);
511 tree->root_node = NULL;
513 g_assert (g_hash_table_size (tree->mark_table) == 0);
514 g_hash_table_destroy (tree->mark_table);
515 tree->mark_table = NULL;
516 if (tree->child_anchor_table != NULL)
518 g_hash_table_destroy (tree->child_anchor_table);
519 tree->child_anchor_table = NULL;
522 g_object_unref (tree->insert_mark);
523 tree->insert_mark = NULL;
524 g_object_unref (tree->selection_bound_mark);
525 tree->selection_bound_mark = NULL;
532 _gtk_text_btree_get_buffer (GtkTextBTree *tree)
538 _gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
540 return tree->chars_changed_stamp;
544 _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
546 return tree->segments_changed_stamp;
550 _gtk_text_btree_segments_changed (GtkTextBTree *tree)
552 g_return_if_fail (tree != NULL);
553 segments_changed (tree);
557 * Indexable segment mutation
561 * The following function is responsible for resolving the bidi direction
562 * for the lines between start and end. But it also calculates any
563 * dependent bidi direction for surrounding lines that change as a result
564 * of the bidi direction decisions within the range. The function is
565 * trying to do as little propagation as is needed.
568 gtk_text_btree_resolve_bidi (GtkTextIter *start,
571 GtkTextBTree *tree = _gtk_text_iter_get_btree (start);
572 GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
573 PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
575 /* Resolve the strong bidi direction for all lines between
578 start_line = _gtk_text_iter_get_text_line (start);
579 start_line_prev = _gtk_text_line_previous (start_line);
580 end_line = _gtk_text_iter_get_text_line (end);
581 end_line_next = _gtk_text_line_next (end_line);
584 while (line && line != end_line_next)
586 /* Loop through the segments and search for a strong character
588 GtkTextLineSegment *seg = line->segments;
589 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
593 if (seg->type == >k_text_char_type && seg->byte_count > 0)
595 PangoDirection pango_dir;
597 pango_dir = pango_find_base_dir (seg->body.chars,
600 if (pango_dir != PANGO_DIRECTION_NEUTRAL)
602 line->dir_strong = pango_dir;
609 line = _gtk_text_line_next (line);
614 /* The variable dir_above_propagated contains the forward propagated
615 * direction before start. It is neutral if start is in the beginning
618 dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
620 dir_above_propagated = start_line_prev->dir_propagated_forward;
622 /* Loop forward and propagate the direction of each paragraph
623 * to all neutral lines.
626 last_strong = dir_above_propagated;
627 while (line != end_line_next)
629 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
630 last_strong = line->dir_strong;
632 line->dir_propagated_forward = last_strong;
634 line = _gtk_text_line_next (line);
637 /* Continue propagating as long as the previous resolved forward
638 * is different from last_strong.
641 GtkTextIter end_propagate;
644 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
645 line->dir_propagated_forward != last_strong)
647 GtkTextLine *prev = line;
648 line->dir_propagated_forward = last_strong;
650 line = _gtk_text_line_next(line);
658 /* The last line to invalidate is the last line before the
659 * line with the strong character. Or in case of the end of the
660 * buffer, the last line of the buffer. (There seems to be an
661 * extra "virtual" last line in the buffer that must not be used
662 * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
663 * _gtk_text_line_previous is ok in that case as well.)
665 line = _gtk_text_line_previous (line);
666 _gtk_text_btree_get_iter_at_line (tree, &end_propagate, line, 0);
667 _gtk_text_btree_invalidate_region (tree, end, &end_propagate, FALSE);
672 /* The variable dir_below_propagated contains the backward propagated
673 * direction after end. It is neutral if end is at the end of
676 dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
678 dir_below_propagated = end_line_next->dir_propagated_back;
680 /* Loop backward and propagate the direction of each paragraph
681 * to all neutral lines.
684 last_strong = dir_below_propagated;
685 while (line != start_line_prev)
687 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
688 last_strong = line->dir_strong;
690 line->dir_propagated_back = last_strong;
692 line = _gtk_text_line_previous (line);
695 /* Continue propagating as long as the resolved backward dir
696 * is different from last_strong.
699 GtkTextIter start_propagate;
702 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
703 line->dir_propagated_back != last_strong)
705 GtkTextLine *prev = line;
706 line->dir_propagated_back = last_strong;
708 line = _gtk_text_line_previous (line);
716 /* We only need to invalidate for backwards propagation if the
717 * line we ended up on didn't get a direction from forwards
720 if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
722 _gtk_text_btree_get_iter_at_line (tree, &start_propagate, line, 0);
723 _gtk_text_btree_invalidate_region (tree, &start_propagate, start, FALSE);
729 _gtk_text_btree_delete (GtkTextIter *start,
732 GtkTextLineSegment *prev_seg; /* The segment just before the start
733 * of the deletion range. */
734 GtkTextLineSegment *last_seg; /* The segment just after the end
735 * of the deletion range. */
736 GtkTextLineSegment *seg, *next, *next2;
737 GtkTextLine *curline;
738 GtkTextBTreeNode *curnode, *node;
740 GtkTextLine *start_line;
741 GtkTextLine *end_line;
743 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
744 gint start_byte_offset;
746 g_return_if_fail (start != NULL);
747 g_return_if_fail (end != NULL);
748 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
749 _gtk_text_iter_get_btree (end));
751 gtk_text_iter_order (start, end);
753 tree = _gtk_text_iter_get_btree (start);
755 if (gtk_debug_flags & GTK_DEBUG_TEXT)
756 _gtk_text_btree_check (tree);
758 /* Broadcast the need for redisplay before we break the iterators */
759 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
760 _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
762 /* Save the byte offset so we can reset the iterators */
763 start_byte_offset = gtk_text_iter_get_line_index (start);
765 start_line = _gtk_text_iter_get_text_line (start);
766 end_line = _gtk_text_iter_get_text_line (end);
769 * Split the start and end segments, so we have a place
770 * to insert our new text.
772 * Tricky point: split at end first; otherwise the split
773 * at end may invalidate seg and/or prev_seg. This allows
774 * us to avoid invalidating segments for start.
777 last_seg = gtk_text_line_segment_split (end);
778 if (last_seg != NULL)
779 last_seg = last_seg->next;
781 last_seg = end_line->segments;
783 prev_seg = gtk_text_line_segment_split (start);
784 if (prev_seg != NULL)
786 seg = prev_seg->next;
787 prev_seg->next = last_seg;
791 seg = start_line->segments;
792 start_line->segments = last_seg;
795 /* notify iterators that their segments need recomputation,
796 just for robustness. */
797 segments_changed (tree);
800 * Delete all of the segments between prev_seg and last_seg.
803 curline = start_line;
804 curnode = curline->parent;
805 while (seg != last_seg)
811 GtkTextLine *nextline;
814 * We just ran off the end of a line. First find the
815 * next line, then go back to the old line and delete it
816 * (unless it's the starting line for the range).
819 nextline = _gtk_text_line_next (curline);
820 if (curline != start_line)
822 if (curnode == start_line->parent)
823 start_line->next = curline->next;
825 curnode->children.line = curline->next;
827 for (node = curnode; node != NULL;
830 /* Don't update node->num_chars, because
831 * that was done when we deleted the segments.
833 node->num_lines -= 1;
836 curnode->num_children -= 1;
837 curline->next = deleted_lines;
838 deleted_lines = curline;
842 seg = curline->segments;
845 * If the GtkTextBTreeNode is empty then delete it and its parents,
846 * recursively upwards until a non-empty GtkTextBTreeNode is found.
849 while (curnode->num_children == 0)
851 GtkTextBTreeNode *parent;
853 parent = curnode->parent;
854 if (parent->children.node == curnode)
856 parent->children.node = curnode->next;
860 GtkTextBTreeNode *prevnode = parent->children.node;
861 while (prevnode->next != curnode)
863 prevnode = prevnode->next;
865 prevnode->next = curnode->next;
867 parent->num_children--;
868 gtk_text_btree_node_free_empty (tree, curnode);
871 curnode = curline->parent;
876 char_count = seg->char_count;
878 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
881 * This segment refuses to die. Move it to prev_seg and
882 * advance prev_seg if the segment has left gravity.
885 if (prev_seg == NULL)
887 seg->next = start_line->segments;
888 start_line->segments = seg;
890 else if (prev_seg->next &&
891 prev_seg->next != last_seg &&
892 seg->type == >k_text_toggle_off_type &&
893 prev_seg->next->type == >k_text_toggle_on_type &&
894 seg->body.toggle.info == prev_seg->next->body.toggle.info)
896 /* Try to match an off toggle with the matching on toggle
897 * if it immediately follows. This is a common case, and
898 * handling it here prevents quadratic blowup in
899 * cleanup_line() below. See bug 317125.
901 next2 = prev_seg->next->next;
902 g_free ((char *)prev_seg->next);
903 prev_seg->next = next2;
904 g_free ((char *)seg);
909 seg->next = prev_seg->next;
910 prev_seg->next = seg;
913 if (seg && seg->type->leftGravity)
920 /* Segment is gone. Decrement the char count of the node and
922 for (node = curnode; node != NULL;
925 node->num_chars -= char_count;
933 * If the beginning and end of the deletion range are in different
934 * lines, join the two lines together and discard the ending line.
937 if (start_line != end_line)
940 GtkTextBTreeNode *ancestor_node;
941 GtkTextLine *prevline;
944 /* last_seg was appended to start_line up at the top of this function */
946 for (seg = last_seg; seg != NULL;
949 chars_moved += seg->char_count;
950 if (seg->type->lineChangeFunc != NULL)
952 (*seg->type->lineChangeFunc)(seg, end_line);
956 for (node = start_line->parent; node != NULL;
959 node->num_chars += chars_moved;
962 curnode = end_line->parent;
963 for (node = curnode; node != NULL;
966 node->num_chars -= chars_moved;
969 curnode->num_children--;
970 prevline = curnode->children.line;
971 if (prevline == end_line)
973 curnode->children.line = end_line->next;
977 while (prevline->next != end_line)
979 prevline = prevline->next;
981 prevline->next = end_line->next;
983 end_line->next = deleted_lines;
984 deleted_lines = end_line;
986 /* We now fix up the per-view aggregates. We add all the height and
987 * width for the deleted lines to the start line, so that when revalidation
988 * occurs, the correct change in size is seen.
990 ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
996 gint deleted_width = 0;
997 gint deleted_height = 0;
999 line = deleted_lines;
1002 GtkTextLine *next_line = line->next;
1003 ld = _gtk_text_line_get_data (line, view->view_id);
1007 deleted_width = MAX (deleted_width, ld->width);
1008 deleted_height += ld->height;
1014 if (deleted_width > 0 || deleted_height > 0)
1016 ld = _gtk_text_line_get_data (start_line, view->view_id);
1020 /* This means that start_line has never been validated.
1021 * We don't really want to do the validation here but
1022 * we do need to store our temporary sizes. So we
1023 * create the line data and assume a line w/h of 0.
1025 ld = _gtk_text_line_data_new (view->layout, start_line);
1026 _gtk_text_line_add_data (start_line, ld);
1032 ld->width = MAX (deleted_width, ld->width);
1033 ld->height += deleted_height;
1037 gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
1038 if (ancestor_node->parent)
1039 gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
1044 line = deleted_lines;
1047 GtkTextLine *next_line = line->next;
1049 gtk_text_line_destroy (tree, line);
1054 /* avoid dangling pointer */
1055 deleted_lines = NULL;
1057 gtk_text_btree_rebalance (tree, curnode);
1061 * Cleanup the segments in the new line.
1064 cleanup_line (start_line);
1067 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1070 gtk_text_btree_rebalance (tree, start_line->parent);
1072 /* Notify outstanding iterators that they
1074 chars_changed (tree);
1075 segments_changed (tree);
1077 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1078 _gtk_text_btree_check (tree);
1080 /* Re-initialize our iterators */
1081 _gtk_text_btree_get_iter_at_line (tree, start, start_line, start_byte_offset);
1084 gtk_text_btree_resolve_bidi (start, end);
1088 _gtk_text_btree_insert (GtkTextIter *iter,
1092 GtkTextLineSegment *prev_seg; /* The segment just before the first
1093 * new segment (NULL means new segment
1094 * is at beginning of line). */
1095 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1096 * are inserted just after this one.
1097 * NULL means insert at beginning of
1099 GtkTextLine *line; /* Current line (new segments are
1100 * added to this line). */
1101 GtkTextLineSegment *seg;
1102 GtkTextLine *newline;
1103 int chunk_len; /* # characters in current chunk. */
1104 gint sol; /* start of line */
1105 gint eol; /* Pointer to character just after last
1106 * one in current chunk.
1108 gint delim; /* index of paragraph delimiter */
1109 int line_count_delta; /* Counts change to total number of
1113 int char_count_delta; /* change to number of chars */
1115 gint start_byte_index;
1116 GtkTextLine *start_line;
1118 g_return_if_fail (text != NULL);
1119 g_return_if_fail (iter != NULL);
1122 len = strlen (text);
1124 /* extract iterator info */
1125 tree = _gtk_text_iter_get_btree (iter);
1126 line = _gtk_text_iter_get_text_line (iter);
1129 start_byte_index = gtk_text_iter_get_line_index (iter);
1131 /* Get our insertion segment split. Note this assumes line allows
1132 * char insertions, which isn't true of the "last" line. But iter
1133 * should not be on that line, as we assert here.
1135 g_assert (!_gtk_text_line_is_last (line, tree));
1136 prev_seg = gtk_text_line_segment_split (iter);
1139 /* Invalidate all iterators */
1140 chars_changed (tree);
1141 segments_changed (tree);
1144 * Chop the text up into lines and create a new segment for
1145 * each line, plus a new line for the leftovers from the
1151 line_count_delta = 0;
1152 char_count_delta = 0;
1157 pango_find_paragraph_boundary (text + sol,
1162 /* make these relative to the start of the text */
1166 g_assert (eol >= sol);
1167 g_assert (delim >= sol);
1168 g_assert (eol >= delim);
1169 g_assert (sol >= 0);
1170 g_assert (eol <= len);
1172 chunk_len = eol - sol;
1174 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1175 seg = _gtk_char_segment_new (&text[sol], chunk_len);
1177 char_count_delta += seg->char_count;
1179 if (cur_seg == NULL)
1181 seg->next = line->segments;
1182 line->segments = seg;
1186 seg->next = cur_seg->next;
1187 cur_seg->next = seg;
1192 /* chunk didn't end with a paragraph separator */
1193 g_assert (eol == len);
1198 * The chunk ended with a newline, so create a new GtkTextLine
1199 * and move the remainder of the old line to it.
1202 newline = gtk_text_line_new ();
1203 gtk_text_line_set_parent (newline, line->parent);
1204 newline->next = line->next;
1205 line->next = newline;
1206 newline->segments = seg->next;
1214 * Cleanup the starting line for the insertion, plus the ending
1215 * line if it's different.
1218 cleanup_line (start_line);
1219 if (line != start_line)
1221 cleanup_line (line);
1224 post_insert_fixup (tree, line, line_count_delta, char_count_delta);
1226 /* Invalidate our region, and reset the iterator the user
1227 passed in to point to the end of the inserted text. */
1233 _gtk_text_btree_get_iter_at_line (tree,
1239 /* We could almost certainly be more efficient here
1240 by saving the information from the insertion loop
1242 gtk_text_iter_forward_chars (&end, char_count_delta);
1244 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1245 _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
1248 /* Convenience for the user */
1251 gtk_text_btree_resolve_bidi (&start, &end);
1256 insert_pixbuf_or_widget_segment (GtkTextIter *iter,
1257 GtkTextLineSegment *seg)
1261 GtkTextLineSegment *prevPtr;
1264 gint start_byte_offset;
1266 line = _gtk_text_iter_get_text_line (iter);
1267 tree = _gtk_text_iter_get_btree (iter);
1268 start_byte_offset = gtk_text_iter_get_line_index (iter);
1270 prevPtr = gtk_text_line_segment_split (iter);
1271 if (prevPtr == NULL)
1273 seg->next = line->segments;
1274 line->segments = seg;
1278 seg->next = prevPtr->next;
1279 prevPtr->next = seg;
1282 post_insert_fixup (tree, line, 0, seg->char_count);
1284 chars_changed (tree);
1285 segments_changed (tree);
1287 /* reset *iter for the user, and invalidate tree nodes */
1289 _gtk_text_btree_get_iter_at_line (tree, &start, line, start_byte_offset);
1292 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1294 DV (g_print ("invalidating due to inserting pixbuf/widget (%s)\n", G_STRLOC));
1295 _gtk_text_btree_invalidate_region (tree, &start, iter, FALSE);
1299 _gtk_text_btree_insert_pixbuf (GtkTextIter *iter,
1302 GtkTextLineSegment *seg;
1304 seg = _gtk_pixbuf_segment_new (pixbuf);
1306 insert_pixbuf_or_widget_segment (iter, seg);
1310 _gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1311 GtkTextChildAnchor *anchor)
1313 GtkTextLineSegment *seg;
1316 if (anchor->segment != NULL)
1318 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1322 seg = _gtk_widget_segment_new (anchor);
1324 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1325 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1327 insert_pixbuf_or_widget_segment (iter, seg);
1329 if (tree->child_anchor_table == NULL)
1330 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1332 g_hash_table_insert (tree->child_anchor_table,
1333 seg->body.child.obj,
1334 seg->body.child.obj);
1338 _gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1340 GtkTextLineSegment *seg;
1342 seg = anchor->segment;
1344 g_hash_table_remove (seg->body.child.tree->child_anchor_table,
1353 find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1354 GtkTextBTreeNode *node, gint y, gint *line_top,
1355 GtkTextLine *last_line)
1359 if (gtk_debug_flags & GTK_DEBUG_TEXT)
1360 _gtk_text_btree_check (tree);
1362 if (node->level == 0)
1366 line = node->children.line;
1368 while (line != NULL && line != last_line)
1370 GtkTextLineData *ld;
1372 ld = _gtk_text_line_get_data (line, view->view_id);
1376 if (y < (current_y + (ld ? ld->height : 0)))
1379 current_y += ld->height;
1380 *line_top += ld->height;
1389 GtkTextBTreeNode *child;
1391 child = node->children.node;
1393 while (child != NULL)
1398 gtk_text_btree_node_get_size (child, view->view_id,
1401 if (y < (current_y + height))
1402 return find_line_by_y (tree, view, child,
1403 y - current_y, line_top,
1406 current_y += height;
1407 *line_top += height;
1409 child = child->next;
1417 _gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1424 GtkTextLine *last_line;
1427 view = gtk_text_btree_get_view (tree, view_id);
1428 g_return_val_if_fail (view != NULL, NULL);
1430 last_line = get_last_line (tree);
1432 line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
1436 *line_top_out = line_top;
1442 find_line_top_in_line_list (GtkTextBTree *tree,
1445 GtkTextLine *target_line,
1448 while (line != NULL)
1450 GtkTextLineData *ld;
1452 if (line == target_line)
1455 ld = _gtk_text_line_get_data (line, view->view_id);
1462 g_assert_not_reached (); /* If we get here, our
1463 target line didn't exist
1464 under its parent node */
1469 _gtk_text_btree_find_line_top (GtkTextBTree *tree,
1470 GtkTextLine *target_line,
1477 GtkTextBTreeNode *node;
1479 view = gtk_text_btree_get_view (tree, view_id);
1481 g_return_val_if_fail (view != NULL, 0);
1484 node = target_line->parent;
1485 while (node != NULL)
1487 nodes = g_slist_prepend (nodes, node);
1488 node = node->parent;
1492 while (iter != NULL)
1496 if (node->level == 0)
1498 g_slist_free (nodes);
1499 return find_line_top_in_line_list (tree, view,
1500 node->children.line,
1505 GtkTextBTreeNode *child;
1506 GtkTextBTreeNode *target_node;
1508 g_assert (iter->next != NULL); /* not at level 0 */
1509 target_node = iter->next->data;
1511 child = node->children.node;
1513 while (child != NULL)
1518 if (child == target_node)
1522 gtk_text_btree_node_get_size (child, view->view_id,
1526 child = child->next;
1528 g_assert (child != NULL); /* should have broken out before we
1532 iter = g_slist_next (iter);
1535 g_assert_not_reached (); /* we return when we find the target line */
1540 _gtk_text_btree_add_view (GtkTextBTree *tree,
1541 GtkTextLayout *layout)
1544 GtkTextLine *last_line;
1545 GtkTextLineData *line_data;
1547 g_return_if_fail (tree != NULL);
1549 view = g_new (BTreeView, 1);
1551 view->view_id = layout;
1552 view->layout = layout;
1554 view->next = tree->views;
1559 g_assert (tree->views->prev == NULL);
1560 tree->views->prev = view;
1565 /* The last line in the buffer has identity values for the per-view
1566 * data so that we can avoid special case checks for it in a large
1569 last_line = get_last_line (tree);
1571 line_data = g_new (GtkTextLineData, 1);
1572 line_data->view_id = layout;
1573 line_data->next = NULL;
1574 line_data->width = 0;
1575 line_data->height = 0;
1576 line_data->valid = TRUE;
1578 _gtk_text_line_add_data (last_line, line_data);
1582 _gtk_text_btree_remove_view (GtkTextBTree *tree,
1586 GtkTextLine *last_line;
1587 GtkTextLineData *line_data;
1589 g_return_if_fail (tree != NULL);
1593 while (view != NULL)
1595 if (view->view_id == view_id)
1601 g_return_if_fail (view != NULL);
1604 view->next->prev = view->prev;
1607 view->prev->next = view->next;
1609 if (view == tree->views)
1610 tree->views = view->next;
1612 /* Remove the line data for the last line which we added ourselves.
1613 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1615 last_line = get_last_line (tree);
1616 line_data = _gtk_text_line_remove_data (last_line, view_id);
1619 gtk_text_btree_node_remove_view (view, tree->root_node, view_id);
1621 view->layout = (gpointer) 0xdeadbeef;
1622 view->view_id = (gpointer) 0xdeadbeef;
1628 _gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1629 const GtkTextIter *start,
1630 const GtkTextIter *end,
1631 gboolean cursors_only)
1637 while (view != NULL)
1640 gtk_text_layout_invalidate_cursors (view->layout, start, end);
1642 gtk_text_layout_invalidate (view->layout, start, end);
1649 _gtk_text_btree_get_view_size (GtkTextBTree *tree,
1654 g_return_if_fail (tree != NULL);
1655 g_return_if_fail (view_id != NULL);
1657 gtk_text_btree_node_get_size (tree->root_node, view_id,
1672 iter_stack_new (void)
1675 stack = g_slice_new (IterStack);
1676 stack->iters = NULL;
1683 iter_stack_push (IterStack *stack,
1684 const GtkTextIter *iter)
1687 if (stack->count > stack->alloced)
1689 stack->alloced = stack->count*2;
1690 stack->iters = g_realloc (stack->iters,
1691 stack->alloced * sizeof (GtkTextIter));
1693 stack->iters[stack->count-1] = *iter;
1697 iter_stack_pop (IterStack *stack,
1700 if (stack->count == 0)
1705 *iter = stack->iters[stack->count];
1711 iter_stack_free (IterStack *stack)
1713 g_free (stack->iters);
1714 g_slice_free (IterStack, stack);
1718 iter_stack_invert (IterStack *stack)
1720 if (stack->count > 0)
1723 guint j = stack->count - 1;
1728 tmp = stack->iters[i];
1729 stack->iters[i] = stack->iters[j];
1730 stack->iters[j] = tmp;
1739 queue_tag_redisplay (GtkTextBTree *tree,
1741 const GtkTextIter *start,
1742 const GtkTextIter *end)
1744 if (_gtk_text_tag_affects_size (tag))
1746 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1747 _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
1749 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1751 /* We only need to queue a redraw, not a relayout */
1752 redisplay_region (tree, start, end, FALSE);
1755 /* We don't need to do anything if the tag doesn't affect display */
1759 _gtk_text_btree_tag (const GtkTextIter *start_orig,
1760 const GtkTextIter *end_orig,
1764 GtkTextLineSegment *seg, *prev;
1765 GtkTextLine *cleanupline;
1766 gboolean toggled_on;
1767 GtkTextLine *start_line;
1768 GtkTextLine *end_line;
1770 GtkTextIter start, end;
1773 GtkTextTagInfo *info;
1775 g_return_if_fail (start_orig != NULL);
1776 g_return_if_fail (end_orig != NULL);
1777 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1778 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1779 _gtk_text_iter_get_btree (end_orig));
1780 g_return_if_fail (tag->table == _gtk_text_iter_get_btree (start_orig)->table);
1783 printf ("%s tag %s from %d to %d\n",
1784 add ? "Adding" : "Removing",
1786 gtk_text_buffer_get_offset (start_orig),
1787 gtk_text_buffer_get_offset (end_orig));
1790 if (gtk_text_iter_equal (start_orig, end_orig))
1793 start = *start_orig;
1796 gtk_text_iter_order (&start, &end);
1798 tree = _gtk_text_iter_get_btree (&start);
1800 queue_tag_redisplay (tree, tag, &start, &end);
1802 info = gtk_text_btree_get_tag_info (tree, tag);
1804 start_line = _gtk_text_iter_get_text_line (&start);
1805 end_line = _gtk_text_iter_get_text_line (&end);
1807 /* Find all tag toggles in the region; we are going to delete them.
1808 We need to find them in advance, because
1809 forward_find_tag_toggle () won't work once we start playing around
1811 stack = iter_stack_new ();
1814 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1815 * which is deliberate - we don't want to delete a toggle at the
1818 while (gtk_text_iter_forward_to_tag_toggle (&iter, tag))
1820 if (gtk_text_iter_compare (&iter, &end) >= 0)
1823 iter_stack_push (stack, &iter);
1826 /* We need to traverse the toggles in order. */
1827 iter_stack_invert (stack);
1830 * See whether the tag is present at the start of the range. If
1831 * the state doesn't already match what we want then add a toggle
1835 toggled_on = gtk_text_iter_has_tag (&start, tag);
1836 if ( (add && !toggled_on) ||
1837 (!add && toggled_on) )
1839 /* This could create a second toggle at the start position;
1840 cleanup_line () will remove it if so. */
1841 seg = _gtk_toggle_segment_new (info, add);
1843 prev = gtk_text_line_segment_split (&start);
1846 seg->next = start_line->segments;
1847 start_line->segments = seg;
1851 seg->next = prev->next;
1855 /* cleanup_line adds the new toggle to the node counts. */
1857 printf ("added toggle at start\n");
1859 /* we should probably call segments_changed, but in theory
1860 any still-cached segments in the iters we are about to
1861 use are still valid, since they're in front
1867 * Scan the range of characters and delete any internal tag
1868 * transitions. Keep track of what the old state was at the end
1869 * of the range, and add a toggle there if it's needed.
1873 cleanupline = start_line;
1874 while (iter_stack_pop (stack, &iter))
1876 GtkTextLineSegment *indexable_seg;
1879 line = _gtk_text_iter_get_text_line (&iter);
1880 seg = _gtk_text_iter_get_any_segment (&iter);
1881 indexable_seg = _gtk_text_iter_get_indexable_segment (&iter);
1883 g_assert (seg != NULL);
1884 g_assert (indexable_seg != NULL);
1885 g_assert (seg != indexable_seg);
1887 prev = line->segments;
1889 /* Find the segment that actually toggles this tag. */
1890 while (seg != indexable_seg)
1892 g_assert (seg != NULL);
1893 g_assert (indexable_seg != NULL);
1894 g_assert (seg != indexable_seg);
1896 if ( (seg->type == >k_text_toggle_on_type ||
1897 seg->type == >k_text_toggle_off_type) &&
1898 (seg->body.toggle.info == info) )
1904 g_assert (seg != NULL);
1905 g_assert (indexable_seg != NULL);
1907 g_assert (seg != indexable_seg); /* If this happens, then
1908 forward_to_tag_toggle was
1910 g_assert (seg->body.toggle.info->tag == tag);
1912 /* If this happens, when previously tagging we didn't merge
1913 overlapping tags. */
1914 g_assert ( (toggled_on && seg->type == >k_text_toggle_off_type) ||
1915 (!toggled_on && seg->type == >k_text_toggle_on_type) );
1917 toggled_on = !toggled_on;
1920 printf ("deleting %s toggle\n",
1921 seg->type == >k_text_toggle_on_type ? "on" : "off");
1923 /* Remove toggle segment from the list. */
1926 line->segments = seg->next;
1930 while (prev->next != seg)
1934 prev->next = seg->next;
1937 /* Inform iterators we've hosed them. This actually reflects a
1938 bit of inefficiency; if you have the same tag toggled on and
1939 off a lot in a single line, we keep having the rescan from
1940 the front of the line. Of course we have to do that to get
1941 "prev" anyway, but here we are doing it an additional
1943 segments_changed (tree);
1945 /* Update node counts */
1946 if (seg->body.toggle.inNodeCounts)
1948 _gtk_change_node_toggle_count (line->parent,
1950 seg->body.toggle.inNodeCounts = FALSE;
1955 /* We only clean up lines when we're done with them, saves some
1956 gratuitous line-segment-traversals */
1958 if (cleanupline != line)
1960 cleanup_line (cleanupline);
1965 iter_stack_free (stack);
1967 /* toggled_on now reflects the toggle state _just before_ the
1968 end iterator. The end iterator could already have a toggle
1969 on or a toggle off. */
1970 if ( (add && !toggled_on) ||
1971 (!add && toggled_on) )
1973 /* This could create a second toggle at the start position;
1974 cleanup_line () will remove it if so. */
1976 seg = _gtk_toggle_segment_new (info, !add);
1978 prev = gtk_text_line_segment_split (&end);
1981 seg->next = end_line->segments;
1982 end_line->segments = seg;
1986 seg->next = prev->next;
1989 /* cleanup_line adds the new toggle to the node counts. */
1990 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1992 printf ("added toggle at end\n");
1997 * Cleanup cleanupline and the last line of the range, if
1998 * these are different.
2001 cleanup_line (cleanupline);
2002 if (cleanupline != end_line)
2004 cleanup_line (end_line);
2007 segments_changed (tree);
2009 queue_tag_redisplay (tree, tag, &start, &end);
2011 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2012 _gtk_text_btree_check (tree);
2021 get_line_internal (GtkTextBTree *tree,
2023 gint *real_line_number,
2024 gboolean include_last)
2026 GtkTextBTreeNode *node;
2031 line_count = _gtk_text_btree_line_count (tree);
2035 if (line_number < 0)
2037 line_number = line_count;
2039 else if (line_number > line_count)
2041 line_number = line_count;
2044 if (real_line_number)
2045 *real_line_number = line_number;
2047 node = tree->root_node;
2048 lines_left = line_number;
2051 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2055 while (node->level != 0)
2057 for (node = node->children.node;
2058 node->num_lines <= lines_left;
2064 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2067 lines_left -= node->num_lines;
2072 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2075 for (line = node->children.line; lines_left > 0;
2081 g_error ("gtk_text_btree_find_line ran out of lines");
2090 _gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2093 _gtk_text_btree_get_line (tree,
2094 _gtk_text_btree_line_count (tree) - 1,
2099 _gtk_text_btree_get_line (GtkTextBTree *tree,
2101 gint *real_line_number)
2103 return get_line_internal (tree, line_number, real_line_number, TRUE);
2107 _gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2109 gint *real_line_number)
2111 return get_line_internal (tree, line_number, real_line_number, FALSE);
2115 _gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2117 gint *line_start_index,
2118 gint *real_char_index)
2120 GtkTextBTreeNode *node;
2122 GtkTextLineSegment *seg;
2126 node = tree->root_node;
2128 /* Clamp to valid indexes (-1 is magic for "highest index"),
2129 * node->num_chars includes the two newlines that aren't really
2132 if (char_index < 0 || char_index >= (node->num_chars - 1))
2134 char_index = node->num_chars - 2;
2137 *real_char_index = char_index;
2140 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2144 chars_left = char_index;
2145 while (node->level != 0)
2147 for (node = node->children.node;
2148 chars_left >= node->num_chars;
2151 chars_left -= node->num_chars;
2153 g_assert (chars_left >= 0);
2157 if (chars_left == 0)
2159 /* Start of a line */
2161 *line_start_index = char_index;
2162 return node->children.line;
2166 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2171 for (line = node->children.line; line != NULL; line = line->next)
2173 seg = line->segments;
2176 if (chars_in_line + seg->char_count > chars_left)
2177 goto found; /* found our line/segment */
2179 chars_in_line += seg->char_count;
2184 chars_left -= chars_in_line;
2192 g_assert (line != NULL); /* hosage, ran out of lines */
2193 g_assert (seg != NULL);
2195 *line_start_index = char_index - chars_left;
2199 /* It returns an array sorted by tags priority, ready to pass to
2200 * _gtk_text_attributes_fill_from_tags() */
2202 _gtk_text_btree_get_tags (const GtkTextIter *iter,
2205 GtkTextBTreeNode *node;
2206 GtkTextLine *siblingline;
2207 GtkTextLineSegment *seg;
2208 int src, dst, index;
2213 #define NUM_TAG_INFOS 10
2215 line = _gtk_text_iter_get_text_line (iter);
2216 byte_index = gtk_text_iter_get_line_index (iter);
2218 tagInfo.numTags = 0;
2219 tagInfo.arraySize = NUM_TAG_INFOS;
2220 tagInfo.tags = g_new (GtkTextTag*, NUM_TAG_INFOS);
2221 tagInfo.counts = g_new (int, NUM_TAG_INFOS);
2224 * Record tag toggles within the line of indexPtr but preceding
2225 * indexPtr. Note that if this loop segfaults, your
2226 * byte_index probably points past the sum of all
2227 * seg->byte_count */
2229 for (index = 0, seg = line->segments;
2230 (index + seg->byte_count) <= byte_index;
2231 index += seg->byte_count, seg = seg->next)
2233 if ((seg->type == >k_text_toggle_on_type)
2234 || (seg->type == >k_text_toggle_off_type))
2236 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2241 * Record toggles for tags in lines that are predecessors of
2242 * line but under the same level-0 GtkTextBTreeNode.
2245 for (siblingline = line->parent->children.line;
2246 siblingline != line;
2247 siblingline = siblingline->next)
2249 for (seg = siblingline->segments; seg != NULL;
2252 if ((seg->type == >k_text_toggle_on_type)
2253 || (seg->type == >k_text_toggle_off_type))
2255 inc_count (seg->body.toggle.info->tag, 1, &tagInfo);
2261 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2262 * toggles for all siblings that precede that GtkTextBTreeNode.
2265 for (node = line->parent; node->parent != NULL;
2266 node = node->parent)
2268 GtkTextBTreeNode *siblingPtr;
2271 for (siblingPtr = node->parent->children.node;
2272 siblingPtr != node; siblingPtr = siblingPtr->next)
2274 for (summary = siblingPtr->summary; summary != NULL;
2275 summary = summary->next)
2277 if (summary->toggle_count & 1)
2279 inc_count (summary->info->tag, summary->toggle_count,
2287 * Go through the tag information and squash out all of the tags
2288 * that have even toggle counts (these tags exist before the point
2289 * of interest, but not at the desired character itself).
2292 for (src = 0, dst = 0; src < tagInfo.numTags; src++)
2294 if (tagInfo.counts[src] & 1)
2296 g_assert (GTK_IS_TEXT_TAG (tagInfo.tags[src]));
2297 tagInfo.tags[dst] = tagInfo.tags[src];
2303 g_free (tagInfo.counts);
2306 g_free (tagInfo.tags);
2310 /* Sort tags in ascending order of priority */
2311 _gtk_text_tag_array_sort (tagInfo.tags, dst);
2313 return tagInfo.tags;
2317 copy_segment (GString *string,
2318 gboolean include_hidden,
2319 gboolean include_nonchars,
2320 const GtkTextIter *start,
2321 const GtkTextIter *end)
2323 GtkTextLineSegment *end_seg;
2324 GtkTextLineSegment *seg;
2326 if (gtk_text_iter_equal (start, end))
2329 seg = _gtk_text_iter_get_indexable_segment (start);
2330 end_seg = _gtk_text_iter_get_indexable_segment (end);
2332 if (seg->type == >k_text_char_type)
2334 gboolean copy = TRUE;
2335 gint copy_bytes = 0;
2336 gint copy_start = 0;
2338 /* Don't copy if we're invisible; segments are invisible/not
2339 as a whole, no need to check each char */
2340 if (!include_hidden &&
2341 _gtk_text_btree_char_is_invisible (start))
2344 /* printf (" <invisible>\n"); */
2347 copy_start = _gtk_text_iter_get_segment_byte (start);
2351 /* End is in the same segment; need to copy fewer bytes. */
2352 gint end_byte = _gtk_text_iter_get_segment_byte (end);
2354 copy_bytes = end_byte - copy_start;
2357 copy_bytes = seg->byte_count - copy_start;
2359 g_assert (copy_bytes != 0); /* Due to iter equality check at
2360 front of this function. */
2364 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2366 g_string_append_len (string,
2367 seg->body.chars + copy_start,
2371 /* printf (" :%s\n", string->str); */
2373 else if (seg->type == >k_text_pixbuf_type ||
2374 seg->type == >k_text_child_type)
2376 gboolean copy = TRUE;
2378 if (!include_nonchars)
2382 else if (!include_hidden &&
2383 _gtk_text_btree_char_is_invisible (start))
2390 g_string_append_len (string,
2391 gtk_text_unknown_char_utf8,
2399 _gtk_text_btree_get_text (const GtkTextIter *start_orig,
2400 const GtkTextIter *end_orig,
2401 gboolean include_hidden,
2402 gboolean include_nonchars)
2404 GtkTextLineSegment *seg;
2405 GtkTextLineSegment *end_seg;
2412 g_return_val_if_fail (start_orig != NULL, NULL);
2413 g_return_val_if_fail (end_orig != NULL, NULL);
2414 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2415 _gtk_text_iter_get_btree (end_orig), NULL);
2417 start = *start_orig;
2420 gtk_text_iter_order (&start, &end);
2422 retval = g_string_new (NULL);
2424 end_seg = _gtk_text_iter_get_indexable_segment (&end);
2426 seg = _gtk_text_iter_get_indexable_segment (&iter);
2427 while (seg != end_seg)
2429 copy_segment (retval, include_hidden, include_nonchars,
2432 _gtk_text_iter_forward_indexable_segment (&iter);
2434 seg = _gtk_text_iter_get_indexable_segment (&iter);
2437 copy_segment (retval, include_hidden, include_nonchars, &iter, &end);
2440 g_string_free (retval, FALSE);
2445 _gtk_text_btree_line_count (GtkTextBTree *tree)
2447 /* Subtract bogus line at the end; we return a count
2449 return tree->root_node->num_lines - 1;
2453 _gtk_text_btree_char_count (GtkTextBTree *tree)
2455 /* Exclude newline in bogus last line and the
2456 * one in the last line that is after the end iterator
2458 return tree->root_node->num_chars - 2;
2461 #define LOTSA_TAGS 1000
2463 _gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2465 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2467 int deftagCnts[LOTSA_TAGS] = { 0, };
2468 int *tagCnts = deftagCnts;
2469 GtkTextTag *deftags[LOTSA_TAGS];
2470 GtkTextTag **tags = deftags;
2472 GtkTextBTreeNode *node;
2473 GtkTextLine *siblingline;
2474 GtkTextLineSegment *seg;
2481 line = _gtk_text_iter_get_text_line (iter);
2482 tree = _gtk_text_iter_get_btree (iter);
2483 byte_index = gtk_text_iter_get_line_index (iter);
2485 numTags = gtk_text_tag_table_get_size (tree->table);
2487 /* almost always avoid malloc, so stay out of system calls */
2488 if (LOTSA_TAGS < numTags)
2490 tagCnts = g_new0 (int, numTags);
2491 tags = g_new (GtkTextTag*, numTags);
2495 * Record tag toggles within the line of indexPtr but preceding
2499 for (index = 0, seg = line->segments;
2500 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2501 index += seg->byte_count, seg = seg->next)
2503 if ((seg->type == >k_text_toggle_on_type)
2504 || (seg->type == >k_text_toggle_off_type))
2506 tag = seg->body.toggle.info->tag;
2507 if (tag->invisible_set)
2509 tags[tag->priority] = tag;
2510 tagCnts[tag->priority]++;
2516 * Record toggles for tags in lines that are predecessors of
2517 * line but under the same level-0 GtkTextBTreeNode.
2520 for (siblingline = line->parent->children.line;
2521 siblingline != line;
2522 siblingline = siblingline->next)
2524 for (seg = siblingline->segments; seg != NULL;
2527 if ((seg->type == >k_text_toggle_on_type)
2528 || (seg->type == >k_text_toggle_off_type))
2530 tag = seg->body.toggle.info->tag;
2531 if (tag->invisible_set)
2533 tags[tag->priority] = tag;
2534 tagCnts[tag->priority]++;
2541 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2542 * for all siblings that precede that GtkTextBTreeNode.
2545 for (node = line->parent; node->parent != NULL;
2546 node = node->parent)
2548 GtkTextBTreeNode *siblingPtr;
2551 for (siblingPtr = node->parent->children.node;
2552 siblingPtr != node; siblingPtr = siblingPtr->next)
2554 for (summary = siblingPtr->summary; summary != NULL;
2555 summary = summary->next)
2557 if (summary->toggle_count & 1)
2559 tag = summary->info->tag;
2560 if (tag->invisible_set)
2562 tags[tag->priority] = tag;
2563 tagCnts[tag->priority] += summary->toggle_count;
2571 * Now traverse from highest priority to lowest,
2572 * take invisible value from first odd count (= on)
2575 for (i = numTags-1; i >=0; i--)
2579 /* FIXME not sure this should be if 0 */
2581 #ifndef ALWAYS_SHOW_SELECTION
2582 /* who would make the selection invisible? */
2583 if ((tag == tkxt->seltag)
2584 && !(tkxt->flags & GOT_FOCUS))
2590 invisible = tags[i]->values->invisible;
2595 if (LOTSA_TAGS < numTags)
2610 redisplay_region (GtkTextBTree *tree,
2611 const GtkTextIter *start,
2612 const GtkTextIter *end,
2613 gboolean cursors_only)
2616 GtkTextLine *start_line, *end_line;
2618 if (gtk_text_iter_compare (start, end) > 0)
2620 const GtkTextIter *tmp = start;
2625 start_line = _gtk_text_iter_get_text_line (start);
2626 end_line = _gtk_text_iter_get_text_line (end);
2629 while (view != NULL)
2631 gint start_y, end_y;
2632 GtkTextLineData *ld;
2634 start_y = _gtk_text_btree_find_line_top (tree, start_line, view->view_id);
2636 if (end_line == start_line)
2639 end_y = _gtk_text_btree_find_line_top (tree, end_line, view->view_id);
2641 ld = _gtk_text_line_get_data (end_line, view->view_id);
2643 end_y += ld->height;
2646 gtk_text_layout_cursors_changed (view->layout, start_y,
2650 gtk_text_layout_changed (view->layout, start_y,
2659 redisplay_mark (GtkTextLineSegment *mark)
2663 gboolean cursor_only;
2665 _gtk_text_btree_get_iter_at_mark (mark->body.mark.tree,
2667 mark->body.mark.obj);
2670 gtk_text_iter_forward_char (&end);
2672 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2673 cursor_only = mark == mark->body.mark.tree->insert_mark->segment;
2674 _gtk_text_btree_invalidate_region (mark->body.mark.tree, &iter, &end, cursor_only);
2678 redisplay_mark_if_visible (GtkTextLineSegment *mark)
2680 if (!mark->body.mark.visible)
2683 redisplay_mark (mark);
2687 ensure_not_off_end (GtkTextBTree *tree,
2688 GtkTextLineSegment *mark,
2691 if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
2692 gtk_text_iter_backward_char (iter);
2695 static GtkTextLineSegment*
2696 real_set_mark (GtkTextBTree *tree,
2697 GtkTextMark *existing_mark,
2699 gboolean left_gravity,
2700 const GtkTextIter *where,
2701 gboolean should_exist,
2702 gboolean redraw_selections)
2704 GtkTextLineSegment *mark;
2707 g_return_val_if_fail (tree != NULL, NULL);
2708 g_return_val_if_fail (where != NULL, NULL);
2709 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2713 if (gtk_text_mark_get_buffer (existing_mark) != NULL)
2714 mark = existing_mark->segment;
2718 else if (name != NULL)
2719 mark = g_hash_table_lookup (tree->mark_table,
2724 if (should_exist && mark == NULL)
2726 g_warning ("No mark `%s' exists!", name);
2730 /* OK if !should_exist and it does already exist, in that case
2736 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2737 _gtk_text_iter_check (&iter);
2741 if (redraw_selections &&
2742 (mark == tree->insert_mark->segment ||
2743 mark == tree->selection_bound_mark->segment))
2745 GtkTextIter old_pos;
2747 _gtk_text_btree_get_iter_at_mark (tree, &old_pos,
2748 mark->body.mark.obj);
2749 redisplay_region (tree, &old_pos, where, TRUE);
2753 * don't let visible marks be after the final newline of the
2757 if (mark->body.mark.visible)
2759 ensure_not_off_end (tree, mark, &iter);
2762 /* Redraw the mark's old location. */
2763 redisplay_mark_if_visible (mark);
2765 /* Unlink mark from its current location.
2766 This could hose our iterator... */
2767 gtk_text_btree_unlink_segment (tree, mark,
2768 mark->body.mark.line);
2769 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2770 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2772 segments_changed (tree); /* make sure the iterator recomputes its
2778 g_object_ref (existing_mark);
2780 existing_mark = gtk_text_mark_new (name, left_gravity);
2782 mark = existing_mark->segment;
2783 _gtk_mark_segment_set_tree (mark, tree);
2785 mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
2787 if (mark->body.mark.name)
2788 g_hash_table_insert (tree->mark_table,
2789 mark->body.mark.name,
2793 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2794 _gtk_text_iter_check (&iter);
2796 /* Link mark into new location */
2797 gtk_text_btree_link_segment (mark, &iter);
2799 /* Invalidate some iterators. */
2800 segments_changed (tree);
2803 * update the screen at the mark's new location.
2806 redisplay_mark_if_visible (mark);
2808 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2809 _gtk_text_iter_check (&iter);
2811 if (gtk_debug_flags & GTK_DEBUG_TEXT)
2812 _gtk_text_btree_check (tree);
2819 _gtk_text_btree_set_mark (GtkTextBTree *tree,
2820 GtkTextMark *existing_mark,
2822 gboolean left_gravity,
2823 const GtkTextIter *iter,
2824 gboolean should_exist)
2826 GtkTextLineSegment *seg;
2828 seg = real_set_mark (tree, existing_mark,
2829 name, left_gravity, iter, should_exist,
2832 return seg ? seg->body.mark.obj : NULL;
2836 _gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2840 GtkTextIter tmp_start, tmp_end;
2842 _gtk_text_btree_get_iter_at_mark (tree, &tmp_start,
2844 _gtk_text_btree_get_iter_at_mark (tree, &tmp_end,
2845 tree->selection_bound_mark);
2847 if (gtk_text_iter_equal (&tmp_start, &tmp_end))
2859 gtk_text_iter_order (&tmp_start, &tmp_end);
2872 _gtk_text_btree_place_cursor (GtkTextBTree *tree,
2873 const GtkTextIter *iter)
2875 _gtk_text_btree_select_range (tree, iter, iter);
2879 _gtk_text_btree_select_range (GtkTextBTree *tree,
2880 const GtkTextIter *ins,
2881 const GtkTextIter *bound)
2883 GtkTextIter old_ins, old_bound;
2885 _gtk_text_btree_get_iter_at_mark (tree, &old_ins,
2887 _gtk_text_btree_get_iter_at_mark (tree, &old_bound,
2888 tree->selection_bound_mark);
2890 /* Check if it's no-op since gtk_text_buffer_place_cursor()
2891 * also calls this, and this will redraw the cursor line. */
2892 if (!gtk_text_iter_equal (&old_ins, ins) ||
2893 !gtk_text_iter_equal (&old_bound, bound))
2895 redisplay_region (tree, &old_ins, &old_bound, TRUE);
2897 /* Move insert AND selection_bound before we redisplay */
2898 real_set_mark (tree, tree->insert_mark,
2899 "insert", FALSE, ins, TRUE, FALSE);
2900 real_set_mark (tree, tree->selection_bound_mark,
2901 "selection_bound", FALSE, bound, TRUE, FALSE);
2903 redisplay_region (tree, ins, bound, TRUE);
2909 _gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2914 g_return_if_fail (tree != NULL);
2915 g_return_if_fail (name != NULL);
2917 mark = g_hash_table_lookup (tree->mark_table,
2920 _gtk_text_btree_remove_mark (tree, mark);
2924 _gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2925 GtkTextLineSegment *segment)
2928 if (segment->body.mark.name)
2929 g_hash_table_remove (tree->mark_table, segment->body.mark.name);
2931 segment->body.mark.tree = NULL;
2932 segment->body.mark.line = NULL;
2934 /* Remove the ref on the mark, which frees segment as a side effect
2935 * if this is the last reference.
2937 g_object_unref (segment->body.mark.obj);
2941 _gtk_text_btree_remove_mark (GtkTextBTree *tree,
2944 GtkTextLineSegment *segment;
2946 g_return_if_fail (mark != NULL);
2947 g_return_if_fail (tree != NULL);
2949 segment = mark->segment;
2951 if (segment->body.mark.not_deleteable)
2953 g_warning ("Can't delete special mark `%s'", segment->body.mark.name);
2957 /* This calls cleanup_line and segments_changed */
2958 gtk_text_btree_unlink_segment (tree, segment, segment->body.mark.line);
2960 _gtk_text_btree_release_mark_segment (tree, segment);
2964 _gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2965 GtkTextMark *segment)
2967 return segment == tree->insert_mark;
2971 _gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2972 GtkTextMark *segment)
2974 return segment == tree->selection_bound_mark;
2978 _gtk_text_btree_get_insert (GtkTextBTree *tree)
2980 return tree->insert_mark;
2984 _gtk_text_btree_get_selection_bound (GtkTextBTree *tree)
2986 return tree->selection_bound_mark;
2990 _gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
2993 GtkTextLineSegment *seg;
2995 g_return_val_if_fail (tree != NULL, NULL);
2996 g_return_val_if_fail (name != NULL, NULL);
2998 seg = g_hash_table_lookup (tree->mark_table, name);
3000 return seg ? seg->body.mark.obj : NULL;
3004 * gtk_text_mark_set_visible:
3005 * @mark: a #GtkTextMark
3006 * @setting: visibility of mark
3008 * Sets the visibility of @mark; the insertion point is normally
3009 * visible, i.e. you can see it as a vertical bar. Also, the text
3010 * widget uses a visible mark to indicate where a drop will occur when
3011 * dragging-and-dropping text. Most other marks are not visible.
3012 * Marks are not visible by default.
3016 gtk_text_mark_set_visible (GtkTextMark *mark,
3019 GtkTextLineSegment *seg;
3021 g_return_if_fail (mark != NULL);
3023 seg = mark->segment;
3025 if (seg->body.mark.visible == setting)
3029 seg->body.mark.visible = setting;
3031 if (seg->body.mark.tree)
3032 redisplay_mark (seg);
3037 _gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
3040 GtkTextBTreeNode *node;
3041 GtkTextTagInfo *info;
3043 g_return_val_if_fail (tree != NULL, NULL);
3047 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3052 if (info->tag_root == NULL)
3055 node = info->tag_root;
3057 /* We know the tag root has instances of the given
3060 continue_outer_loop:
3061 g_assert (node != NULL);
3062 while (node->level > 0)
3064 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3065 node = node->children.node;
3066 while (node != NULL)
3068 if (gtk_text_btree_node_has_tag (node, tag))
3069 goto continue_outer_loop;
3073 g_assert (node != NULL);
3076 g_assert (node != NULL); /* The tag summaries said some node had
3079 g_assert (node->level == 0);
3081 return node->children.line;
3085 /* Looking for any tag at all (tag == NULL).
3086 Unfortunately this can't be done in a simple and efficient way
3087 right now; so I'm just going to return the
3088 first line in the btree. FIXME */
3089 return _gtk_text_btree_get_line (tree, 0, NULL);
3094 _gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3097 GtkTextBTreeNode *node;
3098 GtkTextBTreeNode *last_node;
3100 GtkTextTagInfo *info;
3102 g_return_val_if_fail (tree != NULL, NULL);
3106 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3108 if (info->tag_root == NULL)
3111 node = info->tag_root;
3112 /* We know the tag root has instances of the given
3115 while (node->level > 0)
3117 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3119 node = node->children.node;
3120 while (node != NULL)
3122 if (gtk_text_btree_node_has_tag (node, tag))
3130 g_assert (node != NULL); /* The tag summaries said some node had
3133 g_assert (node->level == 0);
3135 /* Find the last line in this node */
3136 line = node->children.line;
3137 while (line->next != NULL)
3144 /* This search can't be done efficiently at the moment,
3145 at least not without complexity.
3146 So, we just return the last line.
3148 return _gtk_text_btree_get_end_iter_line (tree);
3158 _gtk_text_line_get_number (GtkTextLine *line)
3161 GtkTextBTreeNode *node, *parent, *node2;
3165 * First count how many lines precede this one in its level-0
3169 node = line->parent;
3171 for (line2 = node->children.line; line2 != line;
3172 line2 = line2->next)
3176 g_error ("gtk_text_btree_line_number couldn't find line");
3182 * Now work up through the levels of the tree one at a time,
3183 * counting how many lines are in GtkTextBTreeNodes preceding the current
3187 for (parent = node->parent ; parent != NULL;
3188 node = parent, parent = parent->parent)
3190 for (node2 = parent->children.node; node2 != node;
3191 node2 = node2->next)
3195 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3197 index += node2->num_lines;
3203 static GtkTextLineSegment*
3204 find_toggle_segment_before_char (GtkTextLine *line,
3208 GtkTextLineSegment *seg;
3209 GtkTextLineSegment *toggle_seg;
3214 seg = line->segments;
3215 while ( (index + seg->char_count) <= char_in_line )
3217 if (((seg->type == >k_text_toggle_on_type)
3218 || (seg->type == >k_text_toggle_off_type))
3219 && (seg->body.toggle.info->tag == tag))
3222 index += seg->char_count;
3229 static GtkTextLineSegment*
3230 find_toggle_segment_before_byte (GtkTextLine *line,
3234 GtkTextLineSegment *seg;
3235 GtkTextLineSegment *toggle_seg;
3240 seg = line->segments;
3241 while ( (index + seg->byte_count) <= byte_in_line )
3243 if (((seg->type == >k_text_toggle_on_type)
3244 || (seg->type == >k_text_toggle_off_type))
3245 && (seg->body.toggle.info->tag == tag))
3248 index += seg->byte_count;
3256 find_toggle_outside_current_line (GtkTextLine *line,
3260 GtkTextBTreeNode *node;
3261 GtkTextLine *sibling_line;
3262 GtkTextLineSegment *seg;
3263 GtkTextLineSegment *toggle_seg;
3265 GtkTextTagInfo *info = NULL;
3268 * No toggle in this line. Look for toggles for the tag in lines
3269 * that are predecessors of line but under the same
3270 * level-0 GtkTextBTreeNode.
3273 sibling_line = line->parent->children.line;
3274 while (sibling_line != line)
3276 seg = sibling_line->segments;
3279 if (((seg->type == >k_text_toggle_on_type)
3280 || (seg->type == >k_text_toggle_off_type))
3281 && (seg->body.toggle.info->tag == tag))
3287 sibling_line = sibling_line->next;
3290 if (toggle_seg != NULL)
3291 return (toggle_seg->type == >k_text_toggle_on_type);
3294 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3295 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3296 * siblings that precede that GtkTextBTreeNode.
3299 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3305 node = line->parent;
3306 while (node->parent != NULL)
3308 GtkTextBTreeNode *sibling_node;
3310 sibling_node = node->parent->children.node;
3311 while (sibling_node != node)
3315 summary = sibling_node->summary;
3316 while (summary != NULL)
3318 if (summary->info == info)
3319 toggles += summary->toggle_count;
3321 summary = summary->next;
3324 sibling_node = sibling_node->next;
3327 if (node == info->tag_root)
3330 node = node->parent;
3334 * An odd number of toggles means that the tag is present at the
3338 return (toggles & 1) != 0;
3341 /* FIXME this function is far too slow, for no good reason. */
3343 _gtk_text_line_char_has_tag (GtkTextLine *line,
3348 GtkTextLineSegment *toggle_seg;
3350 g_return_val_if_fail (line != NULL, FALSE);
3353 * Check for toggles for the tag in the line but before
3354 * the char. If there is one, its type indicates whether or
3355 * not the character is tagged.
3358 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3360 if (toggle_seg != NULL)
3361 return (toggle_seg->type == >k_text_toggle_on_type);
3363 return find_toggle_outside_current_line (line, tree, tag);
3367 _gtk_text_line_byte_has_tag (GtkTextLine *line,
3372 GtkTextLineSegment *toggle_seg;
3374 g_return_val_if_fail (line != NULL, FALSE);
3377 * Check for toggles for the tag in the line but before
3378 * the char. If there is one, its type indicates whether or
3379 * not the character is tagged.
3382 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3384 if (toggle_seg != NULL)
3385 return (toggle_seg->type == >k_text_toggle_on_type);
3387 return find_toggle_outside_current_line (line, tree, tag);
3391 _gtk_text_line_is_last (GtkTextLine *line,
3394 return line == get_last_line (tree);
3398 ensure_end_iter_line (GtkTextBTree *tree)
3400 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3404 /* n_lines is without the magic line at the end */
3405 g_assert (_gtk_text_btree_line_count (tree) >= 1);
3407 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, -1, &real_line);
3409 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3414 ensure_end_iter_segment (GtkTextBTree *tree)
3416 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3418 GtkTextLineSegment *seg;
3419 GtkTextLineSegment *last_with_chars;
3421 ensure_end_iter_line (tree);
3423 last_with_chars = NULL;
3425 seg = tree->end_iter_line->segments;
3428 if (seg->char_count > 0)
3429 last_with_chars = seg;
3433 tree->end_iter_segment = last_with_chars;
3435 /* We know the last char in the last line is '\n' */
3436 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3437 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3439 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3441 g_assert (tree->end_iter_segment->type == >k_text_char_type);
3442 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3447 _gtk_text_line_contains_end_iter (GtkTextLine *line,
3450 ensure_end_iter_line (tree);
3452 return line == tree->end_iter_line;
3456 _gtk_text_btree_is_end (GtkTextBTree *tree,
3458 GtkTextLineSegment *seg,
3462 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3464 /* Do this first to avoid walking segments in most cases */
3465 if (!_gtk_text_line_contains_end_iter (line, tree))
3468 ensure_end_iter_segment (tree);
3470 if (seg != tree->end_iter_segment)
3473 if (byte_index >= 0)
3474 return byte_index == tree->end_iter_segment_byte_index;
3476 return char_offset == tree->end_iter_segment_char_offset;
3480 _gtk_text_line_next (GtkTextLine *line)
3482 GtkTextBTreeNode *node;
3484 if (line->next != NULL)
3489 * This was the last line associated with the particular parent
3490 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3491 * then search down from that GtkTextBTreeNode to find the first
3495 node = line->parent;
3496 while (node != NULL && node->next == NULL)
3497 node = node->parent;
3503 while (node->level > 0)
3505 node = node->children.node;
3508 g_assert (node->children.line != line);
3510 return node->children.line;
3515 _gtk_text_line_next_excluding_last (GtkTextLine *line)
3519 next = _gtk_text_line_next (line);
3521 /* If we were on the end iter line, we can't go to
3524 if (next && next->next == NULL && /* these checks are optimization only */
3525 _gtk_text_line_next (next) == NULL)
3532 _gtk_text_line_previous (GtkTextLine *line)
3534 GtkTextBTreeNode *node;
3535 GtkTextBTreeNode *node2;
3539 * Find the line under this GtkTextBTreeNode just before the starting line.
3541 prev = line->parent->children.line; /* First line at leaf */
3542 while (prev != line)
3544 if (prev->next == line)
3550 g_error ("gtk_text_btree_previous_line ran out of lines");
3554 * This was the first line associated with the particular parent
3555 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3556 * then search down from that GtkTextBTreeNode to find its last line.
3558 for (node = line->parent; ; node = node->parent)
3560 if (node == NULL || node->parent == NULL)
3562 else if (node != node->parent->children.node)
3566 for (node2 = node->parent->children.node; ;
3567 node2 = node2->children.node)
3569 while (node2->next != node)
3570 node2 = node2->next;
3572 if (node2->level == 0)
3578 for (prev = node2->children.line ; ; prev = prev->next)
3580 if (prev->next == NULL)
3584 g_assert_not_reached ();
3590 _gtk_text_line_data_new (GtkTextLayout *layout,
3593 GtkTextLineData *line_data;
3595 line_data = g_new (GtkTextLineData, 1);
3597 line_data->view_id = layout;
3598 line_data->next = NULL;
3599 line_data->width = 0;
3600 line_data->height = 0;
3601 line_data->valid = FALSE;
3607 _gtk_text_line_add_data (GtkTextLine *line,
3608 GtkTextLineData *data)
3610 g_return_if_fail (line != NULL);
3611 g_return_if_fail (data != NULL);
3612 g_return_if_fail (data->view_id != NULL);
3616 data->next = line->views;
3626 _gtk_text_line_remove_data (GtkTextLine *line,
3629 GtkTextLineData *prev;
3630 GtkTextLineData *iter;
3632 g_return_val_if_fail (line != NULL, NULL);
3633 g_return_val_if_fail (view_id != NULL, NULL);
3637 while (iter != NULL)
3639 if (iter->view_id == view_id)
3648 prev->next = iter->next;
3650 line->views = iter->next;
3659 _gtk_text_line_get_data (GtkTextLine *line,
3662 GtkTextLineData *iter;
3664 g_return_val_if_fail (line != NULL, NULL);
3665 g_return_val_if_fail (view_id != NULL, NULL);
3668 while (iter != NULL)
3670 if (iter->view_id == view_id)
3679 _gtk_text_line_invalidate_wrap (GtkTextLine *line,
3680 GtkTextLineData *ld)
3682 /* For now this is totally unoptimized. FIXME?
3684 We could probably optimize the case where the width removed
3685 is less than the max width for the parent node,
3686 and the case where the height is unchanged when we re-wrap.
3689 g_return_if_fail (ld != NULL);
3692 gtk_text_btree_node_invalidate_upward (line->parent, ld->view_id);
3696 _gtk_text_line_char_count (GtkTextLine *line)
3698 GtkTextLineSegment *seg;
3702 seg = line->segments;
3705 size += seg->char_count;
3712 _gtk_text_line_byte_count (GtkTextLine *line)
3714 GtkTextLineSegment *seg;
3718 seg = line->segments;
3721 size += seg->byte_count;
3729 _gtk_text_line_char_index (GtkTextLine *target_line)
3731 GSList *node_stack = NULL;
3732 GtkTextBTreeNode *iter;
3736 /* Push all our parent nodes onto a stack */
3737 iter = target_line->parent;
3739 g_assert (iter != NULL);
3741 while (iter != NULL)
3743 node_stack = g_slist_prepend (node_stack, iter);
3745 iter = iter->parent;
3748 /* Check that we have the root node on top of the stack. */
3749 g_assert (node_stack != NULL &&
3750 node_stack->data != NULL &&
3751 ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
3753 /* Add up chars in all nodes before the nodes in our stack.
3757 iter = node_stack->data;
3758 while (iter != NULL)
3760 GtkTextBTreeNode *child_iter;
3761 GtkTextBTreeNode *next_node;
3763 next_node = node_stack->next ?
3764 node_stack->next->data : NULL;
3765 node_stack = g_slist_remove (node_stack, node_stack->data);
3767 if (iter->level == 0)
3769 /* stack should be empty when we're on the last node */
3770 g_assert (node_stack == NULL);
3771 break; /* Our children are now lines */
3774 g_assert (next_node != NULL);
3775 g_assert (iter != NULL);
3776 g_assert (next_node->parent == iter);
3778 /* Add up chars before us in the tree */
3779 child_iter = iter->children.node;
3780 while (child_iter != next_node)
3782 g_assert (child_iter != NULL);
3784 num_chars += child_iter->num_chars;
3786 child_iter = child_iter->next;
3792 g_assert (iter != NULL);
3793 g_assert (iter == target_line->parent);
3795 /* Since we don't store char counts in lines, only in segments, we
3796 have to iterate over the lines adding up segment char counts
3797 until we find our line. */
3798 line = iter->children.line;
3799 while (line != target_line)
3801 g_assert (line != NULL);
3803 num_chars += _gtk_text_line_char_count (line);
3808 g_assert (line == target_line);
3814 _gtk_text_line_byte_to_segment (GtkTextLine *line,
3818 GtkTextLineSegment *seg;
3821 g_return_val_if_fail (line != NULL, NULL);
3823 offset = byte_offset;
3824 seg = line->segments;
3826 while (offset >= seg->byte_count)
3828 offset -= seg->byte_count;
3830 g_assert (seg != NULL); /* means an invalid byte index */
3834 *seg_offset = offset;
3840 _gtk_text_line_char_to_segment (GtkTextLine *line,
3844 GtkTextLineSegment *seg;
3847 g_return_val_if_fail (line != NULL, NULL);
3849 offset = char_offset;
3850 seg = line->segments;
3852 while (offset >= seg->char_count)
3854 offset -= seg->char_count;
3856 g_assert (seg != NULL); /* means an invalid char index */
3860 *seg_offset = offset;
3866 _gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3870 GtkTextLineSegment *seg;
3873 g_return_val_if_fail (line != NULL, NULL);
3875 offset = byte_offset;
3876 seg = line->segments;
3878 while (offset > 0 && offset >= seg->byte_count)
3880 offset -= seg->byte_count;
3882 g_assert (seg != NULL); /* means an invalid byte index */
3886 *seg_offset = offset;
3892 _gtk_text_line_char_to_any_segment (GtkTextLine *line,
3896 GtkTextLineSegment *seg;
3899 g_return_val_if_fail (line != NULL, NULL);
3901 offset = char_offset;
3902 seg = line->segments;
3904 while (offset > 0 && offset >= seg->char_count)
3906 offset -= seg->char_count;
3908 g_assert (seg != NULL); /* means an invalid byte index */
3912 *seg_offset = offset;
3918 _gtk_text_line_byte_to_char (GtkTextLine *line,
3922 GtkTextLineSegment *seg;
3924 g_return_val_if_fail (line != NULL, 0);
3925 g_return_val_if_fail (byte_offset >= 0, 0);
3928 seg = line->segments;
3929 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3930 the next segment) */
3932 byte_offset -= seg->byte_count;
3933 char_offset += seg->char_count;
3935 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3938 g_assert (seg != NULL);
3940 /* Now byte_offset is the offset into the current segment,
3941 and char_offset is the start of the current segment.
3942 Optimize the case where no chars use > 1 byte */
3943 if (seg->byte_count == seg->char_count)
3944 return char_offset + byte_offset;
3947 if (seg->type == >k_text_char_type)
3948 return char_offset + g_utf8_strlen (seg->body.chars, byte_offset);
3951 g_assert (seg->char_count == 1);
3952 g_assert (byte_offset == 0);
3960 _gtk_text_line_char_to_byte (GtkTextLine *line,
3963 g_warning ("FIXME not implemented");
3968 /* FIXME sync with char_locate (or figure out a clean
3969 way to merge the two functions) */
3971 _gtk_text_line_byte_locate (GtkTextLine *line,
3973 GtkTextLineSegment **segment,
3974 GtkTextLineSegment **any_segment,
3975 gint *seg_byte_offset,
3976 gint *line_byte_offset)
3978 GtkTextLineSegment *seg;
3979 GtkTextLineSegment *after_last_indexable;
3980 GtkTextLineSegment *last_indexable;
3984 g_return_val_if_fail (line != NULL, FALSE);
3985 g_return_val_if_fail (byte_offset >= 0, FALSE);
3988 *any_segment = NULL;
3991 offset = byte_offset;
3993 last_indexable = NULL;
3994 after_last_indexable = line->segments;
3995 seg = line->segments;
3997 /* The loop ends when we're inside a segment;
3998 last_indexable refers to the last segment
3999 we passed entirely. */
4000 while (seg && offset >= seg->byte_count)
4002 if (seg->char_count > 0)
4004 offset -= seg->byte_count;
4005 bytes_in_line += seg->byte_count;
4006 last_indexable = seg;
4007 after_last_indexable = last_indexable->next;
4015 /* We went off the end of the line */
4017 g_warning ("%s: byte index off the end of the line", G_STRLOC);
4024 if (after_last_indexable != NULL)
4025 *any_segment = after_last_indexable;
4027 *any_segment = *segment;
4030 /* Override any_segment if we're in the middle of a segment. */
4032 *any_segment = *segment;
4034 *seg_byte_offset = offset;
4036 g_assert (*segment != NULL);
4037 g_assert (*any_segment != NULL);
4038 g_assert (*seg_byte_offset < (*segment)->byte_count);
4040 *line_byte_offset = bytes_in_line + *seg_byte_offset;
4045 /* FIXME sync with byte_locate (or figure out a clean
4046 way to merge the two functions) */
4048 _gtk_text_line_char_locate (GtkTextLine *line,
4050 GtkTextLineSegment **segment,
4051 GtkTextLineSegment **any_segment,
4052 gint *seg_char_offset,
4053 gint *line_char_offset)
4055 GtkTextLineSegment *seg;
4056 GtkTextLineSegment *after_last_indexable;
4057 GtkTextLineSegment *last_indexable;
4061 g_return_val_if_fail (line != NULL, FALSE);
4062 g_return_val_if_fail (char_offset >= 0, FALSE);
4065 *any_segment = NULL;
4068 offset = char_offset;
4070 last_indexable = NULL;
4071 after_last_indexable = line->segments;
4072 seg = line->segments;
4074 /* The loop ends when we're inside a segment;
4075 last_indexable refers to the last segment
4076 we passed entirely. */
4077 while (seg && offset >= seg->char_count)
4079 if (seg->char_count > 0)
4081 offset -= seg->char_count;
4082 chars_in_line += seg->char_count;
4083 last_indexable = seg;
4084 after_last_indexable = last_indexable->next;
4092 /* end of the line */
4094 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4101 if (after_last_indexable != NULL)
4102 *any_segment = after_last_indexable;
4104 *any_segment = *segment;
4107 /* Override any_segment if we're in the middle of a segment. */
4109 *any_segment = *segment;
4111 *seg_char_offset = offset;
4113 g_assert (*segment != NULL);
4114 g_assert (*any_segment != NULL);
4115 g_assert (*seg_char_offset < (*segment)->char_count);
4117 *line_char_offset = chars_in_line + *seg_char_offset;
4123 _gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4125 gint *line_char_offset,
4126 gint *seg_char_offset)
4128 GtkTextLineSegment *seg;
4131 g_return_if_fail (line != NULL);
4132 g_return_if_fail (byte_offset >= 0);
4134 *line_char_offset = 0;
4136 offset = byte_offset;
4137 seg = line->segments;
4139 while (offset >= seg->byte_count)
4141 offset -= seg->byte_count;
4142 *line_char_offset += seg->char_count;
4144 g_assert (seg != NULL); /* means an invalid char offset */
4147 g_assert (seg->char_count > 0); /* indexable. */
4149 /* offset is now the number of bytes into the current segment we
4150 * want to go. Count chars into the current segment.
4153 if (seg->type == >k_text_char_type)
4155 *seg_char_offset = g_utf8_strlen (seg->body.chars, offset);
4157 g_assert (*seg_char_offset < seg->char_count);
4159 *line_char_offset += *seg_char_offset;
4163 g_assert (offset == 0);
4164 *seg_char_offset = 0;
4169 _gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4171 gint *line_byte_offset,
4172 gint *seg_byte_offset)
4174 GtkTextLineSegment *seg;
4177 g_return_if_fail (line != NULL);
4178 g_return_if_fail (char_offset >= 0);
4180 *line_byte_offset = 0;
4182 offset = char_offset;
4183 seg = line->segments;
4185 while (offset >= seg->char_count)
4187 offset -= seg->char_count;
4188 *line_byte_offset += seg->byte_count;
4190 g_assert (seg != NULL); /* means an invalid char offset */
4193 g_assert (seg->char_count > 0); /* indexable. */
4195 /* offset is now the number of chars into the current segment we
4196 want to go. Count bytes into the current segment. */
4198 if (seg->type == >k_text_char_type)
4202 /* if in the last fourth of the segment walk backwards */
4203 if (seg->char_count - offset < seg->char_count / 4)
4204 p = g_utf8_offset_to_pointer (seg->body.chars + seg->byte_count,
4205 offset - seg->char_count);
4207 p = g_utf8_offset_to_pointer (seg->body.chars, offset);
4209 *seg_byte_offset = p - seg->body.chars;
4211 g_assert (*seg_byte_offset < seg->byte_count);
4213 *line_byte_offset += *seg_byte_offset;
4217 g_assert (offset == 0);
4218 *seg_byte_offset = 0;
4223 node_compare (GtkTextBTreeNode *lhs,
4224 GtkTextBTreeNode *rhs)
4226 GtkTextBTreeNode *iter;
4227 GtkTextBTreeNode *node;
4228 GtkTextBTreeNode *common_parent;
4229 GtkTextBTreeNode *parent_of_lower;
4230 GtkTextBTreeNode *parent_of_higher;
4231 gboolean lhs_is_lower;
4232 GtkTextBTreeNode *lower;
4233 GtkTextBTreeNode *higher;
4235 /* This function assumes that lhs and rhs are not underneath each
4242 if (lhs->level < rhs->level)
4244 lhs_is_lower = TRUE;
4250 lhs_is_lower = FALSE;
4255 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4256 * of the common parent we used to reach the common parent; the
4257 * ordering of these child nodes in the child list is the ordering
4261 /* Get on the same level (may be on same level already) */
4263 while (node->level < higher->level)
4264 node = node->parent;
4266 g_assert (node->level == higher->level);
4268 g_assert (node != higher); /* Happens if lower is underneath higher */
4270 /* Go up until we have two children with a common parent.
4272 parent_of_lower = node;
4273 parent_of_higher = higher;
4275 while (parent_of_lower->parent != parent_of_higher->parent)
4277 parent_of_lower = parent_of_lower->parent;
4278 parent_of_higher = parent_of_higher->parent;
4281 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4283 common_parent = parent_of_lower->parent;
4285 g_assert (common_parent != NULL);
4287 /* See which is first in the list of common_parent's children */
4288 iter = common_parent->children.node;
4289 while (iter != NULL)
4291 if (iter == parent_of_higher)
4293 /* higher is less than lower */
4296 return 1; /* lhs > rhs */
4300 else if (iter == parent_of_lower)
4302 /* lower is less than higher */
4305 return -1; /* lhs < rhs */
4313 g_assert_not_reached ();
4317 /* remember that tag == NULL means "any tag" */
4319 _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4323 GtkTextBTreeNode *node;
4324 GtkTextTagInfo *info;
4325 gboolean below_tag_root;
4327 g_return_val_if_fail (line != NULL, NULL);
4329 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4330 _gtk_text_btree_check (tree);
4334 /* Right now we can only offer linear-search if the user wants
4335 * to know about any tag toggle at all.
4337 return _gtk_text_line_next_excluding_last (line);
4340 /* Our tag summaries only have node precision, not line
4341 * precision. This means that if any line under a node could contain a
4342 * tag, then any of the others could also contain a tag.
4344 * In the future we could have some mechanism to keep track of how
4345 * many toggles we've found under a node so far, since we have a
4346 * count of toggles under the node. But for now I'm going with KISS.
4349 /* return same-node line, if any. */
4353 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4357 if (info->tag_root == NULL)
4360 if (info->tag_root == line->parent)
4361 return NULL; /* we were at the last line under the tag root */
4363 /* We need to go up out of this node, and on to the next one with
4364 toggles for the target tag. If we're below the tag root, we need to
4365 find the next node below the tag root that has tag summaries. If
4366 we're not below the tag root, we need to see if the tag root is
4367 after us in the tree, and if so, return the first line underneath
4370 node = line->parent;
4371 below_tag_root = FALSE;
4372 while (node != NULL)
4374 if (node == info->tag_root)
4376 below_tag_root = TRUE;
4380 node = node->parent;
4385 node = line->parent;
4386 while (node != info->tag_root)
4388 if (node->next == NULL)
4389 node = node->parent;
4394 if (gtk_text_btree_node_has_tag (node, tag))
4404 ordering = node_compare (line->parent, info->tag_root);
4408 /* Tag root is ahead of us, so search there. */
4409 node = info->tag_root;
4414 /* Tag root is after us, so no more lines that
4415 * could contain the tag.
4420 g_assert_not_reached ();
4425 g_assert (node != NULL);
4427 /* We have to find the first sub-node of this node that contains
4431 while (node->level > 0)
4433 g_assert (node != NULL); /* If this fails, it likely means an
4434 incorrect tag summary led us on a
4435 wild goose chase down this branch of
4437 node = node->children.node;
4438 while (node != NULL)
4440 if (gtk_text_btree_node_has_tag (node, tag))
4446 g_assert (node != NULL);
4447 g_assert (node->level == 0);
4449 return node->children.line;
4453 prev_line_under_node (GtkTextBTreeNode *node,
4458 prev = node->children.line;
4464 while (prev->next != line)
4474 _gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4478 GtkTextBTreeNode *node;
4479 GtkTextBTreeNode *found_node = NULL;
4480 GtkTextTagInfo *info;
4481 gboolean below_tag_root;
4483 GtkTextBTreeNode *line_ancestor;
4484 GtkTextBTreeNode *line_ancestor_parent;
4486 /* See next_could_contain_tag () for more extensive comments
4487 * on what's going on here.
4490 g_return_val_if_fail (line != NULL, NULL);
4492 if (gtk_debug_flags & GTK_DEBUG_TEXT)
4493 _gtk_text_btree_check (tree);
4497 /* Right now we can only offer linear-search if the user wants
4498 * to know about any tag toggle at all.
4500 return _gtk_text_line_previous (line);
4503 /* Return same-node line, if any. */
4504 prev = prev_line_under_node (line->parent, line);
4508 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4512 if (info->tag_root == NULL)
4515 if (info->tag_root == line->parent)
4516 return NULL; /* we were at the first line under the tag root */
4518 /* Are we below the tag root */
4519 node = line->parent;
4520 below_tag_root = FALSE;
4521 while (node != NULL)
4523 if (node == info->tag_root)
4525 below_tag_root = TRUE;
4529 node = node->parent;
4534 /* Look for a previous node under this tag root that has our
4538 /* this assertion holds because line->parent is not the
4539 * tag root, we are below the tag root, and the tag
4542 g_assert (line->parent->parent != NULL);
4544 line_ancestor = line->parent;
4545 line_ancestor_parent = line->parent->parent;
4547 while (line_ancestor != info->tag_root)
4549 GSList *child_nodes = NULL;
4552 /* Create reverse-order list of nodes before
4555 if (line_ancestor_parent != NULL)
4556 node = line_ancestor_parent->children.node;
4558 node = line_ancestor;
4560 while (node != line_ancestor && node != NULL)
4562 child_nodes = g_slist_prepend (child_nodes, node);
4567 /* Try to find a node with our tag on it in the list */
4571 GtkTextBTreeNode *this_node = tmp->data;
4573 g_assert (this_node != line_ancestor);
4575 if (gtk_text_btree_node_has_tag (this_node, tag))
4577 found_node = this_node;
4578 g_slist_free (child_nodes);
4582 tmp = g_slist_next (tmp);
4585 g_slist_free (child_nodes);
4587 /* Didn't find anything on this level; go up one level. */
4588 line_ancestor = line_ancestor_parent;
4589 line_ancestor_parent = line_ancestor->parent;
4599 ordering = node_compare (line->parent, info->tag_root);
4603 /* Tag root is ahead of us, so no more lines
4610 /* Tag root is after us, so grab last tagged
4611 * line underneath the tag root.
4613 found_node = info->tag_root;
4617 g_assert_not_reached ();
4622 g_assert (found_node != NULL);
4624 /* We have to find the last sub-node of this node that contains
4629 while (node->level > 0)
4631 GSList *child_nodes = NULL;
4633 g_assert (node != NULL); /* If this fails, it likely means an
4634 incorrect tag summary led us on a
4635 wild goose chase down this branch of
4638 node = node->children.node;
4639 while (node != NULL)
4641 child_nodes = g_slist_prepend (child_nodes, node);
4645 node = NULL; /* detect failure to find a child node. */
4648 while (iter != NULL)
4650 if (gtk_text_btree_node_has_tag (iter->data, tag))
4652 /* recurse into this node. */
4657 iter = g_slist_next (iter);
4660 g_slist_free (child_nodes);
4662 g_assert (node != NULL);
4665 g_assert (node != NULL);
4666 g_assert (node->level == 0);
4668 /* this assertion is correct, but slow. */
4669 /* g_assert (node_compare (node, line->parent) < 0); */
4671 /* Return last line in this node. */
4673 prev = node->children.line;
4681 * Non-public function implementations
4685 summary_list_destroy (Summary *summary)
4687 g_slice_free_chain (Summary, summary, next);
4691 get_last_line (GtkTextBTree *tree)
4693 if (tree->last_line_stamp != tree->chars_changed_stamp)
4699 n_lines = _gtk_text_btree_line_count (tree);
4701 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4703 line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
4705 tree->last_line_stamp = tree->chars_changed_stamp;
4706 tree->last_line = line;
4709 return tree->last_line;
4717 gtk_text_line_new (void)
4721 line = g_new0(GtkTextLine, 1);
4722 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4723 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4724 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4730 gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4732 GtkTextLineData *ld;
4733 GtkTextLineData *next;
4735 g_return_if_fail (line != NULL);
4742 view = gtk_text_btree_get_view (tree, ld->view_id);
4744 g_assert (view != NULL);
4747 gtk_text_layout_free_line_data (view->layout, line, ld);
4756 gtk_text_line_set_parent (GtkTextLine *line,
4757 GtkTextBTreeNode *node)
4759 if (line->parent == node)
4761 line->parent = node;
4762 gtk_text_btree_node_invalidate_upward (node, NULL);
4766 cleanup_line (GtkTextLine *line)
4768 GtkTextLineSegment *seg, **prev_p;
4772 * Make a pass over all of the segments in the line, giving each
4773 * a chance to clean itself up. This could potentially change
4774 * the structure of the line, e.g. by merging two segments
4775 * together or having two segments cancel themselves; if so,
4776 * then repeat the whole process again, since the first structure
4777 * change might make other structure changes possible. Repeat
4778 * until eventually there are no changes.
4785 prev_p = &line->segments;
4786 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4788 if (seg->type->cleanupFunc != NULL)
4790 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4798 prev_p = &(*prev_p)->next;
4808 node_data_new (gpointer view_id)
4812 nd = g_slice_new (NodeData);
4814 nd->view_id = view_id;
4824 node_data_destroy (NodeData *nd)
4826 g_slice_free (NodeData, nd);
4830 node_data_list_destroy (NodeData *nd)
4832 g_slice_free_chain (NodeData, nd, next);
4836 node_data_find (NodeData *nd,
4841 if (nd->view_id == view_id)
4849 summary_destroy (Summary *summary)
4851 /* Fill with error-triggering garbage */
4852 summary->info = (void*)0x1;
4853 summary->toggle_count = 567;
4854 summary->next = (void*)0x1;
4855 g_slice_free (Summary, summary);
4858 static GtkTextBTreeNode*
4859 gtk_text_btree_node_new (void)
4861 GtkTextBTreeNode *node;
4863 node = g_new (GtkTextBTreeNode, 1);
4865 node->node_data = NULL;
4871 gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4872 GtkTextTagInfo *info,
4877 summary = node->summary;
4878 while (summary != NULL)
4880 if (summary->info == info)
4882 summary->toggle_count += adjust;
4886 summary = summary->next;
4889 if (summary == NULL)
4891 /* didn't find a summary for our tag. */
4892 g_return_if_fail (adjust > 0);
4893 summary = g_slice_new (Summary);
4894 summary->info = info;
4895 summary->toggle_count = adjust;
4896 summary->next = node->summary;
4897 node->summary = summary;
4901 /* Note that the tag root and above do not have summaries
4902 for the tag; only nodes below the tag root have
4905 gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4909 summary = node->summary;
4910 while (summary != NULL)
4913 summary->info->tag == tag)
4916 summary = summary->next;
4922 /* Add node and all children to the damage region. */
4924 gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4928 nd = node->node_data;
4935 if (node->level == 0)
4939 line = node->children.line;
4940 while (line != NULL)
4942 GtkTextLineData *ld;
4956 GtkTextBTreeNode *child;
4958 child = node->children.node;
4960 while (child != NULL)
4962 gtk_text_btree_node_invalidate_downward (child);
4964 child = child->next;
4970 gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4972 GtkTextBTreeNode *iter;
4975 while (iter != NULL)
4981 nd = node_data_find (iter->node_data, view_id);
4983 if (nd == NULL || !nd->valid)
4984 break; /* Once a node is invalid, we know its parents are as well. */
4990 gboolean should_continue = FALSE;
4992 nd = iter->node_data;
4997 should_continue = TRUE;
5004 if (!should_continue)
5005 break; /* This node was totally invalidated, so are its
5009 iter = iter->parent;
5015 * _gtk_text_btree_is_valid:
5016 * @tree: a #GtkTextBTree
5017 * @view_id: ID for the view
5019 * Check to see if the entire #GtkTextBTree is valid or not for
5022 * Return value: %TRUE if the entire #GtkTextBTree is valid
5025 _gtk_text_btree_is_valid (GtkTextBTree *tree,
5029 g_return_val_if_fail (tree != NULL, FALSE);
5031 nd = node_data_find (tree->root_node->node_data, view_id);
5032 return (nd && nd->valid);
5035 typedef struct _ValidateState ValidateState;
5037 struct _ValidateState
5039 gint remaining_pixels;
5040 gboolean in_validation;
5047 gtk_text_btree_node_validate (BTreeView *view,
5048 GtkTextBTreeNode *node,
5050 ValidateState *state)
5052 gint node_valid = TRUE;
5053 gint node_width = 0;
5054 gint node_height = 0;
5056 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5057 g_return_if_fail (!nd->valid);
5059 if (node->level == 0)
5061 GtkTextLine *line = node->children.line;
5062 GtkTextLineData *ld;
5064 /* Iterate over leading valid lines */
5065 while (line != NULL)
5067 ld = _gtk_text_line_get_data (line, view_id);
5069 if (!ld || !ld->valid)
5071 else if (state->in_validation)
5073 state->in_validation = FALSE;
5078 state->y += ld->height;
5079 node_width = MAX (ld->width, node_width);
5080 node_height += ld->height;
5086 state->in_validation = TRUE;
5088 /* Iterate over invalid lines */
5089 while (line != NULL)
5091 ld = _gtk_text_line_get_data (line, view_id);
5093 if (ld && ld->valid)
5098 state->old_height += ld->height;
5099 ld = gtk_text_layout_wrap (view->layout, line, ld);
5100 state->new_height += ld->height;
5102 node_width = MAX (ld->width, node_width);
5103 node_height += ld->height;
5105 state->remaining_pixels -= ld->height;
5106 if (state->remaining_pixels <= 0)
5116 /* Iterate over the remaining lines */
5117 while (line != NULL)
5119 ld = _gtk_text_line_get_data (line, view_id);
5120 state->in_validation = FALSE;
5122 if (!ld || !ld->valid)
5127 node_width = MAX (ld->width, node_width);
5128 node_height += ld->height;
5136 GtkTextBTreeNode *child;
5139 child = node->children.node;
5141 /* Iterate over leading valid nodes */
5144 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5146 if (!child_nd->valid)
5148 else if (state->in_validation)
5150 state->in_validation = FALSE;
5155 state->y += child_nd->height;
5156 node_width = MAX (node_width, child_nd->width);
5157 node_height += child_nd->height;
5160 child = child->next;
5163 /* Iterate over invalid nodes */
5166 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5168 if (child_nd->valid)
5172 gtk_text_btree_node_validate (view, child, view_id, state);
5174 if (!child_nd->valid)
5176 node_width = MAX (node_width, child_nd->width);
5177 node_height += child_nd->height;
5179 if (!state->in_validation || state->remaining_pixels <= 0)
5181 child = child->next;
5186 child = child->next;
5189 /* Iterate over the remaining lines */
5192 child_nd = gtk_text_btree_node_ensure_data (child, view_id);
5193 state->in_validation = FALSE;
5195 if (!child_nd->valid)
5198 node_width = MAX (child_nd->width, node_width);
5199 node_height += child_nd->height;
5201 child = child->next;
5205 nd->width = node_width;
5206 nd->height = node_height;
5207 nd->valid = node_valid;
5211 * _gtk_text_btree_validate:
5212 * @tree: a #GtkTextBTree
5214 * @max_pixels: the maximum number of pixels to validate. (No more
5215 * than one paragraph beyond this limit will be validated)
5216 * @y: location to store starting y coordinate of validated region
5217 * @old_height: location to store old height of validated region
5218 * @new_height: location to store new height of validated region
5220 * Validate a single contiguous invalid region of a #GtkTextBTree for
5223 * Return value: %TRUE if a region has been validated, %FALSE if the
5224 * entire tree was already valid.
5227 _gtk_text_btree_validate (GtkTextBTree *tree,
5236 g_return_val_if_fail (tree != NULL, FALSE);
5238 view = gtk_text_btree_get_view (tree, view_id);
5239 g_return_val_if_fail (view != NULL, FALSE);
5241 if (!_gtk_text_btree_is_valid (tree, view_id))
5243 ValidateState state;
5245 state.remaining_pixels = max_pixels;
5246 state.in_validation = FALSE;
5248 state.old_height = 0;
5249 state.new_height = 0;
5251 gtk_text_btree_node_validate (view,
5258 *old_height = state.old_height;
5260 *new_height = state.new_height;
5262 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5263 _gtk_text_btree_check (tree);
5272 gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5276 gboolean *valid_out)
5280 gboolean valid = TRUE;
5282 if (node->level == 0)
5284 GtkTextLine *line = node->children.line;
5286 while (line != NULL)
5288 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5290 if (!ld || !ld->valid)
5295 width = MAX (ld->width, width);
5296 height += ld->height;
5304 GtkTextBTreeNode *child = node->children.node;
5308 NodeData *child_nd = node_data_find (child->node_data, view_id);
5310 if (!child_nd || !child_nd->valid)
5315 width = MAX (child_nd->width, width);
5316 height += child_nd->height;
5319 child = child->next;
5324 *height_out = height;
5329 /* Recompute the validity and size of the view data for a given
5330 * view at this node from the immediate children of the node
5333 gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5336 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5341 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5342 &width, &height, &valid);
5344 nd->height = height;
5351 gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5356 gtk_text_btree_node_check_valid (node, view_id);
5357 node = node->parent;
5362 gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5365 if (node->level == 0)
5367 return gtk_text_btree_node_check_valid (node, view_id);
5371 GtkTextBTreeNode *child = node->children.node;
5373 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5381 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
5383 if (!child_nd->valid)
5385 nd->width = MAX (child_nd->width, nd->width);
5386 nd->height += child_nd->height;
5388 child = child->next;
5397 * _gtk_text_btree_validate_line:
5398 * @tree: a #GtkTextBTree
5399 * @line: line to validate
5400 * @view_id: view ID for the view to validate
5402 * Revalidate a single line of the btree for the given view, propagate
5403 * results up through the entire tree.
5406 _gtk_text_btree_validate_line (GtkTextBTree *tree,
5410 GtkTextLineData *ld;
5413 g_return_if_fail (tree != NULL);
5414 g_return_if_fail (line != NULL);
5416 view = gtk_text_btree_get_view (tree, view_id);
5417 g_return_if_fail (view != NULL);
5419 ld = _gtk_text_line_get_data (line, view_id);
5420 if (!ld || !ld->valid)
5422 ld = gtk_text_layout_wrap (view->layout, line, ld);
5424 gtk_text_btree_node_check_valid_upward (line->parent, view_id);
5429 gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5431 if (node->level == 0)
5435 line = node->children.line;
5436 while (line != NULL)
5438 GtkTextLineData *ld;
5440 ld = _gtk_text_line_remove_data (line, view_id);
5443 gtk_text_layout_free_line_data (view->layout, line, ld);
5450 GtkTextBTreeNode *child;
5452 child = node->children.node;
5454 while (child != NULL)
5457 gtk_text_btree_node_remove_view (view, child, view_id);
5459 child = child->next;
5463 gtk_text_btree_node_remove_data (node, view_id);
5467 gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5469 if (node->level == 0)
5472 GtkTextLineSegment *seg;
5474 while (node->children.line != NULL)
5476 line = node->children.line;
5477 node->children.line = line->next;
5478 while (line->segments != NULL)
5480 seg = line->segments;
5481 line->segments = seg->next;
5483 (*seg->type->deleteFunc) (seg, line, TRUE);
5485 gtk_text_line_destroy (tree, line);
5490 GtkTextBTreeNode *childPtr;
5492 while (node->children.node != NULL)
5494 childPtr = node->children.node;
5495 node->children.node = childPtr->next;
5496 gtk_text_btree_node_destroy (tree, childPtr);
5500 gtk_text_btree_node_free_empty (tree, node);
5504 gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5505 GtkTextBTreeNode *node)
5507 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5508 (node->level == 0 && node->children.line == NULL));
5510 summary_list_destroy (node->summary);
5511 node_data_list_destroy (node->node_data);
5516 gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5520 nd = node->node_data;
5523 if (nd->view_id == view_id)
5531 nd = node_data_new (view_id);
5533 if (node->node_data)
5534 nd->next = node->node_data;
5536 node->node_data = nd;
5543 gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5549 nd = node->node_data;
5552 if (nd->view_id == view_id)
5563 prev->next = nd->next;
5565 if (node->node_data == nd)
5566 node->node_data = nd->next;
5570 node_data_destroy (nd);
5574 gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5575 gint *width, gint *height)
5579 g_return_if_fail (width != NULL);
5580 g_return_if_fail (height != NULL);
5582 nd = gtk_text_btree_node_ensure_data (node, view_id);
5587 *height = nd->height;
5590 /* Find the closest common ancestor of the two nodes. FIXME: The interface
5591 * here isn't quite right, since for a lot of operations we want to
5592 * know which children of the common parent correspond to the two nodes
5593 * (e.g., when computing the order of two iters)
5595 static GtkTextBTreeNode *
5596 gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5597 GtkTextBTreeNode *node2)
5599 while (node1->level < node2->level)
5600 node1 = node1->parent;
5601 while (node2->level < node1->level)
5602 node2 = node2->parent;
5603 while (node1 != node2)
5605 node1 = node1->parent;
5606 node2 = node2->parent;
5617 gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5622 while (view != NULL)
5624 if (view->view_id == view_id)
5633 get_tree_bounds (GtkTextBTree *tree,
5637 _gtk_text_btree_get_iter_at_line_char (tree, start, 0, 0);
5638 _gtk_text_btree_get_end_iter (tree, end);
5642 tag_changed_cb (GtkTextTagTable *table,
5644 gboolean size_changed,
5649 /* We need to queue a relayout on all regions that are tagged with
5656 if (_gtk_text_btree_get_iter_at_first_toggle (tree, &start, tag))
5658 /* Must be a last toggle if there was a first one. */
5659 _gtk_text_btree_get_iter_at_last_toggle (tree, &end, tag);
5660 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5661 _gtk_text_btree_invalidate_region (tree, &start, &end, FALSE);
5667 /* We only need to queue a redraw, not a relayout */
5672 while (view != NULL)
5676 _gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
5677 gtk_text_layout_changed (view->layout, 0, height, height);
5685 _gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5688 /* Remove the tag from the tree */
5693 get_tree_bounds (tree, &start, &end);
5695 _gtk_text_btree_tag (&start, &end, tag, FALSE);
5696 gtk_text_btree_remove_tag_info (tree, tag);
5700 /* Rebalance the out-of-whack node "node" */
5702 gtk_text_btree_rebalance (GtkTextBTree *tree,
5703 GtkTextBTreeNode *node)
5706 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5707 * up through the tree one GtkTextBTreeNode at a time until the root
5708 * GtkTextBTreeNode has been processed.
5711 while (node != NULL)
5713 GtkTextBTreeNode *new_node, *child;
5718 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5719 * then split off all but the first MIN_CHILDREN into a separate
5720 * GtkTextBTreeNode following the original one. Then repeat until the
5721 * GtkTextBTreeNode has a decent size.
5724 if (node->num_children > MAX_CHILDREN)
5729 * If the GtkTextBTreeNode being split is the root
5730 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5734 if (node->parent == NULL)
5736 new_node = gtk_text_btree_node_new ();
5737 new_node->parent = NULL;
5738 new_node->next = NULL;
5739 new_node->summary = NULL;
5740 new_node->level = node->level + 1;
5741 new_node->children.node = node;
5742 recompute_node_counts (tree, new_node);
5743 tree->root_node = new_node;
5745 new_node = gtk_text_btree_node_new ();
5746 new_node->parent = node->parent;
5747 new_node->next = node->next;
5748 node->next = new_node;
5749 new_node->summary = NULL;
5750 new_node->level = node->level;
5751 new_node->num_children = node->num_children - MIN_CHILDREN;
5752 if (node->level == 0)
5754 for (i = MIN_CHILDREN-1,
5755 line = node->children.line;
5756 i > 0; i--, line = line->next)
5758 /* Empty loop body. */
5760 new_node->children.line = line->next;
5765 for (i = MIN_CHILDREN-1,
5766 child = node->children.node;
5767 i > 0; i--, child = child->next)
5769 /* Empty loop body. */
5771 new_node->children.node = child->next;
5774 recompute_node_counts (tree, node);
5775 node->parent->num_children++;
5777 if (node->num_children <= MAX_CHILDREN)
5779 recompute_node_counts (tree, node);
5785 while (node->num_children < MIN_CHILDREN)
5787 GtkTextBTreeNode *other;
5788 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5789 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5790 int total_children, first_children, i;
5793 * Too few children for this GtkTextBTreeNode. If this is the root then,
5794 * it's OK for it to have less than MIN_CHILDREN children
5795 * as long as it's got at least two. If it has only one
5796 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5797 * the tree and use its child as the new root.
5800 if (node->parent == NULL)
5802 if ((node->num_children == 1) && (node->level > 0))
5804 tree->root_node = node->children.node;
5805 tree->root_node->parent = NULL;
5807 node->children.node = NULL;
5808 gtk_text_btree_node_free_empty (tree, node);
5814 * Not the root. Make sure that there are siblings to
5818 if (node->parent->num_children < 2)
5820 gtk_text_btree_rebalance (tree, node->parent);
5825 * Find a sibling neighbor to borrow from, and arrange for
5826 * node to be the earlier of the pair.
5829 if (node->next == NULL)
5831 for (other = node->parent->children.node;
5832 other->next != node;
5833 other = other->next)
5835 /* Empty loop body. */
5842 * We're going to either merge the two siblings together
5843 * into one GtkTextBTreeNode or redivide the children among them to
5844 * balance their loads. As preparation, join their two
5845 * child lists into a single list and remember the half-way
5846 * point in the list.
5849 total_children = node->num_children + other->num_children;
5850 first_children = total_children/2;
5851 if (node->children.node == NULL)
5853 node->children = other->children;
5854 other->children.node = NULL;
5855 other->children.line = NULL;
5857 if (node->level == 0)
5861 for (line = node->children.line, i = 1;
5863 line = line->next, i++)
5865 if (i == first_children)
5870 line->next = other->children.line;
5871 while (i <= first_children)
5880 GtkTextBTreeNode *child;
5882 for (child = node->children.node, i = 1;
5883 child->next != NULL;
5884 child = child->next, i++)
5886 if (i <= first_children)
5888 if (i == first_children)
5890 halfwaynode = child;
5894 child->next = other->children.node;
5895 while (i <= first_children)
5897 halfwaynode = child;
5898 child = child->next;
5904 * If the two siblings can simply be merged together, do it.
5907 if (total_children <= MAX_CHILDREN)
5909 recompute_node_counts (tree, node);
5910 node->next = other->next;
5911 node->parent->num_children--;
5913 other->children.node = NULL;
5914 other->children.line = NULL;
5915 gtk_text_btree_node_free_empty (tree, other);
5920 * The siblings can't be merged, so just divide their
5921 * children evenly between them.
5924 if (node->level == 0)
5926 other->children.line = halfwayline->next;
5927 halfwayline->next = NULL;
5931 other->children.node = halfwaynode->next;
5932 halfwaynode->next = NULL;
5935 recompute_node_counts (tree, node);
5936 recompute_node_counts (tree, other);
5939 node = node->parent;
5944 post_insert_fixup (GtkTextBTree *tree,
5946 gint line_count_delta,
5947 gint char_count_delta)
5950 GtkTextBTreeNode *node;
5953 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5954 * point, then rebalance the tree if necessary.
5957 for (node = line->parent ; node != NULL;
5958 node = node->parent)
5960 node->num_lines += line_count_delta;
5961 node->num_chars += char_count_delta;
5963 node = line->parent;
5964 node->num_children += line_count_delta;
5966 if (node->num_children > MAX_CHILDREN)
5968 gtk_text_btree_rebalance (tree, node);
5971 if (gtk_debug_flags & GTK_DEBUG_TEXT)
5972 _gtk_text_btree_check (tree);
5975 static GtkTextTagInfo*
5976 gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5979 GtkTextTagInfo *info;
5983 list = tree->tag_infos;
5984 while (list != NULL)
5987 if (info->tag == tag)
5990 list = g_slist_next (list);
5996 static GtkTextTagInfo*
5997 gtk_text_btree_get_tag_info (GtkTextBTree *tree,
6000 GtkTextTagInfo *info;
6002 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6006 /* didn't find it, create. */
6008 info = g_slice_new (GtkTextTagInfo);
6012 info->tag_root = NULL;
6013 info->toggle_count = 0;
6015 tree->tag_infos = g_slist_prepend (tree->tag_infos, info);
6018 g_print ("Created tag info %p for tag %s(%p)\n",
6019 info, info->tag->name ? info->tag->name : "anon",
6028 gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
6031 GtkTextTagInfo *info;
6036 list = tree->tag_infos;
6037 while (list != NULL)
6040 if (info->tag == tag)
6043 g_print ("Removing tag info %p for tag %s(%p)\n",
6044 info, info->tag->name ? info->tag->name : "anon",
6050 prev->next = list->next;
6054 tree->tag_infos = list->next;
6057 g_slist_free (list);
6059 g_object_unref (info->tag);
6061 g_slice_free (GtkTextTagInfo, info);
6066 list = g_slist_next (list);
6071 recompute_level_zero_counts (GtkTextBTreeNode *node)
6074 GtkTextLineSegment *seg;
6076 g_assert (node->level == 0);
6078 line = node->children.line;
6079 while (line != NULL)
6081 node->num_children++;
6084 if (line->parent != node)
6085 gtk_text_line_set_parent (line, node);
6087 seg = line->segments;
6091 node->num_chars += seg->char_count;
6093 if (((seg->type != >k_text_toggle_on_type)
6094 && (seg->type != >k_text_toggle_off_type))
6095 || !(seg->body.toggle.inNodeCounts))
6101 GtkTextTagInfo *info;
6103 info = seg->body.toggle.info;
6105 gtk_text_btree_node_adjust_toggle_count (node, info, 1);
6116 recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6119 GtkTextBTreeNode *child;
6121 g_assert (node->level > 0);
6123 child = node->children.node;
6124 while (child != NULL)
6126 node->num_children += 1;
6127 node->num_lines += child->num_lines;
6128 node->num_chars += child->num_chars;
6130 if (child->parent != node)
6132 child->parent = node;
6133 gtk_text_btree_node_invalidate_upward (node, NULL);
6136 summary = child->summary;
6137 while (summary != NULL)
6139 gtk_text_btree_node_adjust_toggle_count (node,
6141 summary->toggle_count);
6143 summary = summary->next;
6146 child = child->next;
6151 *----------------------------------------------------------------------
6153 * recompute_node_counts --
6155 * This procedure is called to recompute all the counts in a GtkTextBTreeNode
6156 * (tags, child information, etc.) by scanning the information in
6157 * its descendants. This procedure is called during rebalancing
6158 * when a GtkTextBTreeNode's child structure has changed.
6164 * The tag counts for node are modified to reflect its current
6165 * child structure, as are its num_children, num_lines, num_chars fields.
6166 * Also, all of the childrens' parent fields are made to point
6169 *----------------------------------------------------------------------
6173 recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6176 Summary *summary, *summary2;
6179 * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
6180 * the existing Summary records (most of them will probably be reused).
6183 summary = node->summary;
6184 while (summary != NULL)
6186 summary->toggle_count = 0;
6187 summary = summary->next;
6190 node->num_children = 0;
6191 node->num_lines = 0;
6192 node->num_chars = 0;
6195 * Scan through the children, adding the childrens' tag counts into
6196 * the GtkTextBTreeNode's tag counts and adding new Summary structures if
6200 if (node->level == 0)
6201 recompute_level_zero_counts (node);
6203 recompute_level_nonzero_counts (node);
6208 gtk_text_btree_node_check_valid (node, view->view_id);
6213 * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
6214 * records that still have a zero count, or that have all the toggles.
6215 * The GtkTextBTreeNode with the children that account for all the tags toggles
6216 * have no summary information, and they become the tag_root for the tag.
6220 for (summary = node->summary; summary != NULL; )
6222 if (summary->toggle_count > 0 &&
6223 summary->toggle_count < summary->info->toggle_count)
6225 if (node->level == summary->info->tag_root->level)
6228 * The tag's root GtkTextBTreeNode split and some toggles left.
6229 * The tag root must move up a level.
6231 summary->info->tag_root = node->parent;
6234 summary = summary->next;
6237 if (summary->toggle_count == summary->info->toggle_count)
6240 * A GtkTextBTreeNode merge has collected all the toggles under
6241 * one GtkTextBTreeNode. Push the root down to this level.
6243 summary->info->tag_root = node;
6245 if (summary2 != NULL)
6247 summary2->next = summary->next;
6248 summary_destroy (summary);
6249 summary = summary2->next;
6253 node->summary = summary->next;
6254 summary_destroy (summary);
6255 summary = node->summary;
6261 _gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6262 GtkTextTagInfo *info,
6263 gint delta) /* may be negative */
6265 Summary *summary, *prevPtr;
6266 GtkTextBTreeNode *node2Ptr;
6267 int rootLevel; /* Level of original tag root */
6269 info->toggle_count += delta;
6271 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6273 info->tag_root = node;
6278 * Note the level of the existing root for the tag so we can detect
6279 * if it needs to be moved because of the toggle count change.
6282 rootLevel = info->tag_root->level;
6285 * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
6286 * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
6290 for ( ; node != info->tag_root; node = node->parent)
6293 * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
6294 * perhaps all we have to do is adjust its count.
6297 for (prevPtr = NULL, summary = node->summary;
6299 prevPtr = summary, summary = summary->next)
6301 if (summary->info == info)
6306 if (summary != NULL)
6308 summary->toggle_count += delta;
6309 if (summary->toggle_count > 0 &&
6310 summary->toggle_count < info->toggle_count)
6314 if (summary->toggle_count != 0)
6317 * Should never find a GtkTextBTreeNode with max toggle count at this
6318 * point (there shouldn't have been a summary entry in the
6322 g_error ("%s: bad toggle count (%d) max (%d)",
6323 G_STRLOC, summary->toggle_count, info->toggle_count);
6327 * Zero toggle count; must remove this tag from the list.
6330 if (prevPtr == NULL)
6332 node->summary = summary->next;
6336 prevPtr->next = summary->next;
6338 summary_destroy (summary);
6343 * This tag isn't currently in the summary information list.
6346 if (rootLevel == node->level)
6350 * The old tag root is at the same level in the tree as this
6351 * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
6352 * a level, in the hopes that it will now cover this GtkTextBTreeNode
6353 * as well as the old root (if not, we'll move it up again
6354 * the next time through the loop). To push it up one level
6355 * we copy the original toggle count into the summary
6356 * information at the old root and change the root to its
6357 * parent GtkTextBTreeNode.
6360 GtkTextBTreeNode *rootnode = info->tag_root;
6361 summary = g_slice_new (Summary);
6362 summary->info = info;
6363 summary->toggle_count = info->toggle_count - delta;
6364 summary->next = rootnode->summary;
6365 rootnode->summary = summary;
6366 rootnode = rootnode->parent;
6367 rootLevel = rootnode->level;
6368 info->tag_root = rootnode;
6370 summary = g_slice_new (Summary);
6371 summary->info = info;
6372 summary->toggle_count = delta;
6373 summary->next = node->summary;
6374 node->summary = summary;
6379 * If we've decremented the toggle count, then it may be necessary
6380 * to push the tag root down one or more levels.
6387 if (info->toggle_count == 0)
6389 info->tag_root = (GtkTextBTreeNode *) NULL;
6392 node = info->tag_root;
6393 while (node->level > 0)
6396 * See if a single child GtkTextBTreeNode accounts for all of the tag's
6397 * toggles. If so, push the root down one level.
6400 for (node2Ptr = node->children.node;
6401 node2Ptr != (GtkTextBTreeNode *)NULL ;
6402 node2Ptr = node2Ptr->next)
6404 for (prevPtr = NULL, summary = node2Ptr->summary;
6406 prevPtr = summary, summary = summary->next)
6408 if (summary->info == info)
6413 if (summary == NULL)
6417 if (summary->toggle_count != info->toggle_count)
6420 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6427 * This GtkTextBTreeNode has all the toggles, so push down the root.
6430 if (prevPtr == NULL)
6432 node2Ptr->summary = summary->next;
6436 prevPtr->next = summary->next;
6438 summary_destroy (summary);
6439 info->tag_root = node2Ptr;
6442 node = info->tag_root;
6447 *----------------------------------------------------------------------
6451 * This is a utility procedure used by _gtk_text_btree_get_tags. It
6452 * increments the count for a particular tag, adding a new
6453 * entry for that tag if there wasn't one previously.
6459 * The information at *tagInfoPtr may be modified, and the arrays
6460 * may be reallocated to make them larger.
6462 *----------------------------------------------------------------------
6466 inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6471 for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
6472 count > 0; tag_p++, count--)
6476 tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
6482 * There isn't currently an entry for this tag, so we have to
6483 * make a new one. If the arrays are full, then enlarge the
6487 if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
6489 GtkTextTag **newTags;
6490 int *newCounts, newSize;
6492 newSize = 2*tagInfoPtr->arraySize;
6493 newTags = (GtkTextTag **) g_malloc ((unsigned)
6494 (newSize*sizeof (GtkTextTag *)));
6495 memcpy ((void *) newTags, (void *) tagInfoPtr->tags,
6496 tagInfoPtr->arraySize *sizeof (GtkTextTag *));
6497 g_free ((char *) tagInfoPtr->tags);
6498 tagInfoPtr->tags = newTags;
6499 newCounts = (int *) g_malloc ((unsigned) (newSize*sizeof (int)));
6500 memcpy ((void *) newCounts, (void *) tagInfoPtr->counts,
6501 tagInfoPtr->arraySize *sizeof (int));
6502 g_free ((char *) tagInfoPtr->counts);
6503 tagInfoPtr->counts = newCounts;
6504 tagInfoPtr->arraySize = newSize;
6507 tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
6508 tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
6509 tagInfoPtr->numTags++;
6513 gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6514 const GtkTextIter *iter)
6516 GtkTextLineSegment *prev;
6520 line = _gtk_text_iter_get_text_line (iter);
6521 tree = _gtk_text_iter_get_btree (iter);
6523 prev = gtk_text_line_segment_split (iter);
6526 seg->next = line->segments;
6527 line->segments = seg;
6531 seg->next = prev->next;
6534 cleanup_line (line);
6535 segments_changed (tree);
6537 if (gtk_debug_flags & GTK_DEBUG_TEXT)
6538 _gtk_text_btree_check (tree);
6542 gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6543 GtkTextLineSegment *seg,
6546 GtkTextLineSegment *prev;
6548 if (line->segments == seg)
6550 line->segments = seg->next;
6554 for (prev = line->segments; prev->next != seg;
6557 /* Empty loop body. */
6559 prev->next = seg->next;
6561 cleanup_line (line);
6562 segments_changed (tree);
6566 * This is here because it requires BTree internals, it logically
6567 * belongs in gtktextsegment.c
6572 *--------------------------------------------------------------
6574 * _gtk_toggle_segment_check_func --
6576 * This procedure is invoked to perform consistency checks
6577 * on toggle segments.
6583 * If a consistency problem is found the procedure g_errors.
6585 *--------------------------------------------------------------
6589 _gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6595 if (segPtr->byte_count != 0)
6597 g_error ("toggle_segment_check_func: segment had non-zero size");
6599 if (!segPtr->body.toggle.inNodeCounts)
6601 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6603 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6604 for (summary = line->parent->summary; ;
6605 summary = summary->next)
6607 if (summary == NULL)
6611 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6618 if (summary->info == segPtr->body.toggle.info)
6622 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6634 gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6635 GtkTextBTreeNode *node,
6645 while (view != NULL)
6647 if (view->view_id == nd->view_id)
6654 g_error ("Node has data for a view %p no longer attached to the tree",
6657 gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
6658 &width, &height, &valid);
6660 /* valid aggregate not checked the same as width/height, because on
6661 * btree rebalance we can have invalid nodes where all lines below
6662 * them are actually valid, due to moving lines around between
6665 * The guarantee is that if there are invalid lines the node is
6666 * invalid - we don't guarantee that if the node is invalid there
6667 * are invalid lines.
6670 if (nd->width != width ||
6671 nd->height != height ||
6672 (nd->valid && !valid))
6674 g_error ("Node aggregates for view %p are invalid:\n"
6675 "Are (%d,%d,%s), should be (%d,%d,%s)",
6677 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6678 width, height, valid ? "TRUE" : "FALSE");
6683 gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6684 GtkTextBTreeNode *node)
6686 GtkTextBTreeNode *childnode;
6687 Summary *summary, *summary2;
6689 GtkTextLineSegment *segPtr;
6690 int num_children, num_lines, num_chars, toggle_count, min_children;
6691 GtkTextLineData *ld;
6694 if (node->parent != NULL)
6696 min_children = MIN_CHILDREN;
6698 else if (node->level > 0)
6705 if ((node->num_children < min_children)
6706 || (node->num_children > MAX_CHILDREN))
6708 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6709 node->num_children);
6712 nd = node->node_data;
6715 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6722 if (node->level == 0)
6724 for (line = node->children.line; line != NULL;
6727 if (line->parent != node)
6729 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6731 if (line->segments == NULL)
6733 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6739 /* Just ensuring we don't segv while doing this loop */
6744 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6746 if (segPtr->type->checkFunc != NULL)
6748 (*segPtr->type->checkFunc)(segPtr, line);
6750 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6751 && (segPtr->next != NULL)
6752 && (segPtr->next->byte_count == 0)
6753 && (segPtr->next->type->leftGravity))
6755 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6757 if ((segPtr->next == NULL)
6758 && (segPtr->type != >k_text_char_type))
6760 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6763 num_chars += segPtr->char_count;
6772 for (childnode = node->children.node; childnode != NULL;
6773 childnode = childnode->next)
6775 if (childnode->parent != node)
6777 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6779 if (childnode->level != (node->level-1))
6781 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6782 node->level, childnode->level);
6784 gtk_text_btree_node_check_consistency (tree, childnode);
6785 for (summary = childnode->summary; summary != NULL;
6786 summary = summary->next)
6788 for (summary2 = node->summary; ;
6789 summary2 = summary2->next)
6791 if (summary2 == NULL)
6793 if (summary->info->tag_root == node)
6797 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6798 summary->info->tag->name,
6799 "present in parent summaries");
6801 if (summary->info == summary2->info)
6808 num_lines += childnode->num_lines;
6809 num_chars += childnode->num_chars;
6812 if (num_children != node->num_children)
6814 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6815 num_children, node->num_children);
6817 if (num_lines != node->num_lines)
6819 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6820 num_lines, node->num_lines);
6822 if (num_chars != node->num_chars)
6824 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6825 num_chars, node->num_chars);
6828 for (summary = node->summary; summary != NULL;
6829 summary = summary->next)
6831 if (summary->info->toggle_count == summary->toggle_count)
6833 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6834 summary->info->tag->name);
6837 if (node->level == 0)
6839 for (line = node->children.line; line != NULL;
6842 for (segPtr = line->segments; segPtr != NULL;
6843 segPtr = segPtr->next)
6845 if ((segPtr->type != >k_text_toggle_on_type)
6846 && (segPtr->type != >k_text_toggle_off_type))
6850 if (segPtr->body.toggle.info == summary->info)
6852 if (!segPtr->body.toggle.inNodeCounts)
6853 g_error ("Toggle segment not in the node counts");
6862 for (childnode = node->children.node;
6864 childnode = childnode->next)
6866 for (summary2 = childnode->summary;
6868 summary2 = summary2->next)
6870 if (summary2->info == summary->info)
6872 toggle_count += summary2->toggle_count;
6877 if (toggle_count != summary->toggle_count)
6879 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6880 toggle_count, summary->toggle_count);
6882 for (summary2 = summary->next; summary2 != NULL;
6883 summary2 = summary2->next)
6885 if (summary2->info == summary->info)
6887 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6888 summary->info->tag->name);
6895 listify_foreach (GtkTextTag *tag, gpointer user_data)
6897 GSList** listp = user_data;
6899 *listp = g_slist_prepend (*listp, tag);
6903 list_of_tags (GtkTextTagTable *table)
6905 GSList *list = NULL;
6907 gtk_text_tag_table_foreach (table, listify_foreach, &list);
6913 _gtk_text_btree_check (GtkTextBTree *tree)
6916 GtkTextBTreeNode *node;
6918 GtkTextLineSegment *seg;
6920 GSList *all_tags, *taglist = NULL;
6922 GtkTextTagInfo *info;
6925 * Make sure that the tag toggle counts and the tag root pointers are OK.
6927 all_tags = list_of_tags (tree->table);
6928 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6930 tag = taglist->data;
6931 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6934 node = info->tag_root;
6937 if (info->toggle_count != 0)
6939 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6940 tag->name, info->toggle_count);
6942 continue; /* no ranges for the tag */
6944 else if (info->toggle_count == 0)
6946 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6949 else if (info->toggle_count & 1)
6951 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6952 tag->name, info->toggle_count);
6954 for (summary = node->summary; summary != NULL;
6955 summary = summary->next)
6957 if (summary->info->tag == tag)
6959 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6963 if (node->level > 0)
6965 for (node = node->children.node ; node != NULL ;
6968 for (summary = node->summary; summary != NULL;
6969 summary = summary->next)
6971 if (summary->info->tag == tag)
6973 count += summary->toggle_count;
6980 const GtkTextLineSegmentClass *last = NULL;
6982 for (line = node->children.line ; line != NULL ;
6985 for (seg = line->segments; seg != NULL;
6988 if ((seg->type == >k_text_toggle_on_type ||
6989 seg->type == >k_text_toggle_off_type) &&
6990 seg->body.toggle.info->tag == tag)
6992 if (last == seg->type)
6993 g_error ("Two consecutive toggles on or off weren't merged");
6994 if (!seg->body.toggle.inNodeCounts)
6995 g_error ("Toggle segment not in the node counts");
7004 if (count != info->toggle_count)
7006 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
7007 info->toggle_count, tag->name, count);
7012 g_slist_free (all_tags);
7015 * Call a recursive procedure to do the main body of checks.
7018 node = tree->root_node;
7019 gtk_text_btree_node_check_consistency (tree, tree->root_node);
7022 * Make sure that there are at least two lines in the text and
7023 * that the last line has no characters except a newline.
7026 if (node->num_lines < 2)
7028 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
7030 if (node->num_chars < 2)
7032 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
7034 while (node->level > 0)
7036 node = node->children.node;
7037 while (node->next != NULL)
7042 line = node->children.line;
7043 while (line->next != NULL)
7047 seg = line->segments;
7048 while ((seg->type == >k_text_toggle_off_type)
7049 || (seg->type == >k_text_right_mark_type)
7050 || (seg->type == >k_text_left_mark_type))
7053 * It's OK to toggle a tag off in the last line, but
7054 * not to start a new range. It's also OK to have marks
7060 if (seg->type != >k_text_char_type)
7062 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7064 if (seg->next != NULL)
7066 g_error ("_gtk_text_btree_check: last line has too many segments");
7068 if (seg->byte_count != 1)
7070 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7073 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7075 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7080 void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7081 void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7082 void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7083 void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7086 _gtk_text_btree_spew (GtkTextBTree *tree)
7091 printf ("%d lines in tree %p\n",
7092 _gtk_text_btree_line_count (tree), tree);
7094 line = _gtk_text_btree_get_line (tree, 0, &real_line);
7096 while (line != NULL)
7098 _gtk_text_btree_spew_line (tree, line);
7099 line = _gtk_text_line_next (line);
7102 printf ("=================== Tag information\n");
7107 list = tree->tag_infos;
7109 while (list != NULL)
7111 GtkTextTagInfo *info;
7115 printf (" tag `%s': root at %p, toggle count %d\n",
7116 info->tag->name, info->tag_root, info->toggle_count);
7118 list = g_slist_next (list);
7121 if (tree->tag_infos == NULL)
7123 printf (" (no tags in the tree)\n");
7127 printf ("=================== Tree nodes\n");
7130 _gtk_text_btree_spew_node (tree->root_node, 0);
7135 _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7138 GtkTextLineSegment *seg;
7140 spaces = g_strnfill (indent, ' ');
7142 printf ("%sline %p chars %d bytes %d\n",
7144 _gtk_text_line_char_count (line),
7145 _gtk_text_line_byte_count (line));
7147 seg = line->segments;
7150 if (seg->type == >k_text_char_type)
7152 gchar* str = g_strndup (seg->body.chars, MIN (seg->byte_count, 10));
7157 if (*s == '\n' || *s == '\r')
7161 printf ("%s chars `%s'...\n", spaces, str);
7164 else if (seg->type == >k_text_right_mark_type)
7166 printf ("%s right mark `%s' visible: %d\n",
7168 seg->body.mark.name,
7169 seg->body.mark.visible);
7171 else if (seg->type == >k_text_left_mark_type)
7173 printf ("%s left mark `%s' visible: %d\n",
7175 seg->body.mark.name,
7176 seg->body.mark.visible);
7178 else if (seg->type == >k_text_toggle_on_type ||
7179 seg->type == >k_text_toggle_off_type)
7181 printf ("%s tag `%s' %s\n",
7182 spaces, seg->body.toggle.info->tag->name,
7183 seg->type == >k_text_toggle_off_type ? "off" : "on");
7193 _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7196 GtkTextBTreeNode *iter;
7199 spaces = g_strnfill (indent, ' ');
7201 printf ("%snode %p level %d children %d lines %d chars %d\n",
7202 spaces, node, node->level,
7203 node->num_children, node->num_lines, node->num_chars);
7208 printf ("%s %d toggles of `%s' below this node\n",
7209 spaces, s->toggle_count, s->info->tag->name);
7215 if (node->level > 0)
7217 iter = node->children.node;
7218 while (iter != NULL)
7220 _gtk_text_btree_spew_node (iter, indent + 2);
7227 GtkTextLine *line = node->children.line;
7228 while (line != NULL)
7230 _gtk_text_btree_spew_line_short (line, indent + 2);
7238 _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7240 GtkTextLineSegment * seg;
7242 printf ("%4d| line: %p parent: %p next: %p\n",
7243 _gtk_text_line_get_number (line), line, line->parent, line->next);
7245 seg = line->segments;
7249 _gtk_text_btree_spew_segment (tree, seg);
7255 _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7257 printf (" segment: %p type: %s bytes: %d chars: %d\n",
7258 seg, seg->type->name, seg->byte_count, seg->char_count);
7260 if (seg->type == >k_text_char_type)
7262 gchar* str = g_strndup (seg->body.chars, seg->byte_count);
7263 printf (" `%s'\n", str);
7266 else if (seg->type == >k_text_right_mark_type)
7268 printf (" right mark `%s' visible: %d not_deleteable: %d\n",
7269 seg->body.mark.name,
7270 seg->body.mark.visible,
7271 seg->body.mark.not_deleteable);
7273 else if (seg->type == >k_text_left_mark_type)
7275 printf (" left mark `%s' visible: %d not_deleteable: %d\n",
7276 seg->body.mark.name,
7277 seg->body.mark.visible,
7278 seg->body.mark.not_deleteable);
7280 else if (seg->type == >k_text_toggle_on_type ||
7281 seg->type == >k_text_toggle_off_type)
7283 printf (" tag `%s' priority %d\n",
7284 seg->body.toggle.info->tag->name,
7285 seg->body.toggle.info->tag->priority);
7289 #define __GTK_TEXT_BTREE_C__
7290 #include "gtkaliasdef.c"