1/*
2 * Gtktextbtree.c --
3 *
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.
7 *
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>
12 *
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.
17 *
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.
27 *
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.
33 *
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.
40 *
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.
52 *
53 */
54
55#include "config.h"
56#include "gtktextbtree.h"
57#include <string.h>
58#include <stdlib.h>
59#include <stdio.h>
60#include "gtktextbufferprivate.h"
61#include "gtktexttag.h"
62#include "gtktexttagprivate.h"
63#include "gtktexttagtableprivate.h"
64#include "gtktextlayoutprivate.h"
65#include "gtktextiterprivate.h"
66#include "gtkdebug.h"
67#include "gtktextmarkprivate.h"
68#include "gtktextsegment.h"
69#include "gtkpango.h"
70#include "gdk-private.h"
71
72/*
73 * Types
74 */
75
76
77/*
78 * The structure below is used to pass information between
79 * _gtk_text_btree_get_tags and inc_count:
80 */
81
82typedef struct TagInfo {
83 GPtrArray *tags;
84 GArray *counts;
85} TagInfo;
86
87
88/*
89 * This is used to store per-view width/height info at the tree nodes.
90 */
91
92typedef struct _NodeData NodeData;
93
94struct _NodeData {
95 gpointer view_id;
96 NodeData *next;
97
98 /* Height and width of this node */
99 int height;
100 signed int width : 24;
101
102 /* boolean indicating whether the lines below this node are in need of validation.
103 * However, width/height should always represent the current total width and
104 * max height for lines below this node; the valid flag indicates whether the
105 * width/height on the lines needs recomputing, not whether the totals
106 * need recomputing.
107 */
108 guint valid : 8; /* Actually a boolean */
109};
110
111
112/*
113 * The data structure below keeps summary information about one tag as part
114 * of the tag information in a node.
115 */
116
117typedef struct Summary {
118 GtkTextTagInfo *info; /* Handle for tag. */
119 int toggle_count; /* Number of transitions into or
120 * out of this tag that occur in
121 * the subtree rooted at this node. */
122 struct Summary *next; /* Next in list of all tags for same
123 * node, or NULL if at end of list. */
124} Summary;
125
126/*
127 * The data structure below defines a node in the B-tree.
128 */
129
130struct _GtkTextBTreeNode {
131 GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
132 * this is the root. */
133 GtkTextBTreeNode *next; /* Next in list of siblings with the
134 * same parent node, or NULL for end
135 * of list. */
136 Summary *summary; /* First in malloc-ed list of info
137 * about tags in this subtree (NULL if
138 * no tag info in the subtree). */
139 int level; /* Level of this node in the B-tree.
140 * 0 refers to the bottom of the tree
141 * (children are lines, not nodes). */
142 int num_lines; /* Total number of lines (leaves) in
143 * the subtree rooted here. */
144 int num_chars; /* Number of chars below here */
145 int num_children; /* Number of children of this node. */
146 union { /* First in linked list of children. */
147 struct _GtkTextBTreeNode *node; /* Used if level > 0. */
148 GtkTextLine *line; /* Used if level == 0. */
149 } children;
150
151 NodeData *node_data;
152};
153
154
155/*
156 * Used to store the list of views in our btree
157 */
158
159typedef struct _BTreeView BTreeView;
160
161struct _BTreeView {
162 gpointer view_id;
163 GtkTextLayout *layout;
164 BTreeView *next;
165 BTreeView *prev;
166};
167
168/*
169 * And the tree itself
170 */
171
172struct _GtkTextBTree {
173 GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
174 GtkTextTagTable *table;
175 GHashTable *mark_table;
176 guint refcount;
177 GtkTextMark *insert_mark;
178 GtkTextMark *selection_bound_mark;
179 GtkTextBuffer *buffer;
180 BTreeView *views;
181 GSList *tag_infos;
182 gulong tag_changed_handler;
183
184 /* Incremented when a segment with a byte size > 0
185 * is added to or removed from the tree (i.e. the
186 * length of a line may have changed, and lines may
187 * have been added or removed). This invalidates
188 * all outstanding iterators.
189 */
190 guint chars_changed_stamp;
191 /* Incremented when any segments are added or deleted;
192 * this makes outstanding iterators recalculate their
193 * pointed-to segment and segment offset.
194 */
195 guint segments_changed_stamp;
196
197 /* Cache the last line in the buffer */
198 GtkTextLine *last_line;
199 guint last_line_stamp;
200
201 /* Cache the next-to-last line in the buffer,
202 * containing the end iterator
203 */
204 GtkTextLine *end_iter_line;
205 GtkTextLineSegment *end_iter_segment;
206 int end_iter_segment_byte_index;
207 int end_iter_segment_char_offset;
208 guint end_iter_line_stamp;
209 guint end_iter_segment_stamp;
210
211 GHashTable *child_anchor_table;
212};
213
214
215/*
216 * Upper and lower bounds on how many children a node may have:
217 * rebalance when either of these limits is exceeded. MAX_CHILDREN
218 * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
219 */
220
221/* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
222 shallow. It appears to be faster to locate a particular line number
223 if the tree is narrow and deep, since it is more finely sorted. I
224 guess this may increase memory use though, and make it slower to
225 walk the tree in order, or locate a particular byte index (which
226 is done by walking the tree in order).
227
228 There's basically a tradeoff here. However I'm thinking we want to
229 add pixels, byte counts, and char counts to the tree nodes,
230 at that point narrow and deep should speed up all operations,
231 not just the line number searches.
232*/
233
234#if 1
235#define MAX_CHILDREN 12
236#define MIN_CHILDREN 6
237#else
238#define MAX_CHILDREN 6
239#define MIN_CHILDREN 3
240#endif
241
242/*
243 * Prototypes
244 */
245
246static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
247 gpointer view_id);
248static void gtk_text_btree_rebalance (GtkTextBTree *tree,
249 GtkTextBTreeNode *node);
250static GtkTextLine * get_last_line (GtkTextBTree *tree);
251static void post_insert_fixup (GtkTextBTree *tree,
252 GtkTextLine *insert_line,
253 int line_count_delta,
254 int char_count_delta);
255static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
256 GtkTextTagInfo *info,
257 int adjust);
258static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
259 GtkTextTag *tag);
260
261static void segments_changed (GtkTextBTree *tree);
262static void chars_changed (GtkTextBTree *tree);
263static void summary_list_destroy (Summary *summary);
264static GtkTextLine *gtk_text_line_new (void);
265static void gtk_text_line_destroy (GtkTextBTree *tree,
266 GtkTextLine *line);
267static void gtk_text_line_set_parent (GtkTextLine *line,
268 GtkTextBTreeNode *node);
269static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
270 gpointer view_id);
271
272static GtkTextBTreeNode *gtk_text_btree_node_new (void);
273#if 0
274static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
275#endif
276static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
277 gpointer view_id);
278static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
279 gpointer view_id);
280static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
281 gpointer view_id);
282static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
283 gpointer view_id);
284
285static void gtk_text_btree_node_remove_view (BTreeView *view,
286 GtkTextBTreeNode *node,
287 gpointer view_id);
288static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
289 GtkTextBTreeNode *node);
290static void gtk_text_btree_node_free_empty (GtkTextBTree *tree,
291 GtkTextBTreeNode *node);
292static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
293 gpointer view_id);
294static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
295 gpointer view_id,
296 int *width,
297 int *height);
298static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
299 GtkTextBTreeNode *node2);
300static void get_tree_bounds (GtkTextBTree *tree,
301 GtkTextIter *start,
302 GtkTextIter *end);
303static void tag_changed_cb (GtkTextTagTable *table,
304 GtkTextTag *tag,
305 gboolean size_changed,
306 GtkTextBTree *tree);
307static void cleanup_line (GtkTextLine *line);
308static void recompute_node_counts (GtkTextBTree *tree,
309 GtkTextBTreeNode *node);
310static void inc_count (GtkTextTag *tag,
311 int inc,
312 TagInfo *tagInfoPtr);
313
314static void summary_destroy (Summary *summary);
315
316static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
317 const GtkTextIter *iter);
318static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
319 GtkTextLineSegment *seg,
320 GtkTextLine *line);
321
322
323static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
324 GtkTextTag *tag);
325static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
326 GtkTextTag *tag);
327static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
328 GtkTextTag *tag);
329
330static void redisplay_region (GtkTextBTree *tree,
331 const GtkTextIter *start,
332 const GtkTextIter *end,
333 gboolean cursors_only);
334
335/* Inline thingies */
336
337static inline void
338segments_changed (GtkTextBTree *tree)
339{
340 tree->segments_changed_stamp += 1;
341}
342
343static inline void
344chars_changed (GtkTextBTree *tree)
345{
346 tree->chars_changed_stamp += 1;
347}
348
349/*
350 * BTree operations
351 */
352
353GtkTextBTree*
354_gtk_text_btree_new (GtkTextTagTable *table,
355 GtkTextBuffer *buffer)
356{
357 GtkTextBTree *tree;
358 GtkTextBTreeNode *root_node;
359 GtkTextLine *line, *line2;
360
361 g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
362 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
363
364 /*
365 * The tree will initially have two empty lines. The second line
366 * isn't actually part of the tree's contents, but its presence
367 * makes several operations easier. The tree will have one GtkTextBTreeNode,
368 * which is also the root of the tree.
369 */
370
371 /* Create the root node. */
372
373 root_node = gtk_text_btree_node_new ();
374
375 line = gtk_text_line_new ();
376 line2 = gtk_text_line_new ();
377
378 root_node->parent = NULL;
379 root_node->next = NULL;
380 root_node->summary = NULL;
381 root_node->level = 0;
382 root_node->children.line = line;
383 root_node->num_children = 2;
384 root_node->num_lines = 2;
385 root_node->num_chars = 2;
386
387 line->parent = root_node;
388 line->next = line2;
389
390 line->segments = _gtk_char_segment_new (text: "\n", len: 1);
391
392 line2->parent = root_node;
393 line2->next = NULL;
394 line2->segments = _gtk_char_segment_new (text: "\n", len: 1);
395
396 /* Create the tree itself */
397
398 tree = g_slice_new0 (GtkTextBTree);
399 tree->root_node = root_node;
400 tree->table = table;
401 tree->views = NULL;
402
403 /* Set these to values that are unlikely to be found
404 * in random memory garbage, and also avoid
405 * duplicates between tree instances.
406 */
407 tree->chars_changed_stamp = g_random_int ();
408 tree->segments_changed_stamp = g_random_int ();
409
410 tree->last_line_stamp = tree->chars_changed_stamp - 1;
411 tree->last_line = NULL;
412
413 tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
414 tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
415 tree->end_iter_line = NULL;
416 tree->end_iter_segment_byte_index = 0;
417 tree->end_iter_segment_char_offset = 0;
418
419 g_object_ref (tree->table);
420
421 tree->tag_changed_handler = g_signal_connect (tree->table,
422 "tag-changed",
423 G_CALLBACK (tag_changed_cb),
424 tree);
425
426 tree->mark_table = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal);
427 tree->child_anchor_table = NULL;
428
429 /* We don't ref the buffer, since the buffer owns us;
430 * we'd have some circularity issues. The buffer always
431 * lasts longer than the BTree
432 */
433 tree->buffer = buffer;
434
435 {
436 GtkTextIter start;
437 GtkTextLineSegment *seg;
438
439 _gtk_text_btree_get_iter_at_line_char (tree, iter: &start, line_number: 0, char_index: 0);
440
441
442 tree->insert_mark = _gtk_text_btree_set_mark (tree,
443 NULL,
444 name: "insert",
445 FALSE,
446 index: &start,
447 FALSE);
448
449 seg = tree->insert_mark->segment;
450
451 seg->body.mark.not_deleteable = TRUE;
452 seg->body.mark.visible = TRUE;
453
454 tree->selection_bound_mark = _gtk_text_btree_set_mark (tree,
455 NULL,
456 name: "selection_bound",
457 FALSE,
458 index: &start,
459 FALSE);
460
461 seg = tree->selection_bound_mark->segment;
462
463 seg->body.mark.not_deleteable = TRUE;
464
465 g_object_ref (tree->insert_mark);
466 g_object_ref (tree->selection_bound_mark);
467 }
468
469 tree->refcount = 1;
470
471 return tree;
472}
473
474void
475_gtk_text_btree_ref (GtkTextBTree *tree)
476{
477 g_return_if_fail (tree != NULL);
478 g_return_if_fail (tree->refcount > 0);
479
480 tree->refcount += 1;
481}
482
483void
484_gtk_text_btree_unref (GtkTextBTree *tree)
485{
486 g_return_if_fail (tree != NULL);
487 g_return_if_fail (tree->refcount > 0);
488
489 tree->refcount -= 1;
490
491 if (tree->refcount == 0)
492 {
493 g_signal_handler_disconnect (instance: tree->table,
494 handler_id: tree->tag_changed_handler);
495
496 g_object_unref (object: tree->table);
497 tree->table = NULL;
498
499 gtk_text_btree_node_destroy (tree, node: tree->root_node);
500 tree->root_node = NULL;
501
502 g_assert (g_hash_table_size (tree->mark_table) == 0);
503 g_hash_table_destroy (hash_table: tree->mark_table);
504 tree->mark_table = NULL;
505 if (tree->child_anchor_table != NULL)
506 {
507 g_hash_table_destroy (hash_table: tree->child_anchor_table);
508 tree->child_anchor_table = NULL;
509 }
510
511 g_object_unref (object: tree->insert_mark);
512 tree->insert_mark = NULL;
513 g_object_unref (object: tree->selection_bound_mark);
514 tree->selection_bound_mark = NULL;
515
516 g_slice_free (GtkTextBTree, tree);
517 }
518}
519
520GtkTextBuffer*
521_gtk_text_btree_get_buffer (GtkTextBTree *tree)
522{
523 return tree->buffer;
524}
525
526guint
527_gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
528{
529 return tree->chars_changed_stamp;
530}
531
532guint
533_gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
534{
535 return tree->segments_changed_stamp;
536}
537
538void
539_gtk_text_btree_segments_changed (GtkTextBTree *tree)
540{
541 g_return_if_fail (tree != NULL);
542 segments_changed (tree);
543}
544
545/*
546 * Indexable segment mutation
547 */
548
549/*
550 * The following function is responsible for resolving the bidi direction
551 * for the lines between start and end. But it also calculates any
552 * dependent bidi direction for surrounding lines that change as a result
553 * of the bidi direction decisions within the range. The function is
554 * trying to do as little propagation as is needed.
555 */
556static void
557gtk_text_btree_resolve_bidi (GtkTextIter *start,
558 GtkTextIter *end)
559{
560 GtkTextBTree *tree = _gtk_text_iter_get_btree (iter: start);
561 GtkTextLine *start_line, *end_line, *start_line_prev, *end_line_next, *line;
562 PangoDirection last_strong, dir_above_propagated, dir_below_propagated;
563
564 /* Resolve the strong bidi direction for all lines between
565 * start and end.
566 */
567 start_line = _gtk_text_iter_get_text_line (iter: start);
568 start_line_prev = _gtk_text_line_previous (line: start_line);
569 end_line = _gtk_text_iter_get_text_line (iter: end);
570 end_line_next = _gtk_text_line_next (line: end_line);
571
572 line = start_line;
573 while (line && line != end_line_next)
574 {
575 /* Loop through the segments and search for a strong character
576 */
577 GtkTextLineSegment *seg = line->segments;
578 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
579
580 while (seg)
581 {
582 if (seg->type == &gtk_text_char_type && seg->byte_count > 0)
583 {
584 PangoDirection pango_dir;
585
586 pango_dir = gdk_find_base_dir (text: seg->body.chars, len: seg->byte_count);
587
588 if (pango_dir != PANGO_DIRECTION_NEUTRAL)
589 {
590 line->dir_strong = pango_dir;
591 break;
592 }
593 }
594 seg = seg->next;
595 }
596
597 line = _gtk_text_line_next (line);
598 }
599
600 /* Sweep forward */
601
602 /* The variable dir_above_propagated contains the forward propagated
603 * direction before start. It is neutral if start is in the beginning
604 * of the buffer.
605 */
606 dir_above_propagated = PANGO_DIRECTION_NEUTRAL;
607 if (start_line_prev)
608 dir_above_propagated = start_line_prev->dir_propagated_forward;
609
610 /* Loop forward and propagate the direction of each paragraph
611 * to all neutral lines.
612 */
613 line = start_line;
614 last_strong = dir_above_propagated;
615 while (line != end_line_next)
616 {
617 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
618 last_strong = line->dir_strong;
619
620 line->dir_propagated_forward = last_strong;
621
622 line = _gtk_text_line_next (line);
623 }
624
625 /* Continue propagating as long as the previous resolved forward
626 * is different from last_strong.
627 */
628 {
629 GtkTextIter end_propagate;
630
631 while (line &&
632 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
633 line->dir_propagated_forward != last_strong)
634 {
635 GtkTextLine *prev = line;
636 line->dir_propagated_forward = last_strong;
637
638 line = _gtk_text_line_next(line);
639 if (!line)
640 {
641 line = prev;
642 break;
643 }
644 }
645
646 /* The last line to invalidate is the last line before the
647 * line with the strong character. Or in case of the end of the
648 * buffer, the last line of the buffer. (There seems to be an
649 * extra "virtual" last line in the buffer that must not be used
650 * calling _gtk_text_btree_get_iter_at_line (causes crash). Thus the
651 * _gtk_text_line_previous is ok in that case as well.)
652 */
653 line = _gtk_text_line_previous (line);
654 _gtk_text_btree_get_iter_at_line (tree, iter: &end_propagate, line, byte_offset: 0);
655 _gtk_text_btree_invalidate_region (tree, start: end, end: &end_propagate, FALSE);
656 }
657
658 /* Sweep backward */
659
660 /* The variable dir_below_propagated contains the backward propagated
661 * direction after end. It is neutral if end is at the end of
662 * the buffer.
663 */
664 dir_below_propagated = PANGO_DIRECTION_NEUTRAL;
665 if (end_line_next)
666 dir_below_propagated = end_line_next->dir_propagated_back;
667
668 /* Loop backward and propagate the direction of each paragraph
669 * to all neutral lines.
670 */
671 line = end_line;
672 last_strong = dir_below_propagated;
673 while (line != start_line_prev)
674 {
675 if (line->dir_strong != PANGO_DIRECTION_NEUTRAL)
676 last_strong = line->dir_strong;
677
678 line->dir_propagated_back = last_strong;
679
680 line = _gtk_text_line_previous (line);
681 }
682
683 /* Continue propagating as long as the resolved backward dir
684 * is different from last_strong.
685 */
686 {
687 GtkTextIter start_propagate;
688
689 while (line &&
690 line->dir_strong == PANGO_DIRECTION_NEUTRAL &&
691 line->dir_propagated_back != last_strong)
692 {
693 GtkTextLine *prev = line;
694 line->dir_propagated_back = last_strong;
695
696 line = _gtk_text_line_previous (line);
697 if (!line)
698 {
699 line = prev;
700 break;
701 }
702 }
703
704 /* We only need to invalidate for backwards propagation if the
705 * line we ended up on didn't get a direction from forwards
706 * propagation.
707 */
708 if (line && line->dir_propagated_forward == PANGO_DIRECTION_NEUTRAL)
709 {
710 _gtk_text_btree_get_iter_at_line (tree, iter: &start_propagate, line, byte_offset: 0);
711 _gtk_text_btree_invalidate_region (tree, start: &start_propagate, end: start, FALSE);
712 }
713 }
714}
715
716void
717_gtk_text_btree_delete (GtkTextIter *start,
718 GtkTextIter *end)
719{
720 GtkTextLineSegment *prev_seg; /* The segment just before the start
721 * of the deletion range. */
722 GtkTextLineSegment *last_seg; /* The segment just after the end
723 * of the deletion range. */
724 GtkTextLineSegment *seg, *next, *next2;
725 GtkTextLine *curline;
726 GtkTextBTreeNode *curnode, *node;
727 GtkTextBTree *tree;
728 GtkTextLine *start_line;
729 GtkTextLine *end_line;
730 GtkTextLine *line;
731 GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
732 int start_byte_offset;
733
734 g_return_if_fail (start != NULL);
735 g_return_if_fail (end != NULL);
736 g_return_if_fail (_gtk_text_iter_get_btree (start) ==
737 _gtk_text_iter_get_btree (end));
738
739 gtk_text_iter_order (first: start, second: end);
740
741 tree = _gtk_text_iter_get_btree (iter: start);
742
743#ifdef G_ENABLE_DEBUG
744 if (GTK_DEBUG_CHECK (TEXT))
745 _gtk_text_btree_check (tree);
746#endif
747
748 /* Broadcast the need for redisplay before we break the iterators */
749 DV (g_print ("invalidating due to deleting some text (%s)\n", G_STRLOC));
750 _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
751
752 /* Save the byte offset so we can reset the iterators */
753 start_byte_offset = gtk_text_iter_get_line_index (iter: start);
754
755 start_line = _gtk_text_iter_get_text_line (iter: start);
756 end_line = _gtk_text_iter_get_text_line (iter: end);
757
758 /*
759 * Split the start and end segments, so we have a place
760 * to insert our new text.
761 *
762 * Tricky point: split at end first; otherwise the split
763 * at end may invalidate seg and/or prev_seg. This allows
764 * us to avoid invalidating segments for start.
765 */
766
767 last_seg = gtk_text_line_segment_split (iter: end);
768 if (last_seg != NULL)
769 last_seg = last_seg->next;
770 else
771 last_seg = end_line->segments;
772
773 prev_seg = gtk_text_line_segment_split (iter: start);
774 if (prev_seg != NULL)
775 {
776 seg = prev_seg->next;
777 prev_seg->next = last_seg;
778 }
779 else
780 {
781 seg = start_line->segments;
782 start_line->segments = last_seg;
783 }
784
785 /* notify iterators that their segments need recomputation,
786 just for robustness. */
787 segments_changed (tree);
788
789 /*
790 * Delete all of the segments between prev_seg and last_seg.
791 */
792
793 curline = start_line;
794 curnode = curline->parent;
795 while (seg != last_seg)
796 {
797 int char_count = 0;
798
799 if (seg == NULL)
800 {
801 GtkTextLine *nextline;
802
803 /*
804 * We just ran off the end of a line. First find the
805 * next line, then go back to the old line and delete it
806 * (unless it's the starting line for the range).
807 */
808
809 nextline = _gtk_text_line_next (line: curline);
810 if (curline != start_line)
811 {
812 if (curnode == start_line->parent)
813 start_line->next = curline->next;
814 else
815 curnode->children.line = curline->next;
816
817 for (node = curnode; node != NULL;
818 node = node->parent)
819 {
820 /* Don't update node->num_chars, because
821 * that was done when we deleted the segments.
822 */
823 node->num_lines -= 1;
824 }
825
826 curnode->num_children -= 1;
827 curline->next = deleted_lines;
828 deleted_lines = curline;
829 }
830
831 curline = nextline;
832 seg = curline->segments;
833
834 /*
835 * If the GtkTextBTreeNode is empty then delete it and its parents,
836 * recursively upwards until a non-empty GtkTextBTreeNode is found.
837 */
838
839 while (curnode->num_children == 0)
840 {
841 GtkTextBTreeNode *parent;
842
843 parent = curnode->parent;
844 if (parent->children.node == curnode)
845 {
846 parent->children.node = curnode->next;
847 }
848 else
849 {
850 GtkTextBTreeNode *prevnode = parent->children.node;
851 while (prevnode->next != curnode)
852 {
853 prevnode = prevnode->next;
854 }
855 prevnode->next = curnode->next;
856 }
857 parent->num_children--;
858 gtk_text_btree_node_free_empty (tree, node: curnode);
859 curnode = parent;
860 }
861 curnode = curline->parent;
862 continue;
863 }
864
865 next = seg->next;
866 char_count = seg->char_count;
867
868 if ((*seg->type->deleteFunc)(seg, curline, FALSE) != 0)
869 {
870 /*
871 * This segment refuses to die. Move it to prev_seg and
872 * advance prev_seg if the segment has left gravity.
873 */
874
875 if (prev_seg == NULL)
876 {
877 seg->next = start_line->segments;
878 start_line->segments = seg;
879 }
880 else if (prev_seg->next &&
881 prev_seg->next != last_seg &&
882 seg->type == &gtk_text_toggle_off_type &&
883 prev_seg->next->type == &gtk_text_toggle_on_type &&
884 seg->body.toggle.info == prev_seg->next->body.toggle.info)
885 {
886 /* Try to match an off toggle with the matching on toggle
887 * if it immediately follows. This is a common case, and
888 * handling it here prevents quadratic blowup in
889 * cleanup_line() below. See bug 317125.
890 */
891 next2 = prev_seg->next->next;
892 _gtk_toggle_segment_free (seg: prev_seg->next);
893 prev_seg->next = next2;
894 _gtk_toggle_segment_free (seg);
895 seg = NULL;
896 }
897 else
898 {
899 seg->next = prev_seg->next;
900 prev_seg->next = seg;
901 }
902
903 if (seg && seg->type->leftGravity)
904 {
905 prev_seg = seg;
906 }
907 }
908 else
909 {
910 /* Segment is gone. Decrement the char count of the node and
911 all its parents. */
912 for (node = curnode; node != NULL;
913 node = node->parent)
914 {
915 node->num_chars -= char_count;
916 }
917 }
918
919 seg = next;
920 }
921
922 /*
923 * If the beginning and end of the deletion range are in different
924 * lines, join the two lines together and discard the ending line.
925 */
926
927 if (start_line != end_line)
928 {
929 BTreeView *view;
930 GtkTextBTreeNode *ancestor_node;
931 GtkTextLine *prevline;
932 int chars_moved;
933
934 /* last_seg was appended to start_line up at the top of this function */
935 chars_moved = 0;
936 for (seg = last_seg; seg != NULL;
937 seg = seg->next)
938 {
939 chars_moved += seg->char_count;
940 if (seg->type->lineChangeFunc != NULL)
941 {
942 (*seg->type->lineChangeFunc)(seg, end_line);
943 }
944 }
945
946 for (node = start_line->parent; node != NULL;
947 node = node->parent)
948 {
949 node->num_chars += chars_moved;
950 }
951
952 curnode = end_line->parent;
953 for (node = curnode; node != NULL;
954 node = node->parent)
955 {
956 node->num_chars -= chars_moved;
957 node->num_lines--;
958 }
959 curnode->num_children--;
960 prevline = curnode->children.line;
961 if (prevline == end_line)
962 {
963 curnode->children.line = end_line->next;
964 }
965 else
966 {
967 while (prevline->next != end_line)
968 {
969 prevline = prevline->next;
970 }
971 prevline->next = end_line->next;
972 }
973 end_line->next = deleted_lines;
974 deleted_lines = end_line;
975
976 /* We now fix up the per-view aggregates. We add all the height and
977 * width for the deleted lines to the start line, so that when revalidation
978 * occurs, the correct change in size is seen.
979 */
980 ancestor_node = gtk_text_btree_node_common_parent (node1: curnode, node2: start_line->parent);
981 view = tree->views;
982 while (view)
983 {
984 GtkTextLineData *ld;
985
986 int deleted_width = 0;
987 int deleted_height = 0;
988
989 line = deleted_lines;
990 while (line)
991 {
992 GtkTextLine *next_line = line->next;
993 ld = _gtk_text_line_get_data (line, view_id: view->view_id);
994
995 if (ld)
996 {
997 deleted_width = MAX (deleted_width, ld->width);
998 deleted_height += ld->height;
999 }
1000
1001 line = next_line;
1002 }
1003
1004 if (deleted_width > 0 || deleted_height > 0)
1005 {
1006 ld = _gtk_text_line_get_data (line: start_line, view_id: view->view_id);
1007
1008 if (ld == NULL)
1009 {
1010 /* This means that start_line has never been validated.
1011 * We don't really want to do the validation here but
1012 * we do need to store our temporary sizes. So we
1013 * create the line data and assume a line w/h of 0.
1014 */
1015 ld = _gtk_text_line_data_new (layout: view->layout, line: start_line);
1016 _gtk_text_line_add_data (line: start_line, data: ld);
1017 ld->width = 0;
1018 ld->height = 0;
1019 ld->valid = FALSE;
1020 }
1021
1022 ld->width = MAX (deleted_width, ld->width);
1023 ld->height += deleted_height;
1024 ld->valid = FALSE;
1025 }
1026
1027 gtk_text_btree_node_check_valid_downward (node: ancestor_node, view_id: view->view_id);
1028 if (ancestor_node->parent)
1029 gtk_text_btree_node_check_valid_upward (node: ancestor_node->parent, view_id: view->view_id);
1030
1031 view = view->next;
1032 }
1033
1034 line = deleted_lines;
1035 while (line)
1036 {
1037 GtkTextLine *next_line = line->next;
1038
1039 gtk_text_line_destroy (tree, line);
1040
1041 line = next_line;
1042 }
1043
1044 /* avoid dangling pointer */
1045 deleted_lines = NULL;
1046
1047 gtk_text_btree_rebalance (tree, node: curnode);
1048 }
1049
1050 /*
1051 * Cleanup the segments in the new line.
1052 */
1053
1054 cleanup_line (line: start_line);
1055
1056 /*
1057 * Lastly, rebalance the first GtkTextBTreeNode of the range.
1058 */
1059
1060 gtk_text_btree_rebalance (tree, node: start_line->parent);
1061
1062 /* Notify outstanding iterators that they
1063 are now hosed */
1064 chars_changed (tree);
1065 segments_changed (tree);
1066
1067#ifdef G_ENABLE_DEBUG
1068 if (GTK_DEBUG_CHECK (TEXT))
1069 _gtk_text_btree_check (tree);
1070#endif
1071
1072 /* Re-initialize our iterators */
1073 _gtk_text_btree_get_iter_at_line (tree, iter: start, line: start_line, byte_offset: start_byte_offset);
1074 *end = *start;
1075
1076 gtk_text_btree_resolve_bidi (start, end);
1077}
1078
1079void
1080_gtk_text_btree_insert (GtkTextIter *iter,
1081 const char *text,
1082 int len)
1083{
1084 GtkTextLineSegment *prev_seg; /* The segment just before the first
1085 * new segment (NULL means new segment
1086 * is at beginning of line). */
1087 GtkTextLineSegment *cur_seg; /* Current segment; new characters
1088 * are inserted just after this one.
1089 * NULL means insert at beginning of
1090 * line. */
1091 GtkTextLine *line; /* Current line (new segments are
1092 * added to this line). */
1093 GtkTextLineSegment *seg;
1094 GtkTextLine *newline;
1095 int chunk_len; /* # characters in current chunk. */
1096 int sol; /* start of line */
1097 int eol; /* Pointer to character just after last
1098 * one in current chunk.
1099 */
1100 int delim; /* index of paragraph delimiter */
1101 int line_count_delta; /* Counts change to total number of
1102 * lines in file.
1103 */
1104
1105 int char_count_delta; /* change to number of chars */
1106 GtkTextBTree *tree;
1107 int start_byte_index;
1108 GtkTextLine *start_line;
1109
1110 g_return_if_fail (text != NULL);
1111 g_return_if_fail (iter != NULL);
1112
1113 if (len < 0)
1114 len = strlen (s: text);
1115
1116 /* extract iterator info */
1117 tree = _gtk_text_iter_get_btree (iter);
1118 line = _gtk_text_iter_get_text_line (iter);
1119
1120 start_line = line;
1121 start_byte_index = gtk_text_iter_get_line_index (iter);
1122
1123 /* Get our insertion segment split. Note this assumes line allows
1124 * char insertions, which isn't true of the "last" line. But iter
1125 * should not be on that line, as we assert here.
1126 */
1127 g_assert (!_gtk_text_line_is_last (line, tree));
1128 prev_seg = gtk_text_line_segment_split (iter);
1129 cur_seg = prev_seg;
1130
1131 /* Invalidate all iterators */
1132 chars_changed (tree);
1133 segments_changed (tree);
1134
1135 /*
1136 * Chop the text up into lines and create a new segment for
1137 * each line, plus a new line for the leftovers from the
1138 * previous line.
1139 */
1140
1141 eol = 0;
1142 sol = 0;
1143 line_count_delta = 0;
1144 char_count_delta = 0;
1145 while (eol < len)
1146 {
1147 sol = eol;
1148
1149 pango_find_paragraph_boundary (text: text + sol,
1150 length: len - sol,
1151 paragraph_delimiter_index: &delim,
1152 next_paragraph_start: &eol);
1153
1154 /* make these relative to the start of the text */
1155 delim += sol;
1156 eol += sol;
1157
1158 g_assert (eol >= sol);
1159 g_assert (delim >= sol);
1160 g_assert (eol >= delim);
1161 g_assert (sol >= 0);
1162 g_assert (eol <= len);
1163
1164 chunk_len = eol - sol;
1165
1166 g_assert (g_utf8_validate (&text[sol], chunk_len, NULL));
1167 seg = _gtk_char_segment_new (text: &text[sol], len: chunk_len);
1168
1169 char_count_delta += seg->char_count;
1170
1171 if (cur_seg == NULL)
1172 {
1173 seg->next = line->segments;
1174 line->segments = seg;
1175 }
1176 else
1177 {
1178 seg->next = cur_seg->next;
1179 cur_seg->next = seg;
1180 }
1181
1182 if (delim == eol)
1183 {
1184 /* chunk didn't end with a paragraph separator */
1185 g_assert (eol == len);
1186 break;
1187 }
1188
1189 /*
1190 * The chunk ended with a newline, so create a new GtkTextLine
1191 * and move the remainder of the old line to it.
1192 */
1193
1194 newline = gtk_text_line_new ();
1195 gtk_text_line_set_parent (line: newline, node: line->parent);
1196 newline->next = line->next;
1197 line->next = newline;
1198 newline->segments = seg->next;
1199 seg->next = NULL;
1200 line = newline;
1201 cur_seg = NULL;
1202 line_count_delta++;
1203 }
1204
1205 /*
1206 * Cleanup the starting line for the insertion, plus the ending
1207 * line if it's different.
1208 */
1209
1210 cleanup_line (line: start_line);
1211 if (line != start_line)
1212 {
1213 cleanup_line (line);
1214 }
1215
1216 post_insert_fixup (tree, insert_line: line, line_count_delta, char_count_delta);
1217
1218 /* Invalidate our region, and reset the iterator the user
1219 passed in to point to the end of the inserted text. */
1220 {
1221 GtkTextIter start;
1222 GtkTextIter end;
1223
1224
1225 _gtk_text_btree_get_iter_at_line (tree,
1226 iter: &start,
1227 line: start_line,
1228 byte_offset: start_byte_index);
1229 end = start;
1230
1231 /* We could almost certainly be more efficient here
1232 by saving the information from the insertion loop
1233 above. FIXME */
1234 gtk_text_iter_forward_chars (iter: &end, count: char_count_delta);
1235
1236 DV (g_print ("invalidating due to inserting some text (%s)\n", G_STRLOC));
1237 _gtk_text_btree_invalidate_region (tree, start: &start, end: &end, FALSE);
1238
1239
1240 /* Convenience for the user */
1241 *iter = end;
1242
1243 gtk_text_btree_resolve_bidi (start: &start, end: &end);
1244 }
1245}
1246
1247static void
1248insert_paintable_or_widget_segment (GtkTextIter *iter,
1249 GtkTextLineSegment *seg)
1250{
1251 GtkTextIter start;
1252 GtkTextLineSegment *prevPtr;
1253 GtkTextLine *line;
1254 GtkTextBTree *tree;
1255 int start_byte_offset;
1256
1257 line = _gtk_text_iter_get_text_line (iter);
1258 tree = _gtk_text_iter_get_btree (iter);
1259 start_byte_offset = gtk_text_iter_get_line_index (iter);
1260
1261 prevPtr = gtk_text_line_segment_split (iter);
1262 if (prevPtr == NULL)
1263 {
1264 seg->next = line->segments;
1265 line->segments = seg;
1266 }
1267 else
1268 {
1269 seg->next = prevPtr->next;
1270 prevPtr->next = seg;
1271 }
1272
1273 post_insert_fixup (tree, insert_line: line, line_count_delta: 0, char_count_delta: seg->char_count);
1274
1275 chars_changed (tree);
1276 segments_changed (tree);
1277
1278 /* reset *iter for the user, and invalidate tree nodes */
1279
1280 _gtk_text_btree_get_iter_at_line (tree, iter: &start, line, byte_offset: start_byte_offset);
1281
1282 *iter = start;
1283 gtk_text_iter_forward_char (iter); /* skip forward past the segment */
1284
1285 DV (g_print ("invalidating due to inserting paintable/widget (%s)\n", G_STRLOC));
1286 _gtk_text_btree_invalidate_region (tree, start: &start, end: iter, FALSE);
1287}
1288
1289void
1290_gtk_text_btree_insert_paintable (GtkTextIter *iter,
1291 GdkPaintable *paintable)
1292{
1293 GtkTextLineSegment *seg;
1294
1295 seg = _gtk_paintable_segment_new (paintable);
1296 seg->body.paintable.tree = _gtk_text_iter_get_btree (iter);
1297 seg->body.paintable.line = _gtk_text_iter_get_text_line (iter);
1298
1299 insert_paintable_or_widget_segment (iter, seg);
1300}
1301
1302void
1303_gtk_text_btree_insert_child_anchor (GtkTextIter *iter,
1304 GtkTextChildAnchor *anchor)
1305{
1306 GtkTextLineSegment *seg;
1307 GtkTextBTree *tree;
1308
1309 if (anchor->segment != NULL)
1310 {
1311 g_warning (G_STRLOC": Same child anchor can't be inserted twice");
1312 return;
1313 }
1314
1315 seg = _gtk_widget_segment_new (anchor);
1316
1317 tree = seg->body.child.tree = _gtk_text_iter_get_btree (iter);
1318 seg->body.child.line = _gtk_text_iter_get_text_line (iter);
1319
1320 insert_paintable_or_widget_segment (iter, seg);
1321
1322 if (tree->child_anchor_table == NULL)
1323 tree->child_anchor_table = g_hash_table_new (NULL, NULL);
1324
1325 g_hash_table_insert (hash_table: tree->child_anchor_table,
1326 key: seg->body.child.obj,
1327 value: seg->body.child.obj);
1328}
1329
1330void
1331_gtk_text_btree_unregister_child_anchor (GtkTextChildAnchor *anchor)
1332{
1333 GtkTextLineSegment *seg;
1334
1335 seg = anchor->segment;
1336
1337 g_hash_table_remove (hash_table: seg->body.child.tree->child_anchor_table,
1338 key: anchor);
1339}
1340
1341/*
1342 * View stuff
1343 */
1344
1345static GtkTextLine*
1346find_line_by_y (GtkTextBTree *tree, BTreeView *view,
1347 GtkTextBTreeNode *node, int y, int *line_top,
1348 GtkTextLine *last_line)
1349{
1350 int current_y = 0;
1351
1352#ifdef G_ENABLE_DEBUG
1353 if (GTK_DEBUG_CHECK (TEXT))
1354 _gtk_text_btree_check (tree);
1355#endif
1356
1357 if (node->level == 0)
1358 {
1359 GtkTextLine *line;
1360
1361 line = node->children.line;
1362
1363 while (line != NULL && line != last_line)
1364 {
1365 GtkTextLineData *ld;
1366
1367 ld = _gtk_text_line_get_data (line, view_id: view->view_id);
1368
1369 if (ld)
1370 {
1371 if (y < (current_y + ld->height))
1372 return line;
1373
1374 current_y += ld->height;
1375 *line_top += ld->height;
1376 }
1377
1378 line = line->next;
1379 }
1380 return NULL;
1381 }
1382 else
1383 {
1384 GtkTextBTreeNode *child;
1385
1386 child = node->children.node;
1387
1388 while (child != NULL)
1389 {
1390 int width;
1391 int height;
1392
1393 gtk_text_btree_node_get_size (node: child, view_id: view->view_id,
1394 width: &width, height: &height);
1395
1396 if (y < (current_y + height))
1397 return find_line_by_y (tree, view, node: child,
1398 y: y - current_y, line_top,
1399 last_line);
1400
1401 current_y += height;
1402 *line_top += height;
1403
1404 child = child->next;
1405 }
1406
1407 return NULL;
1408 }
1409}
1410
1411GtkTextLine *
1412_gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
1413 gpointer view_id,
1414 int ypixel,
1415 int *line_top_out)
1416{
1417 GtkTextLine *line;
1418 BTreeView *view;
1419 GtkTextLine *last_line;
1420 int line_top = 0;
1421
1422 view = gtk_text_btree_get_view (tree, view_id);
1423 g_return_val_if_fail (view != NULL, NULL);
1424
1425 last_line = get_last_line (tree);
1426
1427 line = find_line_by_y (tree, view, node: tree->root_node, y: ypixel, line_top: &line_top,
1428 last_line);
1429
1430 if (line_top_out)
1431 *line_top_out = line_top;
1432
1433 return line;
1434}
1435
1436static int
1437find_line_top_in_line_list (GtkTextBTree *tree,
1438 BTreeView *view,
1439 GtkTextLine *line,
1440 GtkTextLine *target_line,
1441 int y)
1442{
1443 while (line != NULL)
1444 {
1445 GtkTextLineData *ld;
1446
1447 if (line == target_line)
1448 return y;
1449
1450 ld = _gtk_text_line_get_data (line, view_id: view->view_id);
1451 if (ld)
1452 y += ld->height;
1453
1454 line = line->next;
1455 }
1456
1457 g_assert_not_reached (); /* If we get here, our
1458 target line didn't exist
1459 under its parent node */
1460 return 0;
1461}
1462
1463int
1464_gtk_text_btree_find_line_top (GtkTextBTree *tree,
1465 GtkTextLine *target_line,
1466 gpointer view_id)
1467{
1468 int y = 0;
1469 BTreeView *view;
1470 GtkTextBTreeNode *node;
1471 GtkTextBTreeNode *nodes[64];
1472 int tos = 0;
1473
1474 view = gtk_text_btree_get_view (tree, view_id);
1475
1476 g_return_val_if_fail (view != NULL, 0);
1477
1478 node = target_line->parent;
1479 while (node != NULL)
1480 {
1481 nodes[tos++] = node;
1482 node = node->parent;
1483 }
1484
1485 tos--;
1486 while (tos >= 0)
1487 {
1488 node = nodes[tos];
1489
1490 if (node->level == 0)
1491 {
1492 return find_line_top_in_line_list (tree, view,
1493 line: node->children.line,
1494 target_line, y);
1495 }
1496 else
1497 {
1498 GtkTextBTreeNode *child;
1499 GtkTextBTreeNode *target_node;
1500
1501 g_assert (tos > 0); /* not at level 0 */
1502 target_node = nodes[tos - 1];
1503
1504 child = node->children.node;
1505
1506 while (child != NULL)
1507 {
1508 int width;
1509 int height;
1510
1511 if (child == target_node)
1512 break;
1513 else
1514 {
1515 gtk_text_btree_node_get_size (node: child, view_id: view->view_id,
1516 width: &width, height: &height);
1517 y += height;
1518 }
1519 child = child->next;
1520 }
1521 g_assert (child != NULL); /* should have broken out before we
1522 ran out of nodes */
1523 }
1524
1525 tos--;
1526 }
1527
1528 g_assert_not_reached (); /* we return when we find the target line */
1529 return 0;
1530}
1531
1532void
1533_gtk_text_btree_add_view (GtkTextBTree *tree,
1534 GtkTextLayout *layout)
1535{
1536 BTreeView *view;
1537 GtkTextLine *last_line;
1538 GtkTextLineData *line_data;
1539
1540 g_return_if_fail (tree != NULL);
1541
1542 view = g_slice_new (BTreeView);
1543
1544 view->view_id = layout;
1545 view->layout = layout;
1546
1547 view->next = tree->views;
1548 view->prev = NULL;
1549
1550 if (tree->views)
1551 {
1552 g_assert (tree->views->prev == NULL);
1553 tree->views->prev = view;
1554 }
1555
1556 tree->views = view;
1557
1558 /* The last line in the buffer has identity values for the per-view
1559 * data so that we can avoid special case checks for it in a large
1560 * number of loops
1561 */
1562 last_line = get_last_line (tree);
1563
1564 line_data = g_slice_new (GtkTextLineData);
1565 line_data->view_id = layout;
1566 line_data->next = NULL;
1567 line_data->width = 0;
1568 line_data->height = 0;
1569 line_data->valid = TRUE;
1570
1571 _gtk_text_line_add_data (line: last_line, data: line_data);
1572}
1573
1574void
1575_gtk_text_btree_remove_view (GtkTextBTree *tree,
1576 gpointer view_id)
1577{
1578 BTreeView *view;
1579 GtkTextLine *last_line;
1580 GtkTextLineData *line_data;
1581
1582 g_return_if_fail (tree != NULL);
1583
1584 view = tree->views;
1585
1586 while (view != NULL)
1587 {
1588 if (view->view_id == view_id)
1589 break;
1590
1591 view = view->next;
1592 }
1593
1594 g_return_if_fail (view != NULL);
1595
1596 if (view->next)
1597 view->next->prev = view->prev;
1598
1599 if (view->prev)
1600 view->prev->next = view->next;
1601
1602 if (view == tree->views)
1603 tree->views = view->next;
1604
1605 /* Remove the line data for the last line which we added ourselves.
1606 * (Do this first, so that we don't try to call the view's line data destructor on it.)
1607 */
1608 last_line = get_last_line (tree);
1609 line_data = _gtk_text_line_remove_data (line: last_line, view_id);
1610 g_slice_free (GtkTextLineData, line_data);
1611
1612 gtk_text_btree_node_remove_view (view, node: tree->root_node, view_id);
1613
1614 view->layout = (gpointer) 0xdeadbeef;
1615 view->view_id = (gpointer) 0xdeadbeef;
1616
1617 g_slice_free (BTreeView, view);
1618}
1619
1620void
1621_gtk_text_btree_invalidate_region (GtkTextBTree *tree,
1622 const GtkTextIter *start,
1623 const GtkTextIter *end,
1624 gboolean cursors_only)
1625{
1626 BTreeView *view;
1627
1628 view = tree->views;
1629
1630 while (view != NULL)
1631 {
1632 if (cursors_only)
1633 gtk_text_layout_invalidate_cursors (layout: view->layout, start, end);
1634 else
1635 gtk_text_layout_invalidate (layout: view->layout, start, end);
1636
1637 view = view->next;
1638 }
1639}
1640
1641void
1642_gtk_text_btree_get_view_size (GtkTextBTree *tree,
1643 gpointer view_id,
1644 int *width,
1645 int *height)
1646{
1647 g_return_if_fail (tree != NULL);
1648 g_return_if_fail (view_id != NULL);
1649
1650 gtk_text_btree_node_get_size (node: tree->root_node, view_id,
1651 width, height);
1652}
1653
1654/*
1655 * Tag
1656 */
1657
1658typedef struct {
1659 GtkTextIter *iters;
1660 guint count;
1661 guint allocated;
1662} IterStack;
1663
1664static IterStack*
1665iter_stack_new (void)
1666{
1667 IterStack *stack;
1668 stack = g_slice_new (IterStack);
1669 stack->iters = NULL;
1670 stack->count = 0;
1671 stack->allocated = 0;
1672 return stack;
1673}
1674
1675static void
1676iter_stack_push (IterStack *stack,
1677 const GtkTextIter *iter)
1678{
1679 stack->count += 1;
1680 if (stack->count > stack->allocated)
1681 {
1682 stack->allocated = stack->count*2;
1683 stack->iters = g_realloc (mem: stack->iters,
1684 n_bytes: stack->allocated * sizeof (GtkTextIter));
1685 }
1686 stack->iters[stack->count-1] = *iter;
1687}
1688
1689static gboolean
1690iter_stack_pop (IterStack *stack,
1691 GtkTextIter *iter)
1692{
1693 if (stack->count == 0)
1694 return FALSE;
1695 else
1696 {
1697 stack->count -= 1;
1698 *iter = stack->iters[stack->count];
1699 return TRUE;
1700 }
1701}
1702
1703static void
1704iter_stack_free (IterStack *stack)
1705{
1706 g_free (mem: stack->iters);
1707 g_slice_free (IterStack, stack);
1708}
1709
1710static void
1711iter_stack_invert (IterStack *stack)
1712{
1713 if (stack->count > 0)
1714 {
1715 guint i = 0;
1716 guint j = stack->count - 1;
1717 while (i < j)
1718 {
1719 GtkTextIter tmp;
1720
1721 tmp = stack->iters[i];
1722 stack->iters[i] = stack->iters[j];
1723 stack->iters[j] = tmp;
1724
1725 ++i;
1726 --j;
1727 }
1728 }
1729}
1730
1731static void
1732queue_tag_redisplay (GtkTextBTree *tree,
1733 GtkTextTag *tag,
1734 const GtkTextIter *start,
1735 const GtkTextIter *end)
1736{
1737 if (_gtk_text_tag_affects_size (tag))
1738 {
1739 DV (g_print ("invalidating due to size-affecting tag (%s)\n", G_STRLOC));
1740 _gtk_text_btree_invalidate_region (tree, start, end, FALSE);
1741 }
1742 else if (_gtk_text_tag_affects_nonsize_appearance (tag))
1743 {
1744 /* We only need to queue a redraw, not a relayout */
1745 redisplay_region (tree, start, end, FALSE);
1746 }
1747
1748 /* We don't need to do anything if the tag doesn't affect display */
1749}
1750
1751void
1752_gtk_text_btree_tag (const GtkTextIter *start_orig,
1753 const GtkTextIter *end_orig,
1754 GtkTextTag *tag,
1755 gboolean add)
1756{
1757 GtkTextLineSegment *seg, *prev;
1758 GtkTextLine *cleanupline;
1759 gboolean toggled_on;
1760 GtkTextLine *start_line;
1761 GtkTextLine *end_line;
1762 GtkTextIter iter;
1763 GtkTextIter start, end;
1764 GtkTextBTree *tree;
1765 IterStack *stack;
1766 GtkTextTagInfo *info;
1767
1768 g_return_if_fail (start_orig != NULL);
1769 g_return_if_fail (end_orig != NULL);
1770 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
1771 g_return_if_fail (_gtk_text_iter_get_btree (start_orig) ==
1772 _gtk_text_iter_get_btree (end_orig));
1773 g_return_if_fail (tag->priv->table == _gtk_text_iter_get_btree (start_orig)->table);
1774
1775#if 0
1776 printf ("%s tag %s from %d to %d\n",
1777 add ? "Adding" : "Removing",
1778 tag->name,
1779 gtk_text_buffer_get_offset (start_orig),
1780 gtk_text_buffer_get_offset (end_orig));
1781#endif
1782
1783 if (gtk_text_iter_equal (lhs: start_orig, rhs: end_orig))
1784 return;
1785
1786 start = *start_orig;
1787 end = *end_orig;
1788
1789 gtk_text_iter_order (first: &start, second: &end);
1790
1791 tree = _gtk_text_iter_get_btree (iter: &start);
1792
1793 queue_tag_redisplay (tree, tag, start: &start, end: &end);
1794
1795 info = gtk_text_btree_get_tag_info (tree, tag);
1796
1797 start_line = _gtk_text_iter_get_text_line (iter: &start);
1798 end_line = _gtk_text_iter_get_text_line (iter: &end);
1799
1800 /* Find all tag toggles in the region; we are going to delete them.
1801 We need to find them in advance, because
1802 forward_find_tag_toggle () won't work once we start playing around
1803 with the tree. */
1804 stack = iter_stack_new ();
1805 iter = start;
1806
1807 /* forward_to_tag_toggle() skips a toggle at the start iterator,
1808 * which is deliberate - we don't want to delete a toggle at the
1809 * start.
1810 */
1811 while (gtk_text_iter_forward_to_tag_toggle (iter: &iter, tag))
1812 {
1813 if (gtk_text_iter_compare (lhs: &iter, rhs: &end) >= 0)
1814 break;
1815 else
1816 iter_stack_push (stack, iter: &iter);
1817 }
1818
1819 /* We need to traverse the toggles in order. */
1820 iter_stack_invert (stack);
1821
1822 /*
1823 * See whether the tag is present at the start of the range. If
1824 * the state doesn't already match what we want then add a toggle
1825 * there.
1826 */
1827
1828 toggled_on = gtk_text_iter_has_tag (iter: &start, tag);
1829 if ( (add && !toggled_on) ||
1830 (!add && toggled_on) )
1831 {
1832 /* This could create a second toggle at the start position;
1833 cleanup_line () will remove it if so. */
1834 seg = _gtk_toggle_segment_new (info, on: add);
1835
1836 prev = gtk_text_line_segment_split (iter: &start);
1837 if (prev == NULL)
1838 {
1839 seg->next = start_line->segments;
1840 start_line->segments = seg;
1841 }
1842 else
1843 {
1844 seg->next = prev->next;
1845 prev->next = seg;
1846 }
1847
1848 /* cleanup_line adds the new toggle to the node counts. */
1849#if 0
1850 printf ("added toggle at start\n");
1851#endif
1852 /* we should probably call segments_changed, but in theory
1853 any still-cached segments in the iters we are about to
1854 use are still valid, since they're in front
1855 of this spot. */
1856 }
1857
1858 /*
1859 *
1860 * Scan the range of characters and delete any internal tag
1861 * transitions. Keep track of what the old state was at the end
1862 * of the range, and add a toggle there if it's needed.
1863 *
1864 */
1865
1866 cleanupline = start_line;
1867 while (iter_stack_pop (stack, iter: &iter))
1868 {
1869 GtkTextLineSegment *indexable_seg;
1870 GtkTextLine *line;
1871
1872 line = _gtk_text_iter_get_text_line (iter: &iter);
1873 seg = _gtk_text_iter_get_any_segment (iter: &iter);
1874 indexable_seg = _gtk_text_iter_get_indexable_segment (iter: &iter);
1875
1876 g_assert (seg != NULL);
1877 g_assert (indexable_seg != NULL);
1878 g_assert (seg != indexable_seg);
1879
1880 prev = line->segments;
1881
1882 /* Find the segment that actually toggles this tag. */
1883 while (seg != indexable_seg)
1884 {
1885 g_assert (seg != NULL);
1886 g_assert (indexable_seg != NULL);
1887 g_assert (seg != indexable_seg);
1888
1889 if ( (seg->type == &gtk_text_toggle_on_type ||
1890 seg->type == &gtk_text_toggle_off_type) &&
1891 (seg->body.toggle.info == info) )
1892 break;
1893 else
1894 seg = seg->next;
1895 }
1896
1897 g_assert (seg != NULL);
1898 g_assert (indexable_seg != NULL);
1899
1900 g_assert (seg != indexable_seg); /* If this happens, then
1901 forward_to_tag_toggle was
1902 full of shit. */
1903 g_assert (seg->body.toggle.info->tag == tag);
1904
1905 /* If this happens, when previously tagging we didn't merge
1906 overlapping tags. */
1907 g_assert ( (toggled_on && seg->type == &gtk_text_toggle_off_type) ||
1908 (!toggled_on && seg->type == &gtk_text_toggle_on_type) );
1909
1910 toggled_on = !toggled_on;
1911
1912#if 0
1913 printf ("deleting %s toggle\n",
1914 seg->type == &gtk_text_toggle_on_type ? "on" : "off");
1915#endif
1916 /* Remove toggle segment from the list. */
1917 if (prev == seg)
1918 {
1919 line->segments = seg->next;
1920 }
1921 else
1922 {
1923 while (prev->next != seg)
1924 {
1925 prev = prev->next;
1926 }
1927 prev->next = seg->next;
1928 }
1929
1930 /* Inform iterators we've hosed them. This actually reflects a
1931 bit of inefficiency; if you have the same tag toggled on and
1932 off a lot in a single line, we keep having the rescan from
1933 the front of the line. Of course we have to do that to get
1934 "prev" anyway, but here we are doing it an additional
1935 time. FIXME */
1936 segments_changed (tree);
1937
1938 /* Update node counts */
1939 if (seg->body.toggle.inNodeCounts)
1940 {
1941 _gtk_change_node_toggle_count (node: line->parent,
1942 info, delta: -1);
1943 seg->body.toggle.inNodeCounts = FALSE;
1944 }
1945
1946 _gtk_toggle_segment_free (seg);
1947
1948 /* We only clean up lines when we're done with them, saves some
1949 gratuitous line-segment-traversals */
1950
1951 if (cleanupline != line)
1952 {
1953 cleanup_line (line: cleanupline);
1954 cleanupline = line;
1955 }
1956 }
1957
1958 iter_stack_free (stack);
1959
1960 /* toggled_on now reflects the toggle state _just before_ the
1961 end iterator. The end iterator could already have a toggle
1962 on or a toggle off. */
1963 if ( (add && !toggled_on) ||
1964 (!add && toggled_on) )
1965 {
1966 /* This could create a second toggle at the start position;
1967 cleanup_line () will remove it if so. */
1968
1969 seg = _gtk_toggle_segment_new (info, on: !add);
1970
1971 prev = gtk_text_line_segment_split (iter: &end);
1972 if (prev == NULL)
1973 {
1974 seg->next = end_line->segments;
1975 end_line->segments = seg;
1976 }
1977 else
1978 {
1979 seg->next = prev->next;
1980 prev->next = seg;
1981 }
1982 /* cleanup_line adds the new toggle to the node counts. */
1983 g_assert (seg->body.toggle.inNodeCounts == FALSE);
1984#if 0
1985 printf ("added toggle at end\n");
1986#endif
1987 }
1988
1989 /*
1990 * Cleanup cleanupline and the last line of the range, if
1991 * these are different.
1992 */
1993
1994 cleanup_line (line: cleanupline);
1995 if (cleanupline != end_line)
1996 {
1997 cleanup_line (line: end_line);
1998 }
1999
2000 segments_changed (tree);
2001
2002 queue_tag_redisplay (tree, tag, start: &start, end: &end);
2003
2004#ifdef G_ENABLE_DEBUG
2005 if (GTK_DEBUG_CHECK (TEXT))
2006 _gtk_text_btree_check (tree);
2007#endif
2008}
2009
2010
2011/*
2012 * "Getters"
2013 */
2014
2015static GtkTextLine*
2016get_line_internal (GtkTextBTree *tree,
2017 int line_number,
2018 int *real_line_number,
2019 gboolean include_last)
2020{
2021 GtkTextBTreeNode *node;
2022 GtkTextLine *line;
2023 int lines_left;
2024 int line_count;
2025
2026 line_count = _gtk_text_btree_line_count (tree);
2027 if (!include_last)
2028 line_count -= 1;
2029
2030 if (line_number < 0)
2031 {
2032 line_number = line_count;
2033 }
2034 else if (line_number > line_count)
2035 {
2036 line_number = line_count;
2037 }
2038
2039 if (real_line_number)
2040 *real_line_number = line_number;
2041
2042 node = tree->root_node;
2043 lines_left = line_number;
2044
2045 /*
2046 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2047 * level 0.
2048 */
2049
2050 while (node->level != 0)
2051 {
2052 for (node = node->children.node;
2053 node->num_lines <= lines_left;
2054 node = node->next)
2055 {
2056#if 0
2057 if (node == NULL)
2058 {
2059 g_error ("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
2060 }
2061#endif
2062 lines_left -= node->num_lines;
2063 }
2064 }
2065
2066 /*
2067 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2068 */
2069
2070 for (line = node->children.line; lines_left > 0;
2071 line = line->next)
2072 {
2073#if 0
2074 if (line == NULL)
2075 {
2076 g_error ("gtk_text_btree_find_line ran out of lines");
2077 }
2078#endif
2079 lines_left -= 1;
2080 }
2081 return line;
2082}
2083
2084GtkTextLine*
2085_gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
2086{
2087 return
2088 _gtk_text_btree_get_line (tree,
2089 line_number: _gtk_text_btree_line_count (tree) - 1,
2090 NULL);
2091}
2092
2093GtkTextLine*
2094_gtk_text_btree_get_line (GtkTextBTree *tree,
2095 int line_number,
2096 int *real_line_number)
2097{
2098 return get_line_internal (tree, line_number, real_line_number, TRUE);
2099}
2100
2101GtkTextLine*
2102_gtk_text_btree_get_line_no_last (GtkTextBTree *tree,
2103 int line_number,
2104 int *real_line_number)
2105{
2106 return get_line_internal (tree, line_number, real_line_number, FALSE);
2107}
2108
2109GtkTextLine*
2110_gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
2111 int char_index,
2112 int *line_start_index,
2113 int *real_char_index)
2114{
2115 GtkTextBTreeNode *node;
2116 GtkTextLine *line;
2117 GtkTextLineSegment *seg;
2118 int chars_left;
2119 int chars_in_line;
2120
2121 node = tree->root_node;
2122
2123 /* Clamp to valid indexes (-1 is magic for "highest index"),
2124 * node->num_chars includes the two newlines that aren't really
2125 * in the buffer.
2126 */
2127 if (char_index < 0 || char_index >= (node->num_chars - 1))
2128 {
2129 char_index = node->num_chars - 2;
2130 }
2131
2132 *real_char_index = char_index;
2133
2134 /*
2135 * Work down through levels of the tree until a GtkTextBTreeNode is found at
2136 * level 0.
2137 */
2138
2139 chars_left = char_index;
2140 while (node->level != 0)
2141 {
2142 for (node = node->children.node;
2143 chars_left >= node->num_chars;
2144 node = node->next)
2145 {
2146 chars_left -= node->num_chars;
2147
2148 g_assert (chars_left >= 0);
2149 }
2150 }
2151
2152 if (chars_left == 0)
2153 {
2154 /* Start of a line */
2155
2156 *line_start_index = char_index;
2157 return node->children.line;
2158 }
2159
2160 /*
2161 * Work through the lines attached to the level-0 GtkTextBTreeNode.
2162 */
2163
2164 chars_in_line = 0;
2165 seg = NULL;
2166 for (line = node->children.line; line != NULL; line = line->next)
2167 {
2168 seg = line->segments;
2169 while (seg != NULL)
2170 {
2171 if (chars_in_line + seg->char_count > chars_left)
2172 goto found; /* found our line/segment */
2173
2174 chars_in_line += seg->char_count;
2175
2176 seg = seg->next;
2177 }
2178
2179 chars_left -= chars_in_line;
2180
2181 chars_in_line = 0;
2182 seg = NULL;
2183 }
2184
2185 found:
2186
2187 g_assert (line != NULL); /* hosage, ran out of lines */
2188 g_assert (seg != NULL);
2189
2190 *line_start_index = char_index - chars_left;
2191 return line;
2192}
2193
2194/* It returns an array sorted by tags priority, ready to pass to
2195 * _gtk_text_attributes_fill_from_tags() */
2196GPtrArray *
2197_gtk_text_btree_get_tags (const GtkTextIter *iter)
2198{
2199 GtkTextBTreeNode *node;
2200 GtkTextLine *siblingline;
2201 GtkTextLineSegment *seg;
2202 int src, dst, index;
2203 TagInfo tagInfo;
2204 GtkTextLine *line;
2205 int byte_index;
2206
2207#define NUM_TAG_INFOS 10
2208
2209 line = _gtk_text_iter_get_text_line (iter);
2210 byte_index = gtk_text_iter_get_line_index (iter);
2211
2212 tagInfo.tags = g_ptr_array_sized_new (NUM_TAG_INFOS);
2213 tagInfo.counts = g_array_new (FALSE, FALSE, element_size: sizeof (int));
2214
2215 /*
2216 * Record tag toggles within the line of indexPtr but preceding
2217 * indexPtr. Note that if this loop segfaults, your
2218 * byte_index probably points past the sum of all
2219 * seg->byte_count */
2220
2221 for (index = 0, seg = line->segments;
2222 (index + seg->byte_count) <= byte_index;
2223 index += seg->byte_count, seg = seg->next)
2224 {
2225 if ((seg->type == &gtk_text_toggle_on_type)
2226 || (seg->type == &gtk_text_toggle_off_type))
2227 {
2228 inc_count (tag: seg->body.toggle.info->tag, inc: 1, tagInfoPtr: &tagInfo);
2229 }
2230 }
2231
2232 /*
2233 * Record toggles for tags in lines that are predecessors of
2234 * line but under the same level-0 GtkTextBTreeNode.
2235 */
2236
2237 for (siblingline = line->parent->children.line;
2238 siblingline != line;
2239 siblingline = siblingline->next)
2240 {
2241 for (seg = siblingline->segments; seg != NULL;
2242 seg = seg->next)
2243 {
2244 if ((seg->type == &gtk_text_toggle_on_type)
2245 || (seg->type == &gtk_text_toggle_off_type))
2246 {
2247 inc_count (tag: seg->body.toggle.info->tag, inc: 1, tagInfoPtr: &tagInfo);
2248 }
2249 }
2250 }
2251
2252 /*
2253 * For each GtkTextBTreeNode in the ancestry of this line, record tag
2254 * toggles for all siblings that precede that GtkTextBTreeNode.
2255 */
2256
2257 for (node = line->parent; node->parent != NULL;
2258 node = node->parent)
2259 {
2260 GtkTextBTreeNode *siblingPtr;
2261 Summary *summary;
2262
2263 for (siblingPtr = node->parent->children.node;
2264 siblingPtr != node; siblingPtr = siblingPtr->next)
2265 {
2266 for (summary = siblingPtr->summary; summary != NULL;
2267 summary = summary->next)
2268 {
2269 if (summary->toggle_count & 1)
2270 {
2271 inc_count (tag: summary->info->tag, inc: summary->toggle_count,
2272 tagInfoPtr: &tagInfo);
2273 }
2274 }
2275 }
2276 }
2277
2278 /*
2279 * Go through the tag information and squash out all of the tags
2280 * that have even toggle counts (these tags exist before the point
2281 * of interest, but not at the desired character itself).
2282 */
2283
2284 for (src = 0, dst = 0; src < tagInfo.tags->len; src++)
2285 {
2286 if (g_array_index (tagInfo.counts, int, src) & 1)
2287 {
2288 g_assert (GTK_IS_TEXT_TAG (g_ptr_array_index (tagInfo.tags, src)));
2289 g_ptr_array_index (tagInfo.tags, dst) = g_ptr_array_index (tagInfo.tags, src);
2290 dst++;
2291 }
2292 }
2293
2294 g_ptr_array_set_size (array: tagInfo.tags, length: dst);
2295 g_array_unref (array: tagInfo.counts);
2296 if (dst == 0)
2297 {
2298 g_ptr_array_unref (array: tagInfo.tags);
2299 return NULL;
2300 }
2301
2302 /* Sort tags in ascending order of priority */
2303 _gtk_text_tag_array_sort (tags: tagInfo.tags);
2304
2305 return tagInfo.tags;
2306}
2307
2308static void
2309copy_segment (GString *string,
2310 gboolean include_hidden,
2311 gboolean include_nonchars,
2312 const GtkTextIter *start,
2313 const GtkTextIter *end)
2314{
2315 GtkTextLineSegment *end_seg;
2316 GtkTextLineSegment *seg;
2317
2318 if (gtk_text_iter_equal (lhs: start, rhs: end))
2319 return;
2320
2321 seg = _gtk_text_iter_get_indexable_segment (iter: start);
2322 end_seg = _gtk_text_iter_get_indexable_segment (iter: end);
2323
2324 if (seg->type == &gtk_text_char_type)
2325 {
2326 gboolean copy = TRUE;
2327 int copy_bytes = 0;
2328 int copy_start = 0;
2329
2330 /* Don't copy if we're invisible; segments are invisible/not
2331 as a whole, no need to check each char */
2332 if (!include_hidden &&
2333 _gtk_text_btree_char_is_invisible (iter: start))
2334 {
2335 copy = FALSE;
2336 /* printf (" <invisible>\n"); */
2337 }
2338
2339 copy_start = _gtk_text_iter_get_segment_byte (iter: start);
2340
2341 if (seg == end_seg)
2342 {
2343 /* End is in the same segment; need to copy fewer bytes. */
2344 int end_byte = _gtk_text_iter_get_segment_byte (iter: end);
2345
2346 copy_bytes = end_byte - copy_start;
2347 }
2348 else
2349 copy_bytes = seg->byte_count - copy_start;
2350
2351 g_assert (copy_bytes != 0); /* Due to iter equality check at
2352 front of this function. */
2353
2354 if (copy)
2355 {
2356 g_assert ((copy_start + copy_bytes) <= seg->byte_count);
2357
2358 g_string_append_len (string,
2359 val: seg->body.chars + copy_start,
2360 len: copy_bytes);
2361 }
2362
2363 /* printf (" :%s\n", string->str); */
2364 }
2365 else if (seg->type == &gtk_text_paintable_type)
2366 {
2367 gboolean copy = TRUE;
2368
2369 if (!include_nonchars)
2370 {
2371 copy = FALSE;
2372 }
2373 else if (!include_hidden &&
2374 _gtk_text_btree_char_is_invisible (iter: start))
2375 {
2376 copy = FALSE;
2377 }
2378
2379 if (copy)
2380 {
2381 g_string_append_len (string,
2382 val: _gtk_text_unknown_char_utf8,
2383 GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN);
2384 }
2385 }
2386 else if (seg->type == &gtk_text_child_type)
2387 {
2388 gboolean copy = TRUE;
2389 if (!include_nonchars &&
2390 g_strcmp0 (str1: _gtk_text_unknown_char_utf8, str2: gtk_text_child_anchor_get_replacement (anchor: seg->body.child.obj)) == 0)
2391 {
2392 copy = FALSE;
2393 }
2394 else if (!include_hidden &&
2395 _gtk_text_btree_char_is_invisible (iter: start))
2396 {
2397 copy = FALSE;
2398 }
2399
2400 if (copy)
2401 {
2402 g_string_append_len (string,
2403 val: gtk_text_child_anchor_get_replacement (anchor: seg->body.child.obj),
2404 len: seg->byte_count);
2405 }
2406 }
2407}
2408
2409char *
2410_gtk_text_btree_get_text (const GtkTextIter *start_orig,
2411 const GtkTextIter *end_orig,
2412 gboolean include_hidden,
2413 gboolean include_nonchars)
2414{
2415 GtkTextLineSegment *seg;
2416 GtkTextLineSegment *end_seg;
2417 GString *retval;
2418 char *str;
2419 GtkTextIter iter;
2420 GtkTextIter start;
2421 GtkTextIter end;
2422
2423 g_return_val_if_fail (start_orig != NULL, NULL);
2424 g_return_val_if_fail (end_orig != NULL, NULL);
2425 g_return_val_if_fail (_gtk_text_iter_get_btree (start_orig) ==
2426 _gtk_text_iter_get_btree (end_orig), NULL);
2427
2428 start = *start_orig;
2429 end = *end_orig;
2430
2431 gtk_text_iter_order (first: &start, second: &end);
2432
2433 retval = g_string_new (NULL);
2434
2435 end_seg = _gtk_text_iter_get_indexable_segment (iter: &end);
2436 iter = start;
2437 seg = _gtk_text_iter_get_indexable_segment (iter: &iter);
2438 while (seg != end_seg)
2439 {
2440 copy_segment (string: retval, include_hidden, include_nonchars,
2441 start: &iter, end: &end);
2442
2443 _gtk_text_iter_forward_indexable_segment (iter: &iter);
2444
2445 seg = _gtk_text_iter_get_indexable_segment (iter: &iter);
2446 }
2447
2448 copy_segment (string: retval, include_hidden, include_nonchars, start: &iter, end: &end);
2449
2450 str = retval->str;
2451 g_string_free (string: retval, FALSE);
2452 return str;
2453}
2454
2455int
2456_gtk_text_btree_line_count (GtkTextBTree *tree)
2457{
2458 /* Subtract bogus line at the end; we return a count
2459 of usable lines. */
2460 return tree->root_node->num_lines - 1;
2461}
2462
2463int
2464_gtk_text_btree_char_count (GtkTextBTree *tree)
2465{
2466 /* Exclude newline in bogus last line and the
2467 * one in the last line that is after the end iterator
2468 */
2469 return tree->root_node->num_chars - 2;
2470}
2471
2472gboolean
2473_gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
2474{
2475 gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
2476 int *tagCnts;
2477 GtkTextTag **tags;
2478 int numTags;
2479 GtkTextBTreeNode *node;
2480 GtkTextLine *siblingline;
2481 GtkTextLineSegment *seg;
2482 GtkTextTag *tag;
2483 int i, index;
2484 GtkTextLine *line;
2485 GtkTextBTree *tree;
2486 int byte_index;
2487
2488 tree = _gtk_text_iter_get_btree (iter);
2489
2490 /* Short-circuit if we've never seen a visibility tag within the
2491 * tag table (meaning everything must be visible).
2492 */
2493 if G_LIKELY (!_gtk_text_tag_table_affects_visibility (tree->table))
2494 return FALSE;
2495
2496 line = _gtk_text_iter_get_text_line (iter);
2497
2498 byte_index = gtk_text_iter_get_line_index (iter);
2499
2500 numTags = gtk_text_tag_table_get_size (table: tree->table);
2501
2502 tagCnts = g_alloca (sizeof (int) * numTags);
2503 tags = g_alloca (sizeof (GtkTextTag *) * numTags);
2504
2505 memset (s: tagCnts, c: 0, n: sizeof (int) * numTags);
2506
2507 /*
2508 * Record tag toggles within the line of indexPtr but preceding
2509 * indexPtr.
2510 */
2511
2512 for (index = 0, seg = line->segments;
2513 (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
2514 index += seg->byte_count, seg = seg->next)
2515 {
2516 if ((seg->type == &gtk_text_toggle_on_type)
2517 || (seg->type == &gtk_text_toggle_off_type))
2518 {
2519 tag = seg->body.toggle.info->tag;
2520 if (tag->priv->invisible_set)
2521 {
2522 tags[tag->priv->priority] = tag;
2523 tagCnts[tag->priv->priority]++;
2524 }
2525 }
2526 }
2527
2528 /*
2529 * Record toggles for tags in lines that are predecessors of
2530 * line but under the same level-0 GtkTextBTreeNode.
2531 */
2532
2533 for (siblingline = line->parent->children.line;
2534 siblingline != line;
2535 siblingline = siblingline->next)
2536 {
2537 for (seg = siblingline->segments; seg != NULL;
2538 seg = seg->next)
2539 {
2540 if ((seg->type == &gtk_text_toggle_on_type)
2541 || (seg->type == &gtk_text_toggle_off_type))
2542 {
2543 tag = seg->body.toggle.info->tag;
2544 if (tag->priv->invisible_set)
2545 {
2546 tags[tag->priv->priority] = tag;
2547 tagCnts[tag->priv->priority]++;
2548 }
2549 }
2550 }
2551 }
2552
2553 /*
2554 * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
2555 * for all siblings that precede that GtkTextBTreeNode.
2556 */
2557
2558 for (node = line->parent; node->parent != NULL;
2559 node = node->parent)
2560 {
2561 GtkTextBTreeNode *siblingPtr;
2562 Summary *summary;
2563
2564 for (siblingPtr = node->parent->children.node;
2565 siblingPtr != node; siblingPtr = siblingPtr->next)
2566 {
2567 for (summary = siblingPtr->summary; summary != NULL;
2568 summary = summary->next)
2569 {
2570 if (summary->toggle_count & 1)
2571 {
2572 tag = summary->info->tag;
2573 if (tag->priv->invisible_set)
2574 {
2575 tags[tag->priv->priority] = tag;
2576 tagCnts[tag->priv->priority] += summary->toggle_count;
2577 }
2578 }
2579 }
2580 }
2581 }
2582
2583 /*
2584 * Now traverse from highest priority to lowest,
2585 * take invisible value from first odd count (= on)
2586 */
2587
2588 for (i = numTags-1; i >=0; i--)
2589 {
2590 if (tagCnts[i] & 1)
2591 {
2592 /* FIXME not sure this should be if 0 */
2593#if 0
2594#ifndef ALWAYS_SHOW_SELECTION
2595 /* who would make the selection invisible? */
2596 if ((tag == tkxt->seltag)
2597 && !(tkxt->flags & GOT_FOCUS))
2598 {
2599 continue;
2600 }
2601#endif
2602#endif
2603 invisible = tags[i]->priv->values->invisible;
2604 break;
2605 }
2606 }
2607
2608 return invisible;
2609}
2610
2611
2612/*
2613 * Manipulate marks
2614 */
2615
2616static void
2617redisplay_region (GtkTextBTree *tree,
2618 const GtkTextIter *start,
2619 const GtkTextIter *end,
2620 gboolean cursors_only)
2621{
2622 BTreeView *view;
2623 GtkTextLine *start_line, *end_line;
2624
2625 if (gtk_text_iter_compare (lhs: start, rhs: end) > 0)
2626 {
2627 const GtkTextIter *tmp = start;
2628 start = end;
2629 end = tmp;
2630 }
2631
2632 start_line = _gtk_text_iter_get_text_line (iter: start);
2633 end_line = _gtk_text_iter_get_text_line (iter: end);
2634
2635 view = tree->views;
2636 while (view != NULL)
2637 {
2638 int start_y, end_y;
2639 GtkTextLineData *ld;
2640
2641 start_y = _gtk_text_btree_find_line_top (tree, target_line: start_line, view_id: view->view_id);
2642
2643 if (end_line == start_line)
2644 end_y = start_y;
2645 else
2646 end_y = _gtk_text_btree_find_line_top (tree, target_line: end_line, view_id: view->view_id);
2647
2648 ld = _gtk_text_line_get_data (line: start_line, view_id: view->view_id);
2649 if (ld)
2650 start_y -= ld->top_ink;
2651
2652 ld = _gtk_text_line_get_data (line: end_line, view_id: view->view_id);
2653 if (ld)
2654 end_y += ld->height + ld->bottom_ink;
2655
2656 if (cursors_only)
2657 gtk_text_layout_cursors_changed (layout: view->layout, y: start_y,
2658 old_height: end_y - start_y,
2659 new_height: end_y - start_y);
2660 else
2661 gtk_text_layout_changed (layout: view->layout, y: start_y,
2662 old_height: end_y - start_y,
2663 new_height: end_y - start_y);
2664
2665 view = view->next;
2666 }
2667}
2668
2669static void
2670redisplay_mark (GtkTextLineSegment *mark)
2671{
2672 GtkTextIter iter;
2673 GtkTextIter end;
2674 gboolean cursor_only;
2675
2676 _gtk_text_btree_get_iter_at_mark (tree: mark->body.mark.tree,
2677 iter: &iter,
2678 mark: mark->body.mark.obj);
2679
2680 end = iter;
2681 gtk_text_iter_forward_char (iter: &end);
2682
2683 DV (g_print ("invalidating due to moving visible mark (%s)\n", G_STRLOC));
2684 cursor_only = mark == mark->body.mark.tree->insert_mark->segment;
2685 _gtk_text_btree_invalidate_region (tree: mark->body.mark.tree, start: &iter, end: &end, cursors_only: cursor_only);
2686}
2687
2688static void
2689redisplay_mark_if_visible (GtkTextLineSegment *mark)
2690{
2691 if (!mark->body.mark.visible)
2692 return;
2693 else
2694 redisplay_mark (mark);
2695}
2696
2697static void
2698ensure_not_off_end (GtkTextBTree *tree,
2699 GtkTextLineSegment *mark,
2700 GtkTextIter *iter)
2701{
2702 if (gtk_text_iter_get_line (iter) == _gtk_text_btree_line_count (tree))
2703 gtk_text_iter_backward_char (iter);
2704}
2705
2706static GtkTextLineSegment*
2707real_set_mark (GtkTextBTree *tree,
2708 GtkTextMark *existing_mark,
2709 const char *name,
2710 gboolean left_gravity,
2711 const GtkTextIter *where,
2712 gboolean should_exist,
2713 gboolean redraw_selections)
2714{
2715 GtkTextLineSegment *mark;
2716 GtkTextIter iter;
2717
2718 g_return_val_if_fail (tree != NULL, NULL);
2719 g_return_val_if_fail (where != NULL, NULL);
2720 g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
2721
2722 if (existing_mark)
2723 {
2724 if (gtk_text_mark_get_buffer (mark: existing_mark) != NULL)
2725 mark = existing_mark->segment;
2726 else
2727 mark = NULL;
2728 }
2729 else if (name != NULL)
2730 mark = g_hash_table_lookup (hash_table: tree->mark_table,
2731 key: name);
2732 else
2733 mark = NULL;
2734
2735 if (should_exist && mark == NULL)
2736 {
2737 g_warning ("No mark '%s' exists!", name);
2738 return NULL;
2739 }
2740
2741 /* OK if !should_exist and it does already exist, in that case
2742 * we just move it.
2743 */
2744
2745 iter = *where;
2746
2747#ifdef G_ENABLE_DEBUG
2748 if (GTK_DEBUG_CHECK (TEXT))
2749 _gtk_text_iter_check (iter: &iter);
2750#endif
2751
2752 if (mark != NULL)
2753 {
2754 if (redraw_selections &&
2755 (mark == tree->insert_mark->segment ||
2756 mark == tree->selection_bound_mark->segment))
2757 {
2758 GtkTextIter old_pos;
2759
2760 _gtk_text_btree_get_iter_at_mark (tree, iter: &old_pos,
2761 mark: mark->body.mark.obj);
2762 redisplay_region (tree, start: &old_pos, end: where, TRUE);
2763 }
2764
2765 /*
2766 * don't let visible marks be after the final newline of the
2767 * file.
2768 */
2769
2770 if (mark->body.mark.visible)
2771 {
2772 ensure_not_off_end (tree, mark, iter: &iter);
2773 }
2774
2775 /* Redraw the mark's old location. */
2776 redisplay_mark_if_visible (mark);
2777
2778 /* Unlink mark from its current location.
2779 This could hose our iterator... */
2780 gtk_text_btree_unlink_segment (tree, seg: mark,
2781 line: mark->body.mark.line);
2782 mark->body.mark.line = _gtk_text_iter_get_text_line (iter: &iter);
2783 g_assert (mark->body.mark.line == _gtk_text_iter_get_text_line (&iter));
2784
2785 segments_changed (tree); /* make sure the iterator recomputes its
2786 segment */
2787 }
2788 else
2789 {
2790 if (existing_mark)
2791 g_object_ref (existing_mark);
2792 else
2793 existing_mark = gtk_text_mark_new (name, left_gravity);
2794
2795 mark = existing_mark->segment;
2796 _gtk_mark_segment_set_tree (mark, tree);
2797
2798 mark->body.mark.line = _gtk_text_iter_get_text_line (iter: &iter);
2799
2800 if (mark->body.mark.name)
2801 g_hash_table_insert (hash_table: tree->mark_table,
2802 key: mark->body.mark.name,
2803 value: mark);
2804 }
2805
2806#ifdef G_ENABLE_DEBUG
2807 if (GTK_DEBUG_CHECK (TEXT))
2808 _gtk_text_iter_check (iter: &iter);
2809#endif
2810
2811 /* Link mark into new location */
2812 gtk_text_btree_link_segment (seg: mark, iter: &iter);
2813
2814 /* Invalidate some iterators. */
2815 segments_changed (tree);
2816
2817 /*
2818 * update the screen at the mark's new location.
2819 */
2820
2821 redisplay_mark_if_visible (mark);
2822
2823#ifdef G_ENABLE_DEBUG
2824 if (GTK_DEBUG_CHECK (TEXT))
2825 {
2826 _gtk_text_iter_check (iter: &iter);
2827 _gtk_text_btree_check (tree);
2828 }
2829#endif
2830
2831 return mark;
2832}
2833
2834
2835GtkTextMark*
2836_gtk_text_btree_set_mark (GtkTextBTree *tree,
2837 GtkTextMark *existing_mark,
2838 const char *name,
2839 gboolean left_gravity,
2840 const GtkTextIter *iter,
2841 gboolean should_exist)
2842{
2843 GtkTextLineSegment *seg;
2844
2845 seg = real_set_mark (tree, existing_mark,
2846 name, left_gravity, where: iter, should_exist,
2847 TRUE);
2848 g_assert (seg);
2849
2850 return seg->body.mark.obj;
2851}
2852
2853gboolean
2854_gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
2855 GtkTextIter *start,
2856 GtkTextIter *end)
2857{
2858 GtkTextIter tmp_start, tmp_end;
2859
2860 _gtk_text_btree_get_iter_at_mark (tree, iter: &tmp_start,
2861 mark: tree->insert_mark);
2862 _gtk_text_btree_get_iter_at_mark (tree, iter: &tmp_end,
2863 mark: tree->selection_bound_mark);
2864
2865 if (gtk_text_iter_equal (lhs: &tmp_start, rhs: &tmp_end))
2866 {
2867 if (start)
2868 *start = tmp_start;
2869
2870 if (end)
2871 *end = tmp_end;
2872
2873 return FALSE;
2874 }
2875 else
2876 {
2877 gtk_text_iter_order (first: &tmp_start, second: &tmp_end);
2878
2879 if (start)
2880 *start = tmp_start;
2881
2882 if (end)
2883 *end = tmp_end;
2884
2885 return TRUE;
2886 }
2887}
2888
2889void
2890_gtk_text_btree_place_cursor (GtkTextBTree *tree,
2891 const GtkTextIter *iter)
2892{
2893 _gtk_text_btree_select_range (tree, ins: iter, bound: iter);
2894}
2895
2896void
2897_gtk_text_btree_select_range (GtkTextBTree *tree,
2898 const GtkTextIter *ins,
2899 const GtkTextIter *bound)
2900{
2901 GtkTextIter old_ins, old_bound;
2902
2903 _gtk_text_btree_get_iter_at_mark (tree, iter: &old_ins,
2904 mark: tree->insert_mark);
2905 _gtk_text_btree_get_iter_at_mark (tree, iter: &old_bound,
2906 mark: tree->selection_bound_mark);
2907
2908 /* Check if it's no-op since gtk_text_buffer_place_cursor()
2909 * also calls this, and this will redraw the cursor line. */
2910 if (!gtk_text_iter_equal (lhs: &old_ins, rhs: ins) ||
2911 !gtk_text_iter_equal (lhs: &old_bound, rhs: bound))
2912 {
2913 redisplay_region (tree, start: &old_ins, end: &old_bound, TRUE);
2914
2915 /* Move insert AND selection_bound before we redisplay */
2916 real_set_mark (tree, existing_mark: tree->insert_mark,
2917 name: "insert", FALSE, where: ins, TRUE, FALSE);
2918 real_set_mark (tree, existing_mark: tree->selection_bound_mark,
2919 name: "selection_bound", FALSE, where: bound, TRUE, FALSE);
2920
2921 redisplay_region (tree, start: ins, end: bound, TRUE);
2922 }
2923}
2924
2925
2926void
2927_gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
2928 const char *name)
2929{
2930 GtkTextMark *mark;
2931
2932 g_return_if_fail (tree != NULL);
2933 g_return_if_fail (name != NULL);
2934
2935 mark = g_hash_table_lookup (hash_table: tree->mark_table,
2936 key: name);
2937
2938 _gtk_text_btree_remove_mark (tree, segment: mark);
2939}
2940
2941void
2942_gtk_text_btree_release_mark_segment (GtkTextBTree *tree,
2943 GtkTextLineSegment *segment)
2944{
2945
2946 if (segment->body.mark.name)
2947 g_hash_table_remove (hash_table: tree->mark_table, key: segment->body.mark.name);
2948
2949 segment->body.mark.tree = NULL;
2950 segment->body.mark.line = NULL;
2951
2952 /* Remove the ref on the mark, which frees segment as a side effect
2953 * if this is the last reference.
2954 */
2955 g_object_unref (object: segment->body.mark.obj);
2956}
2957
2958void
2959_gtk_text_btree_remove_mark (GtkTextBTree *tree,
2960 GtkTextMark *mark)
2961{
2962 GtkTextLineSegment *segment;
2963
2964 g_return_if_fail (mark != NULL);
2965 g_return_if_fail (tree != NULL);
2966
2967 segment = mark->segment;
2968
2969 if (segment->body.mark.not_deleteable)
2970 {
2971 g_warning ("Can't delete special mark '%s'", segment->body.mark.name);
2972 return;
2973 }
2974
2975 /* This calls cleanup_line and segments_changed */
2976 gtk_text_btree_unlink_segment (tree, seg: segment, line: segment->body.mark.line);
2977
2978 _gtk_text_btree_release_mark_segment (tree, segment);
2979}
2980
2981gboolean
2982_gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
2983 GtkTextMark *segment)
2984{
2985 return segment == tree->insert_mark;
2986}
2987
2988gboolean
2989_gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
2990 GtkTextMark *segment)
2991{
2992 return segment == tree->selection_bound_mark;
2993}
2994
2995GtkTextMark *
2996_gtk_text_btree_get_insert (GtkTextBTree *tree)
2997{
2998 return tree->insert_mark;
2999}
3000
3001GtkTextMark *
3002_gtk_text_btree_get_selection_bound (GtkTextBTree *tree)
3003{
3004 return tree->selection_bound_mark;
3005}
3006
3007GtkTextMark*
3008_gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
3009 const char *name)
3010{
3011 GtkTextLineSegment *seg;
3012
3013 g_return_val_if_fail (tree != NULL, NULL);
3014 g_return_val_if_fail (name != NULL, NULL);
3015
3016 seg = g_hash_table_lookup (hash_table: tree->mark_table, key: name);
3017
3018 return seg ? seg->body.mark.obj : NULL;
3019}
3020
3021/**
3022 * gtk_text_mark_set_visible:
3023 * @mark: a GtkTextMark
3024 * @setting: visibility of mark
3025 *
3026 * Sets the visibility of @mark.
3027 *
3028 * The insertion point is normally visible, i.e. you can see it as
3029 * a vertical bar. Also, the text widget uses a visible mark to
3030 * indicate where a drop will occur when dragging-and-dropping text.
3031 * Most other marks are not visible.
3032 *
3033 * Marks are not visible by default.
3034 */
3035void
3036gtk_text_mark_set_visible (GtkTextMark *mark,
3037 gboolean setting)
3038{
3039 GtkTextLineSegment *seg;
3040
3041 g_return_if_fail (mark != NULL);
3042
3043 seg = mark->segment;
3044
3045 if (seg->body.mark.visible == setting)
3046 return;
3047 else
3048 {
3049 seg->body.mark.visible = setting;
3050
3051 if (seg->body.mark.tree)
3052 redisplay_mark (mark: seg);
3053 }
3054}
3055
3056GtkTextLine*
3057_gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
3058 GtkTextTag *tag)
3059{
3060 GtkTextBTreeNode *node;
3061 GtkTextTagInfo *info;
3062
3063 g_return_val_if_fail (tree != NULL, NULL);
3064
3065 if (tag != NULL)
3066 {
3067 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3068
3069 if (info == NULL)
3070 return NULL;
3071
3072 if (info->tag_root == NULL)
3073 return NULL;
3074
3075 node = info->tag_root;
3076
3077 /* We know the tag root has instances of the given
3078 tag below it */
3079
3080 continue_outer_loop:
3081 g_assert (node != NULL);
3082 while (node->level > 0)
3083 {
3084 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3085 node = node->children.node;
3086 while (node != NULL)
3087 {
3088 if (gtk_text_btree_node_has_tag (node, tag))
3089 goto continue_outer_loop;
3090
3091 node = node->next;
3092 }
3093 g_assert (node != NULL);
3094 }
3095
3096 g_assert (node != NULL); /* The tag summaries said some node had
3097 tag toggles... */
3098
3099 g_assert (node->level == 0);
3100
3101 return node->children.line;
3102 }
3103 else
3104 {
3105 /* Looking for any tag at all (tag == NULL).
3106 Unfortunately this can't be done in a simple and efficient way
3107 right now; so I'm just going to return the
3108 first line in the btree. FIXME */
3109 return _gtk_text_btree_get_line (tree, line_number: 0, NULL);
3110 }
3111}
3112
3113GtkTextLine*
3114_gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
3115 GtkTextTag *tag)
3116{
3117 GtkTextBTreeNode *node;
3118 GtkTextBTreeNode *last_node;
3119 GtkTextLine *line;
3120 GtkTextTagInfo *info;
3121
3122 g_return_val_if_fail (tree != NULL, NULL);
3123
3124 if (tag != NULL)
3125 {
3126 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3127
3128 if (info == NULL || info->tag_root == NULL)
3129 return NULL;
3130
3131 node = info->tag_root;
3132 /* We know the tag root has instances of the given
3133 tag below it */
3134
3135 while (node->level > 0)
3136 {
3137 last_node = NULL;
3138 node = node->children.node;
3139 while (node != NULL)
3140 {
3141 if (gtk_text_btree_node_has_tag (node, tag))
3142 last_node = node;
3143 node = node->next;
3144 }
3145
3146 node = last_node;
3147 g_assert (node != NULL); /* Failure probably means bad tag summaries. */
3148 }
3149
3150 g_assert (node != NULL); /* The tag summaries said some node had
3151 tag toggles... */
3152
3153 g_assert (node->level == 0);
3154
3155 /* Find the last line in this node */
3156 line = node->children.line;
3157 while (line->next != NULL)
3158 line = line->next;
3159
3160 return line;
3161 }
3162 else
3163 {
3164 /* This search can't be done efficiently at the moment,
3165 at least not without complexity.
3166 So, we just return the last line.
3167 */
3168 return _gtk_text_btree_get_end_iter_line (tree);
3169 }
3170}
3171
3172
3173/*
3174 * Lines
3175 */
3176
3177int
3178_gtk_text_line_get_number (GtkTextLine *line)
3179{
3180 GtkTextLine *line2;
3181 GtkTextBTreeNode *node, *parent, *node2;
3182 int index;
3183
3184 /*
3185 * First count how many lines precede this one in its level-0
3186 * GtkTextBTreeNode.
3187 */
3188
3189 node = line->parent;
3190 index = 0;
3191 for (line2 = node->children.line; line2 != line;
3192 line2 = line2->next)
3193 {
3194 if (line2 == NULL)
3195 {
3196 g_error ("gtk_text_btree_line_number couldn't find line");
3197 }
3198 index += 1;
3199 }
3200
3201 /*
3202 * Now work up through the levels of the tree one at a time,
3203 * counting how many lines are in GtkTextBTreeNodes preceding the current
3204 * GtkTextBTreeNode.
3205 */
3206
3207 for (parent = node->parent ; parent != NULL;
3208 node = parent, parent = parent->parent)
3209 {
3210 for (node2 = parent->children.node; node2 != node;
3211 node2 = node2->next)
3212 {
3213 if (node2 == NULL)
3214 {
3215 g_error ("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
3216 }
3217 index += node2->num_lines;
3218 }
3219 }
3220 return index;
3221}
3222
3223static GtkTextLineSegment*
3224find_toggle_segment_before_char (GtkTextLine *line,
3225 int char_in_line,
3226 GtkTextTag *tag)
3227{
3228 GtkTextLineSegment *seg;
3229 GtkTextLineSegment *toggle_seg;
3230 int index;
3231
3232 toggle_seg = NULL;
3233 index = 0;
3234 seg = line->segments;
3235 while ( (index + seg->char_count) <= char_in_line )
3236 {
3237 if (((seg->type == &gtk_text_toggle_on_type)
3238 || (seg->type == &gtk_text_toggle_off_type))
3239 && (seg->body.toggle.info->tag == tag))
3240 toggle_seg = seg;
3241
3242 index += seg->char_count;
3243 seg = seg->next;
3244 }
3245
3246 return toggle_seg;
3247}
3248
3249static GtkTextLineSegment*
3250find_toggle_segment_before_byte (GtkTextLine *line,
3251 int byte_in_line,
3252 GtkTextTag *tag)
3253{
3254 GtkTextLineSegment *seg;
3255 GtkTextLineSegment *toggle_seg;
3256 int index;
3257
3258 toggle_seg = NULL;
3259 index = 0;
3260 seg = line->segments;
3261 while ( (index + seg->byte_count) <= byte_in_line )
3262 {
3263 if (((seg->type == &gtk_text_toggle_on_type)
3264 || (seg->type == &gtk_text_toggle_off_type))
3265 && (seg->body.toggle.info->tag == tag))
3266 toggle_seg = seg;
3267
3268 index += seg->byte_count;
3269 seg = seg->next;
3270 }
3271
3272 return toggle_seg;
3273}
3274
3275static gboolean
3276find_toggle_outside_current_line (GtkTextLine *line,
3277 GtkTextBTree *tree,
3278 GtkTextTag *tag)
3279{
3280 GtkTextBTreeNode *node;
3281 GtkTextLine *sibling_line;
3282 GtkTextLineSegment *seg;
3283 GtkTextLineSegment *toggle_seg;
3284 int toggles;
3285 GtkTextTagInfo *info = NULL;
3286
3287 /*
3288 * No toggle in this line. Look for toggles for the tag in lines
3289 * that are predecessors of line but under the same
3290 * level-0 GtkTextBTreeNode.
3291 */
3292 toggle_seg = NULL;
3293 sibling_line = line->parent->children.line;
3294 while (sibling_line != line)
3295 {
3296 seg = sibling_line->segments;
3297 while (seg != NULL)
3298 {
3299 if (((seg->type == &gtk_text_toggle_on_type)
3300 || (seg->type == &gtk_text_toggle_off_type))
3301 && (seg->body.toggle.info->tag == tag))
3302 toggle_seg = seg;
3303
3304 seg = seg->next;
3305 }
3306
3307 sibling_line = sibling_line->next;
3308 }
3309
3310 if (toggle_seg != NULL)
3311 return (toggle_seg->type == &gtk_text_toggle_on_type);
3312
3313 /*
3314 * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
3315 * this GtkTextBTreeNode, counting the number of toggles of the given tag in
3316 * siblings that precede that GtkTextBTreeNode.
3317 */
3318
3319 info = gtk_text_btree_get_existing_tag_info (tree, tag);
3320
3321 if (info == NULL)
3322 return FALSE;
3323
3324 toggles = 0;
3325 node = line->parent;
3326 while (node->parent != NULL)
3327 {
3328 GtkTextBTreeNode *sibling_node;
3329
3330 sibling_node = node->parent->children.node;
3331 while (sibling_node != node)
3332 {
3333 Summary *summary;
3334
3335 summary = sibling_node->summary;
3336 while (summary != NULL)
3337 {
3338 if (summary->info == info)
3339 toggles += summary->toggle_count;
3340
3341 summary = summary->next;
3342 }
3343
3344 sibling_node = sibling_node->next;
3345 }
3346
3347 if (node == info->tag_root)
3348 break;
3349
3350 node = node->parent;
3351 }
3352
3353 /*
3354 * An odd number of toggles means that the tag is present at the
3355 * given point.
3356 */
3357
3358 return (toggles & 1) != 0;
3359}
3360
3361/* FIXME this function is far too slow, for no good reason. */
3362gboolean
3363_gtk_text_line_char_has_tag (GtkTextLine *line,
3364 GtkTextBTree *tree,
3365 int char_in_line,
3366 GtkTextTag *tag)
3367{
3368 GtkTextLineSegment *toggle_seg;
3369
3370 g_return_val_if_fail (line != NULL, FALSE);
3371
3372 /*
3373 * Check for toggles for the tag in the line but before
3374 * the char. If there is one, its type indicates whether or
3375 * not the character is tagged.
3376 */
3377
3378 toggle_seg = find_toggle_segment_before_char (line, char_in_line, tag);
3379
3380 if (toggle_seg != NULL)
3381 return (toggle_seg->type == &gtk_text_toggle_on_type);
3382 else
3383 return find_toggle_outside_current_line (line, tree, tag);
3384}
3385
3386gboolean
3387_gtk_text_line_byte_has_tag (GtkTextLine *line,
3388 GtkTextBTree *tree,
3389 int byte_in_line,
3390 GtkTextTag *tag)
3391{
3392 GtkTextLineSegment *toggle_seg;
3393
3394 g_return_val_if_fail (line != NULL, FALSE);
3395
3396 /*
3397 * Check for toggles for the tag in the line but before
3398 * the char. If there is one, its type indicates whether or
3399 * not the character is tagged.
3400 */
3401
3402 toggle_seg = find_toggle_segment_before_byte (line, byte_in_line, tag);
3403
3404 if (toggle_seg != NULL)
3405 return (toggle_seg->type == &gtk_text_toggle_on_type);
3406 else
3407 return find_toggle_outside_current_line (line, tree, tag);
3408}
3409
3410gboolean
3411_gtk_text_line_is_last (GtkTextLine *line,
3412 GtkTextBTree *tree)
3413{
3414 return line == get_last_line (tree);
3415}
3416
3417static void
3418ensure_end_iter_line (GtkTextBTree *tree)
3419{
3420 if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
3421 {
3422 int real_line;
3423
3424 /* n_lines is without the magic line at the end */
3425 g_assert (_gtk_text_btree_line_count (tree) >= 1);
3426
3427 tree->end_iter_line = _gtk_text_btree_get_line_no_last (tree, line_number: -1, real_line_number: &real_line);
3428
3429 tree->end_iter_line_stamp = tree->chars_changed_stamp;
3430 }
3431}
3432
3433static void
3434ensure_end_iter_segment (GtkTextBTree *tree)
3435{
3436 if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
3437 {
3438 GtkTextLineSegment *seg;
3439 GtkTextLineSegment *last_with_chars;
3440
3441 ensure_end_iter_line (tree);
3442
3443 last_with_chars = NULL;
3444
3445 seg = tree->end_iter_line->segments;
3446 while (seg != NULL)
3447 {
3448 if (seg->char_count > 0)
3449 last_with_chars = seg;
3450 seg = seg->next;
3451 }
3452 g_assert (last_with_chars);
3453
3454 tree->end_iter_segment = last_with_chars;
3455
3456 /* We know the last char in the last line is '\n' */
3457 tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
3458 tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
3459
3460 tree->end_iter_segment_stamp = tree->segments_changed_stamp;
3461
3462 g_assert (tree->end_iter_segment->type == &gtk_text_char_type);
3463 g_assert (tree->end_iter_segment->body.chars[tree->end_iter_segment_byte_index] == '\n');
3464 }
3465}
3466
3467gboolean
3468_gtk_text_line_contains_end_iter (GtkTextLine *line,
3469 GtkTextBTree *tree)
3470{
3471 ensure_end_iter_line (tree);
3472
3473 return line == tree->end_iter_line;
3474}
3475
3476gboolean
3477_gtk_text_btree_is_end (GtkTextBTree *tree,
3478 GtkTextLine *line,
3479 GtkTextLineSegment *seg,
3480 int byte_index,
3481 int char_offset)
3482{
3483 g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
3484
3485 /* Do this first to avoid walking segments in most cases */
3486 if (!_gtk_text_line_contains_end_iter (line, tree))
3487 return FALSE;
3488
3489 ensure_end_iter_segment (tree);
3490
3491 if (seg != tree->end_iter_segment)
3492 return FALSE;
3493
3494 if (byte_index >= 0)
3495 return byte_index == tree->end_iter_segment_byte_index;
3496 else
3497 return char_offset == tree->end_iter_segment_char_offset;
3498}
3499
3500GtkTextLine*
3501_gtk_text_line_next (GtkTextLine *line)
3502{
3503 GtkTextBTreeNode *node;
3504
3505 if (line->next != NULL)
3506 return line->next;
3507 else
3508 {
3509 /*
3510 * This was the last line associated with the particular parent
3511 * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
3512 * then search down from that GtkTextBTreeNode to find the first
3513 * line.
3514 */
3515
3516 node = line->parent;
3517 while (node != NULL && node->next == NULL)
3518 node = node->parent;
3519
3520 if (node == NULL)
3521 return NULL;
3522
3523 node = node->next;
3524 while (node->level > 0)
3525 {
3526 node = node->children.node;
3527 }
3528
3529 g_assert (node->children.line != line);
3530
3531 return node->children.line;
3532 }
3533}
3534
3535GtkTextLine*
3536_gtk_text_line_next_excluding_last (GtkTextLine *line)
3537{
3538 GtkTextLine *next;
3539
3540 next = _gtk_text_line_next (line);
3541
3542 /* If we were on the end iter line, we can't go to
3543 * the last line
3544 */
3545 if (next && next->next == NULL && /* these checks are optimization only */
3546 _gtk_text_line_next (line: next) == NULL)
3547 return NULL;
3548
3549 return next;
3550}
3551
3552GtkTextLine*
3553_gtk_text_line_previous (GtkTextLine *line)
3554{
3555 GtkTextBTreeNode *node;
3556 GtkTextBTreeNode *node2;
3557 GtkTextLine *prev;
3558
3559 /*
3560 * Find the line under this GtkTextBTreeNode just before the starting line.
3561 */
3562 prev = line->parent->children.line; /* First line at leaf */
3563 while (prev != line)
3564 {
3565 if (prev->next == line)
3566 return prev;
3567
3568 prev = prev->next;
3569
3570 if (prev == NULL)
3571 g_error ("gtk_text_btree_previous_line ran out of lines");
3572 }
3573
3574 /*
3575 * This was the first line associated with the particular parent
3576 * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
3577 * then search down from that GtkTextBTreeNode to find its last line.
3578 */
3579 for (node = line->parent; ; node = node->parent)
3580 {
3581 if (node == NULL || node->parent == NULL)
3582 return NULL;
3583 else if (node != node->parent->children.node)
3584 break;
3585 }
3586
3587 for (node2 = node->parent->children.node; ;
3588 node2 = node2->children.node)
3589 {
3590 while (node2->next != node)
3591 node2 = node2->next;
3592
3593 if (node2->level == 0)
3594 break;
3595
3596 node = NULL;
3597 }
3598
3599 for (prev = node2->children.line ; ; prev = prev->next)
3600 {
3601 if (prev->next == NULL)
3602 return prev;
3603 }
3604
3605 g_assert_not_reached ();
3606 return NULL;
3607}
3608
3609
3610GtkTextLineData*
3611_gtk_text_line_data_new (GtkTextLayout *layout,
3612 GtkTextLine *line)
3613{
3614 GtkTextLineData *line_data;
3615
3616 line_data = g_slice_new (GtkTextLineData);
3617
3618 line_data->view_id = layout;
3619 line_data->next = NULL;
3620 line_data->width = 0;
3621 line_data->height = 0;
3622 line_data->top_ink = 0;
3623 line_data->bottom_ink = 0;
3624 line_data->valid = FALSE;
3625
3626 return line_data;
3627}
3628
3629void
3630_gtk_text_line_add_data (GtkTextLine *line,
3631 GtkTextLineData *data)
3632{
3633 g_return_if_fail (line != NULL);
3634 g_return_if_fail (data != NULL);
3635 g_return_if_fail (data->view_id != NULL);
3636
3637 if (line->views)
3638 {
3639 data->next = line->views;
3640 line->views = data;
3641 }
3642 else
3643 {
3644 line->views = data;
3645 }
3646}
3647
3648gpointer
3649_gtk_text_line_remove_data (GtkTextLine *line,
3650 gpointer view_id)
3651{
3652 GtkTextLineData *prev;
3653 GtkTextLineData *iter;
3654
3655 g_return_val_if_fail (line != NULL, NULL);
3656 g_return_val_if_fail (view_id != NULL, NULL);
3657
3658 prev = NULL;
3659 iter = line->views;
3660 while (iter != NULL)
3661 {
3662 if (iter->view_id == view_id)
3663 break;
3664 prev = iter;
3665 iter = iter->next;
3666 }
3667
3668 if (iter)
3669 {
3670 if (prev)
3671 prev->next = iter->next;
3672 else
3673 line->views = iter->next;
3674
3675 return iter;
3676 }
3677 else
3678 return NULL;
3679}
3680
3681gpointer
3682_gtk_text_line_get_data (GtkTextLine *line,
3683 gpointer view_id)
3684{
3685 GtkTextLineData *iter;
3686
3687 g_return_val_if_fail (line != NULL, NULL);
3688 g_return_val_if_fail (view_id != NULL, NULL);
3689
3690 iter = line->views;
3691 while (iter != NULL)
3692 {
3693 if (iter->view_id == view_id)
3694 break;
3695 iter = iter->next;
3696 }
3697
3698 return iter;
3699}
3700
3701void
3702_gtk_text_line_invalidate_wrap (GtkTextLine *line,
3703 GtkTextLineData *ld)
3704{
3705 /* For now this is totally unoptimized. FIXME?
3706
3707 We could probably optimize the case where the width removed
3708 is less than the max width for the parent node,
3709 and the case where the height is unchanged when we re-wrap.
3710 */
3711
3712 g_return_if_fail (ld != NULL);
3713
3714 ld->valid = FALSE;
3715 gtk_text_btree_node_invalidate_upward (node: line->parent, view_id: ld->view_id);
3716}
3717
3718int
3719_gtk_text_line_char_count (GtkTextLine *line)
3720{
3721 GtkTextLineSegment *seg;
3722 int size;
3723
3724 size = 0;
3725 seg = line->segments;
3726 while (seg != NULL)
3727 {
3728 size += seg->char_count;
3729 seg = seg->next;
3730 }
3731 return size;
3732}
3733
3734int
3735_gtk_text_line_byte_count (GtkTextLine *line)
3736{
3737 GtkTextLineSegment *seg;
3738 int size;
3739
3740 size = 0;
3741 seg = line->segments;
3742 while (seg != NULL)
3743 {
3744 size += seg->byte_count;
3745 seg = seg->next;
3746 }
3747
3748 return size;
3749}
3750
3751int
3752_gtk_text_line_char_index (GtkTextLine *target_line)
3753{
3754 GtkTextBTreeNode *node_stack[64];
3755 GtkTextBTreeNode *iter;
3756 GtkTextLine *line;
3757 int num_chars;
3758 int tos = 0;
3759
3760 /* Push all our parent nodes onto a stack */
3761 iter = target_line->parent;
3762
3763 g_assert (iter != NULL);
3764
3765 while (iter != NULL && tos < 64)
3766 {
3767 node_stack[tos++] = iter;
3768 iter = iter->parent;
3769 }
3770
3771 tos--;
3772
3773 /* Check that we have the root node on top of the stack. */
3774 g_assert (node_stack != NULL &&
3775 node_stack[tos] != NULL &&
3776 node_stack[tos]->parent == NULL);
3777
3778 /* Add up chars in all nodes before the nodes in our stack.
3779 */
3780
3781 num_chars = 0;
3782 while (tos >= 0)
3783 {
3784 GtkTextBTreeNode *child_iter;
3785
3786 iter = node_stack[tos];
3787
3788 if (iter->level == 0)
3789 {
3790 /* stack should be empty when we're on the last node */
3791 g_assert (tos == 0);
3792 break; /* Our children are now lines */
3793 }
3794
3795 tos--;
3796
3797 /* Add up chars before us in the tree */
3798 child_iter = iter->children.node;
3799 while (child_iter != node_stack[tos])
3800 {
3801 g_assert (child_iter != NULL);
3802
3803 num_chars += child_iter->num_chars;
3804 child_iter = child_iter->next;
3805 }
3806 }
3807
3808 g_assert (iter != NULL);
3809 g_assert (iter == target_line->parent);
3810
3811 /* Since we don't store char counts in lines, only in segments, we
3812 have to iterate over the lines adding up segment char counts
3813 until we find our line. */
3814 line = iter->children.line;
3815 while (line != target_line)
3816 {
3817 g_assert (line != NULL);
3818
3819 num_chars += _gtk_text_line_char_count (line);
3820 line = line->next;
3821 }
3822
3823 g_assert (line == target_line);
3824
3825 return num_chars;
3826}
3827
3828GtkTextLineSegment*
3829_gtk_text_line_byte_to_segment (GtkTextLine *line,
3830 int byte_offset,
3831 int *seg_offset)
3832{
3833 GtkTextLineSegment *seg;
3834 int offset;
3835
3836 g_return_val_if_fail (line != NULL, NULL);
3837
3838 offset = byte_offset;
3839 seg = line->segments;
3840
3841 while (offset >= seg->byte_count)
3842 {
3843 offset -= seg->byte_count;
3844 seg = seg->next;
3845 g_assert (seg != NULL); /* means an invalid byte index */
3846 }
3847
3848 if (seg_offset)
3849 *seg_offset = offset;
3850
3851 return seg;
3852}
3853
3854GtkTextLineSegment*
3855_gtk_text_line_char_to_segment (GtkTextLine *line,
3856 int char_offset,
3857 int *seg_offset)
3858{
3859 GtkTextLineSegment *seg;
3860 int offset;
3861
3862 g_return_val_if_fail (line != NULL, NULL);
3863
3864 offset = char_offset;
3865 seg = line->segments;
3866
3867 while (offset >= seg->char_count)
3868 {
3869 offset -= seg->char_count;
3870 seg = seg->next;
3871 g_assert (seg != NULL); /* means an invalid char index */
3872 }
3873
3874 if (seg_offset)
3875 *seg_offset = offset;
3876
3877 return seg;
3878}
3879
3880GtkTextLineSegment*
3881_gtk_text_line_byte_to_any_segment (GtkTextLine *line,
3882 int byte_offset,
3883 int *seg_offset)
3884{
3885 GtkTextLineSegment *seg;
3886 int offset;
3887
3888 g_return_val_if_fail (line != NULL, NULL);
3889
3890 offset = byte_offset;
3891 seg = line->segments;
3892
3893 while (offset > 0 && offset >= seg->byte_count)
3894 {
3895 offset -= seg->byte_count;
3896 seg = seg->next;
3897 g_assert (seg != NULL); /* means an invalid byte index */
3898 }
3899
3900 if (seg_offset)
3901 *seg_offset = offset;
3902
3903 return seg;
3904}
3905
3906GtkTextLineSegment*
3907_gtk_text_line_char_to_any_segment (GtkTextLine *line,
3908 int char_offset,
3909 int *seg_offset)
3910{
3911 GtkTextLineSegment *seg;
3912 int offset;
3913
3914 g_return_val_if_fail (line != NULL, NULL);
3915
3916 offset = char_offset;
3917 seg = line->segments;
3918
3919 while (offset > 0 && offset >= seg->char_count)
3920 {
3921 offset -= seg->char_count;
3922 seg = seg->next;
3923 g_assert (seg != NULL); /* means an invalid byte index */
3924 }
3925
3926 if (seg_offset)
3927 *seg_offset = offset;
3928
3929 return seg;
3930}
3931
3932int
3933_gtk_text_line_byte_to_char (GtkTextLine *line,
3934 int byte_offset)
3935{
3936 int char_offset;
3937 GtkTextLineSegment *seg;
3938
3939 g_return_val_if_fail (line != NULL, 0);
3940 g_return_val_if_fail (byte_offset >= 0, 0);
3941
3942 char_offset = 0;
3943 seg = line->segments;
3944 while (byte_offset >= seg->byte_count) /* while (we need to go farther than
3945 the next segment) */
3946 {
3947 byte_offset -= seg->byte_count;
3948 char_offset += seg->char_count;
3949 seg = seg->next;
3950 g_assert (seg != NULL); /* our byte_index was bogus if this happens */
3951 }
3952
3953 g_assert (seg != NULL);
3954
3955 /* Now byte_offset is the offset into the current segment,
3956 and char_offset is the start of the current segment.
3957 Optimize the case where no chars use > 1 byte */
3958 if (seg->byte_count == seg->char_count)
3959 return char_offset + byte_offset;
3960 else
3961 {
3962 if (seg->type == &gtk_text_char_type)
3963 return char_offset + g_utf8_strlen (p: seg->body.chars, max: byte_offset);
3964 else
3965 {
3966 g_assert (seg->char_count == 1);
3967 g_assert (byte_offset == 0);
3968
3969 return char_offset;
3970 }
3971 }
3972}
3973
3974int
3975_gtk_text_line_char_to_byte (GtkTextLine *line,
3976 int char_offset)
3977{
3978 g_warning ("FIXME not implemented");
3979
3980 return 0;
3981}
3982
3983/* FIXME sync with char_locate (or figure out a clean
3984 way to merge the two functions) */
3985gboolean
3986_gtk_text_line_byte_locate (GtkTextLine *line,
3987 int byte_offset,
3988 GtkTextLineSegment **segment,
3989 GtkTextLineSegment **any_segment,
3990 int *seg_byte_offset,
3991 int *line_byte_offset)
3992{
3993 GtkTextLineSegment *seg;
3994 GtkTextLineSegment *after_last_indexable;
3995 GtkTextLineSegment *last_indexable;
3996 int offset;
3997 int bytes_in_line;
3998
3999 g_return_val_if_fail (line != NULL, FALSE);
4000 g_return_val_if_fail (byte_offset >= 0, FALSE);
4001
4002 *segment = NULL;
4003 *any_segment = NULL;
4004 bytes_in_line = 0;
4005
4006 offset = byte_offset;
4007
4008 last_indexable = NULL;
4009 after_last_indexable = line->segments;
4010 seg = line->segments;
4011
4012 /* The loop ends when we're inside a segment;
4013 last_indexable refers to the last segment
4014 we passed entirely. */
4015 while (seg && offset >= seg->byte_count)
4016 {
4017 if (seg->char_count > 0)
4018 {
4019 offset -= seg->byte_count;
4020 bytes_in_line += seg->byte_count;
4021 last_indexable = seg;
4022 after_last_indexable = last_indexable->next;
4023 }
4024
4025 seg = seg->next;
4026 }
4027
4028 if (seg == NULL)
4029 {
4030 /* We went off the end of the line */
4031 if (offset != 0)
4032 g_warning ("%s: byte index off the end of the line", G_STRLOC);
4033
4034 return FALSE;
4035 }
4036 else
4037 {
4038 *segment = seg;
4039 if (after_last_indexable != NULL)
4040 *any_segment = after_last_indexable;
4041 else
4042 *any_segment = *segment;
4043 }
4044
4045 /* Override any_segment if we're in the middle of a segment. */
4046 if (offset > 0)
4047 *any_segment = *segment;
4048
4049 *seg_byte_offset = offset;
4050
4051 g_assert (*segment != NULL);
4052 g_assert (*any_segment != NULL);
4053 g_assert (*seg_byte_offset < (*segment)->byte_count);
4054
4055 *line_byte_offset = bytes_in_line + *seg_byte_offset;
4056
4057 return TRUE;
4058}
4059
4060/* FIXME sync with byte_locate (or figure out a clean
4061 way to merge the two functions) */
4062gboolean
4063_gtk_text_line_char_locate (GtkTextLine *line,
4064 int char_offset,
4065 GtkTextLineSegment **segment,
4066 GtkTextLineSegment **any_segment,
4067 int *seg_char_offset,
4068 int *line_char_offset)
4069{
4070 GtkTextLineSegment *seg;
4071 GtkTextLineSegment *after_last_indexable;
4072 GtkTextLineSegment *last_indexable;
4073 int offset;
4074 int chars_in_line;
4075
4076 g_return_val_if_fail (line != NULL, FALSE);
4077 g_return_val_if_fail (char_offset >= 0, FALSE);
4078
4079 *segment = NULL;
4080 *any_segment = NULL;
4081 chars_in_line = 0;
4082
4083 offset = char_offset;
4084
4085 last_indexable = NULL;
4086 after_last_indexable = line->segments;
4087 seg = line->segments;
4088
4089 /* The loop ends when we're inside a segment;
4090 last_indexable refers to the last segment
4091 we passed entirely. */
4092 while (seg && offset >= seg->char_count)
4093 {
4094 if (seg->char_count > 0)
4095 {
4096 offset -= seg->char_count;
4097 chars_in_line += seg->char_count;
4098 last_indexable = seg;
4099 after_last_indexable = last_indexable->next;
4100 }
4101
4102 seg = seg->next;
4103 }
4104
4105 if (seg == NULL)
4106 {
4107 /* end of the line */
4108 if (offset != 0)
4109 g_warning ("%s: char offset off the end of the line", G_STRLOC);
4110
4111 return FALSE;
4112 }
4113 else
4114 {
4115 *segment = seg;
4116 if (after_last_indexable != NULL)
4117 *any_segment = after_last_indexable;
4118 else
4119 *any_segment = *segment;
4120 }
4121
4122 /* Override any_segment if we're in the middle of a segment. */
4123 if (offset > 0)
4124 *any_segment = *segment;
4125
4126 *seg_char_offset = offset;
4127
4128 g_assert (*segment != NULL);
4129 g_assert (*any_segment != NULL);
4130 g_assert (*seg_char_offset < (*segment)->char_count);
4131
4132 *line_char_offset = chars_in_line + *seg_char_offset;
4133
4134 return TRUE;
4135}
4136
4137void
4138_gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
4139 int byte_offset,
4140 int *line_char_offset,
4141 int *seg_char_offset)
4142{
4143 GtkTextLineSegment *seg;
4144 int offset;
4145
4146 g_return_if_fail (line != NULL);
4147 g_return_if_fail (byte_offset >= 0);
4148
4149 *line_char_offset = 0;
4150
4151 offset = byte_offset;
4152 seg = line->segments;
4153
4154 while (offset >= seg->byte_count)
4155 {
4156 offset -= seg->byte_count;
4157 *line_char_offset += seg->char_count;
4158 seg = seg->next;
4159 g_assert (seg != NULL); /* means an invalid char offset */
4160 }
4161
4162 g_assert (seg->char_count > 0); /* indexable. */
4163
4164 /* offset is now the number of bytes into the current segment we
4165 * want to go. Count chars into the current segment.
4166 */
4167
4168 if (seg->type == &gtk_text_char_type)
4169 {
4170 *seg_char_offset = g_utf8_strlen (p: seg->body.chars, max: offset);
4171
4172 g_assert (*seg_char_offset < seg->char_count);
4173
4174 *line_char_offset += *seg_char_offset;
4175 }
4176 else
4177 {
4178 g_assert (offset == 0);
4179 *seg_char_offset = 0;
4180 }
4181}
4182
4183void
4184_gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
4185 int char_offset,
4186 int *line_byte_offset,
4187 int *seg_byte_offset)
4188{
4189 GtkTextLineSegment *seg;
4190 int offset;
4191
4192 g_return_if_fail (line != NULL);
4193 g_return_if_fail (char_offset >= 0);
4194
4195 *line_byte_offset = 0;
4196
4197 offset = char_offset;
4198 seg = line->segments;
4199
4200 while (offset >= seg->char_count)
4201 {
4202 offset -= seg->char_count;
4203 *line_byte_offset += seg->byte_count;
4204 seg = seg->next;
4205 g_assert (seg != NULL); /* means an invalid char offset */
4206 }
4207
4208 g_assert (seg->char_count > 0); /* indexable. */
4209
4210 /* offset is now the number of chars into the current segment we
4211 want to go. Count bytes into the current segment. */
4212
4213 if (seg->type == &gtk_text_char_type)
4214 {
4215 const char *p;
4216
4217 /* if in the last fourth of the segment walk backwards */
4218 if (seg->char_count - offset < seg->char_count / 4)
4219 p = g_utf8_offset_to_pointer (str: seg->body.chars + seg->byte_count,
4220 offset: offset - seg->char_count);
4221 else
4222 p = g_utf8_offset_to_pointer (str: seg->body.chars, offset);
4223
4224 *seg_byte_offset = p - seg->body.chars;
4225
4226 g_assert (*seg_byte_offset < seg->byte_count);
4227
4228 *line_byte_offset += *seg_byte_offset;
4229 }
4230 else
4231 {
4232 g_assert (offset == 0);
4233 *seg_byte_offset = 0;
4234 }
4235}
4236
4237static int
4238node_compare (GtkTextBTreeNode *lhs,
4239 GtkTextBTreeNode *rhs)
4240{
4241 GtkTextBTreeNode *iter;
4242 GtkTextBTreeNode *node;
4243 GtkTextBTreeNode *common_parent;
4244 GtkTextBTreeNode *parent_of_lower;
4245 GtkTextBTreeNode *parent_of_higher;
4246 gboolean lhs_is_lower;
4247 GtkTextBTreeNode *lower;
4248 GtkTextBTreeNode *higher;
4249
4250 /* This function assumes that lhs and rhs are not underneath each
4251 * other.
4252 */
4253
4254 if (lhs == rhs)
4255 return 0;
4256
4257 if (lhs->level < rhs->level)
4258 {
4259 lhs_is_lower = TRUE;
4260 lower = lhs;
4261 higher = rhs;
4262 }
4263 else
4264 {
4265 lhs_is_lower = FALSE;
4266 lower = rhs;
4267 higher = lhs;
4268 }
4269
4270 /* Algorithm: find common parent of lhs/rhs. Save the child nodes
4271 * of the common parent we used to reach the common parent; the
4272 * ordering of these child nodes in the child list is the ordering
4273 * of lhs and rhs.
4274 */
4275
4276 /* Get on the same level (may be on same level already) */
4277 node = lower;
4278 while (node->level < higher->level)
4279 node = node->parent;
4280
4281 g_assert (node->level == higher->level);
4282
4283 g_assert (node != higher); /* Happens if lower is underneath higher */
4284
4285 /* Go up until we have two children with a common parent.
4286 */
4287 parent_of_lower = node;
4288 parent_of_higher = higher;
4289
4290 while (parent_of_lower->parent != parent_of_higher->parent)
4291 {
4292 parent_of_lower = parent_of_lower->parent;
4293 parent_of_higher = parent_of_higher->parent;
4294 }
4295
4296 g_assert (parent_of_lower->parent == parent_of_higher->parent);
4297
4298 common_parent = parent_of_lower->parent;
4299
4300 g_assert (common_parent != NULL);
4301
4302 /* See which is first in the list of common_parent's children */
4303 iter = common_parent->children.node;
4304 while (iter != NULL)
4305 {
4306 if (iter == parent_of_higher)
4307 {
4308 /* higher is less than lower */
4309
4310 if (lhs_is_lower)
4311 return 1; /* lhs > rhs */
4312 else
4313 return -1;
4314 }
4315 else if (iter == parent_of_lower)
4316 {
4317 /* lower is less than higher */
4318
4319 if (lhs_is_lower)
4320 return -1; /* lhs < rhs */
4321 else
4322 return 1;
4323 }
4324
4325 iter = iter->next;
4326 }
4327
4328 g_assert_not_reached ();
4329 return 0;
4330}
4331
4332/* remember that tag == NULL means "any tag" */
4333GtkTextLine*
4334_gtk_text_line_next_could_contain_tag (GtkTextLine *line,
4335 GtkTextBTree *tree,
4336 GtkTextTag *tag)
4337{
4338 GtkTextBTreeNode *node;
4339 GtkTextTagInfo *info;
4340 gboolean below_tag_root;
4341
4342 g_return_val_if_fail (line != NULL, NULL);
4343
4344#ifdef G_ENABLE_DEBUG
4345 if (GTK_DEBUG_CHECK (TEXT))
4346 _gtk_text_btree_check (tree);
4347#endif
4348
4349 if (tag == NULL)
4350 {
4351 /* Right now we can only offer linear-search if the user wants
4352 * to know about any tag toggle at all.
4353 */
4354 return _gtk_text_line_next_excluding_last (line);
4355 }
4356
4357 /* Our tag summaries only have node precision, not line
4358 * precision. This means that if any line under a node could contain a
4359 * tag, then any of the others could also contain a tag.
4360 *
4361 * In the future we could have some mechanism to keep track of how
4362 * many toggles we've found under a node so far, since we have a
4363 * count of toggles under the node. But for now I'm going with KISS.
4364 */
4365
4366 /* return same-node line, if any. */
4367 if (line->next)
4368 return line->next;
4369
4370 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4371 if (info == NULL)
4372 return NULL;
4373
4374 if (info->tag_root == NULL)
4375 return NULL;
4376
4377 if (info->tag_root == line->parent)
4378 return NULL; /* we were at the last line under the tag root */
4379
4380 /* We need to go up out of this node, and on to the next one with
4381 toggles for the target tag. If we're below the tag root, we need to
4382 find the next node below the tag root that has tag summaries. If
4383 we're not below the tag root, we need to see if the tag root is
4384 after us in the tree, and if so, return the first line underneath
4385 the tag root. */
4386
4387 node = line->parent;
4388 below_tag_root = FALSE;
4389 while (node != NULL)
4390 {
4391 if (node == info->tag_root)
4392 {
4393 below_tag_root = TRUE;
4394 break;
4395 }
4396
4397 node = node->parent;
4398 }
4399
4400 if (below_tag_root)
4401 {
4402 node = line->parent;
4403 while (node != info->tag_root)
4404 {
4405 if (node->next == NULL)
4406 node = node->parent;
4407 else
4408 {
4409 node = node->next;
4410
4411 if (gtk_text_btree_node_has_tag (node, tag))
4412 goto found;
4413 }
4414 }
4415 return NULL;
4416 }
4417 else
4418 {
4419 int ordering;
4420
4421 ordering = node_compare (lhs: line->parent, rhs: info->tag_root);
4422
4423 if (ordering < 0)
4424 {
4425 /* Tag root is ahead of us, so search there. */
4426 node = info->tag_root;
4427 goto found;
4428 }
4429 else
4430 {
4431 /* Tag root is after us, so no more lines that
4432 * could contain the tag.
4433 */
4434 return NULL;
4435 }
4436
4437 g_assert_not_reached ();
4438 }
4439
4440 found:
4441
4442 g_assert (node != NULL);
4443
4444 /* We have to find the first sub-node of this node that contains
4445 * the target tag.
4446 */
4447
4448 while (node->level > 0)
4449 {
4450 node = node->children.node;
4451 while (node != NULL)
4452 {
4453 if (gtk_text_btree_node_has_tag (node, tag))
4454 break;
4455 node = node->next;
4456 }
4457 g_assert (node != NULL); /* If this fails, it likely means an
4458 incorrect tag summary led us on a
4459 wild goose chase down this branch of
4460 the tree. */
4461 }
4462
4463 g_assert (node != NULL);
4464 g_assert (node->level == 0);
4465
4466 return node->children.line;
4467}
4468
4469static GtkTextLine*
4470prev_line_under_node (GtkTextBTreeNode *node,
4471 GtkTextLine *line)
4472{
4473 GtkTextLine *prev;
4474
4475 prev = node->children.line;
4476
4477 g_assert (prev);
4478
4479 if (prev != line)
4480 {
4481 while (prev->next != line)
4482 prev = prev->next;
4483
4484 return prev;
4485 }
4486
4487 return NULL;
4488}
4489
4490GtkTextLine*
4491_gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
4492 GtkTextBTree *tree,
4493 GtkTextTag *tag)
4494{
4495 GtkTextBTreeNode *node;
4496 GtkTextBTreeNode *found_node = NULL;
4497 GtkTextTagInfo *info;
4498 gboolean below_tag_root;
4499 GtkTextLine *prev;
4500 GtkTextBTreeNode *line_ancestor;
4501 GtkTextBTreeNode *line_ancestor_parent;
4502
4503 /* See next_could_contain_tag () for more extensive comments
4504 * on what's going on here.
4505 */
4506
4507 g_return_val_if_fail (line != NULL, NULL);
4508
4509#ifdef G_ENABLE_DEBUG
4510 if (GTK_DEBUG_CHECK (TEXT))
4511 _gtk_text_btree_check (tree);
4512#endif
4513
4514 if (tag == NULL)
4515 {
4516 /* Right now we can only offer linear-search if the user wants
4517 * to know about any tag toggle at all.
4518 */
4519 return _gtk_text_line_previous (line);
4520 }
4521
4522 /* Return same-node line, if any. */
4523 prev = prev_line_under_node (node: line->parent, line);
4524 if (prev)
4525 return prev;
4526
4527 info = gtk_text_btree_get_existing_tag_info (tree, tag);
4528 if (info == NULL)
4529 return NULL;
4530
4531 if (info->tag_root == NULL)
4532 return NULL;
4533
4534 if (info->tag_root == line->parent)
4535 return NULL; /* we were at the first line under the tag root */
4536
4537 /* Are we below the tag root */
4538 node = line->parent;
4539 below_tag_root = FALSE;
4540 while (node != NULL)
4541 {
4542 if (node == info->tag_root)
4543 {
4544 below_tag_root = TRUE;
4545 break;
4546 }
4547
4548 node = node->parent;
4549 }
4550
4551 if (below_tag_root)
4552 {
4553 /* Look for a previous node under this tag root that has our
4554 * tag.
4555 */
4556
4557 /* this assertion holds because line->parent is not the
4558 * tag root, we are below the tag root, and the tag
4559 * root exists.
4560 */
4561 g_assert (line->parent->parent != NULL);
4562
4563 line_ancestor = line->parent;
4564 line_ancestor_parent = line->parent->parent;
4565
4566 while (line_ancestor != info->tag_root)
4567 {
4568 GSList *child_nodes = NULL;
4569 GSList *tmp;
4570
4571 /* Create reverse-order list of nodes before
4572 * line_ancestor
4573 */
4574 if (line_ancestor_parent != NULL)
4575 node = line_ancestor_parent->children.node;
4576 else
4577 node = line_ancestor;
4578
4579 while (node != line_ancestor && node != NULL)
4580 {
4581 child_nodes = g_slist_prepend (list: child_nodes, data: node);
4582
4583 node = node->next;
4584 }
4585
4586 /* Try to find a node with our tag on it in the list */
4587 tmp = child_nodes;
4588 while (tmp != NULL)
4589 {
4590 GtkTextBTreeNode *this_node = tmp->data;
4591
4592 g_assert (this_node != line_ancestor);
4593
4594 if (gtk_text_btree_node_has_tag (node: this_node, tag))
4595 {
4596 found_node = this_node;
4597 g_slist_free (list: child_nodes);
4598 goto found;
4599 }
4600
4601 tmp = tmp->next;
4602 }
4603
4604 g_slist_free (list: child_nodes);
4605
4606 /* Didn't find anything on this level; go up one level. */
4607 line_ancestor = line_ancestor_parent;
4608 line_ancestor_parent = line_ancestor->parent;
4609 }
4610
4611 /* No dice. */
4612 return NULL;
4613 }
4614 else
4615 {
4616 int ordering;
4617
4618 ordering = node_compare (lhs: line->parent, rhs: info->tag_root);
4619
4620 if (ordering < 0)
4621 {
4622 /* Tag root is ahead of us, so no more lines
4623 * with this tag.
4624 */
4625 return NULL;
4626 }
4627 else
4628 {
4629 /* Tag root is after us, so grab last tagged
4630 * line underneath the tag root.
4631 */
4632 found_node = info->tag_root;
4633 goto found;
4634 }
4635
4636 g_assert_not_reached ();
4637 }
4638
4639 found:
4640
4641 g_assert (found_node != NULL);
4642
4643 /* We have to find the last sub-node of this node that contains
4644 * the target tag.
4645 */
4646 node = found_node;
4647
4648 while (node->level > 0)
4649 {
4650 GSList *child_nodes = NULL;
4651 GSList *iter;
4652 g_assert (node != NULL); /* If this fails, it likely means an
4653 incorrect tag summary led us on a
4654 wild goose chase down this branch of
4655 the tree. */
4656
4657 node = node->children.node;
4658 while (node != NULL)
4659 {
4660 child_nodes = g_slist_prepend (list: child_nodes, data: node);
4661 node = node->next;
4662 }
4663
4664 node = NULL; /* detect failure to find a child node. */
4665
4666 iter = child_nodes;
4667 while (iter != NULL)
4668 {
4669 if (gtk_text_btree_node_has_tag (node: iter->data, tag))
4670 {
4671 /* recurse into this node. */
4672 node = iter->data;
4673 break;
4674 }
4675
4676 iter = iter->next;
4677 }
4678
4679 g_slist_free (list: child_nodes);
4680
4681 g_assert (node != NULL);
4682 }
4683
4684 g_assert (node != NULL);
4685 g_assert (node->level == 0);
4686
4687 /* this assertion is correct, but slow. */
4688 /* g_assert (node_compare (node, line->parent) < 0); */
4689
4690 /* Return last line in this node. */
4691
4692 prev = node->children.line;
4693 while (prev->next)
4694 prev = prev->next;
4695
4696 return prev;
4697}
4698
4699/*
4700 * Non-public function implementations
4701 */
4702
4703static void
4704summary_list_destroy (Summary *summary)
4705{
4706 g_slice_free_chain (Summary, summary, next);
4707}
4708
4709static GtkTextLine*
4710get_last_line (GtkTextBTree *tree)
4711{
4712 if (tree->last_line_stamp != tree->chars_changed_stamp)
4713 {
4714 int n_lines;
4715 GtkTextLine *line;
4716 int real_line;
4717
4718 n_lines = _gtk_text_btree_line_count (tree);
4719
4720 g_assert (n_lines >= 1); /* num_lines doesn't return bogus last line. */
4721
4722 line = _gtk_text_btree_get_line (tree, line_number: n_lines, real_line_number: &real_line);
4723
4724 tree->last_line_stamp = tree->chars_changed_stamp;
4725 tree->last_line = line;
4726 }
4727
4728 return tree->last_line;
4729}
4730
4731/*
4732 * Lines
4733 */
4734
4735static GtkTextLine*
4736gtk_text_line_new (void)
4737{
4738 GtkTextLine *line;
4739
4740 line = g_slice_new0 (GtkTextLine);
4741 line->dir_strong = PANGO_DIRECTION_NEUTRAL;
4742 line->dir_propagated_forward = PANGO_DIRECTION_NEUTRAL;
4743 line->dir_propagated_back = PANGO_DIRECTION_NEUTRAL;
4744
4745 return line;
4746}
4747
4748static void
4749gtk_text_line_destroy (GtkTextBTree *tree, GtkTextLine *line)
4750{
4751 GtkTextLineData *ld;
4752 GtkTextLineData *next;
4753
4754 g_return_if_fail (line != NULL);
4755
4756 ld = line->views;
4757 while (ld != NULL)
4758 {
4759 BTreeView *view;
4760
4761 view = gtk_text_btree_get_view (tree, view_id: ld->view_id);
4762
4763 g_assert (view != NULL);
4764
4765 next = ld->next;
4766 gtk_text_layout_free_line_data (layout: view->layout, line, line_data: ld);
4767
4768 ld = next;
4769 }
4770
4771 g_slice_free (GtkTextLine, line);
4772}
4773
4774static void
4775gtk_text_line_set_parent (GtkTextLine *line,
4776 GtkTextBTreeNode *node)
4777{
4778 if (line->parent == node)
4779 return;
4780 line->parent = node;
4781 gtk_text_btree_node_invalidate_upward (node, NULL);
4782}
4783
4784static void
4785cleanup_line (GtkTextLine *line)
4786{
4787 GtkTextLineSegment *seg, **prev_p;
4788 gboolean changed;
4789
4790 /*
4791 * Make a pass over all of the segments in the line, giving each
4792 * a chance to clean itself up. This could potentially change
4793 * the structure of the line, e.g. by merging two segments
4794 * together or having two segments cancel themselves; if so,
4795 * then repeat the whole process again, since the first structure
4796 * change might make other structure changes possible. Repeat
4797 * until eventually there are no changes.
4798 */
4799
4800 changed = TRUE;
4801 while (changed)
4802 {
4803 changed = FALSE;
4804 prev_p = &line->segments;
4805 for (seg = *prev_p; seg != NULL; seg = *prev_p)
4806 {
4807 if (seg->type->cleanupFunc != NULL)
4808 {
4809 *prev_p = (*seg->type->cleanupFunc)(seg, line);
4810 if (seg != *prev_p)
4811 {
4812 changed = TRUE;
4813 continue;
4814 }
4815 }
4816
4817 prev_p = &(*prev_p)->next;
4818 }
4819 }
4820}
4821
4822/*
4823 * Nodes
4824 */
4825
4826static inline NodeData*
4827node_data_new (gpointer view_id,
4828 NodeData *next)
4829{
4830 NodeData *nd;
4831
4832 nd = g_slice_new (NodeData);
4833
4834 nd->view_id = view_id;
4835 nd->next = next;
4836 nd->width = 0;
4837 nd->height = 0;
4838 nd->valid = FALSE;
4839
4840 return nd;
4841}
4842
4843static inline void
4844node_data_destroy (NodeData *nd)
4845{
4846 g_slice_free (NodeData, nd);
4847}
4848
4849static inline void
4850node_data_list_destroy (NodeData *nd)
4851{
4852 g_slice_free_chain (NodeData, nd, next);
4853}
4854
4855static inline NodeData*
4856node_data_find (NodeData *nd,
4857 gpointer view_id)
4858{
4859 while (nd != NULL)
4860 {
4861 if (nd->view_id == view_id)
4862 break;
4863 nd = nd->next;
4864 }
4865 return nd;
4866}
4867
4868static void
4869summary_destroy (Summary *summary)
4870{
4871 /* Fill with error-triggering garbage */
4872 summary->info = (void*)0x1;
4873 summary->toggle_count = 567;
4874 summary->next = (void*)0x1;
4875 g_slice_free (Summary, summary);
4876}
4877
4878static GtkTextBTreeNode*
4879gtk_text_btree_node_new (void)
4880{
4881 GtkTextBTreeNode *node;
4882
4883 node = g_slice_new (GtkTextBTreeNode);
4884
4885 node->node_data = NULL;
4886
4887 return node;
4888}
4889
4890static void
4891gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
4892 GtkTextTagInfo *info,
4893 int adjust)
4894{
4895 Summary *summary;
4896
4897 summary = node->summary;
4898 while (summary != NULL)
4899 {
4900 if (summary->info == info)
4901 {
4902 summary->toggle_count += adjust;
4903 break;
4904 }
4905
4906 summary = summary->next;
4907 }
4908
4909 if (summary == NULL)
4910 {
4911 /* didn't find a summary for our tag. */
4912 g_return_if_fail (adjust > 0);
4913 summary = g_slice_new (Summary);
4914 summary->info = info;
4915 summary->toggle_count = adjust;
4916 summary->next = node->summary;
4917 node->summary = summary;
4918 }
4919}
4920
4921/* Note that the tag root and above do not have summaries
4922 for the tag; only nodes below the tag root have
4923 the summaries. */
4924static gboolean
4925gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
4926{
4927 Summary *summary;
4928
4929 summary = node->summary;
4930 while (summary != NULL)
4931 {
4932 if (tag == NULL ||
4933 summary->info->tag == tag)
4934 return TRUE;
4935
4936 summary = summary->next;
4937 }
4938
4939 return FALSE;
4940}
4941
4942/* Add node and all children to the damage region. */
4943#if 0
4944static void
4945gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node)
4946{
4947 NodeData *nd;
4948
4949 nd = node->node_data;
4950 while (nd != NULL)
4951 {
4952 nd->valid = FALSE;
4953 nd = nd->next;
4954 }
4955
4956 if (node->level == 0)
4957 {
4958 GtkTextLine *line;
4959
4960 line = node->children.line;
4961 while (line != NULL)
4962 {
4963 GtkTextLineData *ld;
4964
4965 ld = line->views;
4966 while (ld != NULL)
4967 {
4968 ld->valid = FALSE;
4969 ld = ld->next;
4970 }
4971
4972 line = line->next;
4973 }
4974 }
4975 else
4976 {
4977 GtkTextBTreeNode *child;
4978
4979 child = node->children.node;
4980
4981 while (child != NULL)
4982 {
4983 gtk_text_btree_node_invalidate_downward (child);
4984
4985 child = child->next;
4986 }
4987 }
4988}
4989#endif
4990
4991static void
4992gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node, gpointer view_id)
4993{
4994 GtkTextBTreeNode *iter;
4995
4996 iter = node;
4997 while (iter != NULL)
4998 {
4999 NodeData *nd;
5000
5001 if (view_id)
5002 {
5003 nd = node_data_find (nd: iter->node_data, view_id);
5004
5005 if (nd == NULL || !nd->valid)
5006 break; /* Once a node is invalid, we know its parents are as well. */
5007
5008 nd->valid = FALSE;
5009 }
5010 else
5011 {
5012 gboolean should_continue = FALSE;
5013
5014 nd = iter->node_data;
5015 while (nd != NULL)
5016 {
5017 if (nd->valid)
5018 {
5019 should_continue = TRUE;
5020 nd->valid = FALSE;
5021 }
5022
5023 nd = nd->next;
5024 }
5025
5026 if (!should_continue)
5027 break; /* This node was totally invalidated, so are its
5028 parents */
5029 }
5030
5031 iter = iter->parent;
5032 }
5033}
5034
5035
5036/**
5037 * _gtk_text_btree_is_valid:
5038 * @tree: a GtkTextBTree
5039 * @view_id: ID for the view
5040 *
5041 * Check to see if the entire GtkTextBTree is valid or not for
5042 * the given view.
5043 *
5044 * Returns: %TRUE if the entire GtkTextBTree is valid
5045 **/
5046gboolean
5047_gtk_text_btree_is_valid (GtkTextBTree *tree,
5048 gpointer view_id)
5049{
5050 NodeData *nd;
5051 g_return_val_if_fail (tree != NULL, FALSE);
5052
5053 nd = node_data_find (nd: tree->root_node->node_data, view_id);
5054 return (nd && nd->valid);
5055}
5056
5057typedef struct _ValidateState ValidateState;
5058
5059struct _ValidateState
5060{
5061 int remaining_pixels;
5062 gboolean in_validation;
5063 int y;
5064 int old_height;
5065 int new_height;
5066};
5067
5068static void
5069gtk_text_btree_node_validate (BTreeView *view,
5070 GtkTextBTreeNode *node,
5071 gpointer view_id,
5072 ValidateState *state)
5073{
5074 int node_valid = TRUE;
5075 int node_width = 0;
5076 int node_height = 0;
5077
5078 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5079 g_return_if_fail (!nd->valid);
5080
5081 if (node->level == 0)
5082 {
5083 GtkTextLine *line = node->children.line;
5084 GtkTextLineData *ld;
5085
5086 /* Iterate over leading valid lines */
5087 while (line != NULL)
5088 {
5089 ld = _gtk_text_line_get_data (line, view_id);
5090
5091 if (!ld || !ld->valid)
5092 break;
5093 else if (state->in_validation)
5094 {
5095 state->in_validation = FALSE;
5096 return;
5097 }
5098 else
5099 {
5100 state->y += ld->height;
5101 node_width = MAX (ld->width, node_width);
5102 node_height += ld->height;
5103 }
5104
5105 line = line->next;
5106 }
5107
5108 state->in_validation = TRUE;
5109
5110 /* Iterate over invalid lines */
5111 while (line != NULL)
5112 {
5113 ld = _gtk_text_line_get_data (line, view_id);
5114
5115 if (ld && ld->valid)
5116 break;
5117 else
5118 {
5119 if (ld)
5120 state->old_height += ld->height;
5121 ld = gtk_text_layout_wrap (layout: view->layout, line, line_data: ld);
5122 state->new_height += ld->height;
5123
5124 node_width = MAX (ld->width, node_width);
5125 node_height += ld->height;
5126
5127 state->remaining_pixels -= ld->height;
5128 if (state->remaining_pixels <= 0)
5129 {
5130 line = line->next;
5131 break;
5132 }
5133 }
5134
5135 line = line->next;
5136 }
5137
5138 /* Iterate over the remaining lines */
5139 while (line != NULL)
5140 {
5141 ld = _gtk_text_line_get_data (line, view_id);
5142 state->in_validation = FALSE;
5143
5144 if (!ld || !ld->valid)
5145 node_valid = FALSE;
5146
5147 if (ld)
5148 {
5149 node_width = MAX (ld->width, node_width);
5150 node_height += ld->height;
5151 }
5152
5153 line = line->next;
5154 }
5155 }
5156 else
5157 {
5158 GtkTextBTreeNode *child;
5159 NodeData *child_nd;
5160
5161 child = node->children.node;
5162
5163 /* Iterate over leading valid nodes */
5164 while (child)
5165 {
5166 child_nd = gtk_text_btree_node_ensure_data (node: child, view_id);
5167
5168 if (!child_nd->valid)
5169 break;
5170 else if (state->in_validation)
5171 {
5172 state->in_validation = FALSE;
5173 return;
5174 }
5175 else
5176 {
5177 state->y += child_nd->height;
5178 node_width = MAX (node_width, child_nd->width);
5179 node_height += child_nd->height;
5180 }
5181
5182 child = child->next;
5183 }
5184
5185 /* Iterate over invalid nodes */
5186 while (child)
5187 {
5188 child_nd = gtk_text_btree_node_ensure_data (node: child, view_id);
5189
5190 if (child_nd->valid)
5191 break;
5192 else
5193 {
5194 gtk_text_btree_node_validate (view, node: child, view_id, state);
5195
5196 if (!child_nd->valid)
5197 node_valid = FALSE;
5198 node_width = MAX (node_width, child_nd->width);
5199 node_height += child_nd->height;
5200
5201 if (!state->in_validation || state->remaining_pixels <= 0)
5202 {
5203 child = child->next;
5204 break;
5205 }
5206 }
5207
5208 child = child->next;
5209 }
5210
5211 /* Iterate over the remaining lines */
5212 while (child)
5213 {
5214 child_nd = gtk_text_btree_node_ensure_data (node: child, view_id);
5215 state->in_validation = FALSE;
5216
5217 if (!child_nd->valid)
5218 node_valid = FALSE;
5219
5220 node_width = MAX (child_nd->width, node_width);
5221 node_height += child_nd->height;
5222
5223 child = child->next;
5224 }
5225 }
5226
5227 nd->width = node_width;
5228 nd->height = node_height;
5229 nd->valid = node_valid;
5230}
5231
5232/**
5233 * _gtk_text_btree_validate:
5234 * @tree: a GtkTextBTree
5235 * @view_id: view id
5236 * @max_pixels: the maximum number of pixels to validate. (No more
5237 * than one paragraph beyond this limit will be validated)
5238 * @y: location to store starting y coordinate of validated region
5239 * @old_height: location to store old height of validated region
5240 * @new_height: location to store new height of validated region
5241 *
5242 * Validate a single contiguous invalid region of a GtkTextBTree for
5243 * a given view.
5244 *
5245 * Returns: %TRUE if a region has been validated, %FALSE if the
5246 * entire tree was already valid.
5247 **/
5248gboolean
5249_gtk_text_btree_validate (GtkTextBTree *tree,
5250 gpointer view_id,
5251 int max_pixels,
5252 int *y,
5253 int *old_height,
5254 int *new_height)
5255{
5256 BTreeView *view;
5257
5258 g_return_val_if_fail (tree != NULL, FALSE);
5259
5260 view = gtk_text_btree_get_view (tree, view_id);
5261 g_return_val_if_fail (view != NULL, FALSE);
5262
5263 if (!_gtk_text_btree_is_valid (tree, view_id))
5264 {
5265 ValidateState state;
5266
5267 state.remaining_pixels = max_pixels;
5268 state.in_validation = FALSE;
5269 state.y = 0;
5270 state.old_height = 0;
5271 state.new_height = 0;
5272
5273 gtk_text_btree_node_validate (view,
5274 node: tree->root_node,
5275 view_id, state: &state);
5276
5277 if (y)
5278 *y = state.y;
5279 if (old_height)
5280 *old_height = state.old_height;
5281 if (new_height)
5282 *new_height = state.new_height;
5283
5284#ifdef G_ENABLE_DEBUG
5285 if (GTK_DEBUG_CHECK (TEXT))
5286 _gtk_text_btree_check (tree);
5287#endif
5288
5289 return TRUE;
5290 }
5291 else
5292 return FALSE;
5293}
5294
5295static void
5296gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
5297 gpointer view_id,
5298 int *width_out,
5299 int *height_out,
5300 gboolean *valid_out)
5301{
5302 int width = 0;
5303 int height = 0;
5304 gboolean valid = TRUE;
5305
5306 if (node->level == 0)
5307 {
5308 GtkTextLine *line = node->children.line;
5309
5310 while (line != NULL)
5311 {
5312 GtkTextLineData *ld = _gtk_text_line_get_data (line, view_id);
5313
5314 if (!ld || !ld->valid)
5315 valid = FALSE;
5316
5317 if (ld)
5318 {
5319 width = MAX (ld->width, width);
5320 height += ld->height;
5321 }
5322
5323 line = line->next;
5324 }
5325 }
5326 else
5327 {
5328 GtkTextBTreeNode *child = node->children.node;
5329
5330 while (child)
5331 {
5332 NodeData *child_nd = node_data_find (nd: child->node_data, view_id);
5333
5334 if (!child_nd || !child_nd->valid)
5335 valid = FALSE;
5336
5337 if (child_nd)
5338 {
5339 width = MAX (child_nd->width, width);
5340 height += child_nd->height;
5341 }
5342
5343 child = child->next;
5344 }
5345 }
5346
5347 *width_out = width;
5348 *height_out = height;
5349 *valid_out = valid;
5350}
5351
5352
5353/* Recompute the validity and size of the view data for a given
5354 * view at this node from the immediate children of the node
5355 */
5356static NodeData *
5357gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
5358 gpointer view_id)
5359{
5360 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5361 gboolean valid;
5362 int width;
5363 int height;
5364
5365 gtk_text_btree_node_compute_view_aggregates (node, view_id,
5366 width_out: &width, height_out: &height, valid_out: &valid);
5367 nd->width = width;
5368 nd->height = height;
5369 nd->valid = valid;
5370
5371 return nd;
5372}
5373
5374static void
5375gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
5376 gpointer view_id)
5377{
5378 while (node)
5379 {
5380 gtk_text_btree_node_check_valid (node, view_id);
5381 node = node->parent;
5382 }
5383}
5384
5385static NodeData *
5386gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
5387 gpointer view_id)
5388{
5389 if (node->level == 0)
5390 {
5391 return gtk_text_btree_node_check_valid (node, view_id);
5392 }
5393 else
5394 {
5395 GtkTextBTreeNode *child = node->children.node;
5396
5397 NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
5398
5399 nd->valid = TRUE;
5400 nd->width = 0;
5401 nd->height = 0;
5402
5403 while (child)
5404 {
5405 NodeData *child_nd = gtk_text_btree_node_check_valid_downward (node: child, view_id);
5406
5407 if (!child_nd->valid)
5408 nd->valid = FALSE;
5409 nd->width = MAX (child_nd->width, nd->width);
5410 nd->height += child_nd->height;
5411
5412 child = child->next;
5413 }
5414 return nd;
5415 }
5416}
5417
5418
5419
5420/**
5421 * _gtk_text_btree_validate_line:
5422 * @tree: a GtkTextBTree
5423 * @line: line to validate
5424 * @view_id: view ID for the view to validate
5425 *
5426 * Revalidate a single line of the btree for the given view, propagate
5427 * results up through the entire tree.
5428 **/
5429void
5430_gtk_text_btree_validate_line (GtkTextBTree *tree,
5431 GtkTextLine *line,
5432 gpointer view_id)
5433{
5434 GtkTextLineData *ld;
5435 BTreeView *view;
5436
5437 g_return_if_fail (tree != NULL);
5438 g_return_if_fail (line != NULL);
5439
5440 view = gtk_text_btree_get_view (tree, view_id);
5441 g_return_if_fail (view != NULL);
5442
5443 ld = _gtk_text_line_get_data (line, view_id);
5444 if (!ld || !ld->valid)
5445 {
5446 gtk_text_layout_wrap (layout: view->layout, line, line_data: ld);
5447 gtk_text_btree_node_check_valid_upward (node: line->parent, view_id);
5448 }
5449}
5450
5451static void
5452gtk_text_btree_node_remove_view (BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
5453{
5454 if (node->level == 0)
5455 {
5456 GtkTextLine *line;
5457
5458 line = node->children.line;
5459 while (line != NULL)
5460 {
5461 GtkTextLineData *ld;
5462
5463 ld = _gtk_text_line_remove_data (line, view_id);
5464
5465 if (ld)
5466 gtk_text_layout_free_line_data (layout: view->layout, line, line_data: ld);
5467
5468 line = line->next;
5469 }
5470 }
5471 else
5472 {
5473 GtkTextBTreeNode *child;
5474
5475 child = node->children.node;
5476
5477 while (child != NULL)
5478 {
5479 /* recurse */
5480 gtk_text_btree_node_remove_view (view, node: child, view_id);
5481
5482 child = child->next;
5483 }
5484 }
5485
5486 gtk_text_btree_node_remove_data (node, view_id);
5487}
5488
5489static void
5490gtk_text_btree_node_destroy (GtkTextBTree *tree, GtkTextBTreeNode *node)
5491{
5492 if (node->level == 0)
5493 {
5494 GtkTextLine *line;
5495 GtkTextLineSegment *seg;
5496
5497 while (node->children.line != NULL)
5498 {
5499 line = node->children.line;
5500 node->children.line = line->next;
5501 while (line->segments != NULL)
5502 {
5503 seg = line->segments;
5504 line->segments = seg->next;
5505
5506 (*seg->type->deleteFunc) (seg, line, TRUE);
5507 }
5508 gtk_text_line_destroy (tree, line);
5509 }
5510 }
5511 else
5512 {
5513 GtkTextBTreeNode *childPtr;
5514
5515 while (node->children.node != NULL)
5516 {
5517 childPtr = node->children.node;
5518 node->children.node = childPtr->next;
5519 gtk_text_btree_node_destroy (tree, node: childPtr);
5520 }
5521 }
5522
5523 gtk_text_btree_node_free_empty (tree, node);
5524}
5525
5526static void
5527gtk_text_btree_node_free_empty (GtkTextBTree *tree,
5528 GtkTextBTreeNode *node)
5529{
5530 g_return_if_fail ((node->level > 0 && node->children.node == NULL) ||
5531 (node->level == 0 && node->children.line == NULL));
5532
5533 summary_list_destroy (summary: node->summary);
5534 node_data_list_destroy (nd: node->node_data);
5535 g_slice_free (GtkTextBTreeNode, node);
5536}
5537
5538static NodeData*
5539gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node, gpointer view_id)
5540{
5541 NodeData *nd;
5542
5543 nd = node_data_find (nd: node->node_data, view_id);
5544
5545 if (nd == NULL)
5546 nd = node->node_data = node_data_new (view_id, next: node->node_data);
5547
5548 return nd;
5549}
5550
5551static void
5552gtk_text_btree_node_remove_data (GtkTextBTreeNode *node, gpointer view_id)
5553{
5554 NodeData *nd;
5555 NodeData *prev;
5556
5557 prev = NULL;
5558 nd = node->node_data;
5559 while (nd != NULL)
5560 {
5561 if (nd->view_id == view_id)
5562 break;
5563
5564 prev = nd;
5565 nd = nd->next;
5566 }
5567
5568 if (nd == NULL)
5569 return;
5570
5571 if (prev != NULL)
5572 prev->next = nd->next;
5573
5574 if (node->node_data == nd)
5575 node->node_data = nd->next;
5576
5577 nd->next = NULL;
5578
5579 node_data_destroy (nd);
5580}
5581
5582static void
5583gtk_text_btree_node_get_size (GtkTextBTreeNode *node, gpointer view_id,
5584 int *width, int *height)
5585{
5586 NodeData *nd;
5587
5588 g_return_if_fail (width != NULL);
5589 g_return_if_fail (height != NULL);
5590
5591 nd = gtk_text_btree_node_ensure_data (node, view_id);
5592
5593 if (width)
5594 *width = nd->width;
5595 if (height)
5596 *height = nd->height;
5597}
5598
5599/* Find the closest common ancestor of the two nodes. FIXME: The interface
5600 * here isn’t quite right, since for a lot of operations we want to
5601 * know which children of the common parent correspond to the two nodes
5602 * (e.g., when computing the order of two iters)
5603 */
5604static GtkTextBTreeNode *
5605gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
5606 GtkTextBTreeNode *node2)
5607{
5608 while (node1->level < node2->level)
5609 node1 = node1->parent;
5610 while (node2->level < node1->level)
5611 node2 = node2->parent;
5612 while (node1 != node2)
5613 {
5614 node1 = node1->parent;
5615 node2 = node2->parent;
5616 }
5617
5618 return node1;
5619}
5620
5621/*
5622 * BTree
5623 */
5624
5625static BTreeView*
5626gtk_text_btree_get_view (GtkTextBTree *tree, gpointer view_id)
5627{
5628 BTreeView *view;
5629
5630 view = tree->views;
5631 while (view != NULL)
5632 {
5633 if (view->view_id == view_id)
5634 break;
5635 view = view->next;
5636 }
5637
5638 return view;
5639}
5640
5641static void
5642get_tree_bounds (GtkTextBTree *tree,
5643 GtkTextIter *start,
5644 GtkTextIter *end)
5645{
5646 _gtk_text_btree_get_iter_at_line_char (tree, iter: start, line_number: 0, char_index: 0);
5647 _gtk_text_btree_get_end_iter (tree, iter: end);
5648}
5649
5650static void
5651tag_changed_cb (GtkTextTagTable *table,
5652 GtkTextTag *tag,
5653 gboolean size_changed,
5654 GtkTextBTree *tree)
5655{
5656 if (size_changed)
5657 {
5658 /* We need to queue a relayout on all regions that are tagged with
5659 * this tag.
5660 */
5661
5662 GtkTextIter start;
5663 GtkTextIter end;
5664
5665 if (_gtk_text_btree_get_iter_at_first_toggle (tree, iter: &start, tag))
5666 {
5667 /* Must be a last toggle if there was a first one. */
5668 _gtk_text_btree_get_iter_at_last_toggle (tree, iter: &end, tag);
5669 DV (g_print ("invalidating due to tag change (%s)\n", G_STRLOC));
5670 _gtk_text_btree_invalidate_region (tree, start: &start, end: &end, FALSE);
5671
5672 }
5673 }
5674 else
5675 {
5676 /* We only need to queue a redraw, not a relayout */
5677 BTreeView *view;
5678
5679 view = tree->views;
5680
5681 while (view != NULL)
5682 {
5683 int width = 0;
5684 int height = 0;
5685
5686 _gtk_text_btree_get_view_size (tree, view_id: view->view_id, width: &width, height: &height);
5687 gtk_text_layout_changed (layout: view->layout, y: 0, old_height: height, new_height: height);
5688
5689 view = view->next;
5690 }
5691 }
5692}
5693
5694void
5695_gtk_text_btree_notify_will_remove_tag (GtkTextBTree *tree,
5696 GtkTextTag *tag)
5697{
5698 /* Remove the tag from the tree */
5699
5700 GtkTextIter start;
5701 GtkTextIter end;
5702
5703 get_tree_bounds (tree, start: &start, end: &end);
5704
5705 _gtk_text_btree_tag (start_orig: &start, end_orig: &end, tag, FALSE);
5706 gtk_text_btree_remove_tag_info (tree, tag);
5707}
5708
5709
5710/* Rebalance the out-of-whack node "node" */
5711static void
5712gtk_text_btree_rebalance (GtkTextBTree *tree,
5713 GtkTextBTreeNode *node)
5714{
5715 /*
5716 * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
5717 * up through the tree one GtkTextBTreeNode at a time until the root
5718 * GtkTextBTreeNode has been processed.
5719 */
5720
5721 while (node != NULL)
5722 {
5723 GtkTextBTreeNode *new_node, *child;
5724 GtkTextLine *line;
5725 int i;
5726
5727 /*
5728 * Check to see if the GtkTextBTreeNode has too many children. If it does,
5729 * then split off all but the first MIN_CHILDREN into a separate
5730 * GtkTextBTreeNode following the original one. Then repeat until the
5731 * GtkTextBTreeNode has a decent size.
5732 */
5733
5734 if (node->num_children > MAX_CHILDREN)
5735 {
5736 while (1)
5737 {
5738 /*
5739 * If the GtkTextBTreeNode being split is the root
5740 * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
5741 * it first.
5742 */
5743
5744 if (node->parent == NULL)
5745 {
5746 new_node = gtk_text_btree_node_new ();
5747 new_node->parent = NULL;
5748 new_node->next = NULL;
5749 new_node->summary = NULL;
5750 new_node->level = node->level + 1;
5751 new_node->children.node = node;
5752 recompute_node_counts (tree, node: new_node);
5753 tree->root_node = new_node;
5754 }
5755 new_node = gtk_text_btree_node_new ();
5756 new_node->parent = node->parent;
5757 new_node->next = node->next;
5758 node->next = new_node;
5759 new_node->summary = NULL;
5760 new_node->level = node->level;
5761 new_node->num_children = node->num_children - MIN_CHILDREN;
5762 if (node->level == 0)
5763 {
5764 for (i = MIN_CHILDREN-1,
5765 line = node->children.line;
5766 i > 0; i--, line = line->next)
5767 {
5768 /* Empty loop body. */
5769 }
5770 new_node->children.line = line->next;
5771 line->next = NULL;
5772 }
5773 else
5774 {
5775 for (i = MIN_CHILDREN-1,
5776 child = node->children.node;
5777 i > 0; i--, child = child->next)
5778 {
5779 /* Empty loop body. */
5780 }
5781 new_node->children.node = child->next;
5782 child->next = NULL;
5783 }
5784 recompute_node_counts (tree, node);
5785 node->parent->num_children++;
5786 node = new_node;
5787 if (node->num_children <= MAX_CHILDREN)
5788 {
5789 recompute_node_counts (tree, node);
5790 break;
5791 }
5792 }
5793 }
5794
5795 while (node->num_children < MIN_CHILDREN)
5796 {
5797 GtkTextBTreeNode *other;
5798 GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
5799 GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
5800 int total_children, first_children;
5801
5802 /*
5803 * Too few children for this GtkTextBTreeNode. If this is the root then,
5804 * it's OK for it to have less than MIN_CHILDREN children
5805 * as long as it's got at least two. If it has only one
5806 * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
5807 * the tree and use its child as the new root.
5808 */
5809
5810 if (node->parent == NULL)
5811 {
5812 if ((node->num_children == 1) && (node->level > 0))
5813 {
5814 tree->root_node = node->children.node;
5815 tree->root_node->parent = NULL;
5816
5817 node->children.node = NULL;
5818 gtk_text_btree_node_free_empty (tree, node);
5819 }
5820 return;
5821 }
5822
5823 /*
5824 * Not the root. Make sure that there are siblings to
5825 * balance with.
5826 */
5827
5828 if (node->parent->num_children < 2)
5829 {
5830 gtk_text_btree_rebalance (tree, node: node->parent);
5831 continue;
5832 }
5833
5834 /*
5835 * Find a sibling neighbor to borrow from, and arrange for
5836 * node to be the earlier of the pair.
5837 */
5838
5839 if (node->next == NULL)
5840 {
5841 for (other = node->parent->children.node;
5842 other->next != node;
5843 other = other->next)
5844 {
5845 /* Empty loop body. */
5846 }
5847 node = other;
5848 }
5849 other = node->next;
5850
5851 /*
5852 * We're going to either merge the two siblings together
5853 * into one GtkTextBTreeNode or redivide the children among them to
5854 * balance their loads. As preparation, join their two
5855 * child lists into a single list and remember the half-way
5856 * point in the list.
5857 */
5858
5859 total_children = node->num_children + other->num_children;
5860 first_children = total_children/2;
5861 if (node->children.node == NULL)
5862 {
5863 node->children = other->children;
5864 other->children.node = NULL;
5865 other->children.line = NULL;
5866 }
5867 if (node->level == 0)
5868 {
5869 GtkTextLine *line2;
5870
5871 for (line2 = node->children.line, i = 1;
5872 line2->next != NULL;
5873 line2 = line2->next, i++)
5874 {
5875 if (i == first_children)
5876 {
5877 halfwayline = line2;
5878 }
5879 }
5880 line2->next = other->children.line;
5881 while (i <= first_children)
5882 {
5883 halfwayline = line2;
5884 line2 = line2->next;
5885 i++;
5886 }
5887 }
5888 else
5889 {
5890 GtkTextBTreeNode *child2;
5891
5892 for (child2 = node->children.node, i = 1;
5893 child2->next != NULL;
5894 child2 = child2->next, i++)
5895 {
5896 if (i <= first_children)
5897 {
5898 if (i == first_children)
5899 {
5900 halfwaynode = child2;
5901 }
5902 }
5903 }
5904 child2->next = other->children.node;
5905 while (i <= first_children)
5906 {
5907 halfwaynode = child2;
5908 child2 = child2->next;
5909 i++;
5910 }
5911 }
5912
5913 /*
5914 * If the two siblings can simply be merged together, do it.
5915 */
5916
5917 if (total_children <= MAX_CHILDREN)
5918 {
5919 recompute_node_counts (tree, node);
5920 node->next = other->next;
5921 node->parent->num_children--;
5922
5923 other->children.node = NULL;
5924 other->children.line = NULL;
5925 gtk_text_btree_node_free_empty (tree, node: other);
5926 continue;
5927 }
5928
5929 /*
5930 * The siblings can't be merged, so just divide their
5931 * children evenly between them.
5932 */
5933
5934 if (node->level == 0)
5935 {
5936 other->children.line = halfwayline->next;
5937 halfwayline->next = NULL;
5938 }
5939 else
5940 {
5941 other->children.node = halfwaynode->next;
5942 halfwaynode->next = NULL;
5943 }
5944
5945 recompute_node_counts (tree, node);
5946 recompute_node_counts (tree, node: other);
5947 }
5948
5949 node = node->parent;
5950 }
5951}
5952
5953static void
5954post_insert_fixup (GtkTextBTree *tree,
5955 GtkTextLine *line,
5956 int line_count_delta,
5957 int char_count_delta)
5958
5959{
5960 GtkTextBTreeNode *node;
5961
5962 /*
5963 * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
5964 * point, then rebalance the tree if necessary.
5965 */
5966
5967 for (node = line->parent ; node != NULL;
5968 node = node->parent)
5969 {
5970 node->num_lines += line_count_delta;
5971 node->num_chars += char_count_delta;
5972 }
5973 node = line->parent;
5974 node->num_children += line_count_delta;
5975
5976 if (node->num_children > MAX_CHILDREN)
5977 {
5978 gtk_text_btree_rebalance (tree, node);
5979 }
5980
5981#ifdef G_ENABLE_DEBUG
5982 if (GTK_DEBUG_CHECK (TEXT))
5983 _gtk_text_btree_check (tree);
5984#endif
5985}
5986
5987static GtkTextTagInfo*
5988gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
5989 GtkTextTag *tag)
5990{
5991 GtkTextTagInfo *info;
5992 GSList *list;
5993
5994
5995 list = tree->tag_infos;
5996 while (list != NULL)
5997 {
5998 info = list->data;
5999 if (info->tag == tag)
6000 return info;
6001
6002 list = list->next;
6003 }
6004
6005 return NULL;
6006}
6007
6008static GtkTextTagInfo*
6009gtk_text_btree_get_tag_info (GtkTextBTree *tree,
6010 GtkTextTag *tag)
6011{
6012 GtkTextTagInfo *info;
6013
6014 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6015
6016 if (info == NULL)
6017 {
6018 /* didn't find it, create. */
6019
6020 info = g_slice_new (GtkTextTagInfo);
6021
6022 info->tag = tag;
6023 g_object_ref (tag);
6024 info->tag_root = NULL;
6025 info->toggle_count = 0;
6026
6027 tree->tag_infos = g_slist_prepend (list: tree->tag_infos, data: info);
6028 }
6029
6030 return info;
6031}
6032
6033static void
6034gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
6035 GtkTextTag *tag)
6036{
6037 GtkTextTagInfo *info;
6038 GSList *list;
6039 GSList *prev;
6040
6041 prev = NULL;
6042 list = tree->tag_infos;
6043 while (list != NULL)
6044 {
6045 info = list->data;
6046 if (info->tag == tag)
6047 {
6048 if (prev != NULL)
6049 {
6050 prev->next = list->next;
6051 }
6052 else
6053 {
6054 tree->tag_infos = list->next;
6055 }
6056 list->next = NULL;
6057 g_slist_free (list);
6058
6059 g_object_unref (object: info->tag);
6060
6061 g_slice_free (GtkTextTagInfo, info);
6062 return;
6063 }
6064
6065 prev = list;
6066 list = list->next;
6067 }
6068}
6069
6070static void
6071recompute_level_zero_counts (GtkTextBTreeNode *node)
6072{
6073 GtkTextLine *line;
6074 GtkTextLineSegment *seg;
6075
6076 g_assert (node->level == 0);
6077
6078 line = node->children.line;
6079 while (line != NULL)
6080 {
6081 node->num_children++;
6082 node->num_lines++;
6083
6084 if (line->parent != node)
6085 gtk_text_line_set_parent (line, node);
6086
6087 seg = line->segments;
6088 while (seg != NULL)
6089 {
6090
6091 node->num_chars += seg->char_count;
6092
6093 if (((seg->type != &gtk_text_toggle_on_type)
6094 && (seg->type != &gtk_text_toggle_off_type))
6095 || !(seg->body.toggle.inNodeCounts))
6096 {
6097 ; /* nothing */
6098 }
6099 else
6100 {
6101 GtkTextTagInfo *info;
6102
6103 info = seg->body.toggle.info;
6104
6105 gtk_text_btree_node_adjust_toggle_count (node, info, adjust: 1);
6106 }
6107
6108 seg = seg->next;
6109 }
6110
6111 line = line->next;
6112 }
6113}
6114
6115static void
6116recompute_level_nonzero_counts (GtkTextBTreeNode *node)
6117{
6118 Summary *summary;
6119 GtkTextBTreeNode *child;
6120
6121 g_assert (node->level > 0);
6122
6123 child = node->children.node;
6124 while (child != NULL)
6125 {
6126 node->num_children += 1;
6127 node->num_lines += child->num_lines;
6128 node->num_chars += child->num_chars;
6129
6130 if (child->parent != node)
6131 {
6132 child->parent = node;
6133 gtk_text_btree_node_invalidate_upward (node, NULL);
6134 }
6135
6136 summary = child->summary;
6137 while (summary != NULL)
6138 {
6139 gtk_text_btree_node_adjust_toggle_count (node,
6140 info: summary->info,
6141 adjust: summary->toggle_count);
6142
6143 summary = summary->next;
6144 }
6145
6146 child = child->next;
6147 }
6148}
6149
6150/*
6151 *----------------------------------------------------------------------
6152 *
6153 * recompute_node_counts --
6154 *
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.
6159 *
6160 * Results:
6161 * None.
6162 *
6163 * Side effects:
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
6167 * to node.
6168 *
6169 *----------------------------------------------------------------------
6170 */
6171
6172static void
6173recompute_node_counts (GtkTextBTree *tree, GtkTextBTreeNode *node)
6174{
6175 BTreeView *view;
6176 Summary *summary, *summary2;
6177
6178 /*
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).
6181 */
6182
6183 summary = node->summary;
6184 while (summary != NULL)
6185 {
6186 summary->toggle_count = 0;
6187 summary = summary->next;
6188 }
6189
6190 node->num_children = 0;
6191 node->num_lines = 0;
6192 node->num_chars = 0;
6193
6194 /*
6195 * Scan through the children, adding the childrens’ tag counts into
6196 * the GtkTextBTreeNode’s tag counts and adding new Summary structures if
6197 * necessary.
6198 */
6199
6200 if (node->level == 0)
6201 recompute_level_zero_counts (node);
6202 else
6203 recompute_level_nonzero_counts (node);
6204
6205 view = tree->views;
6206 while (view)
6207 {
6208 gtk_text_btree_node_check_valid (node, view_id: view->view_id);
6209 view = view->next;
6210 }
6211
6212 /*
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.
6217 */
6218
6219 summary2 = NULL;
6220 for (summary = node->summary; summary != NULL; )
6221 {
6222 if (summary->toggle_count > 0 &&
6223 summary->toggle_count < summary->info->toggle_count)
6224 {
6225 if (node->level == summary->info->tag_root->level)
6226 {
6227 /*
6228 * The tag’s root GtkTextBTreeNode split and some toggles left.
6229 * The tag root must move up a level.
6230 */
6231 summary->info->tag_root = node->parent;
6232 }
6233 summary2 = summary;
6234 summary = summary->next;
6235 continue;
6236 }
6237 if (summary->toggle_count == summary->info->toggle_count)
6238 {
6239 /*
6240 * A GtkTextBTreeNode merge has collected all the toggles under
6241 * one GtkTextBTreeNode. Push the root down to this level.
6242 */
6243 summary->info->tag_root = node;
6244 }
6245 if (summary2 != NULL)
6246 {
6247 summary2->next = summary->next;
6248 summary_destroy (summary);
6249 summary = summary2->next;
6250 }
6251 else
6252 {
6253 node->summary = summary->next;
6254 summary_destroy (summary);
6255 summary = node->summary;
6256 }
6257 }
6258}
6259
6260void
6261_gtk_change_node_toggle_count (GtkTextBTreeNode *node,
6262 GtkTextTagInfo *info,
6263 int delta) /* may be negative */
6264{
6265 Summary *summary, *prevPtr;
6266 GtkTextBTreeNode *node2Ptr;
6267 int rootLevel; /* Level of original tag root */
6268
6269 info->toggle_count += delta;
6270
6271 if (info->tag_root == (GtkTextBTreeNode *) NULL)
6272 {
6273 info->tag_root = node;
6274 return;
6275 }
6276
6277 /*
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.
6280 */
6281
6282 rootLevel = info->tag_root->level;
6283
6284 /*
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
6287 * necessary.
6288 */
6289
6290 for ( ; node != info->tag_root; node = node->parent)
6291 {
6292 /*
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.
6295 */
6296
6297 for (prevPtr = NULL, summary = node->summary;
6298 summary != NULL;
6299 prevPtr = summary, summary = summary->next)
6300 {
6301 if (summary->info == info)
6302 {
6303 break;
6304 }
6305 }
6306 if (summary != NULL)
6307 {
6308 summary->toggle_count += delta;
6309 if (summary->toggle_count > 0 &&
6310 summary->toggle_count < info->toggle_count)
6311 {
6312 continue;
6313 }
6314 if (summary->toggle_count != 0)
6315 {
6316 /*
6317 * Should never find a GtkTextBTreeNode with max toggle count at this
6318 * point (there shouldn’t have been a summary entry in the
6319 * first place).
6320 */
6321
6322 g_error ("%s: bad toggle count (%d) max (%d)",
6323 G_STRLOC, summary->toggle_count, info->toggle_count);
6324 }
6325
6326 /*
6327 * Zero toggle count; must remove this tag from the list.
6328 */
6329
6330 if (prevPtr == NULL)
6331 {
6332 node->summary = summary->next;
6333 }
6334 else
6335 {
6336 prevPtr->next = summary->next;
6337 }
6338 summary_destroy (summary);
6339 }
6340 else
6341 {
6342 /*
6343 * This tag isn’t currently in the summary information list.
6344 */
6345
6346 if (rootLevel == node->level)
6347 {
6348
6349 /*
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.
6358 */
6359
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;
6369 }
6370 summary = g_slice_new (Summary);
6371 summary->info = info;
6372 summary->toggle_count = delta;
6373 summary->next = node->summary;
6374 node->summary = summary;
6375 }
6376 }
6377
6378 /*
6379 * If we’ve decremented the toggle count, then it may be necessary
6380 * to push the tag root down one or more levels.
6381 */
6382
6383 if (delta >= 0)
6384 {
6385 return;
6386 }
6387 if (info->toggle_count == 0)
6388 {
6389 info->tag_root = (GtkTextBTreeNode *) NULL;
6390 return;
6391 }
6392 node = info->tag_root;
6393 while (node->level > 0)
6394 {
6395 /*
6396 * See if a single child GtkTextBTreeNode accounts for all of the tag’s
6397 * toggles. If so, push the root down one level.
6398 */
6399
6400 for (node2Ptr = node->children.node;
6401 node2Ptr != (GtkTextBTreeNode *)NULL ;
6402 node2Ptr = node2Ptr->next)
6403 {
6404 for (prevPtr = NULL, summary = node2Ptr->summary;
6405 summary != NULL;
6406 prevPtr = summary, summary = summary->next)
6407 {
6408 if (summary->info == info)
6409 {
6410 break;
6411 }
6412 }
6413 if (summary == NULL)
6414 {
6415 continue;
6416 }
6417 if (summary->toggle_count != info->toggle_count)
6418 {
6419 /*
6420 * No GtkTextBTreeNode has all toggles, so the root is still valid.
6421 */
6422
6423 return;
6424 }
6425
6426 /*
6427 * This GtkTextBTreeNode has all the toggles, so push down the root.
6428 */
6429
6430 if (prevPtr == NULL)
6431 {
6432 node2Ptr->summary = summary->next;
6433 }
6434 else
6435 {
6436 prevPtr->next = summary->next;
6437 }
6438 summary_destroy (summary);
6439 info->tag_root = node2Ptr;
6440 break;
6441 }
6442 node = info->tag_root;
6443 }
6444}
6445
6446/*
6447 *----------------------------------------------------------------------
6448 *
6449 * inc_count --
6450 *
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.
6454 *
6455 * Results:
6456 * None.
6457 *
6458 * Side effects:
6459 * The information at *tagInfoPtr may be modified, and the arrays
6460 * may be reallocated to make them larger.
6461 *
6462 *----------------------------------------------------------------------
6463 */
6464
6465static void
6466inc_count (GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
6467{
6468 for (int i = 0; i < tagInfoPtr->tags->len; i++)
6469 {
6470 GtkTextTag *t = g_ptr_array_index (tagInfoPtr->tags, i);
6471 if (t == tag)
6472 {
6473 g_array_index (tagInfoPtr->counts, int, i) += inc;
6474 return;
6475 }
6476 }
6477
6478 /*
6479 * There isn’t currently an entry for this tag, so we have to
6480 * make a new one. If the arrays are full, then enlarge the
6481 * arrays first.
6482 */
6483
6484 g_ptr_array_add (array: tagInfoPtr->tags, data: tag);
6485 g_array_append_val (tagInfoPtr->counts, inc);
6486}
6487
6488static void
6489gtk_text_btree_link_segment (GtkTextLineSegment *seg,
6490 const GtkTextIter *iter)
6491{
6492 GtkTextLineSegment *prev;
6493 GtkTextLine *line;
6494 GtkTextBTree *tree;
6495
6496 line = _gtk_text_iter_get_text_line (iter);
6497 tree = _gtk_text_iter_get_btree (iter);
6498
6499 prev = gtk_text_line_segment_split (iter);
6500 if (prev == NULL)
6501 {
6502 seg->next = line->segments;
6503 line->segments = seg;
6504 }
6505 else
6506 {
6507 seg->next = prev->next;
6508 prev->next = seg;
6509 }
6510 cleanup_line (line);
6511 segments_changed (tree);
6512
6513#ifdef G_ENABLE_DEBUG
6514 if (GTK_DEBUG_CHECK (TEXT))
6515 _gtk_text_btree_check (tree);
6516#endif
6517}
6518
6519static void
6520gtk_text_btree_unlink_segment (GtkTextBTree *tree,
6521 GtkTextLineSegment *seg,
6522 GtkTextLine *line)
6523{
6524 GtkTextLineSegment *prev;
6525
6526 if (line->segments == seg)
6527 {
6528 line->segments = seg->next;
6529 }
6530 else
6531 {
6532 for (prev = line->segments; prev->next != seg;
6533 prev = prev->next)
6534 {
6535 /* Empty loop body. */
6536 }
6537 prev->next = seg->next;
6538 }
6539 cleanup_line (line);
6540 segments_changed (tree);
6541}
6542
6543/*
6544 * This is here because it requires BTree internals, it logically
6545 * belongs in gtktextsegment.c
6546 */
6547
6548
6549/*
6550 *--------------------------------------------------------------
6551 *
6552 * _gtk_toggle_segment_check_func --
6553 *
6554 * This procedure is invoked to perform consistency checks
6555 * on toggle segments.
6556 *
6557 * Results:
6558 * None.
6559 *
6560 * Side effects:
6561 * If a consistency problem is found the procedure g_errors.
6562 *
6563 *--------------------------------------------------------------
6564 */
6565
6566void
6567_gtk_toggle_segment_check_func (GtkTextLineSegment *segPtr,
6568 GtkTextLine *line)
6569{
6570 Summary *summary;
6571 int needSummary;
6572
6573 if (segPtr->byte_count != 0)
6574 {
6575 g_error ("toggle_segment_check_func: segment had non-zero size");
6576 }
6577 if (!segPtr->body.toggle.inNodeCounts)
6578 {
6579 g_error ("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
6580 }
6581 needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
6582 for (summary = line->parent->summary; ;
6583 summary = summary->next)
6584 {
6585 if (summary == NULL)
6586 {
6587 if (needSummary)
6588 {
6589 g_error ("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
6590 }
6591 else
6592 {
6593 break;
6594 }
6595 }
6596 if (summary->info == segPtr->body.toggle.info)
6597 {
6598 if (!needSummary)
6599 {
6600 g_error ("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
6601 }
6602 break;
6603 }
6604 }
6605}
6606
6607/*
6608 * Debug
6609 */
6610#ifdef G_ENABLE_DEBUG
6611static void
6612gtk_text_btree_node_view_check_consistency (GtkTextBTree *tree,
6613 GtkTextBTreeNode *node,
6614 NodeData *nd)
6615{
6616 int width;
6617 int height;
6618 gboolean valid;
6619 BTreeView *view;
6620
6621 view = tree->views;
6622
6623 while (view != NULL)
6624 {
6625 if (view->view_id == nd->view_id)
6626 break;
6627
6628 view = view->next;
6629 }
6630
6631 if (view == NULL)
6632 g_error ("Node has data for a view %p no longer attached to the tree",
6633 nd->view_id);
6634
6635 gtk_text_btree_node_compute_view_aggregates (node, view_id: nd->view_id,
6636 width_out: &width, height_out: &height, valid_out: &valid);
6637
6638 /* valid aggregate not checked the same as width/height, because on
6639 * btree rebalance we can have invalid nodes where all lines below
6640 * them are actually valid, due to moving lines around between
6641 * nodes.
6642 *
6643 * The guarantee is that if there are invalid lines the node is
6644 * invalid - we don’t guarantee that if the node is invalid there
6645 * are invalid lines.
6646 */
6647
6648 if (nd->width != width ||
6649 nd->height != height ||
6650 (nd->valid && !valid))
6651 {
6652 g_error ("Node aggregates for view %p are invalid:\n"
6653 "Are (%d,%d,%s), should be (%d,%d,%s)",
6654 nd->view_id,
6655 nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
6656 width, height, valid ? "TRUE" : "FALSE");
6657 }
6658}
6659
6660static void
6661gtk_text_btree_node_check_consistency (GtkTextBTree *tree,
6662 GtkTextBTreeNode *node)
6663{
6664 GtkTextBTreeNode *childnode;
6665 Summary *summary, *summary2;
6666 GtkTextLine *line;
6667 GtkTextLineSegment *segPtr;
6668 int num_children, num_lines, num_chars, toggle_count, min_children;
6669 GtkTextLineData *ld;
6670 NodeData *nd;
6671
6672 if (node->parent != NULL)
6673 {
6674 min_children = MIN_CHILDREN;
6675 }
6676 else if (node->level > 0)
6677 {
6678 min_children = 2;
6679 }
6680 else {
6681 min_children = 1;
6682 }
6683 if ((node->num_children < min_children)
6684 || (node->num_children > MAX_CHILDREN))
6685 {
6686 g_error ("gtk_text_btree_node_check_consistency: bad child count (%d)",
6687 node->num_children);
6688 }
6689
6690 nd = node->node_data;
6691 while (nd != NULL)
6692 {
6693 gtk_text_btree_node_view_check_consistency (tree, node, nd);
6694 nd = nd->next;
6695 }
6696
6697 num_children = 0;
6698 num_lines = 0;
6699 num_chars = 0;
6700 if (node->level == 0)
6701 {
6702 for (line = node->children.line; line != NULL;
6703 line = line->next)
6704 {
6705 if (line->parent != node)
6706 {
6707 g_error ("gtk_text_btree_node_check_consistency: line doesn't point to parent");
6708 }
6709 if (line->segments == NULL)
6710 {
6711 g_error ("gtk_text_btree_node_check_consistency: line has no segments");
6712 }
6713
6714 ld = line->views;
6715 while (ld != NULL)
6716 {
6717 /* Just ensuring we don’t segv while doing this loop */
6718
6719 ld = ld->next;
6720 }
6721
6722 for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
6723 {
6724 if (segPtr->type->checkFunc != NULL)
6725 {
6726 (*segPtr->type->checkFunc)(segPtr, line);
6727 }
6728 if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
6729 && (segPtr->next != NULL)
6730 && (segPtr->next->byte_count == 0)
6731 && (segPtr->next->type->leftGravity))
6732 {
6733 g_error ("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
6734 }
6735 if ((segPtr->next == NULL)
6736 && (segPtr->type != &gtk_text_char_type))
6737 {
6738 g_error ("gtk_text_btree_node_check_consistency: line ended with wrong type");
6739 }
6740
6741 num_chars += segPtr->char_count;
6742 }
6743
6744 num_children++;
6745 num_lines++;
6746 }
6747 }
6748 else
6749 {
6750 for (childnode = node->children.node; childnode != NULL;
6751 childnode = childnode->next)
6752 {
6753 if (childnode->parent != node)
6754 {
6755 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
6756 }
6757 if (childnode->level != (node->level-1))
6758 {
6759 g_error ("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
6760 node->level, childnode->level);
6761 }
6762 gtk_text_btree_node_check_consistency (tree, node: childnode);
6763 for (summary = childnode->summary; summary != NULL;
6764 summary = summary->next)
6765 {
6766 for (summary2 = node->summary; ;
6767 summary2 = summary2->next)
6768 {
6769 if (summary2 == NULL)
6770 {
6771 if (summary->info->tag_root == node)
6772 {
6773 break;
6774 }
6775 g_error ("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
6776 summary->info->tag->priv->name,
6777 "present in parent summaries");
6778 }
6779 if (summary->info == summary2->info)
6780 {
6781 break;
6782 }
6783 }
6784 }
6785 num_children++;
6786 num_lines += childnode->num_lines;
6787 num_chars += childnode->num_chars;
6788 }
6789 }
6790 if (num_children != node->num_children)
6791 {
6792 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
6793 num_children, node->num_children);
6794 }
6795 if (num_lines != node->num_lines)
6796 {
6797 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
6798 num_lines, node->num_lines);
6799 }
6800 if (num_chars != node->num_chars)
6801 {
6802 g_error ("gtk_text_btree_node_check_consistency: mismatch in num_chars (%d %d)",
6803 num_chars, node->num_chars);
6804 }
6805
6806 for (summary = node->summary; summary != NULL;
6807 summary = summary->next)
6808 {
6809 if (summary->info->toggle_count == summary->toggle_count)
6810 {
6811 g_error ("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
6812 summary->info->tag->priv->name);
6813 }
6814 toggle_count = 0;
6815 if (node->level == 0)
6816 {
6817 for (line = node->children.line; line != NULL;
6818 line = line->next)
6819 {
6820 for (segPtr = line->segments; segPtr != NULL;
6821 segPtr = segPtr->next)
6822 {
6823 if ((segPtr->type != &gtk_text_toggle_on_type)
6824 && (segPtr->type != &gtk_text_toggle_off_type))
6825 {
6826 continue;
6827 }
6828 if (segPtr->body.toggle.info == summary->info)
6829 {
6830 if (!segPtr->body.toggle.inNodeCounts)
6831 g_error ("Toggle segment not in the node counts");
6832
6833 toggle_count ++;
6834 }
6835 }
6836 }
6837 }
6838 else
6839 {
6840 for (childnode = node->children.node;
6841 childnode != NULL;
6842 childnode = childnode->next)
6843 {
6844 for (summary2 = childnode->summary;
6845 summary2 != NULL;
6846 summary2 = summary2->next)
6847 {
6848 if (summary2->info == summary->info)
6849 {
6850 toggle_count += summary2->toggle_count;
6851 }
6852 }
6853 }
6854 }
6855 if (toggle_count != summary->toggle_count)
6856 {
6857 g_error ("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
6858 toggle_count, summary->toggle_count);
6859 }
6860 for (summary2 = summary->next; summary2 != NULL;
6861 summary2 = summary2->next)
6862 {
6863 if (summary2->info == summary->info)
6864 {
6865 g_error ("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
6866 summary->info->tag->priv->name);
6867 }
6868 }
6869 }
6870}
6871
6872static void
6873listify_foreach (GtkTextTag *tag, gpointer user_data)
6874{
6875 GSList** listp = user_data;
6876
6877 *listp = g_slist_prepend (list: *listp, data: tag);
6878}
6879
6880static GSList*
6881list_of_tags (GtkTextTagTable *table)
6882{
6883 GSList *list = NULL;
6884
6885 gtk_text_tag_table_foreach (table, func: listify_foreach, data: &list);
6886
6887 return list;
6888}
6889
6890void
6891_gtk_text_btree_check (GtkTextBTree *tree)
6892{
6893 Summary *summary;
6894 GtkTextBTreeNode *node;
6895 GtkTextLine *line;
6896 GtkTextLineSegment *seg;
6897 GtkTextTag *tag;
6898 GSList *all_tags, *taglist = NULL;
6899 int count;
6900 GtkTextTagInfo *info;
6901
6902 /*
6903 * Make sure that the tag toggle counts and the tag root pointers are OK.
6904 */
6905 all_tags = list_of_tags (table: tree->table);
6906 for (taglist = all_tags; taglist != NULL ; taglist = taglist->next)
6907 {
6908 tag = taglist->data;
6909 info = gtk_text_btree_get_existing_tag_info (tree, tag);
6910 if (info != NULL)
6911 {
6912 node = info->tag_root;
6913 if (node == NULL)
6914 {
6915 if (info->toggle_count != 0)
6916 {
6917 g_error ("_gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
6918 tag->priv->name, info->toggle_count);
6919 }
6920 continue; /* no ranges for the tag */
6921 }
6922 else if (info->toggle_count == 0)
6923 {
6924 g_error ("_gtk_text_btree_check found root for \"%s\" with no toggles",
6925 tag->priv->name);
6926 }
6927 else if (info->toggle_count & 1)
6928 {
6929 g_error ("_gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
6930 tag->priv->name, info->toggle_count);
6931 }
6932 for (summary = node->summary; summary != NULL;
6933 summary = summary->next)
6934 {
6935 if (summary->info->tag == tag)
6936 {
6937 g_error ("_gtk_text_btree_check found root GtkTextBTreeNode with summary info");
6938 }
6939 }
6940 count = 0;
6941 if (node->level > 0)
6942 {
6943 for (node = node->children.node ; node != NULL ;
6944 node = node->next)
6945 {
6946 for (summary = node->summary; summary != NULL;
6947 summary = summary->next)
6948 {
6949 if (summary->info->tag == tag)
6950 {
6951 count += summary->toggle_count;
6952 }
6953 }
6954 }
6955 }
6956 else
6957 {
6958 const GtkTextLineSegmentClass *last = NULL;
6959
6960 for (line = node->children.line ; line != NULL ;
6961 line = line->next)
6962 {
6963 for (seg = line->segments; seg != NULL;
6964 seg = seg->next)
6965 {
6966 if ((seg->type == &gtk_text_toggle_on_type ||
6967 seg->type == &gtk_text_toggle_off_type) &&
6968 seg->body.toggle.info->tag == tag)
6969 {
6970 if (last == seg->type)
6971 g_error ("Two consecutive toggles on or off weren't merged");
6972 if (!seg->body.toggle.inNodeCounts)
6973 g_error ("Toggle segment not in the node counts");
6974
6975 last = seg->type;
6976
6977 count++;
6978 }
6979 }
6980 }
6981 }
6982 if (count != info->toggle_count)
6983 {
6984 g_error ("_gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
6985 info->toggle_count, tag->priv->name, count);
6986 }
6987 }
6988 }
6989
6990 g_slist_free (list: all_tags);
6991
6992 /*
6993 * Call a recursive procedure to do the main body of checks.
6994 */
6995
6996 node = tree->root_node;
6997 gtk_text_btree_node_check_consistency (tree, node: tree->root_node);
6998
6999 /*
7000 * Make sure that there are at least two lines in the text and
7001 * that the last line has no characters except a newline.
7002 */
7003
7004 if (node->num_lines < 2)
7005 {
7006 g_error ("_gtk_text_btree_check: less than 2 lines in tree");
7007 }
7008 if (node->num_chars < 2)
7009 {
7010 g_error ("_gtk_text_btree_check: less than 2 chars in tree");
7011 }
7012 while (node->level > 0)
7013 {
7014 node = node->children.node;
7015 while (node->next != NULL)
7016 {
7017 node = node->next;
7018 }
7019 }
7020 line = node->children.line;
7021 while (line->next != NULL)
7022 {
7023 line = line->next;
7024 }
7025 seg = line->segments;
7026 while ((seg->type == &gtk_text_toggle_off_type)
7027 || (seg->type == &gtk_text_right_mark_type)
7028 || (seg->type == &gtk_text_left_mark_type))
7029 {
7030 /*
7031 * It’s OK to toggle a tag off in the last line, but
7032 * not to start a new range. It’s also OK to have marks
7033 * in the last line.
7034 */
7035
7036 seg = seg->next;
7037 }
7038 if (seg->type != &gtk_text_char_type)
7039 {
7040 g_error ("_gtk_text_btree_check: last line has bogus segment type");
7041 }
7042 if (seg->next != NULL)
7043 {
7044 g_error ("_gtk_text_btree_check: last line has too many segments");
7045 }
7046 if (seg->byte_count != 1)
7047 {
7048 g_error ("_gtk_text_btree_check: last line has wrong # characters: %d",
7049 seg->byte_count);
7050 }
7051 if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
7052 {
7053 g_error ("_gtk_text_btree_check: last line had bad value: %s",
7054 seg->body.chars);
7055 }
7056}
7057#endif /* G_ENABLE_DEBUG */
7058
7059void _gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line);
7060void _gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment* seg);
7061void _gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent);
7062void _gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
7063
7064void
7065_gtk_text_btree_spew (GtkTextBTree *tree)
7066{
7067 GtkTextLine * line;
7068 int real_line;
7069
7070 printf (format: "%d lines in tree %p\n",
7071 _gtk_text_btree_line_count (tree), tree);
7072
7073 line = _gtk_text_btree_get_line (tree, line_number: 0, real_line_number: &real_line);
7074
7075 while (line != NULL)
7076 {
7077 _gtk_text_btree_spew_line (tree, line);
7078 line = _gtk_text_line_next (line);
7079 }
7080
7081 printf (format: "=================== Tag information\n");
7082
7083 {
7084 GSList * list;
7085
7086 list = tree->tag_infos;
7087
7088 while (list != NULL)
7089 {
7090 GtkTextTagInfo *info;
7091
7092 info = list->data;
7093
7094 printf (format: " tag '%s': root at %p, toggle count %d\n",
7095 info->tag->priv->name, info->tag_root, info->toggle_count);
7096
7097 list = list->next;
7098 }
7099
7100 if (tree->tag_infos == NULL)
7101 {
7102 printf (format: " (no tags in the tree)\n");
7103 }
7104 }
7105
7106 printf (format: "=================== Tree nodes\n");
7107
7108 {
7109 _gtk_text_btree_spew_node (node: tree->root_node, indent: 0);
7110 }
7111}
7112
7113void
7114_gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
7115{
7116 char * spaces;
7117 GtkTextLineSegment *seg;
7118
7119 spaces = g_strnfill (length: indent, fill_char: ' ');
7120
7121 printf (format: "%sline %p chars %d bytes %d\n",
7122 spaces, line,
7123 _gtk_text_line_char_count (line),
7124 _gtk_text_line_byte_count (line));
7125
7126 seg = line->segments;
7127 while (seg != NULL)
7128 {
7129 if (seg->type == &gtk_text_char_type)
7130 {
7131 char * str = g_strndup (str: seg->body.chars, MIN (seg->byte_count, 10));
7132 char * s;
7133 s = str;
7134 while (*s)
7135 {
7136 if (*s == '\n' || *s == '\r')
7137 *s = '\\';
7138 ++s;
7139 }
7140 printf (format: "%s chars '%s'...\n", spaces, str);
7141 g_free (mem: str);
7142 }
7143 else if (seg->type == &gtk_text_child_type)
7144 {
7145 char *str = g_strndup (str: gtk_text_child_anchor_get_replacement (anchor: seg->body.child.obj), n: seg->byte_count);
7146 printf (format: "%s child '%s'...\n", spaces, str);
7147 g_free (mem: str);
7148 }
7149 else if (seg->type == &gtk_text_right_mark_type)
7150 {
7151 printf (format: "%s right mark '%s' visible: %d\n",
7152 spaces,
7153 seg->body.mark.name,
7154 seg->body.mark.visible);
7155 }
7156 else if (seg->type == &gtk_text_left_mark_type)
7157 {
7158 printf (format: "%s left mark '%s' visible: %d\n",
7159 spaces,
7160 seg->body.mark.name,
7161 seg->body.mark.visible);
7162 }
7163 else if (seg->type == &gtk_text_toggle_on_type ||
7164 seg->type == &gtk_text_toggle_off_type)
7165 {
7166 printf (format: "%s tag '%s' %s\n",
7167 spaces, seg->body.toggle.info->tag->priv->name,
7168 seg->type == &gtk_text_toggle_off_type ? "off" : "on");
7169 }
7170
7171 seg = seg->next;
7172 }
7173
7174 g_free (mem: spaces);
7175}
7176
7177void
7178_gtk_text_btree_spew_node (GtkTextBTreeNode *node, int indent)
7179{
7180 char * spaces;
7181 GtkTextBTreeNode *iter;
7182 Summary *s;
7183
7184 spaces = g_strnfill (length: indent, fill_char: ' ');
7185
7186 printf (format: "%snode %p level %d children %d lines %d chars %d\n",
7187 spaces, node, node->level,
7188 node->num_children, node->num_lines, node->num_chars);
7189
7190 s = node->summary;
7191 while (s)
7192 {
7193 printf (format: "%s %d toggles of '%s' below this node\n",
7194 spaces, s->toggle_count, s->info->tag->priv->name);
7195 s = s->next;
7196 }
7197
7198 g_free (mem: spaces);
7199
7200 if (node->level > 0)
7201 {
7202 iter = node->children.node;
7203 while (iter != NULL)
7204 {
7205 _gtk_text_btree_spew_node (node: iter, indent: indent + 2);
7206
7207 iter = iter->next;
7208 }
7209 }
7210 else
7211 {
7212 GtkTextLine *line = node->children.line;
7213 while (line != NULL)
7214 {
7215 _gtk_text_btree_spew_line_short (line, indent: indent + 2);
7216
7217 line = line->next;
7218 }
7219 }
7220}
7221
7222void
7223_gtk_text_btree_spew_line (GtkTextBTree* tree, GtkTextLine* line)
7224{
7225 GtkTextLineSegment * seg;
7226
7227 printf (format: "%4d| line: %p parent: %p next: %p\n",
7228 _gtk_text_line_get_number (line), line, line->parent, line->next);
7229
7230 seg = line->segments;
7231
7232 while (seg != NULL)
7233 {
7234 _gtk_text_btree_spew_segment (tree, seg);
7235 seg = seg->next;
7236 }
7237}
7238
7239void
7240_gtk_text_btree_spew_segment (GtkTextBTree* tree, GtkTextLineSegment * seg)
7241{
7242 printf (format: " segment: %p type: %s bytes: %d chars: %d\n",
7243 seg, seg->type->name, seg->byte_count, seg->char_count);
7244
7245 if (seg->type == &gtk_text_char_type)
7246 {
7247 char * str = g_strndup (str: seg->body.chars, n: seg->byte_count);
7248 printf (format: " '%s'\n", str);
7249 g_free (mem: str);
7250 }
7251 else if (seg->type == &gtk_text_child_type)
7252 {
7253 char *str = g_strndup (str: gtk_text_child_anchor_get_replacement (anchor: seg->body.child.obj), n: seg->byte_count);
7254 printf (format: " '%s'\n", str);
7255 g_free (mem: str);
7256 }
7257 else if (seg->type == &gtk_text_right_mark_type)
7258 {
7259 printf (format: " right mark '%s' visible: %d not_deleteable: %d\n",
7260 seg->body.mark.name,
7261 seg->body.mark.visible,
7262 seg->body.mark.not_deleteable);
7263 }
7264 else if (seg->type == &gtk_text_left_mark_type)
7265 {
7266 printf (format: " left mark '%s' visible: %d not_deleteable: %d\n",
7267 seg->body.mark.name,
7268 seg->body.mark.visible,
7269 seg->body.mark.not_deleteable);
7270 }
7271 else if (seg->type == &gtk_text_toggle_on_type ||
7272 seg->type == &gtk_text_toggle_off_type)
7273 {
7274 printf (format: " tag '%s' priority %d\n",
7275 seg->body.toggle.info->tag->priv->name,
7276 seg->body.toggle.info->tag->priv->priority);
7277 }
7278}
7279

source code of gtk/gtk/gtktextbtree.c