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

source code of gtk/gtk/gtktextiter.c