Bug Summary

File:ctk/ctktextiter.c
Warning:line 4903, column 20
Out of bound memory access (access exceeds upper limit of memory block)

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 ctktextiter.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-171836-43636-1 -x c ctktextiter.c
1/* CTK - The GIMP Toolkit
2 * ctktextiter.c Copyright (C) 2000 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the CTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the CTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * CTK+ at ftp://ftp.ctk.org/pub/ctk/.
23 */
24
25#define CTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
26#include "config.h"
27#include "ctktextiter.h"
28#include "ctktextbtree.h"
29#include "ctktextbufferprivate.h"
30#include "ctktextiterprivate.h"
31#include "ctkintl.h"
32#include "ctkdebug.h"
33
34#include <string.h>
35
36
37/**
38 * SECTION:ctktextiter
39 * @Short_description: Text buffer iterator
40 * @Title: CtkTextIter
41 *
42 * You may wish to begin by reading the
43 * [text widget conceptual overview][TextWidget]
44 * which gives an overview of all the objects and data
45 * types related to the text widget and how they work together.
46 */
47
48
49#define FIX_OVERFLOWS(varname)if ((varname) == (-2147483647 -1)) (varname) = (-2147483647 -
1) + 1
if ((varname) == G_MININT(-2147483647 -1)) (varname) = G_MININT(-2147483647 -1) + 1
50
51typedef struct _CtkTextRealIter CtkTextRealIter;
52
53struct G_GNUC_MAY_ALIAS__attribute__((may_alias)) _CtkTextRealIter
54{
55 /* Always-valid information */
56 CtkTextBTree *tree;
57 CtkTextLine *line;
58 /* At least one of these is always valid;
59 if invalid, they are -1.
60
61 If the line byte offset is valid, so is the segment byte offset;
62 and ditto for char offsets. */
63 gint line_byte_offset;
64 gint line_char_offset;
65 /* These two are valid if >= 0 */
66 gint cached_char_index;
67 gint cached_line_number;
68 /* Stamps to detect the buffer changing under us */
69 gint chars_changed_stamp;
70 gint segments_changed_stamp;
71 /* Valid if the segments_changed_stamp is up-to-date */
72 CtkTextLineSegment *segment; /* indexable segment we index */
73 CtkTextLineSegment *any_segment; /* first segment in our location,
74 maybe same as "segment" */
75 /* One of these will always be valid if segments_changed_stamp is
76 up-to-date. If invalid, they are -1.
77
78 If the line byte offset is valid, so is the segment byte offset;
79 and ditto for char offsets. */
80 gint segment_byte_offset;
81 gint segment_char_offset;
82
83 /* padding */
84 gint pad1;
85 gpointer pad2;
86};
87
88/* These "set" functions should not assume any fields
89 other than the char stamp and the tree are valid.
90*/
91static void
92iter_set_common (CtkTextRealIter *iter,
93 CtkTextLine *line)
94{
95 /* Update segments stamp */
96 iter->segments_changed_stamp =
97 _ctk_text_btree_get_segments_changed_stamp (iter->tree);
98
99 iter->line = line;
100
101 iter->line_byte_offset = -1;
102 iter->line_char_offset = -1;
103 iter->segment_byte_offset = -1;
104 iter->segment_char_offset = -1;
105 iter->cached_char_index = -1;
106 iter->cached_line_number = -1;
107}
108
109static void
110iter_set_from_byte_offset (CtkTextRealIter *iter,
111 CtkTextLine *line,
112 gint byte_offset)
113{
114 iter_set_common (iter, line);
115
116 if (!_ctk_text_line_byte_locate (iter->line,
117 byte_offset,
118 &iter->segment,
119 &iter->any_segment,
120 &iter->segment_byte_offset,
121 &iter->line_byte_offset))
122 g_error ("Byte index %d is off the end of the line",
123 byte_offset);
124}
125
126static void
127iter_set_from_char_offset (CtkTextRealIter *iter,
128 CtkTextLine *line,
129 gint char_offset)
130{
131 iter_set_common (iter, line);
132
133 if (!_ctk_text_line_char_locate (iter->line,
134 char_offset,
135 &iter->segment,
136 &iter->any_segment,
137 &iter->segment_char_offset,
138 &iter->line_char_offset))
139 g_error ("Char offset %d is off the end of the line",
140 char_offset);
141}
142
143static void
144iter_set_from_segment (CtkTextRealIter *iter,
145 CtkTextLine *line,
146 CtkTextLineSegment *segment)
147{
148 CtkTextLineSegment *seg;
149 gint byte_offset;
150
151 /* This could theoretically be optimized by computing all the iter
152 fields in this same loop, but I'm skipping it for now. */
153 byte_offset = 0;
154 seg = line->segments;
155 while (seg != segment)
156 {
157 byte_offset += seg->byte_count;
158 seg = seg->next;
159 }
160
161 iter_set_from_byte_offset (iter, line, byte_offset);
162}
163
164/* This function ensures that the segment-dependent information is
165 truly computed lazily; often we don't need to do the full make_real
166 work. This ensures the btree and line are valid, but doesn't
167 update the segments. */
168static CtkTextRealIter*
169ctk_text_iter_make_surreal (const CtkTextIter *_iter)
170{
171 CtkTextRealIter *iter = (CtkTextRealIter*)_iter;
172
173 if (iter->chars_changed_stamp !=
174 _ctk_text_btree_get_chars_changed_stamp (iter->tree))
175 {
176 g_warning ("Invalid text buffer iterator: either the iterator "
177 "is uninitialized, or the characters/pixbufs/widgets "
178 "in the buffer have been modified since the iterator "
179 "was created.\nYou must use marks, character numbers, "
180 "or line numbers to preserve a position across buffer "
181 "modifications.\nYou can apply tags and insert marks "
182 "without invalidating your iterators,\n"
183 "but any mutation that affects 'indexable' buffer contents "
184 "(contents that can be referred to by character offset)\n"
185 "will invalidate all outstanding iterators");
186 return NULL((void*)0);
187 }
188
189 /* We don't update the segments information since we are becoming
190 only surreal. However we do invalidate the segments information
191 if appropriate, to be sure we segfault if we try to use it and we
192 should have used make_real. */
193
194 if (iter->segments_changed_stamp !=
195 _ctk_text_btree_get_segments_changed_stamp (iter->tree))
196 {
197 iter->segment = NULL((void*)0);
198 iter->any_segment = NULL((void*)0);
199 /* set to segfault-causing values. */
200 iter->segment_byte_offset = -10000;
201 iter->segment_char_offset = -10000;
202 }
203
204 return iter;
205}
206
207static CtkTextRealIter*
208ctk_text_iter_make_real (const CtkTextIter *_iter)
209{
210 CtkTextRealIter *iter;
211
212 iter = ctk_text_iter_make_surreal (_iter);
213
214 if (iter->segments_changed_stamp !=
215 _ctk_text_btree_get_segments_changed_stamp (iter->tree))
216 {
217 if (iter->line_byte_offset >= 0)
218 {
219 iter_set_from_byte_offset (iter,
220 iter->line,
221 iter->line_byte_offset);
222 }
223 else
224 {
225 g_assert (iter->line_char_offset >= 0)do { if (iter->line_char_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 225, ((const char*) (__func__)), "iter->line_char_offset >= 0"
); } while (0)
;
226
227 iter_set_from_char_offset (iter,
228 iter->line,
229 iter->line_char_offset);
230 }
231 }
232
233 g_assert (iter->segment != NULL)do { if (iter->segment != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 233, ((const char*) (__func__)), "iter->segment != NULL"
); } while (0)
;
234 g_assert (iter->any_segment != NULL)do { if (iter->any_segment != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 234, ((const char*) (__func__)), "iter->any_segment != NULL"
); } while (0)
;
235 g_assert (iter->segment->char_count > 0)do { if (iter->segment->char_count > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 235, ((const char*) (__func__)), "iter->segment->char_count > 0"
); } while (0)
;
236
237 return iter;
238}
239
240static CtkTextRealIter*
241iter_init_common (CtkTextIter *_iter,
242 CtkTextBTree *tree)
243{
244 CtkTextRealIter *iter = (CtkTextRealIter*)_iter;
245
246 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
247 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)
;
248
249 memset (iter, 0, sizeof (CtkTextRealIter));
250
251 iter->tree = tree;
252
253 iter->chars_changed_stamp =
254 _ctk_text_btree_get_chars_changed_stamp (iter->tree);
255
256 return iter;
257}
258
259static CtkTextRealIter*
260iter_init_from_segment (CtkTextIter *iter,
261 CtkTextBTree *tree,
262 CtkTextLine *line,
263 CtkTextLineSegment *segment)
264{
265 CtkTextRealIter *real;
266
267 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)
;
268
269 real = iter_init_common (iter, tree);
270
271 iter_set_from_segment (real, line, segment);
272
273 return real;
274}
275
276static CtkTextRealIter*
277iter_init_from_byte_offset (CtkTextIter *iter,
278 CtkTextBTree *tree,
279 CtkTextLine *line,
280 gint line_byte_offset)
281{
282 CtkTextRealIter *real;
283
284 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)
;
285
286 real = iter_init_common (iter, tree);
287
288 iter_set_from_byte_offset (real, line, line_byte_offset);
289
290 if (real->segment->type == &ctk_text_char_type &&
291 (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
292 g_warning ("Incorrect line byte index %d falls in the middle of a UTF-8 "
293 "character; this will crash the text buffer. "
294 "Byte indexes must refer to the start of a character.",
295 line_byte_offset);
296
297 return real;
298}
299
300static CtkTextRealIter*
301iter_init_from_char_offset (CtkTextIter *iter,
302 CtkTextBTree *tree,
303 CtkTextLine *line,
304 gint line_char_offset)
305{
306 CtkTextRealIter *real;
307
308 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)
;
309
310 real = iter_init_common (iter, tree);
311
312 iter_set_from_char_offset (real, line, line_char_offset);
313
314 return real;
315}
316
317static inline void
318invalidate_char_index (CtkTextRealIter *iter)
319{
320 iter->cached_char_index = -1;
321}
322
323static inline void
324adjust_char_index (CtkTextRealIter *iter, gint count)
325{
326 if (iter->cached_char_index >= 0)
327 iter->cached_char_index += count;
328}
329
330static inline void
331adjust_line_number (CtkTextRealIter *iter, gint count)
332{
333 if (iter->cached_line_number >= 0)
334 iter->cached_line_number += count;
335}
336
337static inline void
338ensure_char_offsets (CtkTextRealIter *iter)
339{
340 if (iter->line_char_offset < 0)
341 {
342 g_assert (iter->line_byte_offset >= 0)do { if (iter->line_byte_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 342, ((const char*) (__func__)), "iter->line_byte_offset >= 0"
); } while (0)
;
343
344 _ctk_text_line_byte_to_char_offsets (iter->line,
345 iter->line_byte_offset,
346 &iter->line_char_offset,
347 &iter->segment_char_offset);
348 }
349}
350
351static inline void
352ensure_byte_offsets (CtkTextRealIter *iter)
353{
354 if (iter->line_byte_offset < 0)
355 {
356 g_assert (iter->line_char_offset >= 0)do { if (iter->line_char_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 356, ((const char*) (__func__)), "iter->line_char_offset >= 0"
); } while (0)
;
357
358 _ctk_text_line_char_to_byte_offsets (iter->line,
359 iter->line_char_offset,
360 &iter->line_byte_offset,
361 &iter->segment_byte_offset);
362 }
363}
364
365static inline gboolean
366is_segment_start (CtkTextRealIter *real)
367{
368 return real->segment_byte_offset == 0 || real->segment_char_offset == 0;
369}
370
371#ifdef G_ENABLE_DEBUG1
372static void
373check_invariants (const CtkTextIter *iter)
374{
375 if (CTK_DEBUG_CHECK (TEXT)(ctk_get_debug_flags () & CTK_DEBUG_TEXT))
376 _ctk_text_iter_check (iter);
377}
378#else
379#define check_invariants(x)
380#endif
381
382/**
383 * ctk_text_iter_get_buffer:
384 * @iter: an iterator
385 *
386 * Returns the #CtkTextBuffer this iterator is associated with.
387 *
388 * Returns: (transfer none): the buffer
389 **/
390CtkTextBuffer*
391ctk_text_iter_get_buffer (const CtkTextIter *iter)
392{
393 CtkTextRealIter *real;
394
395 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
396
397 real = ctk_text_iter_make_surreal (iter);
398
399 if (real == NULL((void*)0))
400 return NULL((void*)0);
401
402 check_invariants (iter);
403
404 return _ctk_text_btree_get_buffer (real->tree);
405}
406
407/**
408 * ctk_text_iter_copy:
409 * @iter: an iterator
410 *
411 * Creates a dynamically-allocated copy of an iterator. This function
412 * is not useful in applications, because iterators can be copied with a
413 * simple assignment (`CtkTextIter i = j;`). The
414 * function is used by language bindings.
415 *
416 * Returns: a copy of the @iter, free with ctk_text_iter_free()
417 **/
418CtkTextIter*
419ctk_text_iter_copy (const CtkTextIter *iter)
420{
421 CtkTextIter *new_iter;
422
423 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
424
425 new_iter = g_slice_new (CtkTextIter)((CtkTextIter*) g_slice_alloc (sizeof (CtkTextIter)));
426
427 *new_iter = *iter;
428
429 return new_iter;
430}
431
432/**
433 * ctk_text_iter_free:
434 * @iter: a dynamically-allocated iterator
435 *
436 * Free an iterator allocated on the heap. This function
437 * is intended for use in language bindings, and is not
438 * especially useful for applications, because iterators can
439 * simply be allocated on the stack.
440 **/
441void
442ctk_text_iter_free (CtkTextIter *iter)
443{
444 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)
;
445
446 g_slice_free (CtkTextIter, iter)do { if (1) g_slice_free1 (sizeof (CtkTextIter), (iter)); else
(void) ((CtkTextIter*) 0 == (iter)); } while (0)
;
447}
448
449/**
450 * ctk_text_iter_assign:
451 * @iter: a #CtkTextIter
452 * @other: another #CtkTextIter
453 *
454 * Assigns the value of @other to @iter. This function
455 * is not useful in applications, because iterators can be assigned
456 * with `CtkTextIter i = j;`. The
457 * function is used by language bindings.
458 *
459 * Since: 3.2
460 **/
461void
462ctk_text_iter_assign (CtkTextIter *iter,
463 const CtkTextIter *other)
464{
465 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)
;
466 g_return_if_fail (other != NULL)do { if ((other != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "other != NULL"); return
; } } while (0)
;
467
468 *iter = *other;
469}
470
471G_DEFINE_BOXED_TYPE (CtkTextIter, ctk_text_iter,static GType ctk_text_iter_get_type_once (void); GType ctk_text_iter_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_text_iter_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_text_iter_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkTextIter
* (*do_copy_type) (CtkTextIter *); CtkTextIter * (*do_const_copy_type
) (const CtkTextIter *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkTextIter
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkTextIter"), ctk_text_iter_copy
, ctk_text_iter_free); { {{};} } return g_define_type_id; }
472 ctk_text_iter_copy,static GType ctk_text_iter_get_type_once (void); GType ctk_text_iter_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_text_iter_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_text_iter_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkTextIter
* (*do_copy_type) (CtkTextIter *); CtkTextIter * (*do_const_copy_type
) (const CtkTextIter *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkTextIter
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkTextIter"), ctk_text_iter_copy
, ctk_text_iter_free); { {{};} } return g_define_type_id; }
473 ctk_text_iter_free)static GType ctk_text_iter_get_type_once (void); GType ctk_text_iter_get_type
(void) { static GType static_g_define_type_id = 0; if ((__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); (void) (
0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0))
; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_text_iter_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_text_iter_get_type_once (void
) { GType (* _g_register_boxed) (const gchar *, union { CtkTextIter
* (*do_copy_type) (CtkTextIter *); CtkTextIter * (*do_const_copy_type
) (const CtkTextIter *); GBoxedCopyFunc do_copy_boxed; } __attribute__
((__transparent_union__)), union { void (* do_free_type) (CtkTextIter
*); GBoxedFreeFunc do_free_boxed; } __attribute__((__transparent_union__
)) ) = g_boxed_type_register_static; GType g_define_type_id =
_g_register_boxed (g_intern_static_string ("CtkTextIter"), ctk_text_iter_copy
, ctk_text_iter_free); { {{};} } return g_define_type_id; }
474
475CtkTextLineSegment*
476_ctk_text_iter_get_indexable_segment (const CtkTextIter *iter)
477{
478 CtkTextRealIter *real;
479
480 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
481
482 real = ctk_text_iter_make_real (iter);
483
484 if (real == NULL((void*)0))
485 return NULL((void*)0);
486
487 check_invariants (iter);
488
489 g_assert (real->segment != NULL)do { if (real->segment != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 489, ((const char*) (__func__)), "real->segment != NULL"
); } while (0)
;
490
491 return real->segment;
492}
493
494CtkTextLineSegment*
495_ctk_text_iter_get_any_segment (const CtkTextIter *iter)
496{
497 CtkTextRealIter *real;
498
499 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
500
501 real = ctk_text_iter_make_real (iter);
502
503 if (real == NULL((void*)0))
504 return NULL((void*)0);
505
506 check_invariants (iter);
507
508 g_assert (real->any_segment != NULL)do { if (real->any_segment != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 508, ((const char*) (__func__)), "real->any_segment != NULL"
); } while (0)
;
509
510 return real->any_segment;
511}
512
513gint
514_ctk_text_iter_get_segment_byte (const CtkTextIter *iter)
515{
516 CtkTextRealIter *real;
517
518 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
519
520 real = ctk_text_iter_make_real (iter);
521
522 if (real == NULL((void*)0))
523 return 0;
524
525 ensure_byte_offsets (real);
526
527 check_invariants (iter);
528
529 return real->segment_byte_offset;
530}
531
532gint
533_ctk_text_iter_get_segment_char (const CtkTextIter *iter)
534{
535 CtkTextRealIter *real;
536
537 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
538
539 real = ctk_text_iter_make_real (iter);
540
541 if (real == NULL((void*)0))
542 return 0;
543
544 ensure_char_offsets (real);
545
546 check_invariants (iter);
547
548 return real->segment_char_offset;
549}
550
551/* This function does not require a still-valid
552 iterator */
553CtkTextLine*
554_ctk_text_iter_get_text_line (const CtkTextIter *iter)
555{
556 const CtkTextRealIter *real;
557
558 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
559
560 real = (const CtkTextRealIter*)iter;
561
562 return real->line;
563}
564
565/* This function does not require a still-valid
566 iterator */
567CtkTextBTree*
568_ctk_text_iter_get_btree (const CtkTextIter *iter)
569{
570 const CtkTextRealIter *real;
571
572 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
573
574 real = (const CtkTextRealIter*)iter;
575
576 return real->tree;
577}
578
579/*
580 * Conversions
581 */
582
583/**
584 * ctk_text_iter_get_offset:
585 * @iter: an iterator
586 *
587 * Returns the character offset of an iterator.
588 * Each character in a #CtkTextBuffer has an offset,
589 * starting with 0 for the first character in the buffer.
590 * Use ctk_text_buffer_get_iter_at_offset() to convert an
591 * offset back into an iterator.
592 *
593 * Returns: a character offset
594 **/
595gint
596ctk_text_iter_get_offset (const CtkTextIter *iter)
597{
598 CtkTextRealIter *real;
599
600 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
601
602 real = ctk_text_iter_make_surreal (iter);
603
604 if (real == NULL((void*)0))
605 return 0;
606
607 check_invariants (iter);
608
609 if (real->cached_char_index < 0)
610 {
611 ensure_char_offsets (real);
612
613 real->cached_char_index =
614 _ctk_text_line_char_index (real->line);
615 real->cached_char_index += real->line_char_offset;
616 }
617
618 check_invariants (iter);
619
620 return real->cached_char_index;
621}
622
623/**
624 * ctk_text_iter_get_line:
625 * @iter: an iterator
626 *
627 * Returns the line number containing the iterator. Lines in
628 * a #CtkTextBuffer are numbered beginning with 0 for the first
629 * line in the buffer.
630 *
631 * Returns: a line number
632 **/
633gint
634ctk_text_iter_get_line (const CtkTextIter *iter)
635{
636 CtkTextRealIter *real;
637
638 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
639
640 real = ctk_text_iter_make_surreal (iter);
641
642 if (real == NULL((void*)0))
643 return 0;
644
645 if (real->cached_line_number < 0)
646 real->cached_line_number =
647 _ctk_text_line_get_number (real->line);
648
649 check_invariants (iter);
650
651 return real->cached_line_number;
652}
653
654/**
655 * ctk_text_iter_get_line_offset:
656 * @iter: an iterator
657 *
658 * Returns the character offset of the iterator,
659 * counting from the start of a newline-terminated line.
660 * The first character on the line has offset 0.
661 *
662 * Returns: offset from start of line
663 **/
664gint
665ctk_text_iter_get_line_offset (const CtkTextIter *iter)
666{
667 CtkTextRealIter *real;
668
669 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
670
671 real = ctk_text_iter_make_surreal (iter);
672
673 if (real == NULL((void*)0))
674 return 0;
675
676 ensure_char_offsets (real);
677
678 check_invariants (iter);
679
680 return real->line_char_offset;
681}
682
683/**
684 * ctk_text_iter_get_line_index:
685 * @iter: an iterator
686 *
687 * Returns the byte index of the iterator, counting
688 * from the start of a newline-terminated line.
689 * Remember that #CtkTextBuffer encodes text in
690 * UTF-8, and that characters can require a variable
691 * number of bytes to represent.
692 *
693 * Returns: distance from start of line, in bytes
694 **/
695gint
696ctk_text_iter_get_line_index (const CtkTextIter *iter)
697{
698 CtkTextRealIter *real;
699
700 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
701
702 real = ctk_text_iter_make_surreal (iter);
703
704 if (real == NULL((void*)0))
705 return 0;
706
707 ensure_byte_offsets (real);
708
709 check_invariants (iter);
710
711 return real->line_byte_offset;
712}
713
714/**
715 * ctk_text_iter_get_visible_line_offset:
716 * @iter: a #CtkTextIter
717 *
718 * Returns the offset in characters from the start of the
719 * line to the given @iter, not counting characters that
720 * are invisible due to tags with the “invisible” flag
721 * toggled on.
722 *
723 * Returns: offset in visible characters from the start of the line
724 **/
725gint
726ctk_text_iter_get_visible_line_offset (const CtkTextIter *iter)
727{
728 CtkTextRealIter *real;
729 gint vis_offset;
730 CtkTextLineSegment *seg;
731 CtkTextIter pos;
732
733 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
734
735 real = ctk_text_iter_make_real (iter);
736
737 if (real == NULL((void*)0))
738 return 0;
739
740 ensure_char_offsets (real);
741
742 check_invariants (iter);
743
744 vis_offset = real->line_char_offset;
745
746 g_assert (vis_offset >= 0)do { if (vis_offset >= 0) ; else g_assertion_message_expr (
"Ctk", "ctktextiter.c", 746, ((const char*) (__func__)), "vis_offset >= 0"
); } while (0)
;
747
748 _ctk_text_btree_get_iter_at_line (real->tree,
749 &pos,
750 real->line,
751 0);
752
753 seg = _ctk_text_iter_get_indexable_segment (&pos);
754
755 while (seg != real->segment)
756 {
757 /* This is a pretty expensive call, making the
758 * whole function pretty lame; we could keep track
759 * of current invisibility state by looking at toggle
760 * segments as we loop, and then call this function
761 * only once per line, in order to speed up the loop
762 * quite a lot.
763 */
764 if (_ctk_text_btree_char_is_invisible (&pos))
765 vis_offset -= seg->char_count;
766
767 _ctk_text_iter_forward_indexable_segment (&pos);
768
769 seg = _ctk_text_iter_get_indexable_segment (&pos);
770 }
771
772 if (_ctk_text_btree_char_is_invisible (&pos))
773 vis_offset -= real->segment_char_offset;
774
775 return vis_offset;
776}
777
778
779/**
780 * ctk_text_iter_get_visible_line_index:
781 * @iter: a #CtkTextIter
782 *
783 * Returns the number of bytes from the start of the
784 * line to the given @iter, not counting bytes that
785 * are invisible due to tags with the “invisible” flag
786 * toggled on.
787 *
788 * Returns: byte index of @iter with respect to the start of the line
789 **/
790gint
791ctk_text_iter_get_visible_line_index (const CtkTextIter *iter)
792{
793 CtkTextRealIter *real;
794 gint vis_offset;
795 CtkTextLineSegment *seg;
796 CtkTextIter pos;
797
798 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
799
800 real = ctk_text_iter_make_real (iter);
801
802 if (real == NULL((void*)0))
803 return 0;
804
805 ensure_byte_offsets (real);
806
807 check_invariants (iter);
808
809 vis_offset = real->line_byte_offset;
810
811 g_assert (vis_offset >= 0)do { if (vis_offset >= 0) ; else g_assertion_message_expr (
"Ctk", "ctktextiter.c", 811, ((const char*) (__func__)), "vis_offset >= 0"
); } while (0)
;
812
813 _ctk_text_btree_get_iter_at_line (real->tree,
814 &pos,
815 real->line,
816 0);
817
818 seg = _ctk_text_iter_get_indexable_segment (&pos);
819
820 while (seg != real->segment)
821 {
822 /* This is a pretty expensive call, making the
823 * whole function pretty lame; we could keep track
824 * of current invisibility state by looking at toggle
825 * segments as we loop, and then call this function
826 * only once per line, in order to speed up the loop
827 * quite a lot.
828 */
829 if (_ctk_text_btree_char_is_invisible (&pos))
830 vis_offset -= seg->byte_count;
831
832 _ctk_text_iter_forward_indexable_segment (&pos);
833
834 seg = _ctk_text_iter_get_indexable_segment (&pos);
835 }
836
837 if (_ctk_text_btree_char_is_invisible (&pos))
838 vis_offset -= real->segment_byte_offset;
839
840 return vis_offset;
841}
842
843/*
844 * Dereferencing
845 */
846
847/**
848 * ctk_text_iter_get_char:
849 * @iter: an iterator
850 *
851 * The Unicode character at this iterator is returned. (Equivalent to
852 * operator* on a C++ iterator.) If the element at this iterator is a
853 * non-character element, such as an image embedded in the buffer, the
854 * Unicode “unknown” character 0xFFFC is returned. If invoked on
855 * the end iterator, zero is returned; zero is not a valid Unicode character.
856 * So you can write a loop which ends when ctk_text_iter_get_char()
857 * returns 0.
858 *
859 * Returns: a Unicode character, or 0 if @iter is not dereferenceable
860 */
861gunichar
862ctk_text_iter_get_char (const CtkTextIter *iter)
863{
864 CtkTextRealIter *real;
865
866 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
867
868 real = ctk_text_iter_make_real (iter);
869
870 if (real == NULL((void*)0))
871 return 0;
872
873 check_invariants (iter);
874
875 if (ctk_text_iter_is_end (iter))
876 return 0;
877 else if (real->segment->type == &ctk_text_char_type)
878 {
879 ensure_byte_offsets (real);
880
881 return g_utf8_get_char (real->segment->body.chars +
882 real->segment_byte_offset);
883 }
884 else
885 {
886 /* Unicode "unknown character" 0xFFFC */
887 return CTK_TEXT_UNKNOWN_CHAR0xFFFC;
888 }
889}
890
891/**
892 * ctk_text_iter_get_slice:
893 * @start: iterator at start of a range
894 * @end: iterator at end of a range
895 *
896 * Returns the text in the given range. A “slice” is an array of
897 * characters encoded in UTF-8 format, including the Unicode “unknown”
898 * character 0xFFFC for iterable non-character elements in the buffer,
899 * such as images. Because images are encoded in the slice, byte and
900 * character offsets in the returned array will correspond to byte
901 * offsets in the text buffer. Note that 0xFFFC can occur in normal
902 * text as well, so it is not a reliable indicator that a pixbuf or
903 * widget is in the buffer.
904 *
905 * Returns: (transfer full): slice of text from the buffer
906 **/
907gchar*
908ctk_text_iter_get_slice (const CtkTextIter *start,
909 const CtkTextIter *end)
910{
911 g_return_val_if_fail (start != NULL, NULL)do { if ((start != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "start != NULL"); return
(((void*)0)); } } while (0)
;
912 g_return_val_if_fail (end != NULL, NULL)do { if ((end != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "end != NULL"); return (
((void*)0)); } } while (0)
;
913
914 check_invariants (start);
915 check_invariants (end);
916
917 return _ctk_text_btree_get_text (start, end, TRUE(!(0)), TRUE(!(0)));
918}
919
920/**
921 * ctk_text_iter_get_text:
922 * @start: iterator at start of a range
923 * @end: iterator at end of a range
924 *
925 * Returns text in the given range. If the range
926 * contains non-text elements such as images, the character and byte
927 * offsets in the returned string will not correspond to character and
928 * byte offsets in the buffer. If you want offsets to correspond, see
929 * ctk_text_iter_get_slice().
930 *
931 * Returns: (transfer full): array of characters from the buffer
932 **/
933gchar*
934ctk_text_iter_get_text (const CtkTextIter *start,
935 const CtkTextIter *end)
936{
937 g_return_val_if_fail (start != NULL, NULL)do { if ((start != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "start != NULL"); return
(((void*)0)); } } while (0)
;
938 g_return_val_if_fail (end != NULL, NULL)do { if ((end != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "end != NULL"); return (
((void*)0)); } } while (0)
;
939
940 check_invariants (start);
941 check_invariants (end);
942
943 return _ctk_text_btree_get_text (start, end, TRUE(!(0)), FALSE(0));
944}
945
946/**
947 * ctk_text_iter_get_visible_slice:
948 * @start: iterator at start of range
949 * @end: iterator at end of range
950 *
951 * Like ctk_text_iter_get_slice(), but invisible text is not included.
952 * Invisible text is usually invisible because a #CtkTextTag with the
953 * “invisible” attribute turned on has been applied to it.
954 *
955 * Returns: (transfer full): slice of text from the buffer
956 **/
957gchar*
958ctk_text_iter_get_visible_slice (const CtkTextIter *start,
959 const CtkTextIter *end)
960{
961 g_return_val_if_fail (start != NULL, NULL)do { if ((start != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "start != NULL"); return
(((void*)0)); } } while (0)
;
962 g_return_val_if_fail (end != NULL, NULL)do { if ((end != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "end != NULL"); return (
((void*)0)); } } while (0)
;
963
964 check_invariants (start);
965 check_invariants (end);
966
967 return _ctk_text_btree_get_text (start, end, FALSE(0), TRUE(!(0)));
968}
969
970/**
971 * ctk_text_iter_get_visible_text:
972 * @start: iterator at start of range
973 * @end: iterator at end of range
974 *
975 * Like ctk_text_iter_get_text(), but invisible text is not included.
976 * Invisible text is usually invisible because a #CtkTextTag with the
977 * “invisible” attribute turned on has been applied to it.
978 *
979 * Returns: (transfer full): string containing visible text in the
980 * range
981 **/
982gchar*
983ctk_text_iter_get_visible_text (const CtkTextIter *start,
984 const CtkTextIter *end)
985{
986 g_return_val_if_fail (start != NULL, NULL)do { if ((start != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "start != NULL"); return
(((void*)0)); } } while (0)
;
987 g_return_val_if_fail (end != NULL, NULL)do { if ((end != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "end != NULL"); return (
((void*)0)); } } while (0)
;
988
989 check_invariants (start);
990 check_invariants (end);
991
992 return _ctk_text_btree_get_text (start, end, FALSE(0), FALSE(0));
993}
994
995/**
996 * ctk_text_iter_get_pixbuf:
997 * @iter: an iterator
998 *
999 * If the element at @iter is a pixbuf, the pixbuf is returned
1000 * (with no new reference count added). Otherwise,
1001 * %NULL is returned.
1002 *
1003 * Returns: (transfer none): the pixbuf at @iter
1004 **/
1005GdkPixbuf*
1006ctk_text_iter_get_pixbuf (const CtkTextIter *iter)
1007{
1008 CtkTextRealIter *real;
1009
1010 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
1011
1012 real = ctk_text_iter_make_real (iter);
1013
1014 if (real == NULL((void*)0))
1015 return NULL((void*)0);
1016
1017 check_invariants (iter);
1018
1019 if (real->segment->type != &ctk_text_pixbuf_type)
1020 return NULL((void*)0);
1021 else
1022 return real->segment->body.pixbuf.pixbuf;
1023}
1024
1025/**
1026 * ctk_text_iter_get_child_anchor:
1027 * @iter: an iterator
1028 *
1029 * If the location at @iter contains a child anchor, the
1030 * anchor is returned (with no new reference count added). Otherwise,
1031 * %NULL is returned.
1032 *
1033 * Returns: (transfer none): the anchor at @iter
1034 **/
1035CtkTextChildAnchor*
1036ctk_text_iter_get_child_anchor (const CtkTextIter *iter)
1037{
1038 CtkTextRealIter *real;
1039
1040 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
1041
1042 real = ctk_text_iter_make_real (iter);
1043
1044 if (real == NULL((void*)0))
1045 return NULL((void*)0);
1046
1047 check_invariants (iter);
1048
1049 if (real->segment->type != &ctk_text_child_type)
1050 return NULL((void*)0);
1051 else
1052 return real->segment->body.child.obj;
1053}
1054
1055/**
1056 * ctk_text_iter_get_marks:
1057 * @iter: an iterator
1058 *
1059 * Returns a list of all #CtkTextMark at this location. Because marks
1060 * are not iterable (they don’t take up any "space" in the buffer,
1061 * they are just marks in between iterable locations), multiple marks
1062 * can exist in the same place. The returned list is not in any
1063 * meaningful order.
1064 *
1065 * Returns: (element-type CtkTextMark) (transfer container): list of #CtkTextMark
1066 **/
1067GSList*
1068ctk_text_iter_get_marks (const CtkTextIter *iter)
1069{
1070 CtkTextRealIter *real;
1071 CtkTextLineSegment *seg;
1072 GSList *retval;
1073
1074 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
1075
1076 real = ctk_text_iter_make_real (iter);
1077
1078 if (real == NULL((void*)0))
1079 return NULL((void*)0);
1080
1081 check_invariants (iter);
1082
1083 retval = NULL((void*)0);
1084 seg = real->any_segment;
1085 while (seg != real->segment)
1086 {
1087 if (seg->type == &ctk_text_left_mark_type ||
1088 seg->type == &ctk_text_right_mark_type)
1089 retval = g_slist_prepend (retval, seg->body.mark.obj);
1090
1091 seg = seg->next;
1092 }
1093
1094 /* The returned list isn't guaranteed to be in any special order,
1095 and it isn't. */
1096 return retval;
1097}
1098
1099/**
1100 * ctk_text_iter_get_toggled_tags:
1101 * @iter: an iterator
1102 * @toggled_on: %TRUE to get toggled-on tags
1103 *
1104 * Returns a list of #CtkTextTag that are toggled on or off at this
1105 * point. (If @toggled_on is %TRUE, the list contains tags that are
1106 * toggled on.) If a tag is toggled on at @iter, then some non-empty
1107 * range of characters following @iter has that tag applied to it. If
1108 * a tag is toggled off, then some non-empty range following @iter
1109 * does not have the tag applied to it.
1110 *
1111 * Returns: (element-type CtkTextTag) (transfer container): tags toggled at this point
1112 **/
1113GSList*
1114ctk_text_iter_get_toggled_tags (const CtkTextIter *iter,
1115 gboolean toggled_on)
1116{
1117 CtkTextRealIter *real;
1118 CtkTextLineSegment *seg;
1119 GSList *retval;
1120
1121 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
1122
1123 real = ctk_text_iter_make_real (iter);
1124
1125 if (real == NULL((void*)0))
1126 return NULL((void*)0);
1127
1128 check_invariants (iter);
1129
1130 retval = NULL((void*)0);
1131 seg = real->any_segment;
1132 while (seg != real->segment)
1133 {
1134 if (toggled_on)
1135 {
1136 if (seg->type == &ctk_text_toggle_on_type)
1137 {
1138 retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1139 }
1140 }
1141 else
1142 {
1143 if (seg->type == &ctk_text_toggle_off_type)
1144 {
1145 retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1146 }
1147 }
1148
1149 seg = seg->next;
1150 }
1151
1152 /* The returned list isn't guaranteed to be in any special order,
1153 and it isn't. */
1154 return retval;
1155}
1156
1157/**
1158 * ctk_text_iter_starts_tag:
1159 * @iter: an iterator
1160 * @tag: (nullable): a #CtkTextTag, or %NULL
1161 *
1162 * Returns %TRUE if @tag is toggled on at exactly this point. If @tag
1163 * is %NULL, returns %TRUE if any tag is toggled on at this point.
1164 *
1165 * Note that if ctk_text_iter_starts_tag() returns %TRUE, it means that @iter is
1166 * at the beginning of the tagged range, and that the
1167 * character at @iter is inside the tagged range. In other
1168 * words, unlike ctk_text_iter_ends_tag(), if ctk_text_iter_starts_tag() returns
1169 * %TRUE, ctk_text_iter_has_tag() will also return %TRUE for the same
1170 * parameters.
1171 *
1172 * Returns: whether @iter is the start of a range tagged with @tag
1173 * Since: 3.20
1174 **/
1175gboolean
1176ctk_text_iter_starts_tag (const CtkTextIter *iter,
1177 CtkTextTag *tag)
1178{
1179 CtkTextRealIter *real;
1180 CtkTextLineSegment *seg;
1181
1182 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1183
1184 real = ctk_text_iter_make_real (iter);
1185
1186 if (real == NULL((void*)0))
1187 return FALSE(0);
1188
1189 check_invariants (iter);
1190
1191 seg = real->any_segment;
1192 while (seg != real->segment)
1193 {
1194 if (seg->type == &ctk_text_toggle_on_type)
1195 {
1196 if (tag == NULL((void*)0) ||
1197 seg->body.toggle.info->tag == tag)
1198 return TRUE(!(0));
1199 }
1200
1201 seg = seg->next;
1202 }
1203
1204 return FALSE(0);
1205}
1206
1207/**
1208 * ctk_text_iter_begins_tag:
1209 * @iter: an iterator
1210 * @tag: (nullable): a #CtkTextTag, or %NULL
1211 *
1212 * Returns %TRUE if @tag is toggled on at exactly this point. If @tag
1213 * is %NULL, returns %TRUE if any tag is toggled on at this point.
1214 *
1215 * Note that if ctk_text_iter_begins_tag() returns %TRUE, it means that @iter is
1216 * at the beginning of the tagged range, and that the
1217 * character at @iter is inside the tagged range. In other
1218 * words, unlike ctk_text_iter_ends_tag(), if ctk_text_iter_begins_tag() returns
1219 * %TRUE, ctk_text_iter_has_tag() will also return %TRUE for the same
1220 * parameters.
1221 *
1222 * Returns: whether @iter is the start of a range tagged with @tag
1223 * Deprecated: 3.20: Use ctk_text_iter_starts_tag() instead.
1224 **/
1225gboolean
1226ctk_text_iter_begins_tag (const CtkTextIter *iter,
1227 CtkTextTag *tag)
1228{
1229 return ctk_text_iter_starts_tag (iter, tag);
1230}
1231
1232/**
1233 * ctk_text_iter_ends_tag:
1234 * @iter: an iterator
1235 * @tag: (allow-none): a #CtkTextTag, or %NULL
1236 *
1237 * Returns %TRUE if @tag is toggled off at exactly this point. If @tag
1238 * is %NULL, returns %TRUE if any tag is toggled off at this point.
1239 *
1240 * Note that if ctk_text_iter_ends_tag() returns %TRUE, it means that @iter is
1241 * at the end of the tagged range, but that the character
1242 * at @iter is outside the tagged range. In other words,
1243 * unlike ctk_text_iter_starts_tag(), if ctk_text_iter_ends_tag() returns %TRUE,
1244 * ctk_text_iter_has_tag() will return %FALSE for the same parameters.
1245 *
1246 * Returns: whether @iter is the end of a range tagged with @tag
1247 **/
1248gboolean
1249ctk_text_iter_ends_tag (const CtkTextIter *iter,
1250 CtkTextTag *tag)
1251{
1252 CtkTextRealIter *real;
1253 CtkTextLineSegment *seg;
1254
1255 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1256
1257 real = ctk_text_iter_make_real (iter);
1258
1259 if (real == NULL((void*)0))
1260 return FALSE(0);
1261
1262 check_invariants (iter);
1263
1264 seg = real->any_segment;
1265 while (seg != real->segment)
1266 {
1267 if (seg->type == &ctk_text_toggle_off_type)
1268 {
1269 if (tag == NULL((void*)0) ||
1270 seg->body.toggle.info->tag == tag)
1271 return TRUE(!(0));
1272 }
1273
1274 seg = seg->next;
1275 }
1276
1277 return FALSE(0);
1278}
1279
1280/**
1281 * ctk_text_iter_toggles_tag:
1282 * @iter: an iterator
1283 * @tag: (allow-none): a #CtkTextTag, or %NULL
1284 *
1285 * This is equivalent to (ctk_text_iter_starts_tag() ||
1286 * ctk_text_iter_ends_tag()), i.e. it tells you whether a range with
1287 * @tag applied to it begins or ends at @iter.
1288 *
1289 * Returns: whether @tag is toggled on or off at @iter
1290 **/
1291gboolean
1292ctk_text_iter_toggles_tag (const CtkTextIter *iter,
1293 CtkTextTag *tag)
1294{
1295 CtkTextRealIter *real;
1296 CtkTextLineSegment *seg;
1297
1298 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1299
1300 real = ctk_text_iter_make_real (iter);
1301
1302 if (real == NULL((void*)0))
1303 return FALSE(0);
1304
1305 check_invariants (iter);
1306
1307 seg = real->any_segment;
1308 while (seg != real->segment)
1309 {
1310 if ( (seg->type == &ctk_text_toggle_off_type ||
1311 seg->type == &ctk_text_toggle_on_type) &&
1312 (tag == NULL((void*)0) ||
1313 seg->body.toggle.info->tag == tag) )
1314 return TRUE(!(0));
1315
1316 seg = seg->next;
1317 }
1318
1319 return FALSE(0);
1320}
1321
1322/**
1323 * ctk_text_iter_has_tag:
1324 * @iter: an iterator
1325 * @tag: a #CtkTextTag
1326 *
1327 * Returns %TRUE if @iter points to a character that is part of a range tagged
1328 * with @tag. See also ctk_text_iter_starts_tag() and ctk_text_iter_ends_tag().
1329 *
1330 * Returns: whether @iter is tagged with @tag
1331 **/
1332gboolean
1333ctk_text_iter_has_tag (const CtkTextIter *iter,
1334 CtkTextTag *tag)
1335{
1336 CtkTextRealIter *real;
1337
1338 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1339 g_return_val_if_fail (CTK_IS_TEXT_TAG (tag), FALSE)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 ((0)); } } while (0)
;
1340
1341 real = ctk_text_iter_make_surreal (iter);
1342
1343 if (real == NULL((void*)0))
1344 return FALSE(0);
1345
1346 check_invariants (iter);
1347
1348 if (real->line_byte_offset >= 0)
1349 {
1350 return _ctk_text_line_byte_has_tag (real->line, real->tree,
1351 real->line_byte_offset, tag);
1352 }
1353 else
1354 {
1355 g_assert (real->line_char_offset >= 0)do { if (real->line_char_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 1355, ((const char*) (__func__)), "real->line_char_offset >= 0"
); } while (0)
;
1356 return _ctk_text_line_char_has_tag (real->line, real->tree,
1357 real->line_char_offset, tag);
1358 }
1359}
1360
1361/**
1362 * ctk_text_iter_get_tags:
1363 * @iter: a #CtkTextIter
1364 *
1365 * Returns a list of tags that apply to @iter, in ascending order of
1366 * priority (highest-priority tags are last). The #CtkTextTag in the
1367 * list don’t have a reference added, but you have to free the list
1368 * itself.
1369 *
1370 * Returns: (element-type CtkTextTag) (transfer container): list of #CtkTextTag
1371 **/
1372GSList*
1373ctk_text_iter_get_tags (const CtkTextIter *iter)
1374{
1375 CtkTextTag** tags;
1376 gint tag_count = 0;
1377 gint i;
1378 GSList *retval;
1379
1380 g_return_val_if_fail (iter != NULL, NULL)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
((void*)0)); } } while (0)
;
1381
1382 /* Get the tags at this spot */
1383 tags = _ctk_text_btree_get_tags (iter, &tag_count);
1384
1385 /* No tags, use default style */
1386 if (tags == NULL((void*)0) || tag_count == 0)
1387 {
1388 g_free (tags);
1389
1390 return NULL((void*)0);
1391 }
1392
1393 retval = NULL((void*)0);
1394 i = 0;
1395 while (i < tag_count)
1396 {
1397 retval = g_slist_prepend (retval, tags[i]);
1398 ++i;
1399 }
1400
1401 g_free (tags);
1402
1403 /* Return tags in ascending order of priority */
1404 return g_slist_reverse (retval);
1405}
1406
1407/**
1408 * ctk_text_iter_editable:
1409 * @iter: an iterator
1410 * @default_setting: %TRUE if text is editable by default
1411 *
1412 * Returns whether the character at @iter is within an editable region
1413 * of text. Non-editable text is “locked” and can’t be changed by the
1414 * user via #CtkTextView. This function is simply a convenience
1415 * wrapper around ctk_text_iter_get_attributes(). If no tags applied
1416 * to this text affect editability, @default_setting will be returned.
1417 *
1418 * You don’t want to use this function to decide whether text can be
1419 * inserted at @iter, because for insertion you don’t want to know
1420 * whether the char at @iter is inside an editable range, you want to
1421 * know whether a new character inserted at @iter would be inside an
1422 * editable range. Use ctk_text_iter_can_insert() to handle this
1423 * case.
1424 *
1425 * Returns: whether @iter is inside an editable range
1426 **/
1427gboolean
1428ctk_text_iter_editable (const CtkTextIter *iter,
1429 gboolean default_setting)
1430{
1431 CtkTextAttributes *values;
1432 gboolean retval;
1433
1434 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1435
1436 values = ctk_text_attributes_new ();
1437
1438 values->editable = default_setting;
1439
1440 ctk_text_iter_get_attributes (iter, values);
1441
1442 retval = values->editable;
1443
1444 ctk_text_attributes_unref (values);
1445
1446 return retval;
1447}
1448
1449/**
1450 * ctk_text_iter_can_insert:
1451 * @iter: an iterator
1452 * @default_editability: %TRUE if text is editable by default
1453 *
1454 * Considering the default editability of the buffer, and tags that
1455 * affect editability, determines whether text inserted at @iter would
1456 * be editable. If text inserted at @iter would be editable then the
1457 * user should be allowed to insert text at @iter.
1458 * ctk_text_buffer_insert_interactive() uses this function to decide
1459 * whether insertions are allowed at a given position.
1460 *
1461 * Returns: whether text inserted at @iter would be editable
1462 **/
1463gboolean
1464ctk_text_iter_can_insert (const CtkTextIter *iter,
1465 gboolean default_editability)
1466{
1467 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1468
1469 if (ctk_text_iter_editable (iter, default_editability))
1470 return TRUE(!(0));
1471 /* If at start/end of buffer, default editability is used */
1472 else if ((ctk_text_iter_is_start (iter) ||
1473 ctk_text_iter_is_end (iter)) &&
1474 default_editability)
1475 return TRUE(!(0));
1476 else
1477 {
1478 /* if iter isn't editable, and the char before iter is,
1479 * then iter is the first char in an editable region
1480 * and thus insertion at iter results in editable text.
1481 */
1482 CtkTextIter prev = *iter;
1483 ctk_text_iter_backward_char (&prev);
1484 return ctk_text_iter_editable (&prev, default_editability);
1485 }
1486}
1487
1488
1489/**
1490 * ctk_text_iter_get_language:
1491 * @iter: an iterator
1492 *
1493 * A convenience wrapper around ctk_text_iter_get_attributes(),
1494 * which returns the language in effect at @iter. If no tags affecting
1495 * language apply to @iter, the return value is identical to that of
1496 * ctk_get_default_language().
1497 *
1498 * Returns: (transfer full): language in effect at @iter
1499 **/
1500PangoLanguage *
1501ctk_text_iter_get_language (const CtkTextIter *iter)
1502{
1503 CtkTextAttributes *values;
1504 PangoLanguage *retval;
1505
1506 values = ctk_text_attributes_new ();
1507
1508 ctk_text_iter_get_attributes (iter, values);
1509
1510 retval = values->language;
1511
1512 ctk_text_attributes_unref (values);
1513
1514 return retval;
1515}
1516
1517/**
1518 * ctk_text_iter_starts_line:
1519 * @iter: an iterator
1520 *
1521 * Returns %TRUE if @iter begins a paragraph,
1522 * i.e. if ctk_text_iter_get_line_offset() would return 0.
1523 * However this function is potentially more efficient than
1524 * ctk_text_iter_get_line_offset() because it doesn’t have to compute
1525 * the offset, it just has to see whether it’s 0.
1526 *
1527 * Returns: whether @iter begins a line
1528 **/
1529gboolean
1530ctk_text_iter_starts_line (const CtkTextIter *iter)
1531{
1532 CtkTextRealIter *real;
1533
1534 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1535
1536 real = ctk_text_iter_make_surreal (iter);
1537
1538 if (real == NULL((void*)0))
1539 return FALSE(0);
1540
1541 check_invariants (iter);
1542
1543 if (real->line_byte_offset >= 0)
1544 {
1545 return (real->line_byte_offset == 0);
1546 }
1547 else
1548 {
1549 g_assert (real->line_char_offset >= 0)do { if (real->line_char_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 1549, ((const char*) (__func__)), "real->line_char_offset >= 0"
); } while (0)
;
1550 return (real->line_char_offset == 0);
1551 }
1552}
1553
1554/**
1555 * ctk_text_iter_ends_line:
1556 * @iter: an iterator
1557 *
1558 * Returns %TRUE if @iter points to the start of the paragraph
1559 * delimiter characters for a line (delimiters will be either a
1560 * newline, a carriage return, a carriage return followed by a
1561 * newline, or a Unicode paragraph separator character). Note that an
1562 * iterator pointing to the \n of a \r\n pair will not be counted as
1563 * the end of a line, the line ends before the \r. The end iterator is
1564 * considered to be at the end of a line, even though there are no
1565 * paragraph delimiter chars there.
1566 *
1567 * Returns: whether @iter is at the end of a line
1568 **/
1569gboolean
1570ctk_text_iter_ends_line (const CtkTextIter *iter)
1571{
1572 gunichar wc;
1573
1574 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1575
1576 check_invariants (iter);
1577
1578 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1579 * Unicode 3.0; update this if that changes.
1580 */
1581#define PARAGRAPH_SEPARATOR0x2029 0x2029
1582
1583 wc = ctk_text_iter_get_char (iter);
1584
1585 if (wc == '\r' || wc == PARAGRAPH_SEPARATOR0x2029 || wc == 0) /* wc == 0 is end iterator */
1586 return TRUE(!(0));
1587 else if (wc == '\n')
1588 {
1589 CtkTextIter tmp = *iter;
1590
1591 /* need to determine if a \r precedes the \n, in which case
1592 * we aren't the end of the line.
1593 * Note however that if \r and \n are on different lines, they
1594 * both are terminators. This for instance may happen after
1595 * deleting some text:
1596
1597 1 some text\r delete 'a' 1 some text\r
1598 2 a\n ---------> 2 \n
1599 3 ... 3 ...
1600
1601 */
1602
1603 if (ctk_text_iter_get_line_offset (&tmp) == 0)
1604 return TRUE(!(0));
1605
1606 if (!ctk_text_iter_backward_char (&tmp))
1607 return TRUE(!(0));
1608
1609 return ctk_text_iter_get_char (&tmp) != '\r';
1610 }
1611 else
1612 return FALSE(0);
1613}
1614
1615/**
1616 * ctk_text_iter_is_end:
1617 * @iter: an iterator
1618 *
1619 * Returns %TRUE if @iter is the end iterator, i.e. one past the last
1620 * dereferenceable iterator in the buffer. ctk_text_iter_is_end() is
1621 * the most efficient way to check whether an iterator is the end
1622 * iterator.
1623 *
1624 * Returns: whether @iter is the end iterator
1625 **/
1626gboolean
1627ctk_text_iter_is_end (const CtkTextIter *iter)
1628{
1629 CtkTextRealIter *real;
1630
1631 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1632
1633 real = ctk_text_iter_make_surreal (iter);
1634
1635 if (real == NULL((void*)0))
1636 return FALSE(0);
1637
1638 check_invariants (iter);
1639
1640 if (!_ctk_text_line_contains_end_iter (real->line, real->tree))
1641 return FALSE(0);
1642
1643 /* Now we need the segments validated */
1644 real = ctk_text_iter_make_real (iter);
1645
1646 if (real == NULL((void*)0))
1647 return FALSE(0);
1648
1649 return _ctk_text_btree_is_end (real->tree, real->line,
1650 real->segment,
1651 real->segment_byte_offset,
1652 real->segment_char_offset);
1653}
1654
1655/**
1656 * ctk_text_iter_is_start:
1657 * @iter: an iterator
1658 *
1659 * Returns %TRUE if @iter is the first iterator in the buffer, that is
1660 * if @iter has a character offset of 0.
1661 *
1662 * Returns: whether @iter is the first in the buffer
1663 **/
1664gboolean
1665ctk_text_iter_is_start (const CtkTextIter *iter)
1666{
1667 return ctk_text_iter_get_offset (iter) == 0;
1668}
1669
1670/**
1671 * ctk_text_iter_get_chars_in_line:
1672 * @iter: an iterator
1673 *
1674 * Returns the number of characters in the line containing @iter,
1675 * including the paragraph delimiters.
1676 *
1677 * Returns: number of characters in the line
1678 **/
1679gint
1680ctk_text_iter_get_chars_in_line (const CtkTextIter *iter)
1681{
1682 CtkTextRealIter *real;
1683 gint count;
1684 CtkTextLineSegment *seg;
1685
1686 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
1687
1688 real = ctk_text_iter_make_surreal (iter);
1689
1690 if (real == NULL((void*)0))
1691 return 0;
1692
1693 check_invariants (iter);
1694
1695 if (real->line_char_offset >= 0)
1696 {
1697 /* We can start at the segments we've already found. */
1698 count = real->line_char_offset - real->segment_char_offset;
1699 seg = _ctk_text_iter_get_indexable_segment (iter);
1700 }
1701 else
1702 {
1703 /* count whole line. */
1704 seg = real->line->segments;
1705 count = 0;
1706 }
1707
1708
1709 while (seg != NULL((void*)0))
1710 {
1711 count += seg->char_count;
1712
1713 seg = seg->next;
1714 }
1715
1716 if (_ctk_text_line_contains_end_iter (real->line, real->tree))
1717 count -= 1; /* Dump the newline that was in the last segment of the end iter line */
1718
1719 return count;
1720}
1721
1722/**
1723 * ctk_text_iter_get_bytes_in_line:
1724 * @iter: an iterator
1725 *
1726 * Returns the number of bytes in the line containing @iter,
1727 * including the paragraph delimiters.
1728 *
1729 * Returns: number of bytes in the line
1730 **/
1731gint
1732ctk_text_iter_get_bytes_in_line (const CtkTextIter *iter)
1733{
1734 CtkTextRealIter *real;
1735 gint count;
1736 CtkTextLineSegment *seg;
1737
1738 g_return_val_if_fail (iter != NULL, 0)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
0); } } while (0)
;
1739
1740 real = ctk_text_iter_make_surreal (iter);
1741
1742 if (real == NULL((void*)0))
1743 return 0;
1744
1745 check_invariants (iter);
1746
1747 if (real->line_byte_offset >= 0)
1748 {
1749 /* We can start at the segments we've already found. */
1750 count = real->line_byte_offset - real->segment_byte_offset;
1751 seg = _ctk_text_iter_get_indexable_segment (iter);
1752 }
1753 else
1754 {
1755 /* count whole line. */
1756 seg = real->line->segments;
1757 count = 0;
1758 }
1759
1760 while (seg != NULL((void*)0))
1761 {
1762 count += seg->byte_count;
1763
1764 seg = seg->next;
1765 }
1766
1767 if (_ctk_text_line_contains_end_iter (real->line, real->tree))
1768 count -= 1; /* Dump the newline that was in the last segment of the end iter line */
1769
1770 return count;
1771}
1772
1773/**
1774 * ctk_text_iter_get_attributes:
1775 * @iter: an iterator
1776 * @values: (out): a #CtkTextAttributes to be filled in
1777 *
1778 * Computes the effect of any tags applied to this spot in the
1779 * text. The @values parameter should be initialized to the default
1780 * settings you wish to use if no tags are in effect. You’d typically
1781 * obtain the defaults from ctk_text_view_get_default_attributes().
1782 *
1783 * ctk_text_iter_get_attributes() will modify @values, applying the
1784 * effects of any tags present at @iter. If any tags affected @values,
1785 * the function returns %TRUE.
1786 *
1787 * Returns: %TRUE if @values was modified
1788 **/
1789gboolean
1790ctk_text_iter_get_attributes (const CtkTextIter *iter,
1791 CtkTextAttributes *values)
1792{
1793 CtkTextTag** tags;
1794 gint tag_count = 0;
1795
1796 /* Get the tags at this spot */
1797 tags = _ctk_text_btree_get_tags (iter, &tag_count);
1798
1799 /* No tags, use default style */
1800 if (tags == NULL((void*)0) || tag_count == 0)
1801 {
1802 g_free (tags);
1803
1804 return FALSE(0);
1805 }
1806
1807 _ctk_text_attributes_fill_from_tags (values,
1808 tags,
1809 tag_count);
1810
1811 g_free (tags);
1812
1813 return TRUE(!(0));
1814}
1815
1816/*
1817 * Increments/decrements
1818 */
1819
1820/* The return value of this indicates WHETHER WE MOVED.
1821 * The return value of public functions indicates
1822 * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
1823 *
1824 * This function will not change the iterator if
1825 * it’s already on the last (end iter) line, i.e. it
1826 * won’t move to the end of the last line.
1827 */
1828static gboolean
1829forward_line_leaving_caches_unmodified (CtkTextRealIter *real)
1830{
1831 if (!_ctk_text_line_contains_end_iter (real->line, real->tree))
1832 {
1833 CtkTextLine *new_line;
1834
1835 new_line = _ctk_text_line_next (real->line);
1836 g_assert (new_line)do { if (new_line) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 1836, ((const char*) (__func__)), "new_line"); } while (0)
;
1837 g_assert (new_line != real->line)do { if (new_line != real->line) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 1837, ((const char*) (__func__)), "new_line != real->line"
); } while (0)
;
1838 g_assert (!_ctk_text_line_is_last (new_line, real->tree))do { if (!_ctk_text_line_is_last (new_line, real->tree)) ;
else g_assertion_message_expr ("Ctk", "ctktextiter.c", 1838,
((const char*) (__func__)), "!_ctk_text_line_is_last (new_line, real->tree)"
); } while (0)
;
1839
1840 real->line = new_line;
1841
1842 real->line_byte_offset = 0;
1843 real->line_char_offset = 0;
1844
1845 real->segment_byte_offset = 0;
1846 real->segment_char_offset = 0;
1847
1848 /* Find first segments in new line */
1849 real->any_segment = real->line->segments;
1850 real->segment = real->any_segment;
1851 while (real->segment->char_count == 0)
1852 real->segment = real->segment->next;
1853
1854 return TRUE(!(0));
1855 }
1856 else
1857 {
1858 /* There is no way to move forward a line; we were already at
1859 * the line containing the end iterator.
1860 * However we may not be at the end iterator itself.
1861 */
1862
1863 return FALSE(0);
1864 }
1865}
1866
1867#if 0
1868/* The return value of this indicates WHETHER WE MOVED.
1869 * The return value of public functions indicates
1870 * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
1871 *
1872 * This function is currently unused, thus it is #if-0-ed. It is
1873 * left here, since it’s non-trivial code that might be useful in
1874 * the future.
1875 */
1876static gboolean
1877backward_line_leaving_caches_unmodified (CtkTextRealIter *real)
1878{
1879 CtkTextLine *new_line;
1880
1881 new_line = _ctk_text_line_previous (real->line);
1882
1883 g_assert (new_line != real->line)do { if (new_line != real->line) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 1883, ((const char*) (__func__)), "new_line != real->line"
); } while (0)
;
1884
1885 if (new_line != NULL((void*)0))
1886 {
1887 real->line = new_line;
1888
1889 real->line_byte_offset = 0;
1890 real->line_char_offset = 0;
1891
1892 real->segment_byte_offset = 0;
1893 real->segment_char_offset = 0;
1894
1895 /* Find first segments in new line */
1896 real->any_segment = real->line->segments;
1897 real->segment = real->any_segment;
1898 while (real->segment->char_count == 0)
1899 real->segment = real->segment->next;
1900
1901 return TRUE(!(0));
1902 }
1903 else
1904 {
1905 /* There is no way to move backward; we were already
1906 at the first line. */
1907
1908 /* We leave real->line as-is */
1909
1910 /* Note that we didn't clamp to the start of the first line. */
1911
1912 return FALSE(0);
1913 }
1914}
1915#endif
1916
1917/* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
1918 * DEREFERENCEABLE)
1919 */
1920static gboolean
1921forward_char (CtkTextRealIter *real)
1922{
1923 CtkTextIter *iter = (CtkTextIter*)real;
1924
1925 check_invariants ((CtkTextIter*)real);
1926
1927 ensure_char_offsets (real);
1928
1929 if ( (real->segment_char_offset + 1) == real->segment->char_count)
1930 {
1931 /* Need to move to the next segment; if no next segment,
1932 need to move to next line. */
1933 return _ctk_text_iter_forward_indexable_segment (iter);
1934 }
1935 else
1936 {
1937 /* Just moving within a segment. Keep byte count
1938 up-to-date, if it was already up-to-date. */
1939
1940 g_assert (real->segment->type == &ctk_text_char_type)do { if (real->segment->type == &ctk_text_char_type
) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c", 1940
, ((const char*) (__func__)), "real->segment->type == &ctk_text_char_type"
); } while (0)
;
1941
1942 if (real->line_byte_offset >= 0)
1943 {
1944 gint bytes;
1945 const char * start =
1946 real->segment->body.chars + real->segment_byte_offset;
1947
1948 bytes = g_utf8_next_char (start)((start) + g_utf8_skip[*(const guchar *)(start)]) - start;
1949
1950 real->line_byte_offset += bytes;
1951 real->segment_byte_offset += bytes;
1952
1953 g_assert (real->segment_byte_offset < real->segment->byte_count)do { if (real->segment_byte_offset < real->segment->
byte_count) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 1953, ((const char*) (__func__)), "real->segment_byte_offset < real->segment->byte_count"
); } while (0)
;
1954 }
1955
1956 real->line_char_offset += 1;
1957 real->segment_char_offset += 1;
1958
1959 adjust_char_index (real, 1);
1960
1961 g_assert (real->segment_char_offset < real->segment->char_count)do { if (real->segment_char_offset < real->segment->
char_count) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 1961, ((const char*) (__func__)), "real->segment_char_offset < real->segment->char_count"
); } while (0)
;
1962
1963 /* We moved into the middle of a segment, so the any_segment
1964 must now be the segment we're in the middle of. */
1965 real->any_segment = real->segment;
1966
1967 check_invariants ((CtkTextIter*)real);
1968
1969 if (ctk_text_iter_is_end ((CtkTextIter*)real))
1970 return FALSE(0);
1971 else
1972 return TRUE(!(0));
1973 }
1974}
1975
1976gboolean
1977_ctk_text_iter_forward_indexable_segment (CtkTextIter *iter)
1978{
1979 /* Need to move to the next segment; if no next segment,
1980 need to move to next line. */
1981 CtkTextLineSegment *seg;
1982 CtkTextLineSegment *any_seg;
1983 CtkTextRealIter *real;
1984 gint chars_skipped;
1985 gint bytes_skipped;
1986
1987 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1988
1989 real = ctk_text_iter_make_real (iter);
1990
1991 if (real == NULL((void*)0))
1992 return FALSE(0);
1993
1994 check_invariants (iter);
1995
1996 if (real->line_char_offset >= 0)
1997 {
1998 chars_skipped = real->segment->char_count - real->segment_char_offset;
1999 g_assert (chars_skipped > 0)do { if (chars_skipped > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 1999, ((const char*) (__func__)), "chars_skipped > 0"
); } while (0)
;
2000 }
2001 else
2002 chars_skipped = 0;
2003
2004 if (real->line_byte_offset >= 0)
2005 {
2006 bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
2007 g_assert (bytes_skipped > 0)do { if (bytes_skipped > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2007, ((const char*) (__func__)), "bytes_skipped > 0"
); } while (0)
;
2008 }
2009 else
2010 bytes_skipped = 0;
2011
2012 /* Get first segment of any kind */
2013 any_seg = real->segment->next;
2014 /* skip non-indexable segments, if any */
2015 seg = any_seg;
2016 while (seg != NULL((void*)0) && seg->char_count == 0)
2017 seg = seg->next;
2018
2019 if (seg != NULL((void*)0))
2020 {
2021 real->any_segment = any_seg;
2022 real->segment = seg;
2023
2024 if (real->line_byte_offset >= 0)
2025 {
2026 g_assert (bytes_skipped > 0)do { if (bytes_skipped > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2026, ((const char*) (__func__)), "bytes_skipped > 0"
); } while (0)
;
2027 real->segment_byte_offset = 0;
2028 real->line_byte_offset += bytes_skipped;
2029 }
2030
2031 if (real->line_char_offset >= 0)
2032 {
2033 g_assert (chars_skipped > 0)do { if (chars_skipped > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2033, ((const char*) (__func__)), "chars_skipped > 0"
); } while (0)
;
2034 real->segment_char_offset = 0;
2035 real->line_char_offset += chars_skipped;
2036 adjust_char_index (real, chars_skipped);
2037 }
2038
2039 check_invariants (iter);
2040
2041 return !ctk_text_iter_is_end (iter);
2042 }
2043 else
2044 {
2045 /* End of the line */
2046 if (forward_line_leaving_caches_unmodified (real))
2047 {
2048 adjust_line_number (real, 1);
2049 if (real->line_char_offset >= 0)
2050 adjust_char_index (real, chars_skipped);
2051
2052 g_assert (real->line_byte_offset == 0)do { if (real->line_byte_offset == 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2052, ((const char*) (__func__)), "real->line_byte_offset == 0"
); } while (0)
;
2053 g_assert (real->line_char_offset == 0)do { if (real->line_char_offset == 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2053, ((const char*) (__func__)), "real->line_char_offset == 0"
); } while (0)
;
2054 g_assert (real->segment_byte_offset == 0)do { if (real->segment_byte_offset == 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2054, ((const char*) (__func__)), "real->segment_byte_offset == 0"
); } while (0)
;
2055 g_assert (real->segment_char_offset == 0)do { if (real->segment_char_offset == 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2055, ((const char*) (__func__)), "real->segment_char_offset == 0"
); } while (0)
;
2056 g_assert (ctk_text_iter_starts_line (iter))do { if (ctk_text_iter_starts_line (iter)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2056, ((const char*) (__func__)), "ctk_text_iter_starts_line (iter)"
); } while (0)
;
2057
2058 check_invariants (iter);
2059
2060 return !ctk_text_iter_is_end (iter);
2061 }
2062 else
2063 {
2064 /* End of buffer, but iter is still at start of last segment,
2065 * not at the end iterator. We put it on the end iterator.
2066 */
2067
2068 check_invariants (iter);
2069
2070 g_assert (!_ctk_text_line_is_last (real->line, real->tree))do { if (!_ctk_text_line_is_last (real->line, real->tree
)) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c", 2070
, ((const char*) (__func__)), "!_ctk_text_line_is_last (real->line, real->tree)"
); } while (0)
;
2071 g_assert (_ctk_text_line_contains_end_iter (real->line, real->tree))do { if (_ctk_text_line_contains_end_iter (real->line, real
->tree)) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 2071, ((const char*) (__func__)), "_ctk_text_line_contains_end_iter (real->line, real->tree)"
); } while (0)
;
2072
2073 ctk_text_iter_forward_to_line_end (iter);
2074
2075 g_assert (ctk_text_iter_is_end (iter))do { if (ctk_text_iter_is_end (iter)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2075, ((const char*) (__func__)), "ctk_text_iter_is_end (iter)"
); } while (0)
;
2076
2077 return FALSE(0);
2078 }
2079 }
2080}
2081
2082static gboolean
2083at_last_indexable_segment (CtkTextRealIter *real)
2084{
2085 CtkTextLineSegment *seg;
2086
2087 /* Return TRUE if there are no indexable segments after
2088 * this iterator.
2089 */
2090
2091 seg = real->segment->next;
2092 while (seg)
2093 {
2094 if (seg->char_count > 0)
2095 return FALSE(0);
2096 seg = seg->next;
2097 }
2098 return TRUE(!(0));
2099}
2100
2101/* Goes back to the start of the next segment, even if
2102 * we’re not at the start of the current segment (always
2103 * ends up on a different segment if it returns TRUE)
2104 */
2105gboolean
2106_ctk_text_iter_backward_indexable_segment (CtkTextIter *iter)
2107{
2108 /* Move to the start of the previous segment; if no previous
2109 * segment, to the last segment in the previous line. This is
2110 * inherently a bit inefficient due to the singly-linked list and
2111 * tree nodes, but we can't afford the RAM for doubly-linked.
2112 */
2113 CtkTextRealIter *real;
2114 CtkTextLineSegment *seg;
2115 CtkTextLineSegment *any_seg;
2116 CtkTextLineSegment *prev_seg;
2117 CtkTextLineSegment *prev_any_seg;
2118 gint bytes_skipped;
2119 gint chars_skipped;
2120
2121 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2122
2123 real = ctk_text_iter_make_real (iter);
2124
2125 if (real == NULL((void*)0))
2126 return FALSE(0);
2127
2128 check_invariants (iter);
2129
2130 /* Find first segments in line */
2131 any_seg = real->line->segments;
2132 seg = any_seg;
2133 while (seg->char_count == 0)
2134 seg = seg->next;
2135
2136 if (seg == real->segment)
2137 {
2138 /* Could probably do this case faster by hand-coding the
2139 * iteration.
2140 */
2141
2142 /* We were already at the start of a line;
2143 * go back to the previous line.
2144 */
2145 if (ctk_text_iter_backward_line (iter))
2146 {
2147 /* Go forward to last indexable segment in line. */
2148 while (!at_last_indexable_segment (real))
2149 _ctk_text_iter_forward_indexable_segment (iter);
2150
2151 check_invariants (iter);
2152
2153 return TRUE(!(0));
2154 }
2155 else
2156 return FALSE(0); /* We were at the start of the first line. */
2157 }
2158
2159 /* We must be in the middle of a line; so find the indexable
2160 * segment just before our current segment.
2161 */
2162 g_assert (seg != real->segment)do { if (seg != real->segment) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2162, ((const char*) (__func__)), "seg != real->segment"
); } while (0)
;
2163 do
2164 {
2165 prev_seg = seg;
2166 prev_any_seg = any_seg;
2167
2168 any_seg = seg->next;
2169 seg = any_seg;
2170 while (seg->char_count == 0)
2171 seg = seg->next;
2172 }
2173 while (seg != real->segment);
2174
2175 g_assert (prev_seg != NULL)do { if (prev_seg != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2175, ((const char*) (__func__)), "prev_seg != NULL"
); } while (0)
;
2176 g_assert (prev_any_seg != NULL)do { if (prev_any_seg != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2176, ((const char*) (__func__)), "prev_any_seg != NULL"
); } while (0)
;
2177 g_assert (prev_seg->char_count > 0)do { if (prev_seg->char_count > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2177, ((const char*) (__func__)), "prev_seg->char_count > 0"
); } while (0)
;
2178
2179 /* We skipped the entire previous segment, plus any
2180 * chars we were into the current segment.
2181 */
2182 if (real->segment_byte_offset >= 0)
2183 bytes_skipped = prev_seg->byte_count + real->segment_byte_offset;
2184 else
2185 bytes_skipped = -1;
2186
2187 if (real->segment_char_offset >= 0)
2188 chars_skipped = prev_seg->char_count + real->segment_char_offset;
2189 else
2190 chars_skipped = -1;
2191
2192 real->segment = prev_seg;
2193 real->any_segment = prev_any_seg;
2194 real->segment_byte_offset = 0;
2195 real->segment_char_offset = 0;
2196
2197 if (bytes_skipped >= 0)
2198 {
2199 if (real->line_byte_offset >= 0)
2200 {
2201 real->line_byte_offset -= bytes_skipped;
2202 g_assert (real->line_byte_offset >= 0)do { if (real->line_byte_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2202, ((const char*) (__func__)), "real->line_byte_offset >= 0"
); } while (0)
;
2203 }
2204 }
2205 else
2206 real->line_byte_offset = -1;
2207
2208 if (chars_skipped >= 0)
2209 {
2210 if (real->line_char_offset >= 0)
2211 {
2212 real->line_char_offset -= chars_skipped;
2213 g_assert (real->line_char_offset >= 0)do { if (real->line_char_offset >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2213, ((const char*) (__func__)), "real->line_char_offset >= 0"
); } while (0)
;
2214 }
2215
2216 if (real->cached_char_index >= 0)
2217 {
2218 real->cached_char_index -= chars_skipped;
2219 g_assert (real->cached_char_index >= 0)do { if (real->cached_char_index >= 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2219, ((const char*) (__func__)), "real->cached_char_index >= 0"
); } while (0)
;
2220 }
2221 }
2222 else
2223 {
2224 real->line_char_offset = -1;
2225 real->cached_char_index = -1;
2226 }
2227
2228 /* line number is unchanged. */
2229
2230 check_invariants (iter);
2231
2232 return TRUE(!(0));
2233}
2234
2235/**
2236 * ctk_text_iter_forward_char:
2237 * @iter: an iterator
2238 *
2239 * Moves @iter forward by one character offset. Note that images
2240 * embedded in the buffer occupy 1 character slot, so
2241 * ctk_text_iter_forward_char() may actually move onto an image instead
2242 * of a character, if you have images in your buffer. If @iter is the
2243 * end iterator or one character before it, @iter will now point at
2244 * the end iterator, and ctk_text_iter_forward_char() returns %FALSE for
2245 * convenience when writing loops.
2246 *
2247 * Returns: whether @iter moved and is dereferenceable
2248 **/
2249gboolean
2250ctk_text_iter_forward_char (CtkTextIter *iter)
2251{
2252 CtkTextRealIter *real;
2253
2254 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2255
2256 real = ctk_text_iter_make_real (iter);
2257
2258 if (real == NULL((void*)0))
2259 return FALSE(0);
2260 else
2261 {
2262 check_invariants (iter);
2263 return forward_char (real);
2264 }
2265}
2266
2267/**
2268 * ctk_text_iter_backward_char:
2269 * @iter: an iterator
2270 *
2271 * Moves backward by one character offset. Returns %TRUE if movement
2272 * was possible; if @iter was the first in the buffer (character
2273 * offset 0), ctk_text_iter_backward_char() returns %FALSE for convenience when
2274 * writing loops.
2275 *
2276 * Returns: whether movement was possible
2277 **/
2278gboolean
2279ctk_text_iter_backward_char (CtkTextIter *iter)
2280{
2281 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2282
2283 check_invariants (iter);
2284
2285 return ctk_text_iter_backward_chars (iter, 1);
2286}
2287
2288/*
2289 Definitely we should try to linear scan as often as possible for
2290 movement within a single line, because we can't use the BTree to
2291 speed within-line searches up; for movement between lines, we would
2292 like to avoid the linear scan probably.
2293
2294 Instead of using this constant, it might be nice to cache the line
2295 length in the iterator and linear scan if motion is within a single
2296 line.
2297
2298 I guess you'd have to profile the various approaches.
2299*/
2300#define MAX_LINEAR_SCAN150 150
2301
2302
2303/**
2304 * ctk_text_iter_forward_chars:
2305 * @iter: an iterator
2306 * @count: number of characters to move, may be negative
2307 *
2308 * Moves @count characters if possible (if @count would move past the
2309 * start or end of the buffer, moves to the start or end of the
2310 * buffer). The return value indicates whether the new position of
2311 * @iter is different from its original position, and dereferenceable
2312 * (the last iterator in the buffer is not dereferenceable). If @count
2313 * is 0, the function does nothing and returns %FALSE.
2314 *
2315 * Returns: whether @iter moved and is dereferenceable
2316 **/
2317gboolean
2318ctk_text_iter_forward_chars (CtkTextIter *iter, gint count)
2319{
2320 CtkTextRealIter *real;
2321
2322 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2323
2324 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2325
2326 real = ctk_text_iter_make_real (iter);
2327
2328 if (real == NULL((void*)0))
2329 return FALSE(0);
2330 else if (count == 0)
2331 return FALSE(0);
2332 else if (count < 0)
2333 return ctk_text_iter_backward_chars (iter, 0 - count);
2334 else if (count < MAX_LINEAR_SCAN150)
2335 {
2336 check_invariants (iter);
2337
2338 while (count > 1)
2339 {
2340 if (!forward_char (real))
2341 return FALSE(0);
2342 --count;
2343 }
2344
2345 return forward_char (real);
2346 }
2347 else
2348 {
2349 gint current_char_index;
2350 gint new_char_index;
2351
2352 check_invariants (iter);
2353
2354 current_char_index = ctk_text_iter_get_offset (iter);
2355
2356 if (current_char_index == _ctk_text_btree_char_count (real->tree))
2357 return FALSE(0); /* can't move forward */
2358
2359 new_char_index = current_char_index + count;
2360 ctk_text_iter_set_offset (iter, new_char_index);
2361
2362 check_invariants (iter);
2363
2364 /* Return FALSE if we're on the non-dereferenceable end
2365 * iterator.
2366 */
2367 if (ctk_text_iter_is_end (iter))
2368 return FALSE(0);
2369 else
2370 return TRUE(!(0));
2371 }
2372}
2373
2374/**
2375 * ctk_text_iter_backward_chars:
2376 * @iter: an iterator
2377 * @count: number of characters to move
2378 *
2379 * Moves @count characters backward, if possible (if @count would move
2380 * past the start or end of the buffer, moves to the start or end of
2381 * the buffer). The return value indicates whether the iterator moved
2382 * onto a dereferenceable position; if the iterator didn’t move, or
2383 * moved onto the end iterator, then %FALSE is returned. If @count is 0,
2384 * the function does nothing and returns %FALSE.
2385 *
2386 * Returns: whether @iter moved and is dereferenceable
2387 *
2388 **/
2389gboolean
2390ctk_text_iter_backward_chars (CtkTextIter *iter, gint count)
2391{
2392 CtkTextRealIter *real;
2393
2394 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2395
2396 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2397
2398 real = ctk_text_iter_make_real (iter);
2399
2400 if (real == NULL((void*)0))
2401 return FALSE(0);
2402 else if (count == 0)
2403 return FALSE(0);
2404 else if (count < 0)
2405 return ctk_text_iter_forward_chars (iter, 0 - count);
2406
2407 ensure_char_offsets (real);
2408 check_invariants (iter);
2409
2410 /* <, not <=, because if count == segment_char_offset
2411 * we're going to the front of the segment and the any_segment
2412 * might change
2413 */
2414 if (count < real->segment_char_offset)
2415 {
2416 /* Optimize the within-segment case */
2417 g_assert (real->segment->char_count > 0)do { if (real->segment->char_count > 0) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 2417, ((const char*) (__func__)), "real->segment->char_count > 0"
); } while (0)
;
2418 g_assert (real->segment->type == &ctk_text_char_type)do { if (real->segment->type == &ctk_text_char_type
) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c", 2418
, ((const char*) (__func__)), "real->segment->type == &ctk_text_char_type"
); } while (0)
;
2419
2420 if (real->line_byte_offset >= 0)
2421 {
2422 const char *p;
2423 gint new_byte_offset;
2424
2425 /* if in the last fourth of the segment walk backwards */
2426 if (count < real->segment_char_offset / 4)
2427 p = g_utf8_offset_to_pointer (real->segment->body.chars + real->segment_byte_offset,
2428 -count);
2429 else
2430 p = g_utf8_offset_to_pointer (real->segment->body.chars,
2431 real->segment_char_offset - count);
2432
2433 new_byte_offset = p - real->segment->body.chars;
2434 real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
2435 real->segment_byte_offset = new_byte_offset;
2436 }
2437
2438 real->segment_char_offset -= count;
2439 real->line_char_offset -= count;
2440
2441 adjust_char_index (real, 0 - count);
2442
2443 check_invariants (iter);
2444
2445 return TRUE(!(0));
2446 }
2447 else
2448 {
2449 /* We need to go back into previous segments. For now,
2450 * just keep this really simple. FIXME
2451 * use backward_indexable_segment.
2452 */
2453 if (TRUE(!(0)) || count > MAX_LINEAR_SCAN150)
2454 {
2455 gint current_char_index;
2456 gint new_char_index;
2457
2458 current_char_index = ctk_text_iter_get_offset (iter);
2459
2460 if (current_char_index == 0)
2461 return FALSE(0); /* can't move backward */
2462
2463 new_char_index = current_char_index - count;
2464 if (new_char_index < 0)
2465 new_char_index = 0;
2466
2467 ctk_text_iter_set_offset (iter, new_char_index);
2468
2469 check_invariants (iter);
2470
2471 return TRUE(!(0));
2472 }
2473 else
2474 {
2475 /* FIXME backward_indexable_segment here */
2476
2477 return FALSE(0);
2478 }
2479 }
2480}
2481
2482#if 0
2483
2484/* These two can't be implemented efficiently (always have to use
2485 * a linear scan, since that’s the only way to find all the non-text
2486 * segments)
2487 */
2488
2489/**
2490 * ctk_text_iter_forward_text_chars:
2491 * @iter: a #CtkTextIter
2492 * @count: number of chars to move
2493 *
2494 * Moves forward by @count text characters (pixbufs, widgets,
2495 * etc. do not count as characters for this). Equivalent to moving
2496 * through the results of ctk_text_iter_get_text(), rather than
2497 * ctk_text_iter_get_slice().
2498 *
2499 * Returns: whether @iter moved and is dereferenceable
2500 **/
2501gboolean
2502ctk_text_iter_forward_text_chars (CtkTextIter *iter,
2503 gint count)
2504{
2505
2506
2507
2508}
2509
2510/**
2511 * ctk_text_iter_backward_text_chars:
2512 * @iter: a #CtkTextIter
2513 * @count: number of chars to move
2514 *
2515 * Moves backward by @count text characters (pixbufs, widgets,
2516 * etc. do not count as characters for this). Equivalent to moving
2517 * through the results of ctk_text_iter_get_text(), rather than
2518 * ctk_text_iter_get_slice().
2519 *
2520 * Returns: whether @iter moved and is dereferenceable
2521 **/
2522gboolean
2523ctk_text_iter_backward_text_chars (CtkTextIter *iter,
2524 gint count)
2525{
2526
2527
2528}
2529#endif
2530
2531/**
2532 * ctk_text_iter_forward_line:
2533 * @iter: an iterator
2534 *
2535 * Moves @iter to the start of the next line. If the iter is already on the
2536 * last line of the buffer, moves the iter to the end of the current line.
2537 * If after the operation, the iter is at the end of the buffer and not
2538 * dereferencable, returns %FALSE. Otherwise, returns %TRUE.
2539 *
2540 * Returns: whether @iter can be dereferenced
2541 **/
2542gboolean
2543ctk_text_iter_forward_line (CtkTextIter *iter)
2544{
2545 CtkTextRealIter *real;
2546
2547 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2548
2549 real = ctk_text_iter_make_real (iter);
2550
2551 if (real == NULL((void*)0))
2552 return FALSE(0);
2553
2554 check_invariants (iter);
2555
2556 if (forward_line_leaving_caches_unmodified (real))
2557 {
2558 invalidate_char_index (real);
2559 adjust_line_number (real, 1);
2560
2561 check_invariants (iter);
2562
2563 if (ctk_text_iter_is_end (iter))
2564 return FALSE(0);
2565 else
2566 return TRUE(!(0));
2567 }
2568 else
2569 {
2570 /* On the last line, move to end of it */
2571
2572 if (!ctk_text_iter_is_end (iter))
2573 ctk_text_iter_forward_to_end (iter);
2574
2575 check_invariants (iter);
2576 return FALSE(0);
2577 }
2578}
2579
2580/**
2581 * ctk_text_iter_backward_line:
2582 * @iter: an iterator
2583 *
2584 * Moves @iter to the start of the previous line. Returns %TRUE if
2585 * @iter could be moved; i.e. if @iter was at character offset 0, this
2586 * function returns %FALSE. Therefore if @iter was already on line 0,
2587 * but not at the start of the line, @iter is snapped to the start of
2588 * the line and the function returns %TRUE. (Note that this implies that
2589 * in a loop calling this function, the line number may not change on
2590 * every iteration, if your first iteration is on line 0.)
2591 *
2592 * Returns: whether @iter moved
2593 **/
2594gboolean
2595ctk_text_iter_backward_line (CtkTextIter *iter)
2596{
2597 CtkTextLine *new_line;
2598 CtkTextRealIter *real;
2599 gboolean offset_will_change;
2600 gint offset;
2601
2602 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
2603
2604 real = ctk_text_iter_make_real (iter);
2605
2606 if (real == NULL((void*)0))
2607 return FALSE(0);
2608
2609 ensure_char_offsets (real);
2610
2611 check_invariants (iter);
2612
2613 new_line = _ctk_text_line_previous (real->line);
2614
2615 offset_will_change = FALSE(0);
2616 if (real->line_char_offset > 0)
2617 offset_will_change = TRUE(!(0));
2618
2619 if (new_line != NULL((void*)0))
2620 {
2621 real->line = new_line;
2622
2623 adjust_line_number (real, -1);
2624 }
2625 else
2626 {
2627 if (!offset_will_change)
2628 return FALSE(0);
2629 }
2630
2631 invalidate_char_index (real);
2632
2633 real->line_byte_offset = 0;
2634 real->line_char_offset = 0;
2635
2636 real->segment_byte_offset = 0;
2637 real->segment_char_offset = 0;
2638
2639 /* Find first segment in line */
2640 real->any_segment = real->line->segments;
2641 real->segment = _ctk_text_line_byte_to_segment (real->line,
2642 0, &offset);
2643
2644 g_assert (offset == 0)do { if (offset == 0) ; else g_assertion_message_expr ("Ctk",
"ctktextiter.c", 2644, ((const char*) (__func__)), "offset == 0"
); } while (0)
;
2645
2646 /* Note that if we are on the first line, we snap to the start of
2647 * the first line and return TRUE, so TRUE means the iterator
2648 * changed, not that the line changed; this is maybe a bit
2649 * weird. I'm not sure there's an obvious right thing to do though.
2650 */
2651
2652 check_invariants (iter);
2653
2654 return TRUE(!(0));
2655}
2656
2657
2658/**
2659 * ctk_text_iter_forward_lines:
2660 * @iter: a #CtkTextIter
2661 * @count: number of lines to move forward
2662 *
2663 * Moves @count lines forward, if possible (if @count would move
2664 * past the start or end of the buffer, moves to the start or end of
2665 * the buffer). The return value indicates whether the iterator moved
2666 * onto a dereferenceable position; if the iterator didn’t move, or
2667 * moved onto the end iterator, then %FALSE is returned. If @count is 0,
2668 * the function does nothing and returns %FALSE. If @count is negative,
2669 * moves backward by 0 - @count lines.
2670 *
2671 * Returns: whether @iter moved and is dereferenceable
2672 **/
2673gboolean
2674ctk_text_iter_forward_lines (CtkTextIter *iter, gint count)
2675{
2676 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2677
2678 if (count < 0)
2679 return ctk_text_iter_backward_lines (iter, 0 - count);
2680 else if (count == 0)
2681 return FALSE(0);
2682 else if (count == 1)
2683 {
2684 check_invariants (iter);
2685 return ctk_text_iter_forward_line (iter);
2686 }
2687 else
2688 {
2689 gint old_line;
2690
2691 if (ctk_text_iter_is_end (iter))
2692 return FALSE(0);
2693
2694 old_line = ctk_text_iter_get_line (iter);
2695
2696 ctk_text_iter_set_line (iter, old_line + count);
2697
2698 if ((ctk_text_iter_get_line (iter) - old_line) < count)
2699 {
2700 /* count went past the last line, so move to end of last line */
2701 if (!ctk_text_iter_is_end (iter))
2702 ctk_text_iter_forward_to_end (iter);
2703 }
2704
2705 return !ctk_text_iter_is_end (iter);
2706 }
2707}
2708
2709/**
2710 * ctk_text_iter_backward_lines:
2711 * @iter: a #CtkTextIter
2712 * @count: number of lines to move backward
2713 *
2714 * Moves @count lines backward, if possible (if @count would move
2715 * past the start or end of the buffer, moves to the start or end of
2716 * the buffer). The return value indicates whether the iterator moved
2717 * onto a dereferenceable position; if the iterator didn’t move, or
2718 * moved onto the end iterator, then %FALSE is returned. If @count is 0,
2719 * the function does nothing and returns %FALSE. If @count is negative,
2720 * moves forward by 0 - @count lines.
2721 *
2722 * Returns: whether @iter moved and is dereferenceable
2723 **/
2724gboolean
2725ctk_text_iter_backward_lines (CtkTextIter *iter, gint count)
2726{
2727 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2728
2729 if (count < 0)
2730 return ctk_text_iter_forward_lines (iter, 0 - count);
2731 else if (count == 0)
2732 return FALSE(0);
2733 else if (count == 1)
2734 {
2735 return ctk_text_iter_backward_line (iter);
2736 }
2737 else
2738 {
2739 gint old_line;
2740
2741 old_line = ctk_text_iter_get_line (iter);
2742
2743 ctk_text_iter_set_line (iter, MAX (old_line - count, 0)(((old_line - count) > (0)) ? (old_line - count) : (0)));
2744
2745 return (ctk_text_iter_get_line (iter) != old_line);
2746 }
2747}
2748
2749/**
2750 * ctk_text_iter_forward_visible_line:
2751 * @iter: an iterator
2752 *
2753 * Moves @iter to the start of the next visible line. Returns %TRUE if there
2754 * was a next line to move to, and %FALSE if @iter was simply moved to
2755 * the end of the buffer and is now not dereferenceable, or if @iter was
2756 * already at the end of the buffer.
2757 *
2758 * Returns: whether @iter can be dereferenced
2759 *
2760 * Since: 2.8
2761 **/
2762gboolean
2763ctk_text_iter_forward_visible_line (CtkTextIter *iter)
2764{
2765 while (ctk_text_iter_forward_line (iter))
2766 {
2767 if (!_ctk_text_btree_char_is_invisible (iter))
2768 return TRUE(!(0));
2769 else
2770 {
2771 do
2772 {
2773 if (!ctk_text_iter_forward_char (iter))
2774 return FALSE(0);
2775
2776 if (!_ctk_text_btree_char_is_invisible (iter))
2777 return TRUE(!(0));
2778 }
2779 while (!ctk_text_iter_ends_line (iter));
2780 }
2781 }
2782
2783 return FALSE(0);
2784}
2785
2786/**
2787 * ctk_text_iter_backward_visible_line:
2788 * @iter: an iterator
2789 *
2790 * Moves @iter to the start of the previous visible line. Returns %TRUE if
2791 * @iter could be moved; i.e. if @iter was at character offset 0, this
2792 * function returns %FALSE. Therefore if @iter was already on line 0,
2793 * but not at the start of the line, @iter is snapped to the start of
2794 * the line and the function returns %TRUE. (Note that this implies that
2795 * in a loop calling this function, the line number may not change on
2796 * every iteration, if your first iteration is on line 0.)
2797 *
2798 * Returns: whether @iter moved
2799 *
2800 * Since: 2.8
2801 **/
2802gboolean
2803ctk_text_iter_backward_visible_line (CtkTextIter *iter)
2804{
2805 while (ctk_text_iter_backward_line (iter))
2806 {
2807 if (!_ctk_text_btree_char_is_invisible (iter))
2808 return TRUE(!(0));
2809 else
2810 {
2811 do
2812 {
2813 if (!ctk_text_iter_backward_char (iter))
2814 return FALSE(0);
2815
2816 if (!_ctk_text_btree_char_is_invisible (iter))
2817 return TRUE(!(0));
2818 }
2819 while (!ctk_text_iter_starts_line (iter));
2820 }
2821 }
2822
2823 return FALSE(0);
2824}
2825
2826/**
2827 * ctk_text_iter_forward_visible_lines:
2828 * @iter: a #CtkTextIter
2829 * @count: number of lines to move forward
2830 *
2831 * Moves @count visible lines forward, if possible (if @count would move
2832 * past the start or end of the buffer, moves to the start or end of
2833 * the buffer). The return value indicates whether the iterator moved
2834 * onto a dereferenceable position; if the iterator didn’t move, or
2835 * moved onto the end iterator, then %FALSE is returned. If @count is 0,
2836 * the function does nothing and returns %FALSE. If @count is negative,
2837 * moves backward by 0 - @count lines.
2838 *
2839 * Returns: whether @iter moved and is dereferenceable
2840 *
2841 * Since: 2.8
2842 **/
2843gboolean
2844ctk_text_iter_forward_visible_lines (CtkTextIter *iter,
2845 gint count)
2846{
2847 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2848
2849 if (count < 0)
2850 return ctk_text_iter_backward_visible_lines (iter, 0 - count);
2851 else if (count == 0)
2852 return FALSE(0);
2853 else if (count == 1)
2854 {
2855 check_invariants (iter);
2856 return ctk_text_iter_forward_visible_line (iter);
2857 }
2858 else
2859 {
2860 while (ctk_text_iter_forward_visible_line (iter) && count > 0)
2861 count--;
2862 return count == 0;
2863 }
2864}
2865
2866/**
2867 * ctk_text_iter_backward_visible_lines:
2868 * @iter: a #CtkTextIter
2869 * @count: number of lines to move backward
2870 *
2871 * Moves @count visible lines backward, if possible (if @count would move
2872 * past the start or end of the buffer, moves to the start or end of
2873 * the buffer). The return value indicates whether the iterator moved
2874 * onto a dereferenceable position; if the iterator didn’t move, or
2875 * moved onto the end iterator, then %FALSE is returned. If @count is 0,
2876 * the function does nothing and returns %FALSE. If @count is negative,
2877 * moves forward by 0 - @count lines.
2878 *
2879 * Returns: whether @iter moved and is dereferenceable
2880 *
2881 * Since: 2.8
2882 **/
2883gboolean
2884ctk_text_iter_backward_visible_lines (CtkTextIter *iter,
2885 gint count)
2886{
2887 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
2888
2889 if (count < 0)
2890 return ctk_text_iter_forward_visible_lines (iter, 0 - count);
2891 else if (count == 0)
2892 return FALSE(0);
2893 else if (count == 1)
2894 {
2895 return ctk_text_iter_backward_visible_line (iter);
2896 }
2897 else
2898 {
2899 while (ctk_text_iter_backward_visible_line (iter) && count > 0)
2900 count--;
2901 return count == 0;
2902 }
2903}
2904
2905typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
2906 gint offset,
2907 gint len,
2908 gint *found_offset,
2909 gboolean already_moved_initially);
2910
2911typedef gboolean (* TestLogAttrFunc) (const PangoLogAttr *attrs,
2912 gint offset,
2913 gint min_offset,
2914 gint len);
2915
2916/* Word funcs */
2917
2918static gboolean
2919find_word_end_func (const PangoLogAttr *attrs,
2920 gint offset,
2921 gint len,
2922 gint *found_offset,
2923 gboolean already_moved_initially)
2924{
2925 if (!already_moved_initially)
2926 ++offset;
2927
2928 /* Find end of next word */
2929 while (offset <= len)
2930 {
2931 if (attrs[offset].is_word_end)
2932 {
2933 *found_offset = offset;
2934 return TRUE(!(0));
2935 }
2936
2937 ++offset;
2938 }
2939
2940 return FALSE(0);
2941}
2942
2943static gboolean
2944is_word_end_func (const PangoLogAttr *attrs,
2945 gint offset,
2946 gint min_offset G_GNUC_UNUSED__attribute__ ((__unused__)),
2947 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
2948{
2949 return attrs[offset].is_word_end;
2950}
2951
2952static gboolean
2953find_word_start_func (const PangoLogAttr *attrs,
2954 gint offset,
2955 gint len G_GNUC_UNUSED__attribute__ ((__unused__)),
2956 gint *found_offset,
2957 gboolean already_moved_initially)
2958{
2959 if (!already_moved_initially)
2960 --offset;
2961
2962 /* Find start of prev word */
2963 while (offset >= 0)
2964 {
2965 if (attrs[offset].is_word_start)
2966 {
2967 *found_offset = offset;
2968 return TRUE(!(0));
2969 }
2970
2971 --offset;
2972 }
2973
2974 return FALSE(0);
2975}
2976
2977static gboolean
2978is_word_start_func (const PangoLogAttr *attrs,
2979 gint offset,
2980 gint min_offset G_GNUC_UNUSED__attribute__ ((__unused__)),
2981 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
2982{
2983 return attrs[offset].is_word_start;
2984}
2985
2986static gboolean
2987inside_word_func (const PangoLogAttr *attrs,
2988 gint offset,
2989 gint min_offset,
2990 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
2991{
2992 /* Find next word start or end */
2993 while (offset >= min_offset &&
2994 !(attrs[offset].is_word_start || attrs[offset].is_word_end))
2995 --offset;
2996
2997 if (offset >= 0)
2998 return attrs[offset].is_word_start;
2999 else
3000 return FALSE(0);
3001}
3002
3003/* Sentence funcs */
3004
3005static gboolean
3006find_sentence_end_func (const PangoLogAttr *attrs,
3007 gint offset,
3008 gint len,
3009 gint *found_offset,
3010 gboolean already_moved_initially)
3011{
3012 if (!already_moved_initially)
3013 ++offset;
3014
3015 /* Find end of next sentence */
3016 while (offset <= len)
3017 {
3018 if (attrs[offset].is_sentence_end)
3019 {
3020 *found_offset = offset;
3021 return TRUE(!(0));
3022 }
3023
3024 ++offset;
3025 }
3026
3027 return FALSE(0);
3028}
3029
3030static gboolean
3031is_sentence_end_func (const PangoLogAttr *attrs,
3032 gint offset,
3033 gint min_offset G_GNUC_UNUSED__attribute__ ((__unused__)),
3034 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
3035{
3036 return attrs[offset].is_sentence_end;
3037}
3038
3039static gboolean
3040find_sentence_start_func (const PangoLogAttr *attrs,
3041 gint offset,
3042 gint len G_GNUC_UNUSED__attribute__ ((__unused__)),
3043 gint *found_offset,
3044 gboolean already_moved_initially)
3045{
3046 if (!already_moved_initially)
3047 --offset;
3048
3049 /* Find start of prev sentence */
3050 while (offset >= 0)
3051 {
3052 if (attrs[offset].is_sentence_start)
3053 {
3054 *found_offset = offset;
3055 return TRUE(!(0));
3056 }
3057
3058 --offset;
3059 }
3060
3061 return FALSE(0);
3062}
3063
3064static gboolean
3065is_sentence_start_func (const PangoLogAttr *attrs,
3066 gint offset,
3067 gint min_offset G_GNUC_UNUSED__attribute__ ((__unused__)),
3068 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
3069{
3070 return attrs[offset].is_sentence_start;
3071}
3072
3073static gboolean
3074inside_sentence_func (const PangoLogAttr *attrs,
3075 gint offset,
3076 gint min_offset,
3077 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
3078{
3079 /* Find next sentence start or end */
3080 while (!(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
3081 {
3082 --offset;
3083 if (offset < min_offset)
3084 return FALSE(0);
3085 }
3086
3087 return attrs[offset].is_sentence_start;
3088}
3089
3090static gboolean
3091test_log_attrs (const CtkTextIter *iter,
3092 TestLogAttrFunc func)
3093{
3094 gint char_len;
3095 const PangoLogAttr *attrs;
3096 gint offset;
3097
3098 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
3099
3100 attrs = _ctk_text_buffer_get_line_log_attrs (ctk_text_iter_get_buffer (iter),
3101 iter, &char_len);
3102
3103 offset = ctk_text_iter_get_line_offset (iter);
3104
3105 g_assert (offset <= char_len)do { if (offset <= char_len) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 3105, ((const char*) (__func__)), "offset <= char_len"
); } while (0)
;
3106
3107 return (* func) (attrs, offset, 0, char_len);
3108}
3109
3110static gboolean
3111find_line_log_attrs (const CtkTextIter *iter,
3112 FindLogAttrFunc func,
3113 gint *found_offset,
3114 gboolean already_moved_initially)
3115{
3116 gint char_len;
3117 const PangoLogAttr *attrs;
3118 gint offset;
3119
3120 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
3121
3122 attrs = _ctk_text_buffer_get_line_log_attrs (ctk_text_iter_get_buffer (iter),
3123 iter, &char_len);
3124
3125 offset = ctk_text_iter_get_line_offset (iter);
3126
3127 return (* func) (attrs,
3128 offset,
3129 char_len,
3130 found_offset,
3131 already_moved_initially);
3132}
3133
3134static gboolean
3135find_by_log_attrs (CtkTextIter *arg_iter,
3136 FindLogAttrFunc func,
3137 gboolean forward)
3138{
3139 CtkTextIter iter;
3140 gboolean already_moved_initially = FALSE(0);
3141
3142 g_return_val_if_fail (arg_iter != NULL, FALSE)do { if ((arg_iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "arg_iter != NULL"); return
((0)); } } while (0)
;
3143
3144 iter = *arg_iter;
3145
3146 while (TRUE(!(0)))
3147 {
3148 gint offset = 0;
3149 gboolean found;
3150
3151 found = find_line_log_attrs (&iter, func, &offset, already_moved_initially);
3152
3153 if (found)
3154 {
3155 gboolean moved;
3156
3157 ctk_text_iter_set_line_offset (&iter, offset);
3158
3159 moved = !ctk_text_iter_equal (&iter, arg_iter);
3160
3161 *arg_iter = iter;
3162 return moved && !ctk_text_iter_is_end (arg_iter);
3163 }
3164
3165 if (forward)
3166 {
3167 if (!ctk_text_iter_forward_line (&iter))
3168 return FALSE(0);
3169
3170 already_moved_initially = TRUE(!(0));
3171 }
3172 else
3173 {
3174 /* Go to end of previous line. First go to the current line offset 0,
3175 * because backward_line() snaps to start of line 0 if iter is already
3176 * on line 0.
3177 */
3178 ctk_text_iter_set_line_offset (&iter, 0);
3179
3180 if (ctk_text_iter_backward_line (&iter))
3181 {
3182 if (!ctk_text_iter_ends_line (&iter))
3183 ctk_text_iter_forward_to_line_end (&iter);
3184
3185 already_moved_initially = TRUE(!(0));
3186 }
3187 else
3188 {
3189 return FALSE(0);
3190 }
3191 }
3192 }
3193}
3194
3195static gboolean
3196find_visible_by_log_attrs (CtkTextIter *iter,
3197 FindLogAttrFunc func,
3198 gboolean forward)
3199{
3200 CtkTextIter pos;
3201
3202 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
3203
3204 pos = *iter;
3205
3206 while (TRUE(!(0)))
3207 {
3208 CtkTextIter pos_before = pos;
3209
3210 find_by_log_attrs (&pos, func, forward);
3211
3212 if (ctk_text_iter_equal (&pos_before, &pos))
3213 break;
3214
3215 if (!_ctk_text_btree_char_is_invisible (&pos))
3216 {
3217 *iter = pos;
3218 return !ctk_text_iter_is_end (iter);
3219 }
3220 }
3221
3222 return FALSE(0);
3223}
3224
3225typedef gboolean (* OneStepFunc) (CtkTextIter *iter);
3226typedef gboolean (* MultipleStepFunc) (CtkTextIter *iter, gint count);
3227
3228static gboolean
3229move_multiple_steps (CtkTextIter *iter,
3230 gint count,
3231 OneStepFunc step_forward,
3232 MultipleStepFunc n_steps_backward)
3233{
3234 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
3235
3236 FIX_OVERFLOWS (count)if ((count) == (-2147483647 -1)) (count) = (-2147483647 -1) +
1
;
3237
3238 if (count == 0)
3239 return FALSE(0);
3240
3241 if (count < 0)
3242 return n_steps_backward (iter, -count);
3243
3244 if (!step_forward (iter))
3245 return FALSE(0);
3246 --count;
3247
3248 while (count > 0)
3249 {
3250 if (!step_forward (iter))
3251 break;
3252 --count;
3253 }
3254
3255 return !ctk_text_iter_is_end (iter);
3256}
3257
3258
3259/**
3260 * ctk_text_iter_forward_word_end:
3261 * @iter: a #CtkTextIter
3262 *
3263 * Moves forward to the next word end. (If @iter is currently on a
3264 * word end, moves forward to the next one after that.) Word breaks
3265 * are determined by Pango and should be correct for nearly any
3266 * language (if not, the correct fix would be to the Pango word break
3267 * algorithms).
3268 *
3269 * Returns: %TRUE if @iter moved and is not the end iterator
3270 **/
3271gboolean
3272ctk_text_iter_forward_word_end (CtkTextIter *iter)
3273{
3274 return find_by_log_attrs (iter, find_word_end_func, TRUE(!(0)));
3275}
3276
3277/**
3278 * ctk_text_iter_backward_word_start:
3279 * @iter: a #CtkTextIter
3280 *
3281 * Moves backward to the previous word start. (If @iter is currently on a
3282 * word start, moves backward to the next one after that.) Word breaks
3283 * are determined by Pango and should be correct for nearly any
3284 * language (if not, the correct fix would be to the Pango word break
3285 * algorithms).
3286 *
3287 * Returns: %TRUE if @iter moved and is not the end iterator
3288 **/
3289gboolean
3290ctk_text_iter_backward_word_start (CtkTextIter *iter)
3291{
3292 return find_by_log_attrs (iter, find_word_start_func, FALSE(0));
3293}
3294
3295/* FIXME a loop around a truly slow function means
3296 * a truly spectacularly slow function.
3297 */
3298
3299/**
3300 * ctk_text_iter_forward_word_ends:
3301 * @iter: a #CtkTextIter
3302 * @count: number of times to move
3303 *
3304 * Calls ctk_text_iter_forward_word_end() up to @count times.
3305 *
3306 * Returns: %TRUE if @iter moved and is not the end iterator
3307 **/
3308gboolean
3309ctk_text_iter_forward_word_ends (CtkTextIter *iter,
3310 gint count)
3311{
3312 return move_multiple_steps (iter, count,
3313 ctk_text_iter_forward_word_end,
3314 ctk_text_iter_backward_word_starts);
3315}
3316
3317/**
3318 * ctk_text_iter_backward_word_starts:
3319 * @iter: a #CtkTextIter
3320 * @count: number of times to move
3321 *
3322 * Calls ctk_text_iter_backward_word_start() up to @count times.
3323 *
3324 * Returns: %TRUE if @iter moved and is not the end iterator
3325 **/
3326gboolean
3327ctk_text_iter_backward_word_starts (CtkTextIter *iter,
3328 gint count)
3329{
3330 return move_multiple_steps (iter, count,
3331 ctk_text_iter_backward_word_start,
3332 ctk_text_iter_forward_word_ends);
3333}
3334
3335/**
3336 * ctk_text_iter_forward_visible_word_end:
3337 * @iter: a #CtkTextIter
3338 *
3339 * Moves forward to the next visible word end. (If @iter is currently on a
3340 * word end, moves forward to the next one after that.) Word breaks
3341 * are determined by Pango and should be correct for nearly any
3342 * language (if not, the correct fix would be to the Pango word break
3343 * algorithms).
3344 *
3345 * Returns: %TRUE if @iter moved and is not the end iterator
3346 *
3347 * Since: 2.4
3348 **/
3349gboolean
3350ctk_text_iter_forward_visible_word_end (CtkTextIter *iter)
3351{
3352 return find_visible_by_log_attrs (iter, find_word_end_func, TRUE(!(0)));
3353}
3354
3355/**
3356 * ctk_text_iter_backward_visible_word_start:
3357 * @iter: a #CtkTextIter
3358 *
3359 * Moves backward to the previous visible word start. (If @iter is currently
3360 * on a word start, moves backward to the next one after that.) Word breaks
3361 * are determined by Pango and should be correct for nearly any
3362 * language (if not, the correct fix would be to the Pango word break
3363 * algorithms).
3364 *
3365 * Returns: %TRUE if @iter moved and is not the end iterator
3366 *
3367 * Since: 2.4
3368 **/
3369gboolean
3370ctk_text_iter_backward_visible_word_start (CtkTextIter *iter)
3371{
3372 return find_visible_by_log_attrs (iter, find_word_start_func, FALSE(0));
3373}
3374
3375/**
3376 * ctk_text_iter_forward_visible_word_ends:
3377 * @iter: a #CtkTextIter
3378 * @count: number of times to move
3379 *
3380 * Calls ctk_text_iter_forward_visible_word_end() up to @count times.
3381 *
3382 * Returns: %TRUE if @iter moved and is not the end iterator
3383 *
3384 * Since: 2.4
3385 **/
3386gboolean
3387ctk_text_iter_forward_visible_word_ends (CtkTextIter *iter,
3388 gint count)
3389{
3390 return move_multiple_steps (iter, count,
3391 ctk_text_iter_forward_visible_word_end,
3392 ctk_text_iter_backward_visible_word_starts);
3393}
3394
3395/**
3396 * ctk_text_iter_backward_visible_word_starts:
3397 * @iter: a #CtkTextIter
3398 * @count: number of times to move
3399 *
3400 * Calls ctk_text_iter_backward_visible_word_start() up to @count times.
3401 *
3402 * Returns: %TRUE if @iter moved and is not the end iterator
3403 *
3404 * Since: 2.4
3405 **/
3406gboolean
3407ctk_text_iter_backward_visible_word_starts (CtkTextIter *iter,
3408 gint count)
3409{
3410 return move_multiple_steps (iter, count,
3411 ctk_text_iter_backward_visible_word_start,
3412 ctk_text_iter_forward_visible_word_ends);
3413}
3414
3415/**
3416 * ctk_text_iter_starts_word:
3417 * @iter: a #CtkTextIter
3418 *
3419 * Determines whether @iter begins a natural-language word. Word
3420 * breaks are determined by Pango and should be correct for nearly any
3421 * language (if not, the correct fix would be to the Pango word break
3422 * algorithms).
3423 *
3424 * Returns: %TRUE if @iter is at the start of a word
3425 **/
3426gboolean
3427ctk_text_iter_starts_word (const CtkTextIter *iter)
3428{
3429 return test_log_attrs (iter, is_word_start_func);
3430}
3431
3432/**
3433 * ctk_text_iter_ends_word:
3434 * @iter: a #CtkTextIter
3435 *
3436 * Determines whether @iter ends a natural-language word. Word breaks
3437 * are determined by Pango and should be correct for nearly any
3438 * language (if not, the correct fix would be to the Pango word break
3439 * algorithms).
3440 *
3441 * Returns: %TRUE if @iter is at the end of a word
3442 **/
3443gboolean
3444ctk_text_iter_ends_word (const CtkTextIter *iter)
3445{
3446 return test_log_attrs (iter, is_word_end_func);
3447}
3448
3449/**
3450 * ctk_text_iter_inside_word:
3451 * @iter: a #CtkTextIter
3452 *
3453 * Determines whether the character pointed by @iter is part of a
3454 * natural-language word (as opposed to say inside some whitespace). Word
3455 * breaks are determined by Pango and should be correct for nearly any language
3456 * (if not, the correct fix would be to the Pango word break algorithms).
3457 *
3458 * Note that if ctk_text_iter_starts_word() returns %TRUE, then this function
3459 * returns %TRUE too, since @iter points to the first character of the word.
3460 *
3461 * Returns: %TRUE if @iter is inside a word
3462 **/
3463gboolean
3464ctk_text_iter_inside_word (const CtkTextIter *iter)
3465{
3466 return test_log_attrs (iter, inside_word_func);
3467}
3468
3469/**
3470 * ctk_text_iter_starts_sentence:
3471 * @iter: a #CtkTextIter
3472 *
3473 * Determines whether @iter begins a sentence. Sentence boundaries are
3474 * determined by Pango and should be correct for nearly any language
3475 * (if not, the correct fix would be to the Pango text boundary
3476 * algorithms).
3477 *
3478 * Returns: %TRUE if @iter is at the start of a sentence.
3479 **/
3480gboolean
3481ctk_text_iter_starts_sentence (const CtkTextIter *iter)
3482{
3483 return test_log_attrs (iter, is_sentence_start_func);
3484}
3485
3486/**
3487 * ctk_text_iter_ends_sentence:
3488 * @iter: a #CtkTextIter
3489 *
3490 * Determines whether @iter ends a sentence. Sentence boundaries are
3491 * determined by Pango and should be correct for nearly any language
3492 * (if not, the correct fix would be to the Pango text boundary
3493 * algorithms).
3494 *
3495 * Returns: %TRUE if @iter is at the end of a sentence.
3496 **/
3497gboolean
3498ctk_text_iter_ends_sentence (const CtkTextIter *iter)
3499{
3500 return test_log_attrs (iter, is_sentence_end_func);
3501}
3502
3503/**
3504 * ctk_text_iter_inside_sentence:
3505 * @iter: a #CtkTextIter
3506 *
3507 * Determines whether @iter is inside a sentence (as opposed to in
3508 * between two sentences, e.g. after a period and before the first
3509 * letter of the next sentence). Sentence boundaries are determined
3510 * by Pango and should be correct for nearly any language (if not, the
3511 * correct fix would be to the Pango text boundary algorithms).
3512 *
3513 * Returns: %TRUE if @iter is inside a sentence.
3514 **/
3515gboolean
3516ctk_text_iter_inside_sentence (const CtkTextIter *iter)
3517{
3518 return test_log_attrs (iter, inside_sentence_func);
3519}
3520
3521/**
3522 * ctk_text_iter_forward_sentence_end:
3523 * @iter: a #CtkTextIter
3524 *
3525 * Moves forward to the next sentence end. (If @iter is at the end of
3526 * a sentence, moves to the next end of sentence.) Sentence
3527 * boundaries are determined by Pango and should be correct for nearly
3528 * any language (if not, the correct fix would be to the Pango text
3529 * boundary algorithms).
3530 *
3531 * Returns: %TRUE if @iter moved and is not the end iterator
3532 **/
3533gboolean
3534ctk_text_iter_forward_sentence_end (CtkTextIter *iter)
3535{
3536 return find_by_log_attrs (iter, find_sentence_end_func, TRUE(!(0)));
3537}
3538
3539/**
3540 * ctk_text_iter_backward_sentence_start:
3541 * @iter: a #CtkTextIter
3542 *
3543 * Moves backward to the previous sentence start; if @iter is already at
3544 * the start of a sentence, moves backward to the next one. Sentence
3545 * boundaries are determined by Pango and should be correct for nearly
3546 * any language (if not, the correct fix would be to the Pango text
3547 * boundary algorithms).
3548 *
3549 * Returns: %TRUE if @iter moved and is not the end iterator
3550 **/
3551gboolean
3552ctk_text_iter_backward_sentence_start (CtkTextIter *iter)
3553{
3554 return find_by_log_attrs (iter, find_sentence_start_func, FALSE(0));
3555}
3556
3557/* FIXME a loop around a truly slow function means
3558 * a truly spectacularly slow function.
3559 */
3560/**
3561 * ctk_text_iter_forward_sentence_ends:
3562 * @iter: a #CtkTextIter
3563 * @count: number of sentences to move
3564 *
3565 * Calls ctk_text_iter_forward_sentence_end() @count times (or until
3566 * ctk_text_iter_forward_sentence_end() returns %FALSE). If @count is
3567 * negative, moves backward instead of forward.
3568 *
3569 * Returns: %TRUE if @iter moved and is not the end iterator
3570 **/
3571gboolean
3572ctk_text_iter_forward_sentence_ends (CtkTextIter *iter,
3573 gint count)
3574{
3575 return move_multiple_steps (iter, count,
3576 ctk_text_iter_forward_sentence_end,
3577 ctk_text_iter_backward_sentence_starts);
3578}
3579
3580/**
3581 * ctk_text_iter_backward_sentence_starts:
3582 * @iter: a #CtkTextIter
3583 * @count: number of sentences to move
3584 *
3585 * Calls ctk_text_iter_backward_sentence_start() up to @count times,
3586 * or until it returns %FALSE. If @count is negative, moves forward
3587 * instead of backward.
3588 *
3589 * Returns: %TRUE if @iter moved and is not the end iterator
3590 **/
3591gboolean
3592ctk_text_iter_backward_sentence_starts (CtkTextIter *iter,
3593 gint count)
3594{
3595 return move_multiple_steps (iter, count,
3596 ctk_text_iter_backward_sentence_start,
3597 ctk_text_iter_forward_sentence_ends);
3598}
3599
3600static gboolean
3601find_forward_cursor_pos_func (const PangoLogAttr *attrs,
3602 gint offset,
3603 gint len,
3604 gint *found_offset,
3605 gboolean already_moved_initially)
3606{
3607 if (!already_moved_initially)
3608 ++offset;
3609
3610 while (offset <= len)
3611 {
3612 if (attrs[offset].is_cursor_position)
3613 {
3614 *found_offset = offset;
3615 return TRUE(!(0));
3616 }
3617
3618 ++offset;
3619 }
3620
3621 return FALSE(0);
3622}
3623
3624static gboolean
3625find_backward_cursor_pos_func (const PangoLogAttr *attrs,
3626 gint offset,
3627 gint len G_GNUC_UNUSED__attribute__ ((__unused__)),
3628 gint *found_offset,
3629 gboolean already_moved_initially)
3630{
3631 if (!already_moved_initially)
3632 --offset;
3633
3634 while (offset >= 0)
3635 {
3636 if (attrs[offset].is_cursor_position)
3637 {
3638 *found_offset = offset;
3639 return TRUE(!(0));
3640 }
3641
3642 --offset;
3643 }
3644
3645 return FALSE(0);
3646}
3647
3648static gboolean
3649is_cursor_pos_func (const PangoLogAttr *attrs,
3650 gint offset,
3651 gint min_offset G_GNUC_UNUSED__attribute__ ((__unused__)),
3652 gint len G_GNUC_UNUSED__attribute__ ((__unused__)))
3653{
3654 return attrs[offset].is_cursor_position;
3655}
3656
3657/**
3658 * ctk_text_iter_forward_cursor_position:
3659 * @iter: a #CtkTextIter
3660 *
3661 * Moves @iter forward by a single cursor position. Cursor positions
3662 * are (unsurprisingly) positions where the cursor can appear. Perhaps
3663 * surprisingly, there may not be a cursor position between all
3664 * characters. The most common example for European languages would be
3665 * a carriage return/newline sequence. For some Unicode characters,
3666 * the equivalent of say the letter “a” with an accent mark will be
3667 * represented as two characters, first the letter then a "combining
3668 * mark" that causes the accent to be rendered; so the cursor can’t go
3669 * between those two characters. See also the #PangoLogAttr-struct and
3670 * pango_break() function.
3671 *
3672 * Returns: %TRUE if we moved and the new position is dereferenceable
3673 **/
3674gboolean
3675ctk_text_iter_forward_cursor_position (CtkTextIter *iter)
3676{
3677 return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE(!(0)));
3678}
3679
3680/**
3681 * ctk_text_iter_backward_cursor_position:
3682 * @iter: a #CtkTextIter
3683 *
3684 * Like ctk_text_iter_forward_cursor_position(), but moves backward.
3685 *
3686 * Returns: %TRUE if we moved
3687 **/
3688gboolean
3689ctk_text_iter_backward_cursor_position (CtkTextIter *iter)
3690{
3691 return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE(0));
3692}
3693
3694/**
3695 * ctk_text_iter_forward_cursor_positions:
3696 * @iter: a #CtkTextIter
3697 * @count: number of positions to move
3698 *
3699 * Moves up to @count cursor positions. See
3700 * ctk_text_iter_forward_cursor_position() for details.
3701 *
3702 * Returns: %TRUE if we moved and the new position is dereferenceable
3703 **/
3704gboolean
3705ctk_text_iter_forward_cursor_positions (CtkTextIter *iter,
3706 gint count)
3707{
3708 return move_multiple_steps (iter, count,
3709 ctk_text_iter_forward_cursor_position,
3710 ctk_text_iter_backward_cursor_positions);
3711}
3712
3713/**
3714 * ctk_text_iter_backward_cursor_positions:
3715 * @iter: a #CtkTextIter
3716 * @count: number of positions to move
3717 *
3718 * Moves up to @count cursor positions. See
3719 * ctk_text_iter_forward_cursor_position() for details.
3720 *
3721 * Returns: %TRUE if we moved and the new position is dereferenceable
3722 **/
3723gboolean
3724ctk_text_iter_backward_cursor_positions (CtkTextIter *iter,
3725 gint count)
3726{
3727 return move_multiple_steps (iter, count,
3728 ctk_text_iter_backward_cursor_position,
3729 ctk_text_iter_forward_cursor_positions);
3730}
3731
3732/**
3733 * ctk_text_iter_forward_visible_cursor_position:
3734 * @iter: a #CtkTextIter
3735 *
3736 * Moves @iter forward to the next visible cursor position. See
3737 * ctk_text_iter_forward_cursor_position() for details.
3738 *
3739 * Returns: %TRUE if we moved and the new position is dereferenceable
3740 *
3741 * Since: 2.4
3742 **/
3743gboolean
3744ctk_text_iter_forward_visible_cursor_position (CtkTextIter *iter)
3745{
3746 return find_visible_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE(!(0)));
3747}
3748
3749/**
3750 * ctk_text_iter_backward_visible_cursor_position:
3751 * @iter: a #CtkTextIter
3752 *
3753 * Moves @iter forward to the previous visible cursor position. See
3754 * ctk_text_iter_backward_cursor_position() for details.
3755 *
3756 * Returns: %TRUE if we moved and the new position is dereferenceable
3757 *
3758 * Since: 2.4
3759 **/
3760gboolean
3761ctk_text_iter_backward_visible_cursor_position (CtkTextIter *iter)
3762{
3763 return find_visible_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE(0));
3764}
3765
3766/**
3767 * ctk_text_iter_forward_visible_cursor_positions:
3768 * @iter: a #CtkTextIter
3769 * @count: number of positions to move
3770 *
3771 * Moves up to @count visible cursor positions. See
3772 * ctk_text_iter_forward_cursor_position() for details.
3773 *
3774 * Returns: %TRUE if we moved and the new position is dereferenceable
3775 *
3776 * Since: 2.4
3777 **/
3778gboolean
3779ctk_text_iter_forward_visible_cursor_positions (CtkTextIter *iter,
3780 gint count)
3781{
3782 return move_multiple_steps (iter, count,
3783 ctk_text_iter_forward_visible_cursor_position,
3784 ctk_text_iter_backward_visible_cursor_positions);
3785}
3786
3787/**
3788 * ctk_text_iter_backward_visible_cursor_positions:
3789 * @iter: a #CtkTextIter
3790 * @count: number of positions to move
3791 *
3792 * Moves up to @count visible cursor positions. See
3793 * ctk_text_iter_backward_cursor_position() for details.
3794 *
3795 * Returns: %TRUE if we moved and the new position is dereferenceable
3796 *
3797 * Since: 2.4
3798 **/
3799gboolean
3800ctk_text_iter_backward_visible_cursor_positions (CtkTextIter *iter,
3801 gint count)
3802{
3803 return move_multiple_steps (iter, count,
3804 ctk_text_iter_backward_visible_cursor_position,
3805 ctk_text_iter_forward_visible_cursor_positions);
3806}
3807
3808/**
3809 * ctk_text_iter_is_cursor_position:
3810 * @iter: a #CtkTextIter
3811 *
3812 * See ctk_text_iter_forward_cursor_position() or #PangoLogAttr or
3813 * pango_break() for details on what a cursor position is.
3814 *
3815 * Returns: %TRUE if the cursor can be placed at @iter
3816 **/
3817gboolean
3818ctk_text_iter_is_cursor_position (const CtkTextIter *iter)
3819{
3820 return test_log_attrs (iter, is_cursor_pos_func);
3821}
3822
3823/**
3824 * ctk_text_iter_set_line_offset:
3825 * @iter: a #CtkTextIter
3826 * @char_on_line: a character offset relative to the start of @iter’s current line
3827 *
3828 * Moves @iter within a line, to a new character
3829 * (not byte) offset. The given character offset must be less than or
3830 * equal to the number of characters in the line; if equal, @iter
3831 * moves to the start of the next line. See
3832 * ctk_text_iter_set_line_index() if you have a byte index rather than
3833 * a character offset.
3834 *
3835 **/
3836void
3837ctk_text_iter_set_line_offset (CtkTextIter *iter,
3838 gint char_on_line)
3839{
3840 CtkTextRealIter *real;
3841 gint chars_in_line;
3842
3843 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)
;
3844
3845 real = ctk_text_iter_make_surreal (iter);
3846
3847 if (real == NULL((void*)0))
3848 return;
3849
3850 check_invariants (iter);
3851
3852 chars_in_line = ctk_text_iter_get_chars_in_line (iter);
3853
3854 g_return_if_fail (char_on_line <= chars_in_line)do { if ((char_on_line <= chars_in_line)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "char_on_line <= chars_in_line"
); return; } } while (0)
;
3855
3856 if (char_on_line < chars_in_line)
3857 iter_set_from_char_offset (real, real->line, char_on_line);
3858 else
3859 ctk_text_iter_forward_line (iter); /* set to start of next line */
3860
3861 check_invariants (iter);
3862}
3863
3864/**
3865 * ctk_text_iter_set_line_index:
3866 * @iter: a #CtkTextIter
3867 * @byte_on_line: a byte index relative to the start of @iter’s current line
3868 *
3869 * Same as ctk_text_iter_set_line_offset(), but works with a
3870 * byte index. The given byte index must be at
3871 * the start of a character, it can’t be in the middle of a UTF-8
3872 * encoded character.
3873 *
3874 **/
3875void
3876ctk_text_iter_set_line_index (CtkTextIter *iter,
3877 gint byte_on_line)
3878{
3879 CtkTextRealIter *real;
3880 gint bytes_in_line;
3881
3882 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)
;
3883
3884 real = ctk_text_iter_make_surreal (iter);
3885
3886 if (real == NULL((void*)0))
3887 return;
3888
3889 check_invariants (iter);
3890
3891 bytes_in_line = ctk_text_iter_get_bytes_in_line (iter);
3892
3893 g_return_if_fail (byte_on_line <= bytes_in_line)do { if ((byte_on_line <= bytes_in_line)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "byte_on_line <= bytes_in_line"
); return; } } while (0)
;
3894
3895 if (byte_on_line < bytes_in_line)
3896 iter_set_from_byte_offset (real, real->line, byte_on_line);
3897 else
3898 ctk_text_iter_forward_line (iter);
3899
3900 if (real->segment->type == &ctk_text_char_type &&
3901 (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
3902 g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
3903 "character; this will crash the text buffer. "
3904 "Byte indexes must refer to the start of a character.",
3905 G_STRLOC"ctktextiter.c" ":" "3905", byte_on_line);
3906
3907 check_invariants (iter);
3908}
3909
3910
3911/**
3912 * ctk_text_iter_set_visible_line_offset:
3913 * @iter: a #CtkTextIter
3914 * @char_on_line: a character offset
3915 *
3916 * Like ctk_text_iter_set_line_offset(), but the offset is in visible
3917 * characters, i.e. text with a tag making it invisible is not
3918 * counted in the offset.
3919 **/
3920void
3921ctk_text_iter_set_visible_line_offset (CtkTextIter *iter,
3922 gint char_on_line)
3923{
3924 gint chars_seen = 0;
3925 CtkTextIter pos;
3926
3927 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)
;
3928
3929 ctk_text_iter_set_line_offset (iter, 0);
3930
3931 pos = *iter;
3932
3933 /* For now we use a ludicrously slow implementation */
3934 while (chars_seen < char_on_line)
3935 {
3936 if (!_ctk_text_btree_char_is_invisible (&pos))
3937 ++chars_seen;
3938
3939 if (!ctk_text_iter_forward_char (&pos))
3940 break;
3941
3942 if (chars_seen == char_on_line)
3943 break;
3944 }
3945
3946 if (_ctk_text_iter_get_text_line (&pos) == _ctk_text_iter_get_text_line (iter))
3947 *iter = pos;
3948 else
3949 ctk_text_iter_forward_line (iter);
3950}
3951
3952/**
3953 * ctk_text_iter_set_visible_line_index:
3954 * @iter: a #CtkTextIter
3955 * @byte_on_line: a byte index
3956 *
3957 * Like ctk_text_iter_set_line_index(), but the index is in visible
3958 * bytes, i.e. text with a tag making it invisible is not counted
3959 * in the index.
3960 **/
3961void
3962ctk_text_iter_set_visible_line_index (CtkTextIter *iter,
3963 gint byte_on_line)
3964{
3965 CtkTextRealIter *real;
3966 gint offset = 0;
3967 CtkTextIter pos;
3968 CtkTextLineSegment *seg;
3969
3970 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)
;
3971
3972 ctk_text_iter_set_line_offset (iter, 0);
3973
3974 pos = *iter;
3975
3976 real = ctk_text_iter_make_real (&pos);
3977
3978 if (real == NULL((void*)0))
3979 return;
3980
3981 ensure_byte_offsets (real);
3982
3983 check_invariants (&pos);
3984
3985 seg = _ctk_text_iter_get_indexable_segment (&pos);
3986
3987 while (seg != NULL((void*)0) && byte_on_line > 0)
3988 {
3989 if (!_ctk_text_btree_char_is_invisible (&pos))
3990 {
3991 if (byte_on_line < seg->byte_count)
3992 {
3993 iter_set_from_byte_offset (real, real->line, offset + byte_on_line);
3994 byte_on_line = 0;
3995 break;
3996 }
3997 else
3998 byte_on_line -= seg->byte_count;
3999 }
4000
4001 offset += seg->byte_count;
4002 _ctk_text_iter_forward_indexable_segment (&pos);
4003 seg = _ctk_text_iter_get_indexable_segment (&pos);
4004 }
4005
4006 if (byte_on_line == 0)
4007 *iter = pos;
4008 else
4009 ctk_text_iter_forward_line (iter);
4010}
4011
4012/**
4013 * ctk_text_iter_set_line:
4014 * @iter: a #CtkTextIter
4015 * @line_number: line number (counted from 0)
4016 *
4017 * Moves iterator @iter to the start of the line @line_number. If
4018 * @line_number is negative or larger than the number of lines in the
4019 * buffer, moves @iter to the start of the last line in the buffer.
4020 *
4021 **/
4022void
4023ctk_text_iter_set_line (CtkTextIter *iter,
4024 gint line_number)
4025{
4026 CtkTextLine *line;
4027 gint real_line;
4028 CtkTextRealIter *real;
4029
4030 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)
;
4031
4032 real = ctk_text_iter_make_surreal (iter);
4033
4034 if (real == NULL((void*)0))
4035 return;
4036
4037 check_invariants (iter);
4038
4039 line = _ctk_text_btree_get_line_no_last (real->tree, line_number, &real_line);
4040
4041 iter_set_from_char_offset (real, line, 0);
4042
4043 /* We might as well cache this, since we know it. */
4044 real->cached_line_number = real_line;
4045
4046 check_invariants (iter);
4047}
4048
4049/**
4050 * ctk_text_iter_set_offset:
4051 * @iter: a #CtkTextIter
4052 * @char_offset: a character number
4053 *
4054 * Sets @iter to point to @char_offset. @char_offset counts from the start
4055 * of the entire text buffer, starting with 0.
4056 **/
4057void
4058ctk_text_iter_set_offset (CtkTextIter *iter,
4059 gint char_offset)
4060{
4061 CtkTextLine *line;
4062 CtkTextRealIter *real;
4063 gint line_start;
4064 gint real_char_index;
4065
4066 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)
;
4067
4068 real = ctk_text_iter_make_surreal (iter);
4069
4070 if (real == NULL((void*)0))
4071 return;
4072
4073 check_invariants (iter);
4074
4075 if (real->cached_char_index >= 0 &&
4076 real->cached_char_index == char_offset)
4077 return;
4078
4079 line = _ctk_text_btree_get_line_at_char (real->tree,
4080 char_offset,
4081 &line_start,
4082 &real_char_index);
4083
4084 iter_set_from_char_offset (real, line, real_char_index - line_start);
4085
4086 /* Go ahead and cache this since we have it. */
4087 real->cached_char_index = real_char_index;
4088
4089 check_invariants (iter);
4090}
4091
4092/**
4093 * ctk_text_iter_forward_to_end:
4094 * @iter: a #CtkTextIter
4095 *
4096 * Moves @iter forward to the “end iterator,” which points one past the last
4097 * valid character in the buffer. ctk_text_iter_get_char() called on the
4098 * end iterator returns 0, which is convenient for writing loops.
4099 **/
4100void
4101ctk_text_iter_forward_to_end (CtkTextIter *iter)
4102{
4103 CtkTextBuffer *buffer;
4104 CtkTextRealIter *real;
4105
4106 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)
;
4107
4108 real = ctk_text_iter_make_surreal (iter);
4109
4110 if (real == NULL((void*)0))
4111 return;
4112
4113 buffer = _ctk_text_btree_get_buffer (real->tree);
4114
4115 ctk_text_buffer_get_end_iter (buffer, iter);
4116}
4117
4118/* FIXME this and ctk_text_iter_forward_to_line_end() could be cleaned up
4119 * and made faster. Look at iter_ends_line() for inspiration, perhaps.
4120 * If all else fails we could cache the para delimiter pos in the iter.
4121 * I think forward_to_line_end() actually gets called fairly often.
4122 */
4123static int
4124find_paragraph_delimiter_for_line (CtkTextIter *iter)
4125{
4126 CtkTextIter end;
4127 end = *iter;
4128
4129 if (_ctk_text_line_contains_end_iter (_ctk_text_iter_get_text_line (&end),
4130 _ctk_text_iter_get_btree (&end)))
4131 {
4132 ctk_text_iter_forward_to_end (&end);
4133 }
4134 else
4135 {
4136 /* if we aren't on the last line, go forward to start of next line, then scan
4137 * back for the delimiters on the previous line
4138 */
4139 ctk_text_iter_forward_line (&end);
4140 ctk_text_iter_backward_char (&end);
4141 while (!ctk_text_iter_ends_line (&end))
4142 ctk_text_iter_backward_char (&end);
4143 }
4144
4145 return ctk_text_iter_get_line_offset (&end);
4146}
4147
4148/**
4149 * ctk_text_iter_forward_to_line_end:
4150 * @iter: a #CtkTextIter
4151 *
4152 * Moves the iterator to point to the paragraph delimiter characters,
4153 * which will be either a newline, a carriage return, a carriage
4154 * return/newline in sequence, or the Unicode paragraph separator
4155 * character. If the iterator is already at the paragraph delimiter
4156 * characters, moves to the paragraph delimiter characters for the
4157 * next line. If @iter is on the last line in the buffer, which does
4158 * not end in paragraph delimiters, moves to the end iterator (end of
4159 * the last line), and returns %FALSE.
4160 *
4161 * Returns: %TRUE if we moved and the new location is not the end iterator
4162 **/
4163gboolean
4164ctk_text_iter_forward_to_line_end (CtkTextIter *iter)
4165{
4166 gint current_offset;
4167 gint new_offset;
4168
4169
4170 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4171
4172 current_offset = ctk_text_iter_get_line_offset (iter);
4173 new_offset = find_paragraph_delimiter_for_line (iter);
4174
4175 if (current_offset < new_offset)
4176 {
4177 /* Move to end of this line. */
4178 ctk_text_iter_set_line_offset (iter, new_offset);
4179 return !ctk_text_iter_is_end (iter);
4180 }
4181 else
4182 {
4183 /* Move to end of next line. */
4184 if (ctk_text_iter_forward_line (iter))
4185 {
4186 /* We don't want to move past all
4187 * empty lines.
4188 */
4189 if (!ctk_text_iter_ends_line (iter))
4190 ctk_text_iter_forward_to_line_end (iter);
4191 return !ctk_text_iter_is_end (iter);
4192 }
4193 else
4194 return FALSE(0);
4195 }
4196}
4197
4198/**
4199 * ctk_text_iter_forward_to_tag_toggle:
4200 * @iter: a #CtkTextIter
4201 * @tag: (allow-none): a #CtkTextTag, or %NULL
4202 *
4203 * Moves forward to the next toggle (on or off) of the
4204 * #CtkTextTag @tag, or to the next toggle of any tag if
4205 * @tag is %NULL. If no matching tag toggles are found,
4206 * returns %FALSE, otherwise %TRUE. Does not return toggles
4207 * located at @iter, only toggles after @iter. Sets @iter to
4208 * the location of the toggle, or to the end of the buffer
4209 * if no toggle is found.
4210 *
4211 * Returns: whether we found a tag toggle after @iter
4212 **/
4213gboolean
4214ctk_text_iter_forward_to_tag_toggle (CtkTextIter *iter,
4215 CtkTextTag *tag)
4216{
4217 CtkTextLine *next_line;
4218 CtkTextLine *current_line;
4219 CtkTextRealIter *real;
4220
4221 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4222
4223 real = ctk_text_iter_make_real (iter);
4224
4225 if (real == NULL((void*)0))
4226 return FALSE(0);
4227
4228 check_invariants (iter);
4229
4230 if (ctk_text_iter_is_end (iter))
4231 return FALSE(0);
4232
4233 current_line = real->line;
4234 next_line = _ctk_text_line_next_could_contain_tag (current_line,
4235 real->tree, tag);
4236
4237 while (_ctk_text_iter_forward_indexable_segment (iter))
4238 {
4239 /* If we went forward to a line that couldn't contain a toggle
4240 for the tag, then skip forward to a line that could contain
4241 it. This potentially skips huge hunks of the tree, so we
4242 aren't a purely linear search. */
4243 if (real->line != current_line)
4244 {
4245 if (next_line == NULL((void*)0))
4246 {
4247 /* End of search. Set to end of buffer. */
4248 _ctk_text_btree_get_end_iter (real->tree, iter);
4249 return FALSE(0);
4250 }
4251
4252 if (real->line != next_line)
4253 iter_set_from_byte_offset (real, next_line, 0);
4254
4255 current_line = real->line;
4256 next_line = _ctk_text_line_next_could_contain_tag (current_line,
4257 real->tree,
4258 tag);
4259 }
4260
4261 if (ctk_text_iter_toggles_tag (iter, tag))
4262 {
4263 /* If there's a toggle here, it isn't indexable so
4264 any_segment can't be the indexable segment. */
4265 g_assert (real->any_segment != real->segment)do { if (real->any_segment != real->segment) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 4265, ((const char*) (__func__)), "real->any_segment != real->segment"
); } while (0)
;
4266 return TRUE(!(0));
4267 }
4268 }
4269
4270 /* Check end iterator for tags */
4271 if (ctk_text_iter_toggles_tag (iter, tag))
4272 {
4273 /* If there's a toggle here, it isn't indexable so
4274 any_segment can't be the indexable segment. */
4275 g_assert (real->any_segment != real->segment)do { if (real->any_segment != real->segment) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 4275, ((const char*) (__func__)), "real->any_segment != real->segment"
); } while (0)
;
4276 return TRUE(!(0));
4277 }
4278
4279 /* Reached end of buffer */
4280 return FALSE(0);
4281}
4282
4283/**
4284 * ctk_text_iter_backward_to_tag_toggle:
4285 * @iter: a #CtkTextIter
4286 * @tag: (allow-none): a #CtkTextTag, or %NULL
4287 *
4288 * Moves backward to the next toggle (on or off) of the
4289 * #CtkTextTag @tag, or to the next toggle of any tag if
4290 * @tag is %NULL. If no matching tag toggles are found,
4291 * returns %FALSE, otherwise %TRUE. Does not return toggles
4292 * located at @iter, only toggles before @iter. Sets @iter
4293 * to the location of the toggle, or the start of the buffer
4294 * if no toggle is found.
4295 *
4296 * Returns: whether we found a tag toggle before @iter
4297 **/
4298gboolean
4299ctk_text_iter_backward_to_tag_toggle (CtkTextIter *iter,
4300 CtkTextTag *tag)
4301{
4302 CtkTextLine *prev_line;
4303 CtkTextLine *current_line;
4304 CtkTextRealIter *real;
4305
4306 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4307
4308 real = ctk_text_iter_make_real (iter);
4309
4310 if (real == NULL((void*)0))
4311 return FALSE(0);
4312
4313 check_invariants (iter);
4314
4315 current_line = real->line;
4316 prev_line = _ctk_text_line_previous_could_contain_tag (current_line,
4317 real->tree, tag);
4318
4319
4320 /* If we're at segment start, go to the previous segment;
4321 * if mid-segment, snap to start of current segment.
4322 */
4323 if (is_segment_start (real))
4324 {
4325 if (!_ctk_text_iter_backward_indexable_segment (iter))
4326 return FALSE(0);
4327 }
4328 else
4329 {
4330 ensure_char_offsets (real);
4331
4332 if (!ctk_text_iter_backward_chars (iter, real->segment_char_offset))
4333 return FALSE(0);
4334 }
4335
4336 do
4337 {
4338 /* If we went backward to a line that couldn't contain a toggle
4339 * for the tag, then skip backward further to a line that
4340 * could contain it. This potentially skips huge hunks of the
4341 * tree, so we aren't a purely linear search.
4342 */
4343 if (real->line != current_line)
4344 {
4345 if (prev_line == NULL((void*)0))
4346 {
4347 /* End of search. Set to start of buffer. */
4348 _ctk_text_btree_get_iter_at_char (real->tree, iter, 0);
4349 return FALSE(0);
4350 }
4351
4352 if (real->line != prev_line)
4353 {
4354 /* Set to last segment in prev_line (could do this
4355 * more quickly)
4356 */
4357 iter_set_from_byte_offset (real, prev_line, 0);
4358
4359 while (!at_last_indexable_segment (real))
4360 _ctk_text_iter_forward_indexable_segment (iter);
4361 }
4362
4363 current_line = real->line;
4364 prev_line = _ctk_text_line_previous_could_contain_tag (current_line,
4365 real->tree,
4366 tag);
4367 }
4368
4369 if (ctk_text_iter_toggles_tag (iter, tag))
4370 {
4371 /* If there's a toggle here, it isn't indexable so
4372 * any_segment can't be the indexable segment.
4373 */
4374 g_assert (real->any_segment != real->segment)do { if (real->any_segment != real->segment) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 4374, ((const char*) (__func__)), "real->any_segment != real->segment"
); } while (0)
;
4375 return TRUE(!(0));
4376 }
4377 }
4378 while (_ctk_text_iter_backward_indexable_segment (iter));
4379
4380 /* Reached front of buffer */
4381 return FALSE(0);
4382}
4383
4384static gboolean
4385matches_pred (CtkTextIter *iter,
4386 CtkTextCharPredicate pred,
4387 gpointer user_data)
4388{
4389 gint ch;
4390
4391 ch = ctk_text_iter_get_char (iter);
4392
4393 return (*pred) (ch, user_data);
4394}
4395
4396/**
4397 * ctk_text_iter_forward_find_char:
4398 * @iter: a #CtkTextIter
4399 * @pred: (scope call): a function to be called on each character
4400 * @user_data: user data for @pred
4401 * @limit: (allow-none): search limit, or %NULL for none
4402 *
4403 * Advances @iter, calling @pred on each character. If
4404 * @pred returns %TRUE, returns %TRUE and stops scanning.
4405 * If @pred never returns %TRUE, @iter is set to @limit if
4406 * @limit is non-%NULL, otherwise to the end iterator.
4407 *
4408 * Returns: whether a match was found
4409 **/
4410gboolean
4411ctk_text_iter_forward_find_char (CtkTextIter *iter,
4412 CtkTextCharPredicate pred,
4413 gpointer user_data,
4414 const CtkTextIter *limit)
4415{
4416 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4417 g_return_val_if_fail (pred != NULL, FALSE)do { if ((pred != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "pred != NULL"); return (
(0)); } } while (0)
;
4418
4419 if (limit &&
4420 ctk_text_iter_compare (iter, limit) >= 0)
4421 return FALSE(0);
4422
4423 while ((limit == NULL((void*)0) ||
4424 !ctk_text_iter_equal (limit, iter)) &&
4425 ctk_text_iter_forward_char (iter))
4426 {
4427 if (matches_pred (iter, pred, user_data))
4428 return TRUE(!(0));
4429 }
4430
4431 return FALSE(0);
4432}
4433
4434/**
4435 * ctk_text_iter_backward_find_char:
4436 * @iter: a #CtkTextIter
4437 * @pred: (scope call): function to be called on each character
4438 * @user_data: user data for @pred
4439 * @limit: (allow-none): search limit, or %NULL for none
4440 *
4441 * Same as ctk_text_iter_forward_find_char(), but goes backward from @iter.
4442 *
4443 * Returns: whether a match was found
4444 **/
4445gboolean
4446ctk_text_iter_backward_find_char (CtkTextIter *iter,
4447 CtkTextCharPredicate pred,
4448 gpointer user_data,
4449 const CtkTextIter *limit)
4450{
4451 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4452 g_return_val_if_fail (pred != NULL, FALSE)do { if ((pred != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "pred != NULL"); return (
(0)); } } while (0)
;
4453
4454 if (limit &&
4455 ctk_text_iter_compare (iter, limit) <= 0)
4456 return FALSE(0);
4457
4458 while ((limit == NULL((void*)0) ||
4459 !ctk_text_iter_equal (limit, iter)) &&
4460 ctk_text_iter_backward_char (iter))
4461 {
4462 if (matches_pred (iter, pred, user_data))
4463 return TRUE(!(0));
4464 }
4465
4466 return FALSE(0);
4467}
4468
4469static void
4470forward_chars_with_skipping (CtkTextIter *iter,
4471 gint count,
4472 gboolean skip_invisible,
4473 gboolean skip_nontext,
4474 gboolean skip_decomp)
4475{
4476 gint i;
4477
4478 g_return_if_fail (count >= 0)do { if ((count >= 0)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "count >= 0"); return
; } } while (0)
;
4479
4480 i = count;
4481
4482 while (i > 0)
4483 {
4484 gboolean ignored = FALSE(0);
4485
4486 /* minimal workaround to avoid the infinite loop of bug #168247. */
4487 if (ctk_text_iter_is_end (iter))
4488 return;
4489
4490 if (skip_nontext &&
4491 ctk_text_iter_get_char (iter) == CTK_TEXT_UNKNOWN_CHAR0xFFFC)
4492 ignored = TRUE(!(0));
4493
4494 if (!ignored &&
4495 skip_invisible &&
4496 _ctk_text_btree_char_is_invisible (iter))
4497 ignored = TRUE(!(0));
4498
4499 if (!ignored && skip_decomp)
4500 {
4501 /* being UTF8 correct sucks: this accounts for extra
4502 offsets coming from canonical decompositions of
4503 UTF8 characters (e.g. accented characters) which
4504 g_utf8_normalize() performs */
4505 gchar *normal;
4506 gchar *casefold;
4507 gchar buffer[6];
4508 gint buffer_len;
4509
4510 buffer_len = g_unichar_to_utf8 (ctk_text_iter_get_char (iter), buffer);
4511 casefold = g_utf8_casefold (buffer, buffer_len);
4512 normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4513 i -= (g_utf8_strlen (normal, -1) - 1);
4514 g_free (normal);
4515 g_free (casefold);
4516 }
4517
4518 ctk_text_iter_forward_char (iter);
4519
4520 if (!ignored)
4521 --i;
4522 }
4523}
4524
4525static const gchar *
4526pointer_from_offset_skipping_decomp (const gchar *str,
4527 gint offset)
4528{
4529 gchar *casefold, *normal;
4530 const gchar *p, *q;
4531
4532 p = str;
4533
4534 while (offset > 0)
4535 {
4536 q = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]);
4537 casefold = g_utf8_casefold (p, q - p);
4538 normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4539 offset -= g_utf8_strlen (normal, -1);
4540 g_free (casefold);
4541 g_free (normal);
4542 p = q;
4543 }
4544
4545 return p;
4546}
4547
4548static gboolean
4549exact_prefix_cmp (const gchar *string,
4550 const gchar *prefix,
4551 guint prefix_len)
4552{
4553 GUnicodeType type;
4554
4555 if (strncmp (string, prefix, prefix_len) != 0)
4556 return FALSE(0);
4557 if (string[prefix_len] == '\0')
4558 return TRUE(!(0));
4559
4560 type = g_unichar_type (g_utf8_get_char (string + prefix_len));
4561
4562 /* If string contains prefix, check that prefix is not followed
4563 * by a unicode mark symbol, e.g. that trailing 'a' in prefix
4564 * is not part of two-char a-with-hat symbol in string. */
4565 return type != G_UNICODE_SPACING_MARK &&
4566 type != G_UNICODE_ENCLOSING_MARK &&
4567 type != G_UNICODE_NON_SPACING_MARK;
4568}
4569
4570static const gchar *
4571utf8_strcasestr (const gchar *haystack,
4572 const gchar *needle)
4573{
4574 gsize needle_len;
4575 gsize haystack_len;
4576 const gchar *ret = NULL((void*)0);
4577 gchar *p;
4578 gchar *casefold;
4579 gchar *caseless_haystack;
4580 gint i;
4581
4582 g_return_val_if_fail (haystack != NULL, NULL)do { if ((haystack != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "haystack != NULL"); return
(((void*)0)); } } while (0)
;
4583 g_return_val_if_fail (needle != NULL, NULL)do { if ((needle != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "needle != NULL"); return
(((void*)0)); } } while (0)
;
4584
4585 casefold = g_utf8_casefold (haystack, -1);
4586 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4587 g_free (casefold);
4588
4589 needle_len = g_utf8_strlen (needle, -1);
4590 haystack_len = g_utf8_strlen (caseless_haystack, -1);
4591
4592 if (needle_len == 0)
4593 {
4594 ret = (gchar *)haystack;
4595 goto finally;
4596 }
4597
4598 if (haystack_len < needle_len)
4599 {
4600 ret = NULL((void*)0);
4601 goto finally;
4602 }
4603
4604 p = (gchar *)caseless_haystack;
4605 needle_len = strlen (needle);
4606 i = 0;
4607
4608 while (*p)
4609 {
4610 if (exact_prefix_cmp (p, needle, needle_len))
4611 {
4612 ret = pointer_from_offset_skipping_decomp (haystack, i);
4613 goto finally;
4614 }
4615
4616 p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]);
4617 i++;
4618 }
4619
4620finally:
4621 g_free (caseless_haystack);
4622
4623 return ret;
4624}
4625
4626static const gchar *
4627utf8_strrcasestr (const gchar *haystack,
4628 const gchar *needle)
4629{
4630 gsize needle_len;
4631 gsize haystack_len;
4632 const gchar *ret = NULL((void*)0);
4633 gchar *p;
4634 gchar *casefold;
4635 gchar *caseless_haystack;
4636 gint i;
4637
4638 g_return_val_if_fail (haystack != NULL, NULL)do { if ((haystack != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "haystack != NULL"); return
(((void*)0)); } } while (0)
;
4639 g_return_val_if_fail (needle != NULL, NULL)do { if ((needle != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "needle != NULL"); return
(((void*)0)); } } while (0)
;
4640
4641 casefold = g_utf8_casefold (haystack, -1);
4642 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4643 g_free (casefold);
4644
4645 needle_len = g_utf8_strlen (needle, -1);
4646 haystack_len = g_utf8_strlen (caseless_haystack, -1);
4647
4648 if (needle_len == 0)
4649 {
4650 ret = (gchar *)haystack;
4651 goto finally;
4652 }
4653
4654 if (haystack_len < needle_len)
4655 {
4656 ret = NULL((void*)0);
4657 goto finally;
4658 }
4659
4660 i = haystack_len - needle_len;
4661 p = g_utf8_offset_to_pointer (caseless_haystack, i);
4662 needle_len = strlen (needle);
4663
4664 while (TRUE(!(0)))
4665 {
4666 if (exact_prefix_cmp (p, needle, needle_len))
4667 {
4668 ret = pointer_from_offset_skipping_decomp (haystack, i);
4669 break;
4670 }
4671
4672 if (p == caseless_haystack)
4673 break;
4674
4675 p = g_utf8_prev_char (p);
4676 i--;
4677 }
4678
4679finally:
4680 g_free (caseless_haystack);
4681
4682 return ret;
4683}
4684
4685/* normalizes caseless strings and returns true if @s2 matches
4686 the start of @s1 */
4687static gboolean
4688utf8_caselessnmatch (const gchar *s1,
4689 const gchar *s2,
4690 gssize n1,
4691 gssize n2)
4692{
4693 gchar *casefold;
4694 gchar *normalized_s1;
4695 gchar *normalized_s2;
4696 gint len_s1;
4697 gint len_s2;
4698 gboolean ret = FALSE(0);
4699
4700 g_return_val_if_fail (s1 != NULL, FALSE)do { if ((s1 != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "s1 != NULL"); return ((
0)); } } while (0)
;
4701 g_return_val_if_fail (s2 != NULL, FALSE)do { if ((s2 != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "s2 != NULL"); return ((
0)); } } while (0)
;
4702 g_return_val_if_fail (n1 > 0, FALSE)do { if ((n1 > 0)) { } else { g_return_if_fail_warning ("Ctk"
, ((const char*) (__func__)), "n1 > 0"); return ((0)); } }
while (0)
;
4703 g_return_val_if_fail (n2 > 0, FALSE)do { if ((n2 > 0)) { } else { g_return_if_fail_warning ("Ctk"
, ((const char*) (__func__)), "n2 > 0"); return ((0)); } }
while (0)
;
4704
4705 casefold = g_utf8_casefold (s1, n1);
4706 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4707 g_free (casefold);
4708
4709 casefold = g_utf8_casefold (s2, n2);
4710 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4711 g_free (casefold);
4712
4713 len_s1 = strlen (normalized_s1);
4714 len_s2 = strlen (normalized_s2);
4715
4716 if (len_s1 >= len_s2)
4717 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
4718
4719 g_free (normalized_s1);
4720 g_free (normalized_s2);
4721
4722 return ret;
4723}
4724
4725static gboolean
4726lines_match (const CtkTextIter *start,
4727 const gchar **lines,
4728 gboolean visible_only,
4729 gboolean slice,
4730 gboolean case_insensitive,
4731 CtkTextIter *match_start,
4732 CtkTextIter *match_end)
4733{
4734 CtkTextIter next;
4735 gchar *line_text;
4736 const gchar *found;
4737 gint offset;
4738
4739 if (*lines == NULL((void*)0) || **lines == '\0')
4740 {
4741 if (match_start)
4742 *match_start = *start;
4743
4744 if (match_end)
4745 *match_end = *start;
4746 return TRUE(!(0));
4747 }
4748
4749 next = *start;
4750 ctk_text_iter_forward_line (&next);
4751
4752 /* No more text in buffer, but *lines is nonempty */
4753 if (ctk_text_iter_equal (start, &next))
4754 {
4755 return FALSE(0);
4756 }
4757
4758 if (slice)
4759 {
4760 if (visible_only)
4761 line_text = ctk_text_iter_get_visible_slice (start, &next);
4762 else
4763 line_text = ctk_text_iter_get_slice (start, &next);
4764 }
4765 else
4766 {
4767 if (visible_only)
4768 line_text = ctk_text_iter_get_visible_text (start, &next);
4769 else
4770 line_text = ctk_text_iter_get_text (start, &next);
4771 }
4772
4773 if (match_start) /* if this is the first line we're matching */
4774 {
4775 if (!case_insensitive)
4776 found = strstr (line_text, *lines);
4777 else
4778 found = utf8_strcasestr (line_text, *lines);
4779 }
4780 else
4781 {
4782 /* If it's not the first line, we have to match from the
4783 * start of the line.
4784 */
4785 if ((!case_insensitive &&
4786 (strncmp (line_text, *lines, strlen (*lines)) == 0)) ||
4787 (case_insensitive &&
4788 utf8_caselessnmatch (line_text, *lines, strlen (line_text),
4789 strlen (*lines))))
4790 {
4791 found = line_text;
4792 }
4793 else
4794 found = NULL((void*)0);
4795 }
4796
4797 if (found == NULL((void*)0))
4798 {
4799 g_free (line_text);
4800 return FALSE(0);
4801 }
4802
4803 /* Get offset to start of search string */
4804 offset = g_utf8_strlen (line_text, found - line_text);
4805
4806 next = *start;
4807
4808 /* If match start needs to be returned, set it to the
4809 * start of the search string.
4810 */
4811 forward_chars_with_skipping (&next, offset,
4812 visible_only, !slice, FALSE(0));
4813 if (match_start)
4814 *match_start = next;
4815
4816 /* Go to end of search string */
4817 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1),
4818 visible_only, !slice, case_insensitive);
4819
4820 g_free (line_text);
4821
4822 ++lines;
4823
4824 if (match_end)
4825 *match_end = next;
4826
4827 /* pass NULL for match_start, since we don't need to find the
4828 * start again.
4829 */
4830 return lines_match (&next, lines, visible_only, slice, case_insensitive, NULL((void*)0), match_end);
4831}
4832
4833/* strsplit() that retains the delimiter as part of the string. */
4834static gchar **
4835strbreakup (const char *string,
4836 const char *delimiter,
4837 gint max_tokens,
4838 gint *num_strings,
4839 gboolean case_insensitive)
4840{
4841 GSList *string_list = NULL((void*)0), *slist;
4842 gchar **str_array, *s;
4843 gchar *casefold, *new_string;
4844 guint i, n = 1;
4845
4846 g_return_val_if_fail (string != NULL, NULL)do { if ((string != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "string != NULL"); return
(((void*)0)); } } while (0)
;
14
Taking true branch
15
Loop condition is false. Exiting loop
4847 g_return_val_if_fail (delimiter != NULL, NULL)do { if ((delimiter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "delimiter != NULL"); return
(((void*)0)); } } while (0)
;
16
Taking true branch
17
Loop condition is false. Exiting loop
4848
4849 if (max_tokens
17.1
'max_tokens' is < 1
< 1)
18
Taking true branch
4850 max_tokens = G_MAXINT2147483647;
4851
4852 s = strstr (string, delimiter);
4853 if (s)
19
Assuming 's' is null
20
Taking false branch
4854 {
4855 guint delimiter_len = strlen (delimiter);
4856
4857 do
4858 {
4859 guint len;
4860
4861 len = s - string + delimiter_len;
4862 new_string = g_new (gchar, len + 1)((gchar *) g_malloc_n ((len + 1), sizeof (gchar)));
4863 strncpy (new_string, string, len);
4864 new_string[len] = 0;
4865
4866 if (case_insensitive)
4867 {
4868 casefold = g_utf8_casefold (new_string, -1);
4869 g_free (new_string);
4870 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4871 g_free (casefold);
4872 }
4873
4874 string_list = g_slist_prepend (string_list, new_string);
4875 n++;
4876 string = s + delimiter_len;
4877 s = strstr (string, delimiter);
4878 }
4879 while (--max_tokens && s);
4880 }
4881 if (*string)
21
Taking true branch
4882 {
4883 n++;
4884
4885 if (case_insensitive
21.1
'case_insensitive' is 1
)
22
Taking true branch
4886 {
4887 casefold = g_utf8_casefold (string, -1);
4888 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
4889 g_free (casefold);
4890 }
4891 else
4892 new_string = g_strdup (string)g_strdup_inline (string);
4893
4894 string_list = g_slist_prepend (string_list, new_string);
4895 }
4896
4897 str_array = g_new (gchar*, n)((gchar* *) g_malloc_n ((n), sizeof (gchar*)));
4898
4899 i = n - 1;
4900
4901 str_array[i--] = NULL((void*)0);
4902 for (slist = string_list; slist; slist = slist->next)
23
Loop condition is true. Entering loop body
24
Loop condition is true. Entering loop body
4903 str_array[i--] = slist->data;
25
Out of bound memory access (access exceeds upper limit of memory block)
4904
4905 g_slist_free (string_list);
4906
4907 if (num_strings != NULL((void*)0))
4908 *num_strings = n - 1;
4909
4910 return str_array;
4911}
4912
4913/**
4914 * ctk_text_iter_forward_search:
4915 * @iter: start of search
4916 * @str: a search string
4917 * @flags: flags affecting how the search is done
4918 * @match_start: (out caller-allocates) (allow-none): return location for start of match, or %NULL
4919 * @match_end: (out caller-allocates) (allow-none): return location for end of match, or %NULL
4920 * @limit: (allow-none): location of last possible @match_end, or %NULL for the end of the buffer
4921 *
4922 * Searches forward for @str. Any match is returned by setting
4923 * @match_start to the first character of the match and @match_end to the
4924 * first character after the match. The search will not continue past
4925 * @limit. Note that a search is a linear or O(n) operation, so you
4926 * may wish to use @limit to avoid locking up your UI on large
4927 * buffers.
4928 *
4929 * @match_start will never be set to a #CtkTextIter located before @iter, even if
4930 * there is a possible @match_end after or at @iter.
4931 *
4932 * Returns: whether a match was found
4933 **/
4934gboolean
4935ctk_text_iter_forward_search (const CtkTextIter *iter,
4936 const gchar *str,
4937 CtkTextSearchFlags flags,
4938 CtkTextIter *match_start,
4939 CtkTextIter *match_end,
4940 const CtkTextIter *limit)
4941{
4942 gchar **lines = NULL((void*)0);
4943 CtkTextIter match;
4944 gboolean retval = FALSE(0);
4945 CtkTextIter search;
4946 gboolean visible_only;
4947 gboolean slice;
4948 gboolean case_insensitive;
4949
4950 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
4951 g_return_val_if_fail (str != NULL, FALSE)do { if ((str != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "str != NULL"); return (
(0)); } } while (0)
;
4952
4953 if (limit &&
4954 ctk_text_iter_compare (iter, limit) >= 0)
4955 return FALSE(0);
4956
4957 if (*str == '\0')
4958 {
4959 /* If we can move one char, return the empty string there */
4960 match = *iter;
4961
4962 if (ctk_text_iter_forward_char (&match))
4963 {
4964 if (limit &&
4965 ctk_text_iter_equal (&match, limit))
4966 return FALSE(0);
4967
4968 if (match_start)
4969 *match_start = match;
4970 if (match_end)
4971 *match_end = match;
4972 return TRUE(!(0));
4973 }
4974 else
4975 return FALSE(0);
4976 }
4977
4978 visible_only = (flags & CTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
4979 slice = (flags & CTK_TEXT_SEARCH_TEXT_ONLY) == 0;
4980 case_insensitive = (flags & CTK_TEXT_SEARCH_CASE_INSENSITIVE) != 0;
4981
4982 /* locate all lines */
4983
4984 lines = strbreakup (str, "\n", -1, NULL((void*)0), case_insensitive);
4985
4986 search = *iter;
4987
4988 do
4989 {
4990 /* This loop has an inefficient worst-case, where
4991 * ctk_text_iter_get_text() is called repeatedly on
4992 * a single line.
4993 */
4994 CtkTextIter end;
4995
4996 if (limit &&
4997 ctk_text_iter_compare (&search, limit) >= 0)
4998 break;
4999
5000 if (lines_match (&search, (const gchar**)lines,
5001 visible_only, slice, case_insensitive, &match, &end))
5002 {
5003 if (limit == NULL((void*)0) ||
5004 (limit &&
5005 ctk_text_iter_compare (&end, limit) <= 0))
5006 {
5007 retval = TRUE(!(0));
5008
5009 if (match_start)
5010 *match_start = match;
5011
5012 if (match_end)
5013 *match_end = end;
5014 }
5015
5016 break;
5017 }
5018 }
5019 while (ctk_text_iter_forward_line (&search));
5020
5021 g_strfreev ((gchar**)lines);
5022
5023 return retval;
5024}
5025
5026static gboolean
5027vectors_equal_ignoring_trailing (gchar **vec1,
5028 gchar **vec2,
5029 gboolean case_insensitive)
5030{
5031 /* Ignores trailing chars in vec2's last line */
5032
5033 gchar **i1, **i2;
5034
5035 i1 = vec1;
5036 i2 = vec2;
5037
5038 while (*i1 && *i2)
5039 {
5040 gint len1;
5041 gint len2;
5042
5043 if (!case_insensitive)
5044 {
5045 if (strcmp (*i1, *i2) != 0)
5046 {
5047 if (*(i2 + 1) == NULL((void*)0)) /* if this is the last line */
5048 {
5049 len1 = strlen (*i1);
5050 len2 = strlen (*i2);
5051
5052 if (len2 >= len1 &&
5053 strncmp (*i1, *i2, len1) == 0)
5054 {
5055 /* We matched ignoring the trailing stuff in vec2 */
5056 return TRUE(!(0));
5057 }
5058 else
5059 {
5060 return FALSE(0);
5061 }
5062 }
5063 else
5064 {
5065 return FALSE(0);
5066 }
5067 }
5068 }
5069 else
5070 {
5071 len1 = strlen (*i1);
5072 len2 = strlen (*i2);
5073
5074 if (!utf8_caselessnmatch (*i1, *i2, len1, len2))
5075 {
5076 if (*(i2 + 1) == NULL((void*)0)) /* if this is the last line */
5077 {
5078 if (utf8_caselessnmatch (*i2, *i1, len2, len1))
5079 {
5080 /* We matched ignoring the trailing stuff in vec2 */
5081 return TRUE(!(0));
5082 }
5083 else
5084 {
5085 return FALSE(0);
5086 }
5087 }
5088 else
5089 {
5090 return FALSE(0);
5091 }
5092 }
5093 }
5094
5095 ++i1;
5096 ++i2;
5097 }
5098
5099 if (*i1 || *i2)
5100 return FALSE(0);
5101 else
5102 return TRUE(!(0));
5103}
5104
5105typedef struct _LinesWindow LinesWindow;
5106
5107struct _LinesWindow
5108{
5109 gint n_lines;
5110 gchar **lines;
5111
5112 CtkTextIter first_line_start;
5113 CtkTextIter first_line_end;
5114
5115 guint slice : 1;
5116 guint visible_only : 1;
5117};
5118
5119static void
5120lines_window_init (LinesWindow *win,
5121 const CtkTextIter *start)
5122{
5123 gint i;
5124 CtkTextIter line_start;
5125 CtkTextIter line_end;
5126
5127 /* If we start on line 1, there are 2 lines to search (0 and 1), so
5128 * n_lines can be 2.
5129 */
5130 if (ctk_text_iter_is_start (start) ||
5131 ctk_text_iter_get_line (start) + 1 < win->n_lines)
5132 {
5133 /* Already at the end, or not enough lines to match */
5134 win->lines = g_new0 (gchar*, 1)((gchar* *) g_malloc0_n ((1), sizeof (gchar*)));
5135 *win->lines = NULL((void*)0);
5136 return;
5137 }
5138
5139 line_start = *start;
5140 line_end = *start;
5141
5142 /* Move to start iter to start of line */
5143 ctk_text_iter_set_line_offset (&line_start, 0);
5144
5145 if (ctk_text_iter_equal (&line_start, &line_end))
5146 {
5147 /* we were already at the start; so go back one line */
5148 ctk_text_iter_backward_line (&line_start);
5149 }
5150
5151 win->first_line_start = line_start;
5152 win->first_line_end = line_end;
5153
5154 win->lines = g_new0 (gchar*, win->n_lines + 1)((gchar* *) g_malloc0_n ((win->n_lines + 1), sizeof (gchar
*)))
;
5155
5156 i = win->n_lines - 1;
5157 while (i >= 0)
5158 {
5159 gchar *line_text;
5160
5161 if (win->slice)
5162 {
5163 if (win->visible_only)
5164 line_text = ctk_text_iter_get_visible_slice (&line_start, &line_end);
5165 else
5166 line_text = ctk_text_iter_get_slice (&line_start, &line_end);
5167 }
5168 else
5169 {
5170 if (win->visible_only)
5171 line_text = ctk_text_iter_get_visible_text (&line_start, &line_end);
5172 else
5173 line_text = ctk_text_iter_get_text (&line_start, &line_end);
5174 }
5175
5176 win->lines[i] = line_text;
5177 win->first_line_start = line_start;
5178 win->first_line_end = line_end;
5179
5180 line_end = line_start;
5181 ctk_text_iter_backward_line (&line_start);
5182
5183 --i;
5184 }
5185}
5186
5187static gboolean
5188lines_window_back (LinesWindow *win)
5189{
5190 CtkTextIter new_start;
5191 gchar *line_text;
5192
5193 new_start = win->first_line_start;
5194
5195 if (!ctk_text_iter_backward_line (&new_start))
5196 return FALSE(0);
5197 else
5198 {
5199 win->first_line_start = new_start;
5200 win->first_line_end = new_start;
5201
5202 ctk_text_iter_forward_line (&win->first_line_end);
5203 }
5204
5205 if (win->slice)
5206 {
5207 if (win->visible_only)
5208 line_text = ctk_text_iter_get_visible_slice (&win->first_line_start,
5209 &win->first_line_end);
5210 else
5211 line_text = ctk_text_iter_get_slice (&win->first_line_start,
5212 &win->first_line_end);
5213 }
5214 else
5215 {
5216 if (win->visible_only)
5217 line_text = ctk_text_iter_get_visible_text (&win->first_line_start,
5218 &win->first_line_end);
5219 else
5220 line_text = ctk_text_iter_get_text (&win->first_line_start,
5221 &win->first_line_end);
5222 }
5223
5224 /* Move lines to make room for first line. */
5225 memmove (win->lines + 1, win->lines, win->n_lines * sizeof (gchar*));
5226
5227 *win->lines = line_text;
5228
5229 /* Free old last line and NULL-terminate */
5230 g_free (win->lines[win->n_lines]);
5231 win->lines[win->n_lines] = NULL((void*)0);
5232
5233 return TRUE(!(0));
5234}
5235
5236static void
5237lines_window_free (LinesWindow *win)
5238{
5239 g_strfreev (win->lines);
5240}
5241
5242/**
5243 * ctk_text_iter_backward_search:
5244 * @iter: a #CtkTextIter where the search begins
5245 * @str: search string
5246 * @flags: bitmask of flags affecting the search
5247 * @match_start: (out caller-allocates) (allow-none): return location for start of match, or %NULL
5248 * @match_end: (out caller-allocates) (allow-none): return location for end of match, or %NULL
5249 * @limit: (allow-none): location of last possible @match_start, or %NULL for start of buffer
5250 *
5251 * Same as ctk_text_iter_forward_search(), but moves backward.
5252 *
5253 * @match_end will never be set to a #CtkTextIter located after @iter, even if
5254 * there is a possible @match_start before or at @iter.
5255 *
5256 * Returns: whether a match was found
5257 **/
5258gboolean
5259ctk_text_iter_backward_search (const CtkTextIter *iter,
5260 const gchar *str,
5261 CtkTextSearchFlags flags,
5262 CtkTextIter *match_start,
5263 CtkTextIter *match_end,
5264 const CtkTextIter *limit)
5265{
5266 gchar **lines = NULL((void*)0);
5267 gchar **l;
5268 gint n_lines;
5269 LinesWindow win;
5270 gboolean retval = FALSE(0);
5271 gboolean visible_only;
5272 gboolean slice;
5273 gboolean case_insensitive;
5274
5275 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
1
Assuming 'iter' is not equal to null
2
Taking true branch
3
Loop condition is false. Exiting loop
5276 g_return_val_if_fail (str != NULL, FALSE)do { if ((str != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "str != NULL"); return (
(0)); } } while (0)
;
4
Assuming 'str' is not equal to null
5
Taking true branch
5277
5278 if (limit &&
6
Loop condition is false. Exiting loop
7
Assuming 'limit' is null
5279 ctk_text_iter_compare (limit, iter) > 0)
5280 return FALSE(0);
5281
5282 if (*str == '\0')
8
Assuming the condition is false
9
Taking false branch
5283 {
5284 /* If we can move one char, return the empty string there */
5285 CtkTextIter match = *iter;
5286
5287 if (limit && ctk_text_iter_equal (limit, &match))
5288 return FALSE(0);
5289
5290 if (ctk_text_iter_backward_char (&match))
5291 {
5292 if (match_start)
5293 *match_start = match;
5294 if (match_end)
5295 *match_end = match;
5296 return TRUE(!(0));
5297 }
5298 else
5299 return FALSE(0);
5300 }
5301
5302 visible_only = (flags & CTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
10
Assuming the condition is false
5303 slice = (flags & CTK_TEXT_SEARCH_TEXT_ONLY) == 0;
11
Assuming the condition is false
5304 case_insensitive = (flags & CTK_TEXT_SEARCH_CASE_INSENSITIVE) != 0;
12
Assuming the condition is true
5305
5306 /* locate all lines */
5307
5308 lines = strbreakup (str, "\n", -1, &n_lines, case_insensitive);
13
Calling 'strbreakup'
5309
5310 win.n_lines = n_lines;
5311 win.slice = slice;
5312 win.visible_only = visible_only;
5313
5314 lines_window_init (&win, iter);
5315
5316 if (*win.lines == NULL((void*)0))
5317 goto out;
5318
5319 do
5320 {
5321 const gchar *first_line_match;
5322
5323 if (limit &&
5324 ctk_text_iter_compare (limit, &win.first_line_end) > 0)
5325 {
5326 /* We're now before the search limit, abort. */
5327 goto out;
5328 }
5329
5330 /* If there are multiple lines, the first line will
5331 * end in '\n', so this will only match at the
5332 * end of the first line, which is correct.
5333 */
5334 if (!case_insensitive)
5335 first_line_match = g_strrstr (*win.lines, *lines);
5336 else
5337 first_line_match = utf8_strrcasestr (*win.lines, *lines);
5338
5339 if (first_line_match &&
5340 vectors_equal_ignoring_trailing (lines + 1, win.lines + 1,
5341 case_insensitive))
5342 {
5343 /* Match! */
5344 gint offset;
5345 CtkTextIter start_tmp;
5346 CtkTextIter end_tmp;
5347
5348 /* Offset to start of search string */
5349 offset = g_utf8_strlen (*win.lines, first_line_match - *win.lines);
5350
5351 start_tmp = win.first_line_start;
5352 forward_chars_with_skipping (&start_tmp, offset,
5353 visible_only, !slice, FALSE(0));
5354
5355 if (limit &&
5356 ctk_text_iter_compare (limit, &start_tmp) > 0)
5357 goto out; /* match was bogus */
5358
5359 if (match_start)
5360 *match_start = start_tmp;
5361
5362 /* Go to end of search string */
5363 offset = 0;
5364 for (l = lines; *l != NULL((void*)0); l++)
5365 offset += g_utf8_strlen (*l, -1);
5366
5367 end_tmp = start_tmp;
5368 forward_chars_with_skipping (&end_tmp, offset,
5369 visible_only, !slice, case_insensitive);
5370
5371 if (match_end)
5372 *match_end = end_tmp;
5373
5374 retval = TRUE(!(0));
5375 goto out;
5376 }
5377 }
5378 while (lines_window_back (&win));
5379
5380 out:
5381 lines_window_free (&win);
5382 g_strfreev (lines);
5383
5384 return retval;
5385}
5386
5387/*
5388 * Comparisons
5389 */
5390
5391/**
5392 * ctk_text_iter_equal:
5393 * @lhs: a #CtkTextIter
5394 * @rhs: another #CtkTextIter
5395 *
5396 * Tests whether two iterators are equal, using the fastest possible
5397 * mechanism. This function is very fast; you can expect it to perform
5398 * better than e.g. getting the character offset for each iterator and
5399 * comparing the offsets yourself. Also, it’s a bit faster than
5400 * ctk_text_iter_compare().
5401 *
5402 * Returns: %TRUE if the iterators point to the same place in the buffer
5403 **/
5404gboolean
5405ctk_text_iter_equal (const CtkTextIter *lhs,
5406 const CtkTextIter *rhs)
5407{
5408 CtkTextRealIter *real_lhs;
5409 CtkTextRealIter *real_rhs;
5410
5411 real_lhs = (CtkTextRealIter*)lhs;
5412 real_rhs = (CtkTextRealIter*)rhs;
5413
5414 check_invariants (lhs);
5415 check_invariants (rhs);
5416
5417 if (real_lhs->line != real_rhs->line)
5418 return FALSE(0);
5419 else if (real_lhs->line_byte_offset >= 0 &&
5420 real_rhs->line_byte_offset >= 0)
5421 return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
5422 else
5423 {
5424 /* the ensure_char_offsets() calls do nothing if the char offsets
5425 are already up-to-date. */
5426 ensure_char_offsets (real_lhs);
5427 ensure_char_offsets (real_rhs);
5428 return real_lhs->line_char_offset == real_rhs->line_char_offset;
5429 }
5430}
5431
5432/**
5433 * ctk_text_iter_compare:
5434 * @lhs: a #CtkTextIter
5435 * @rhs: another #CtkTextIter
5436 *
5437 * A qsort()-style function that returns negative if @lhs is less than
5438 * @rhs, positive if @lhs is greater than @rhs, and 0 if they’re equal.
5439 * Ordering is in character offset order, i.e. the first character in the buffer
5440 * is less than the second character in the buffer.
5441 *
5442 * Returns: -1 if @lhs is less than @rhs, 1 if @lhs is greater, 0 if they are equal
5443 **/
5444gint
5445ctk_text_iter_compare (const CtkTextIter *lhs,
5446 const CtkTextIter *rhs)
5447{
5448 CtkTextRealIter *real_lhs;
5449 CtkTextRealIter *real_rhs;
5450
5451 real_lhs = ctk_text_iter_make_surreal (lhs);
5452 real_rhs = ctk_text_iter_make_surreal (rhs);
5453
5454 if (real_lhs == NULL((void*)0) ||
5455 real_rhs == NULL((void*)0))
5456 return -1; /* why not */
5457
5458 check_invariants (lhs);
5459 check_invariants (rhs);
5460
5461 if (real_lhs->line == real_rhs->line)
5462 {
5463 gint left_index, right_index;
5464
5465 if (real_lhs->line_byte_offset >= 0 &&
5466 real_rhs->line_byte_offset >= 0)
5467 {
5468 left_index = real_lhs->line_byte_offset;
5469 right_index = real_rhs->line_byte_offset;
5470 }
5471 else
5472 {
5473 /* the ensure_char_offsets() calls do nothing if
5474 the offsets are already up-to-date. */
5475 ensure_char_offsets (real_lhs);
5476 ensure_char_offsets (real_rhs);
5477 left_index = real_lhs->line_char_offset;
5478 right_index = real_rhs->line_char_offset;
5479 }
5480
5481 if (left_index < right_index)
5482 return -1;
5483 else if (left_index > right_index)
5484 return 1;
5485 else
5486 return 0;
5487 }
5488 else
5489 {
5490 gint line1, line2;
5491
5492 line1 = ctk_text_iter_get_line (lhs);
5493 line2 = ctk_text_iter_get_line (rhs);
5494 if (line1 < line2)
5495 return -1;
5496 else if (line1 > line2)
5497 return 1;
5498 else
5499 return 0;
5500 }
5501}
5502
5503/**
5504 * ctk_text_iter_in_range:
5505 * @iter: a #CtkTextIter
5506 * @start: start of range
5507 * @end: end of range
5508 *
5509 * Checks whether @iter falls in the range [@start, @end).
5510 * @start and @end must be in ascending order.
5511 *
5512 * Returns: %TRUE if @iter is in the range
5513 **/
5514gboolean
5515ctk_text_iter_in_range (const CtkTextIter *iter,
5516 const CtkTextIter *start,
5517 const CtkTextIter *end)
5518{
5519 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
5520 g_return_val_if_fail (start != NULL, FALSE)do { if ((start != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "start != NULL"); return
((0)); } } while (0)
;
5521 g_return_val_if_fail (end != NULL, FALSE)do { if ((end != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "end != NULL"); return (
(0)); } } while (0)
;
5522 g_return_val_if_fail (ctk_text_iter_compare (start, end) <= 0, FALSE)do { if ((ctk_text_iter_compare (start, end) <= 0)) { } else
{ g_return_if_fail_warning ("Ctk", ((const char*) (__func__)
), "ctk_text_iter_compare (start, end) <= 0"); return ((0)
); } } while (0)
;
5523
5524 return ctk_text_iter_compare (iter, start) >= 0 &&
5525 ctk_text_iter_compare (iter, end) < 0;
5526}
5527
5528/**
5529 * ctk_text_iter_order:
5530 * @first: a #CtkTextIter
5531 * @second: another #CtkTextIter
5532 *
5533 * Swaps the value of @first and @second if @second comes before
5534 * @first in the buffer. That is, ensures that @first and @second are
5535 * in sequence. Most text buffer functions that take a range call this
5536 * automatically on your behalf, so there’s no real reason to call it yourself
5537 * in those cases. There are some exceptions, such as ctk_text_iter_in_range(),
5538 * that expect a pre-sorted range.
5539 *
5540 **/
5541void
5542ctk_text_iter_order (CtkTextIter *first,
5543 CtkTextIter *second)
5544{
5545 g_return_if_fail (first != NULL)do { if ((first != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "first != NULL"); return
; } } while (0)
;
5546 g_return_if_fail (second != NULL)do { if ((second != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "second != NULL"); return
; } } while (0)
;
5547
5548 if (ctk_text_iter_compare (first, second) > 0)
5549 {
5550 CtkTextIter tmp;
5551
5552 tmp = *first;
5553 *first = *second;
5554 *second = tmp;
5555 }
5556}
5557
5558/*
5559 * Init iterators from the BTree
5560 */
5561
5562void
5563_ctk_text_btree_get_iter_at_char (CtkTextBTree *tree,
5564 CtkTextIter *iter,
5565 gint char_index)
5566{
5567 CtkTextRealIter *real = (CtkTextRealIter*)iter;
5568 gint real_char_index;
5569 gint line_start;
5570 CtkTextLine *line;
5571
5572 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)
;
5573 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)
;
5574
5575 line = _ctk_text_btree_get_line_at_char (tree, char_index,
5576 &line_start, &real_char_index);
5577
5578 iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
5579
5580 real->cached_char_index = real_char_index;
5581
5582 check_invariants (iter);
5583}
5584
5585void
5586_ctk_text_btree_get_iter_at_line_char (CtkTextBTree *tree,
5587 CtkTextIter *iter,
5588 gint line_number,
5589 gint char_on_line)
5590{
5591 CtkTextRealIter *real = (CtkTextRealIter*)iter;
5592 CtkTextLine *line;
5593 gint real_line;
5594
5595 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)
;
5596 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)
;
5597
5598 line = _ctk_text_btree_get_line_no_last (tree, line_number, &real_line);
5599
5600 iter_init_from_char_offset (iter, tree, line, char_on_line);
5601
5602 /* We might as well cache this, since we know it. */
5603 real->cached_line_number = real_line;
5604
5605 check_invariants (iter);
5606}
5607
5608void
5609_ctk_text_btree_get_iter_at_line_byte (CtkTextBTree *tree,
5610 CtkTextIter *iter,
5611 gint line_number,
5612 gint byte_index)
5613{
5614 CtkTextRealIter *real = (CtkTextRealIter*)iter;
5615 CtkTextLine *line;
5616 gint real_line;
5617
5618 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)
;
5619 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)
;
5620
5621 line = _ctk_text_btree_get_line_no_last (tree, line_number, &real_line);
5622
5623 iter_init_from_byte_offset (iter, tree, line, byte_index);
5624
5625 /* We might as well cache this, since we know it. */
5626 real->cached_line_number = real_line;
5627
5628 check_invariants (iter);
5629}
5630
5631void
5632_ctk_text_btree_get_iter_at_line (CtkTextBTree *tree,
5633 CtkTextIter *iter,
5634 CtkTextLine *line,
5635 gint byte_offset)
5636{
5637 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)
;
5638 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)
;
5639 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)
;
5640
5641 iter_init_from_byte_offset (iter, tree, line, byte_offset);
5642
5643 check_invariants (iter);
5644}
5645
5646gboolean
5647_ctk_text_btree_get_iter_at_first_toggle (CtkTextBTree *tree,
5648 CtkTextIter *iter,
5649 CtkTextTag *tag)
5650{
5651 CtkTextLine *line;
5652
5653 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
5654 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)
;
5655
5656 line = _ctk_text_btree_first_could_contain_tag (tree, tag);
5657
5658 if (line == NULL((void*)0))
5659 {
5660 /* Set iter to last in tree */
5661 _ctk_text_btree_get_end_iter (tree, iter);
5662 check_invariants (iter);
5663 return FALSE(0);
5664 }
5665 else
5666 {
5667 iter_init_from_byte_offset (iter, tree, line, 0);
5668
5669 if (!ctk_text_iter_toggles_tag (iter, tag))
5670 ctk_text_iter_forward_to_tag_toggle (iter, tag);
5671
5672 check_invariants (iter);
5673 return TRUE(!(0));
5674 }
5675}
5676
5677gboolean
5678_ctk_text_btree_get_iter_at_last_toggle (CtkTextBTree *tree,
5679 CtkTextIter *iter,
5680 CtkTextTag *tag)
5681{
5682 gboolean found;
5683
5684 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
5685 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)
;
5686
5687 _ctk_text_btree_get_end_iter (tree, iter);
5688
5689 if (ctk_text_iter_toggles_tag (iter, tag))
5690 found = TRUE(!(0));
5691 else
5692 found = ctk_text_iter_backward_to_tag_toggle (iter, tag);
5693
5694 check_invariants (iter);
5695
5696 return found;
5697}
5698
5699gboolean
5700_ctk_text_btree_get_iter_at_mark_name (CtkTextBTree *tree,
5701 CtkTextIter *iter,
5702 const gchar *mark_name)
5703{
5704 CtkTextMark *mark;
5705
5706 g_return_val_if_fail (iter != NULL, FALSE)do { if ((iter != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "iter != NULL"); return (
(0)); } } while (0)
;
5707 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)
;
5708
5709 mark = _ctk_text_btree_get_mark_by_name (tree, mark_name);
5710
5711 if (mark == NULL((void*)0))
5712 return FALSE(0);
5713 else
5714 {
5715 _ctk_text_btree_get_iter_at_mark (tree, iter, mark);
5716 check_invariants (iter);
5717 return TRUE(!(0));
5718 }
5719}
5720
5721void
5722_ctk_text_btree_get_iter_at_mark (CtkTextBTree *tree,
5723 CtkTextIter *iter,
5724 CtkTextMark *mark)
5725{
5726 CtkTextLineSegment *seg;
5727
5728 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)
;
5729 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)
;
5730 g_return_if_fail (CTK_IS_TEXT_MARK (mark))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((mark)); GType __t = ((ctk_text_mark_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_MARK (mark)"); return; } } while (0)
;
5731
5732 seg = mark->segment;
5733
5734 iter_init_from_segment (iter, tree,
5735 seg->body.mark.line, seg);
5736 g_assert (seg->body.mark.line == _ctk_text_iter_get_text_line (iter))do { if (seg->body.mark.line == _ctk_text_iter_get_text_line
(iter)) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 5736, ((const char*) (__func__)), "seg->body.mark.line == _ctk_text_iter_get_text_line (iter)"
); } while (0)
;
5737 check_invariants (iter);
5738}
5739
5740void
5741_ctk_text_btree_get_iter_at_child_anchor (CtkTextBTree *tree,
5742 CtkTextIter *iter,
5743 CtkTextChildAnchor *anchor)
5744{
5745 CtkTextLineSegment *seg;
5746
5747 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)
;
5748 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)
;
5749 g_return_if_fail (CTK_IS_TEXT_CHILD_ANCHOR (anchor))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((anchor)); GType __t = ((ctk_text_child_anchor_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_CHILD_ANCHOR (anchor)"); return
; } } while (0)
;
5750
5751 seg = anchor->segment;
5752
5753 g_assert (seg->body.child.line != NULL)do { if (seg->body.child.line != ((void*)0)) ; else g_assertion_message_expr
("Ctk", "ctktextiter.c", 5753, ((const char*) (__func__)), "seg->body.child.line != NULL"
); } while (0)
;
5754
5755 iter_init_from_segment (iter, tree,
5756 seg->body.child.line, seg);
5757 g_assert (seg->body.child.line == _ctk_text_iter_get_text_line (iter))do { if (seg->body.child.line == _ctk_text_iter_get_text_line
(iter)) ; else g_assertion_message_expr ("Ctk", "ctktextiter.c"
, 5757, ((const char*) (__func__)), "seg->body.child.line == _ctk_text_iter_get_text_line (iter)"
); } while (0)
;
5758 check_invariants (iter);
5759}
5760
5761void
5762_ctk_text_btree_get_end_iter (CtkTextBTree *tree,
5763 CtkTextIter *iter)
5764{
5765 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)
;
5766 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)
;
5767
5768 _ctk_text_btree_get_iter_at_char (tree,
5769 iter,
5770 _ctk_text_btree_char_count (tree));
5771 check_invariants (iter);
5772}
5773
5774void
5775_ctk_text_iter_check (const CtkTextIter *iter)
5776{
5777 const CtkTextRealIter *real = (const CtkTextRealIter*)iter;
5778 gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
5779 CtkTextLineSegment *byte_segment = NULL((void*)0);
5780 CtkTextLineSegment *byte_any_segment = NULL((void*)0);
5781 CtkTextLineSegment *char_segment = NULL((void*)0);
5782 CtkTextLineSegment *char_any_segment = NULL((void*)0);
5783 gboolean segments_updated;
5784
5785 /* This function checks our class invariants for the Iter class. */
5786
5787 g_assert (sizeof (CtkTextIter) == sizeof (CtkTextRealIter))do { if (sizeof (CtkTextIter) == sizeof (CtkTextRealIter)) ; else
g_assertion_message_expr ("Ctk", "ctktextiter.c", 5787, ((const
char*) (__func__)), "sizeof (CtkTextIter) == sizeof (CtkTextRealIter)"
); } while (0)
;
5788
5789 if (real->chars_changed_stamp !=
5790 _ctk_text_btree_get_chars_changed_stamp (real->tree))
5791 g_error ("iterator check failed: invalid iterator");
5792
5793 if (real->line_char_offset < 0 && real->line_byte_offset < 0)
5794 g_error ("iterator check failed: both char and byte offsets are invalid");
5795
5796 segments_updated = (real->segments_changed_stamp ==
5797 _ctk_text_btree_get_segments_changed_stamp (real->tree));
5798
5799#if 0
5800 printf ("checking iter, segments %s updated, byte %d char %d\n",
5801 segments_updated ? "are" : "aren't",
5802 real->line_byte_offset,
5803 real->line_char_offset);
5804#endif
5805
5806 if (segments_updated)
5807 {
5808 if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
5809 g_error ("iterator check failed: both char and byte segment offsets are invalid");
5810
5811 if (real->segment->char_count == 0)
5812 g_error ("iterator check failed: segment is not indexable.");
5813
5814 if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
5815 g_error ("segment char offset is not properly up-to-date");
5816
5817 if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
5818 g_error ("segment byte offset is not properly up-to-date");
5819
5820 if (real->segment_byte_offset >= 0 &&
5821 real->segment_byte_offset >= real->segment->byte_count)
5822 g_error ("segment byte offset is too large.");
5823
5824 if (real->segment_char_offset >= 0 &&
5825 real->segment_char_offset >= real->segment->char_count)
5826 g_error ("segment char offset is too large.");
5827 }
5828
5829 if (real->line_byte_offset >= 0)
5830 {
5831 _ctk_text_line_byte_locate (real->line, real->line_byte_offset,
5832 &byte_segment, &byte_any_segment,
5833 &seg_byte_offset, &line_byte_offset);
5834
5835 if (line_byte_offset != real->line_byte_offset)
5836 g_error ("wrong byte offset was stored in iterator");
5837
5838 if (segments_updated)
5839 {
5840 if (real->segment != byte_segment)
5841 g_error ("wrong segment was stored in iterator");
5842
5843 if (real->any_segment != byte_any_segment)
5844 g_error ("wrong any_segment was stored in iterator");
5845
5846 if (seg_byte_offset != real->segment_byte_offset)
5847 g_error ("wrong segment byte offset was stored in iterator");
5848
5849 if (byte_segment->type == &ctk_text_char_type)
5850 {
5851 const gchar *p;
5852 p = byte_segment->body.chars + seg_byte_offset;
5853
5854 if (!ctk_text_byte_begins_utf8_char (p))
5855 g_error ("broken iterator byte index pointed into the middle of a character");
5856 }
5857 }
5858 }
5859
5860 if (real->line_char_offset >= 0)
5861 {
5862 _ctk_text_line_char_locate (real->line, real->line_char_offset,
5863 &char_segment, &char_any_segment,
5864 &seg_char_offset, &line_char_offset);
5865
5866 if (line_char_offset != real->line_char_offset)
5867 g_error ("wrong char offset was stored in iterator");
5868
5869 if (segments_updated)
5870 {
5871 if (real->segment != char_segment)
5872 g_error ("wrong segment was stored in iterator");
5873
5874 if (real->any_segment != char_any_segment)
5875 g_error ("wrong any_segment was stored in iterator");
5876
5877 if (seg_char_offset != real->segment_char_offset)
5878 g_error ("wrong segment char offset was stored in iterator");
5879
5880 if (char_segment->type == &ctk_text_char_type)
5881 {
5882 const gchar *p;
5883 p = g_utf8_offset_to_pointer (char_segment->body.chars,
5884 seg_char_offset);
5885
5886 /* hmm, not likely to happen eh */
5887 if (!ctk_text_byte_begins_utf8_char (p))
5888 g_error ("broken iterator char offset pointed into the middle of a character");
5889 }
5890 }
5891 }
5892
5893 if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
5894 {
5895 if (byte_segment != char_segment)
5896 g_error ("char and byte offsets did not point to the same segment");
5897
5898 if (byte_any_segment != char_any_segment)
5899 g_error ("char and byte offsets did not point to the same any segment");
5900
5901 /* Make sure the segment offsets are equivalent, if it's a char
5902 segment. */
5903 if (char_segment->type == &ctk_text_char_type)
5904 {
5905 gint byte_offset = 0;
5906 gint char_offset = 0;
5907 while (char_offset < seg_char_offset)
5908 {
5909 const char * start = char_segment->body.chars + byte_offset;
5910 byte_offset += g_utf8_next_char (start)((start) + g_utf8_skip[*(const guchar *)(start)]) - start;
5911 char_offset += 1;
5912 }
5913
5914 if (byte_offset != seg_byte_offset)
5915 g_error ("byte offset did not correspond to char offset");
5916
5917 char_offset =
5918 g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
5919
5920 if (char_offset != seg_char_offset)
5921 g_error ("char offset did not correspond to byte offset");
5922
5923 if (!ctk_text_byte_begins_utf8_char (char_segment->body.chars + seg_byte_offset))
5924 g_error ("byte index for iterator does not index the start of a character");
5925 }
5926 }
5927
5928 if (real->cached_line_number >= 0)
5929 {
5930 gint should_be;
5931
5932 should_be = _ctk_text_line_get_number (real->line);
5933 if (real->cached_line_number != should_be)
5934 g_error ("wrong line number was cached");
5935 }
5936
5937 if (real->cached_char_index >= 0)
5938 {
5939 if (real->line_char_offset >= 0) /* only way we can check it
5940 efficiently, not a real
5941 invariant. */
5942 {
5943 gint char_index;
5944
5945 char_index = _ctk_text_line_char_index (real->line);
5946 char_index += real->line_char_offset;
5947
5948 if (real->cached_char_index != char_index)
5949 g_error ("wrong char index was cached");
5950 }
5951 }
5952
5953 if (_ctk_text_line_is_last (real->line, real->tree))
5954 g_error ("Iterator was on last line (past the end iterator)");
5955}