Bug Summary

File:ctk/ctktextbtree.c
Warning:line 3465, column 43
Access to field 'byte_count' results in a dereference of a null pointer (loaded from variable 'last_with_chars')

Annotated Source Code

Press '?' to see keyboard shortcuts

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