1 /* GTK - The GIMP Toolkit
2 * gtktextiter.c Copyright (C) 2000 Red Hat, Inc.
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gtktextiter.h"
28 #include "gtktextbtree.h"
29 #include "gtktextiterprivate.h"
34 #define FIX_OVERFLOWS(varname) if ((varname) == G_MININT) (varname) = G_MININT + 1
36 typedef struct _GtkTextRealIter GtkTextRealIter;
38 struct _GtkTextRealIter
40 /* Always-valid information */
43 /* At least one of these is always valid;
44 if invalid, they are -1.
46 If the line byte offset is valid, so is the segment byte offset;
47 and ditto for char offsets. */
48 gint line_byte_offset;
49 gint line_char_offset;
50 /* These two are valid if >= 0 */
51 gint cached_char_index;
52 gint cached_line_number;
53 /* Stamps to detect the buffer changing under us */
54 gint chars_changed_stamp;
55 gint segments_changed_stamp;
56 /* Valid if the segments_changed_stamp is up-to-date */
57 GtkTextLineSegment *segment; /* indexable segment we index */
58 GtkTextLineSegment *any_segment; /* first segment in our location,
59 maybe same as "segment" */
60 /* One of these will always be valid if segments_changed_stamp is
61 up-to-date. If invalid, they are -1.
63 If the line byte offset is valid, so is the segment byte offset;
64 and ditto for char offsets. */
65 gint segment_byte_offset;
66 gint segment_char_offset;
69 /* These "set" functions should not assume any fields
70 other than the char stamp and the tree are valid.
73 iter_set_common (GtkTextRealIter *iter,
76 /* Update segments stamp */
77 iter->segments_changed_stamp =
78 _gtk_text_btree_get_segments_changed_stamp (iter->tree);
82 iter->line_byte_offset = -1;
83 iter->line_char_offset = -1;
84 iter->segment_byte_offset = -1;
85 iter->segment_char_offset = -1;
86 iter->cached_char_index = -1;
87 iter->cached_line_number = -1;
91 iter_set_from_byte_offset (GtkTextRealIter *iter,
95 iter_set_common (iter, line);
97 if (!_gtk_text_line_byte_locate (iter->line,
101 &iter->segment_byte_offset,
102 &iter->line_byte_offset))
103 g_error ("Byte index %d is off the end of the line",
108 iter_set_from_char_offset (GtkTextRealIter *iter,
112 iter_set_common (iter, line);
114 if (!_gtk_text_line_char_locate (iter->line,
118 &iter->segment_char_offset,
119 &iter->line_char_offset))
120 g_error ("Char offset %d is off the end of the line",
125 iter_set_from_segment (GtkTextRealIter *iter,
127 GtkTextLineSegment *segment)
129 GtkTextLineSegment *seg;
132 /* This could theoretically be optimized by computing all the iter
133 fields in this same loop, but I'm skipping it for now. */
135 seg = line->segments;
136 while (seg != segment)
138 byte_offset += seg->byte_count;
142 iter_set_from_byte_offset (iter, line, byte_offset);
145 /* This function ensures that the segment-dependent information is
146 truly computed lazily; often we don't need to do the full make_real
147 work. This ensures the btree and line are valid, but doesn't
148 update the segments. */
149 static GtkTextRealIter*
150 gtk_text_iter_make_surreal (const GtkTextIter *_iter)
152 GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
154 if (iter->chars_changed_stamp !=
155 _gtk_text_btree_get_chars_changed_stamp (iter->tree))
157 g_warning ("Invalid text buffer iterator: either the iterator "
158 "is uninitialized, or the characters/pixbufs/widgets "
159 "in the buffer have been modified since the iterator "
160 "was created.\nYou must use marks, character numbers, "
161 "or line numbers to preserve a position across buffer "
162 "modifications.\nYou can apply tags and insert marks "
163 "without invalidating your iterators,\n"
164 "but any mutation that affects 'indexable' buffer contents "
165 "(contents that can be referred to by character offset)\n"
166 "will invalidate all outstanding iterators");
170 /* We don't update the segments information since we are becoming
171 only surreal. However we do invalidate the segments information
172 if appropriate, to be sure we segfault if we try to use it and we
173 should have used make_real. */
175 if (iter->segments_changed_stamp !=
176 _gtk_text_btree_get_segments_changed_stamp (iter->tree))
178 iter->segment = NULL;
179 iter->any_segment = NULL;
180 /* set to segfault-causing values. */
181 iter->segment_byte_offset = -10000;
182 iter->segment_char_offset = -10000;
188 static GtkTextRealIter*
189 gtk_text_iter_make_real (const GtkTextIter *_iter)
191 GtkTextRealIter *iter;
193 iter = gtk_text_iter_make_surreal (_iter);
195 if (iter->segments_changed_stamp !=
196 _gtk_text_btree_get_segments_changed_stamp (iter->tree))
198 if (iter->line_byte_offset >= 0)
200 iter_set_from_byte_offset (iter,
202 iter->line_byte_offset);
206 g_assert (iter->line_char_offset >= 0);
208 iter_set_from_char_offset (iter,
210 iter->line_char_offset);
214 g_assert (iter->segment != NULL);
215 g_assert (iter->any_segment != NULL);
216 g_assert (iter->segment->char_count > 0);
221 static GtkTextRealIter*
222 iter_init_common (GtkTextIter *_iter,
225 GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
227 g_return_val_if_fail (iter != NULL, NULL);
228 g_return_val_if_fail (tree != NULL, NULL);
232 iter->chars_changed_stamp =
233 _gtk_text_btree_get_chars_changed_stamp (iter->tree);
238 static GtkTextRealIter*
239 iter_init_from_segment (GtkTextIter *iter,
242 GtkTextLineSegment *segment)
244 GtkTextRealIter *real;
246 g_return_val_if_fail (line != NULL, NULL);
248 real = iter_init_common (iter, tree);
250 iter_set_from_segment (real, line, segment);
255 static GtkTextRealIter*
256 iter_init_from_byte_offset (GtkTextIter *iter,
259 gint line_byte_offset)
261 GtkTextRealIter *real;
263 g_return_val_if_fail (line != NULL, NULL);
265 real = iter_init_common (iter, tree);
267 iter_set_from_byte_offset (real, line, line_byte_offset);
269 if (real->segment->type == >k_text_char_type &&
270 (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
271 g_warning ("Incorrect line byte index %d falls in the middle of a UTF-8 "
272 "character; this will crash the text buffer. "
273 "Byte indexes must refer to the start of a character.",
279 static GtkTextRealIter*
280 iter_init_from_char_offset (GtkTextIter *iter,
283 gint line_char_offset)
285 GtkTextRealIter *real;
287 g_return_val_if_fail (line != NULL, NULL);
289 real = iter_init_common (iter, tree);
291 iter_set_from_char_offset (real, line, line_char_offset);
297 invalidate_segment (GtkTextRealIter *iter)
299 iter->segments_changed_stamp -= 1;
303 invalidate_char_index (GtkTextRealIter *iter)
305 iter->cached_char_index = -1;
309 invalidate_line_number (GtkTextRealIter *iter)
311 iter->cached_line_number = -1;
315 adjust_char_index (GtkTextRealIter *iter, gint count)
317 if (iter->cached_char_index >= 0)
318 iter->cached_char_index += count;
322 adjust_line_number (GtkTextRealIter *iter, gint count)
324 if (iter->cached_line_number >= 0)
325 iter->cached_line_number += count;
329 adjust_char_offsets (GtkTextRealIter *iter, gint count)
331 if (iter->line_char_offset >= 0)
333 iter->line_char_offset += count;
334 g_assert (iter->segment_char_offset >= 0);
335 iter->segment_char_offset += count;
340 adjust_byte_offsets (GtkTextRealIter *iter, gint count)
342 if (iter->line_byte_offset >= 0)
344 iter->line_byte_offset += count;
345 g_assert (iter->segment_byte_offset >= 0);
346 iter->segment_byte_offset += count;
351 ensure_char_offsets (GtkTextRealIter *iter)
353 if (iter->line_char_offset < 0)
355 g_assert (iter->line_byte_offset >= 0);
357 _gtk_text_line_byte_to_char_offsets (iter->line,
358 iter->line_byte_offset,
359 &iter->line_char_offset,
360 &iter->segment_char_offset);
365 ensure_byte_offsets (GtkTextRealIter *iter)
367 if (iter->line_byte_offset < 0)
369 g_assert (iter->line_char_offset >= 0);
371 _gtk_text_line_char_to_byte_offsets (iter->line,
372 iter->line_char_offset,
373 &iter->line_byte_offset,
374 &iter->segment_byte_offset);
378 static inline gboolean
379 is_segment_start (GtkTextRealIter *real)
381 return real->segment_byte_offset == 0 || real->segment_char_offset == 0;
386 check_invariants (const GtkTextIter *iter)
388 if (gtk_debug_flags & GTK_DEBUG_TEXT)
389 _gtk_text_iter_check (iter);
392 #define check_invariants (x)
396 * gtk_text_iter_get_buffer:
399 * Return the #GtkTextBuffer this iterator is associated with
401 * Return value: the buffer
404 gtk_text_iter_get_buffer (const GtkTextIter *iter)
406 GtkTextRealIter *real;
408 g_return_val_if_fail (iter != NULL, NULL);
410 real = gtk_text_iter_make_surreal (iter);
415 check_invariants (iter);
417 return _gtk_text_btree_get_buffer (real->tree);
421 * gtk_text_iter_copy:
424 * Create a dynamically-allocated copy of an iterator. This function
425 * is not useful in applications, because iterators can be copied with a
426 * simple assignment (<literal>GtkTextIter i = j;</literal>). The
427 * function is used by language bindings.
429 * Return value: a copy of the @iter, free with gtk_text_iter_free ()
432 gtk_text_iter_copy (const GtkTextIter *iter)
434 GtkTextIter *new_iter;
436 g_return_val_if_fail (iter != NULL, NULL);
438 new_iter = g_new (GtkTextIter, 1);
446 * gtk_text_iter_free:
447 * @iter: a dynamically-allocated iterator
449 * Free an iterator allocated on the heap. This function
450 * is intended for use in language bindings, and is not
451 * especially useful for applications, because iterators can
452 * simply be allocated on the stack.
456 gtk_text_iter_free (GtkTextIter *iter)
458 g_return_if_fail (iter != NULL);
464 _gtk_text_iter_get_indexable_segment (const GtkTextIter *iter)
466 GtkTextRealIter *real;
468 g_return_val_if_fail (iter != NULL, 0);
470 real = gtk_text_iter_make_real (iter);
475 check_invariants (iter);
477 g_assert (real->segment != NULL);
479 return real->segment;
483 _gtk_text_iter_get_any_segment (const GtkTextIter *iter)
485 GtkTextRealIter *real;
487 g_return_val_if_fail (iter != NULL, 0);
489 real = gtk_text_iter_make_real (iter);
494 check_invariants (iter);
496 g_assert (real->any_segment != NULL);
498 return real->any_segment;
502 _gtk_text_iter_get_segment_byte (const GtkTextIter *iter)
504 GtkTextRealIter *real;
506 g_return_val_if_fail (iter != NULL, 0);
508 real = gtk_text_iter_make_real (iter);
513 ensure_byte_offsets (real);
515 check_invariants (iter);
517 return real->segment_byte_offset;
521 _gtk_text_iter_get_segment_char (const GtkTextIter *iter)
523 GtkTextRealIter *real;
525 g_return_val_if_fail (iter != NULL, 0);
527 real = gtk_text_iter_make_real (iter);
532 ensure_char_offsets (real);
534 check_invariants (iter);
536 return real->segment_char_offset;
539 /* This function does not require a still-valid
542 _gtk_text_iter_get_text_line (const GtkTextIter *iter)
544 const GtkTextRealIter *real;
546 g_return_val_if_fail (iter != NULL, 0);
548 real = (const GtkTextRealIter*)iter;
553 /* This function does not require a still-valid
556 _gtk_text_iter_get_btree (const GtkTextIter *iter)
558 const GtkTextRealIter *real;
560 g_return_val_if_fail (iter != NULL, 0);
562 real = (const GtkTextRealIter*)iter;
572 * gtk_text_iter_get_offset:
575 * Returns the character offset of an iterator.
576 * Each character in a #GtkTextBuffer has an offset,
577 * starting with 0 for the first character in the buffer.
578 * Use gtk_text_buffer_get_iter_at_offset () to convert an
579 * offset back into an iterator.
581 * Return value: a character offset
584 gtk_text_iter_get_offset (const GtkTextIter *iter)
586 GtkTextRealIter *real;
588 g_return_val_if_fail (iter != NULL, 0);
590 real = gtk_text_iter_make_surreal (iter);
595 check_invariants (iter);
597 if (real->cached_char_index < 0)
599 ensure_char_offsets (real);
601 real->cached_char_index =
602 _gtk_text_line_char_index (real->line);
603 real->cached_char_index += real->line_char_offset;
606 check_invariants (iter);
608 return real->cached_char_index;
612 * gtk_text_iter_get_line:
615 * Returns the line number containing the iterator. Lines in
616 * a #GtkTextBuffer are numbered beginning with 0 for the first
617 * line in the buffer.
619 * Return value: a line number
622 gtk_text_iter_get_line (const GtkTextIter *iter)
624 GtkTextRealIter *real;
626 g_return_val_if_fail (iter != NULL, 0);
628 real = gtk_text_iter_make_surreal (iter);
633 if (real->cached_line_number < 0)
634 real->cached_line_number =
635 _gtk_text_line_get_number (real->line);
637 check_invariants (iter);
639 return real->cached_line_number;
643 * gtk_text_iter_get_line_offset:
646 * Returns the character offset of the iterator,
647 * counting from the start of a newline-terminated line.
648 * The first character on the line has offset 0.
650 * Return value: offset from start of line
653 gtk_text_iter_get_line_offset (const GtkTextIter *iter)
655 GtkTextRealIter *real;
657 g_return_val_if_fail (iter != NULL, 0);
659 real = gtk_text_iter_make_surreal (iter);
664 ensure_char_offsets (real);
666 check_invariants (iter);
668 return real->line_char_offset;
672 * gtk_text_iter_get_line_index:
675 * Returns the byte index of the iterator, counting
676 * from the start of a newline-terminated line.
677 * Remember that #GtkTextBuffer encodes text in
678 * UTF-8, and that characters can require a variable
679 * number of bytes to represent.
681 * Return value: distance from start of line, in bytes
684 gtk_text_iter_get_line_index (const GtkTextIter *iter)
686 GtkTextRealIter *real;
688 g_return_val_if_fail (iter != NULL, 0);
690 real = gtk_text_iter_make_surreal (iter);
695 ensure_byte_offsets (real);
697 check_invariants (iter);
699 return real->line_byte_offset;
703 gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter)
705 GtkTextRealIter *real;
707 GtkTextLineSegment *seg;
710 g_return_val_if_fail (iter != NULL, 0);
712 real = gtk_text_iter_make_real (iter);
717 ensure_char_offsets (real);
719 check_invariants (iter);
721 vis_offset = real->line_char_offset;
723 _gtk_text_btree_get_iter_at_line (real->tree,
728 seg = _gtk_text_iter_get_indexable_segment (&pos);
730 while (seg != real->segment)
732 /* This is a pretty expensive call, making the
733 * whole function pretty lame; we could keep track
734 * of current invisibility state by looking at toggle
735 * segments as we loop, and then call this function
736 * only once per line, in order to speed up the loop
739 if (_gtk_text_btree_char_is_invisible (&pos))
740 vis_offset -= seg->char_count;
742 _gtk_text_iter_forward_indexable_segment (&pos);
744 seg = _gtk_text_iter_get_indexable_segment (&pos);
747 if (_gtk_text_btree_char_is_invisible (&pos))
748 vis_offset -= real->segment_char_offset;
754 gtk_text_iter_get_visible_line_index (const GtkTextIter *iter)
756 GtkTextRealIter *real;
758 GtkTextLineSegment *seg;
761 g_return_val_if_fail (iter != NULL, 0);
763 real = gtk_text_iter_make_real (iter);
768 ensure_char_offsets (real);
770 check_invariants (iter);
772 vis_offset = real->line_byte_offset;
774 _gtk_text_btree_get_iter_at_line (real->tree,
779 seg = _gtk_text_iter_get_indexable_segment (&pos);
781 while (seg != real->segment)
783 /* This is a pretty expensive call, making the
784 * whole function pretty lame; we could keep track
785 * of current invisibility state by looking at toggle
786 * segments as we loop, and then call this function
787 * only once per line, in order to speed up the loop
790 if (_gtk_text_btree_char_is_invisible (&pos))
791 vis_offset -= seg->byte_count;
793 _gtk_text_iter_forward_indexable_segment (&pos);
795 seg = _gtk_text_iter_get_indexable_segment (&pos);
798 if (_gtk_text_btree_char_is_invisible (&pos))
799 vis_offset -= real->segment_byte_offset;
809 * gtk_text_iter_get_char:
812 * Returns the Unicode character at this iterator. (Equivalent to
813 * operator* on a C++ iterator.) If the iterator points at a
814 * non-character element, such as an image embedded in the buffer, the
815 * Unicode "unknown" character 0xFFFC is returned. If invoked on
816 * the end iterator, zero is returned; zero is not a valid Unicode character.
817 * So you can write a loop which ends when gtk_text_iter_get_char ()
820 * Return value: a Unicode character, or 0 if @iter is not dereferenceable
823 gtk_text_iter_get_char (const GtkTextIter *iter)
825 GtkTextRealIter *real;
827 g_return_val_if_fail (iter != NULL, 0);
829 real = gtk_text_iter_make_real (iter);
834 check_invariants (iter);
836 if (gtk_text_iter_is_end (iter))
838 else if (real->segment->type == >k_text_char_type)
840 ensure_byte_offsets (real);
842 return g_utf8_get_char (real->segment->body.chars +
843 real->segment_byte_offset);
847 /* Unicode "unknown character" 0xFFFC */
848 return GTK_TEXT_UNKNOWN_CHAR;
853 * gtk_text_iter_get_slice:
854 * @start: iterator at start of a range
855 * @end: iterator at end of a range
857 * Returns the text in the given range. A "slice" is an array of
858 * characters encoded in UTF-8 format, including the Unicode "unknown"
859 * character 0xFFFC for iterable non-character elements in the buffer,
860 * such as images. Because images are encoded in the slice, byte and
861 * character offsets in the returned array will correspond to byte
862 * offsets in the text buffer. Note that 0xFFFC can occur in normal
863 * text as well, so it is not a reliable indicator that a pixbuf or
864 * widget is in the buffer.
866 * Return value: slice of text from the buffer
869 gtk_text_iter_get_slice (const GtkTextIter *start,
870 const GtkTextIter *end)
872 g_return_val_if_fail (start != NULL, NULL);
873 g_return_val_if_fail (end != NULL, NULL);
875 check_invariants (start);
876 check_invariants (end);
878 return _gtk_text_btree_get_text (start, end, TRUE, TRUE);
882 * gtk_text_iter_get_text:
883 * @start: iterator at start of a range
884 * @end: iterator at end of a range
886 * Returns <emphasis>text</emphasis> in the given range. If the range
887 * contains non-text elements such as images, the character and byte
888 * offsets in the returned string will not correspond to character and
889 * byte offsets in the buffer. If you want offsets to correspond, see
890 * gtk_text_iter_get_slice ().
892 * Return value: array of characters from the buffer
895 gtk_text_iter_get_text (const GtkTextIter *start,
896 const GtkTextIter *end)
898 g_return_val_if_fail (start != NULL, NULL);
899 g_return_val_if_fail (end != NULL, NULL);
901 check_invariants (start);
902 check_invariants (end);
904 return _gtk_text_btree_get_text (start, end, TRUE, FALSE);
908 * gtk_text_iter_get_visible_slice:
909 * @start: iterator at start of range
910 * @end: iterator at end of range
912 * Like gtk_text_iter_get_slice (), but invisible text is not included.
913 * Invisible text is usually invisible because a #GtkTextTag with the
914 * "invisible" attribute turned on has been applied to it.
916 * Return value: slice of text from the buffer
919 gtk_text_iter_get_visible_slice (const GtkTextIter *start,
920 const GtkTextIter *end)
922 g_return_val_if_fail (start != NULL, NULL);
923 g_return_val_if_fail (end != NULL, NULL);
925 check_invariants (start);
926 check_invariants (end);
928 return _gtk_text_btree_get_text (start, end, FALSE, TRUE);
932 * gtk_text_iter_get_visible_text:
933 * @start: iterator at start of range
934 * @end: iterator at end of range
936 * Like gtk_text_iter_get_text (), but invisible text is not included.
937 * Invisible text is usually invisible because a #GtkTextTag with the
938 * "invisible" attribute turned on has been applied to it.
940 * Return value: string containing visible text in the range
943 gtk_text_iter_get_visible_text (const GtkTextIter *start,
944 const GtkTextIter *end)
946 g_return_val_if_fail (start != NULL, NULL);
947 g_return_val_if_fail (end != NULL, NULL);
949 check_invariants (start);
950 check_invariants (end);
952 return _gtk_text_btree_get_text (start, end, FALSE, FALSE);
956 * gtk_text_iter_get_pixbuf:
959 * If the location pointed to by @iter contains a pixbuf, the pixbuf
960 * is returned (with no new reference count added). Otherwise,
963 * Return value: the pixbuf at @iter
966 gtk_text_iter_get_pixbuf (const GtkTextIter *iter)
968 GtkTextRealIter *real;
970 g_return_val_if_fail (iter != NULL, NULL);
972 real = gtk_text_iter_make_real (iter);
977 check_invariants (iter);
979 if (real->segment->type != >k_text_pixbuf_type)
982 return real->segment->body.pixbuf.pixbuf;
986 * gtk_text_iter_get_child_anchor:
989 * If the location pointed to by @iter contains a child anchor, the
990 * anchor is returned (with no new reference count added). Otherwise,
993 * Return value: the anchor at @iter
996 gtk_text_iter_get_child_anchor (const GtkTextIter *iter)
998 GtkTextRealIter *real;
1000 g_return_val_if_fail (iter != NULL, NULL);
1002 real = gtk_text_iter_make_real (iter);
1007 check_invariants (iter);
1009 if (real->segment->type != >k_text_child_type)
1012 return real->segment->body.child.obj;
1016 * gtk_text_iter_get_marks:
1017 * @iter: an iterator
1019 * Returns a list of all #GtkTextMark at this location. Because marks
1020 * are not iterable (they don't take up any "space" in the buffer,
1021 * they are just marks in between iterable locations), multiple marks
1022 * can exist in the same place. The returned list is not in any
1025 * Return value: list of #GtkTextMark
1028 gtk_text_iter_get_marks (const GtkTextIter *iter)
1030 GtkTextRealIter *real;
1031 GtkTextLineSegment *seg;
1034 g_return_val_if_fail (iter != NULL, NULL);
1036 real = gtk_text_iter_make_real (iter);
1041 check_invariants (iter);
1044 seg = real->any_segment;
1045 while (seg != real->segment)
1047 if (seg->type == >k_text_left_mark_type ||
1048 seg->type == >k_text_right_mark_type)
1049 retval = g_slist_prepend (retval, seg->body.mark.obj);
1054 /* The returned list isn't guaranteed to be in any special order,
1060 * gtk_text_iter_get_toggled_tags:
1061 * @iter: an iterator
1062 * @toggled_on: TRUE to get toggled-on tags
1064 * Returns a list of #GtkTextTag that are toggled on or off at this
1065 * point. (If @toggled_on is TRUE, the list contains tags that are
1066 * toggled on.) If a tag is toggled on at @iter, then some non-empty
1067 * range of characters following @iter has that tag applied to it. If
1068 * a tag is toggled off, then some non-empty range following @iter
1069 * does <emphasis>not</emphasis> have the tag applied to it.
1071 * Return value: tags toggled at this point
1074 gtk_text_iter_get_toggled_tags (const GtkTextIter *iter,
1075 gboolean toggled_on)
1077 GtkTextRealIter *real;
1078 GtkTextLineSegment *seg;
1081 g_return_val_if_fail (iter != NULL, NULL);
1083 real = gtk_text_iter_make_real (iter);
1088 check_invariants (iter);
1091 seg = real->any_segment;
1092 while (seg != real->segment)
1096 if (seg->type == >k_text_toggle_on_type)
1098 retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1103 if (seg->type == >k_text_toggle_off_type)
1105 retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1112 /* The returned list isn't guaranteed to be in any special order,
1118 * gtk_text_iter_begins_tag:
1119 * @iter: an iterator
1120 * @tag: a #GtkTextTag, or NULL
1122 * Returns TRUE if @tag is toggled on at exactly this point. If @tag
1123 * is NULL, returns TRUE if any tag is toggled on at this point. Note
1124 * that the gtk_text_iter_begins_tag () returns TRUE if @iter is the
1125 * <emphasis>start</emphasis> of the tagged range;
1126 * gtk_text_iter_has_tag () tells you whether an iterator is
1127 * <emphasis>within</emphasis> a tagged range.
1129 * Return value: whether @iter is the start of a range tagged with @tag
1132 gtk_text_iter_begins_tag (const GtkTextIter *iter,
1135 GtkTextRealIter *real;
1136 GtkTextLineSegment *seg;
1138 g_return_val_if_fail (iter != NULL, FALSE);
1140 real = gtk_text_iter_make_real (iter);
1145 check_invariants (iter);
1147 seg = real->any_segment;
1148 while (seg != real->segment)
1150 if (seg->type == >k_text_toggle_on_type)
1153 seg->body.toggle.info->tag == tag)
1164 * gtk_text_iter_ends_tag:
1165 * @iter: an iterator
1166 * @tag: a #GtkTextTag, or NULL
1168 * Returns TRUE if @tag is toggled off at exactly this point. If @tag
1169 * is NULL, returns TRUE if any tag is toggled off at this point. Note
1170 * that the gtk_text_iter_ends_tag () returns TRUE if @iter is the
1171 * <emphasis>end</emphasis> of the tagged range;
1172 * gtk_text_iter_has_tag () tells you whether an iterator is
1173 * <emphasis>within</emphasis> a tagged range.
1175 * Return value: whether @iter is the end of a range tagged with @tag
1179 gtk_text_iter_ends_tag (const GtkTextIter *iter,
1182 GtkTextRealIter *real;
1183 GtkTextLineSegment *seg;
1185 g_return_val_if_fail (iter != NULL, FALSE);
1187 real = gtk_text_iter_make_real (iter);
1192 check_invariants (iter);
1194 seg = real->any_segment;
1195 while (seg != real->segment)
1197 if (seg->type == >k_text_toggle_off_type)
1200 seg->body.toggle.info->tag == tag)
1211 * gtk_text_iter_toggles_tag:
1212 * @iter: an iterator
1213 * @tag: a #GtkTextTag, or NULL
1215 * This is equivalent to (gtk_text_iter_begins_tag () ||
1216 * gtk_text_iter_ends_tag ()), i.e. it tells you whether a range with
1217 * @tag applied to it begins <emphasis>or</emphasis> ends at @iter.
1219 * Return value: whether @tag is toggled on or off at @iter
1222 gtk_text_iter_toggles_tag (const GtkTextIter *iter,
1225 GtkTextRealIter *real;
1226 GtkTextLineSegment *seg;
1228 g_return_val_if_fail (iter != NULL, FALSE);
1230 real = gtk_text_iter_make_real (iter);
1235 check_invariants (iter);
1237 seg = real->any_segment;
1238 while (seg != real->segment)
1240 if ( (seg->type == >k_text_toggle_off_type ||
1241 seg->type == >k_text_toggle_on_type) &&
1243 seg->body.toggle.info->tag == tag) )
1253 * gtk_text_iter_has_tag:
1254 * @iter: an iterator
1255 * @tag: a #GtkTextTag
1257 * Returns TRUE if @iter is within a range tagged with @tag.
1259 * Return value: whether @iter is tagged with @tag
1262 gtk_text_iter_has_tag (const GtkTextIter *iter,
1265 GtkTextRealIter *real;
1267 g_return_val_if_fail (iter != NULL, FALSE);
1268 g_return_val_if_fail (GTK_IS_TEXT_TAG (tag), FALSE);
1270 real = gtk_text_iter_make_surreal (iter);
1275 check_invariants (iter);
1277 if (real->line_byte_offset >= 0)
1279 return _gtk_text_line_byte_has_tag (real->line, real->tree,
1280 real->line_byte_offset, tag);
1284 g_assert (real->line_char_offset >= 0);
1285 return _gtk_text_line_char_has_tag (real->line, real->tree,
1286 real->line_char_offset, tag);
1291 * gtk_text_iter_get_tags:
1292 * @iter: a #GtkTextIter
1294 * Returns a list of tags that apply to @iter, in ascending order of
1295 * priority (highest-priority tags are last). The #GtkTextTag in the
1296 * list don't have a reference added, but you have to free the list
1299 * Return value: list of #GtkTextTag
1302 gtk_text_iter_get_tags (const GtkTextIter *iter)
1309 g_return_val_if_fail (iter != NULL, NULL);
1311 /* Get the tags at this spot */
1312 tags = _gtk_text_btree_get_tags (iter, &tag_count);
1314 /* No tags, use default style */
1315 if (tags == NULL || tag_count == 0)
1323 /* Sort tags in ascending order of priority */
1324 _gtk_text_tag_array_sort (tags, tag_count);
1328 while (i < tag_count)
1330 retval = g_slist_prepend (retval, tags[i]);
1336 /* Return tags in ascending order of priority */
1337 return g_slist_reverse (retval);
1341 * gtk_text_iter_editable:
1342 * @iter: an iterator
1343 * @default_setting: TRUE if text is editable by default
1345 * Returns whether @iter is within an editable region of text.
1346 * Non-editable text is "locked" and can't be changed by the user via
1347 * #GtkTextView. This function is simply a convenience wrapper around
1348 * gtk_text_iter_get_attributes (). If no tags applied to this text
1349 * affect editability, @default_setting will be returned.
1351 * Return value: whether @iter is inside an editable range
1354 gtk_text_iter_editable (const GtkTextIter *iter,
1355 gboolean default_setting)
1357 GtkTextAttributes *values;
1360 values = gtk_text_attributes_new ();
1362 values->editable = default_setting;
1364 gtk_text_iter_get_attributes (iter, values);
1366 retval = values->editable;
1368 gtk_text_attributes_unref (values);
1374 * gtk_text_iter_get_language:
1375 * @iter: an iterator
1377 * A convenience wrapper around gtk_text_iter_get_attributes (),
1378 * which returns the language in effect at @iter. If no tags affecting
1379 * language * apply to @iter, the return value is identical to that of
1380 * gtk_get_default_language ().
1382 * Return value: language in effect at @iter
1385 gtk_text_iter_get_language (const GtkTextIter *iter)
1387 GtkTextAttributes *values;
1388 PangoLanguage *retval;
1390 values = gtk_text_attributes_new ();
1392 gtk_text_iter_get_attributes (iter, values);
1394 retval = values->language;
1396 gtk_text_attributes_unref (values);
1402 * gtk_text_iter_starts_line:
1403 * @iter: an iterator
1405 * Returns TRUE if @iter begins a paragraph,
1406 * i.e. if gtk_text_iter_get_line_offset () would return 0.
1407 * However this function is potentially more efficient than
1408 * gtk_text_iter_get_line_offset () because it doesn't have to compute
1409 * the offset, it just has to see whether it's 0.
1411 * Return value: whether @iter begins a line
1414 gtk_text_iter_starts_line (const GtkTextIter *iter)
1416 GtkTextRealIter *real;
1418 g_return_val_if_fail (iter != NULL, FALSE);
1420 real = gtk_text_iter_make_surreal (iter);
1425 check_invariants (iter);
1427 if (real->line_byte_offset >= 0)
1429 return (real->line_byte_offset == 0);
1433 g_assert (real->line_char_offset >= 0);
1434 return (real->line_char_offset == 0);
1439 * gtk_text_iter_ends_line:
1440 * @iter: an iterator
1442 * Returns TRUE if @iter points to the start of the paragraph delimiter
1443 * characters for a line (delimiters will be either a newline, a
1444 * carriage return, a carriage return followed by a newline, or a
1445 * Unicode paragraph separator character). Note that an iterator pointing
1446 * to the \n of a \r\n pair will not be counted as the end of a line,
1447 * the line ends before the \r.
1449 * Return value: whether @iter is at the end of a line
1452 gtk_text_iter_ends_line (const GtkTextIter *iter)
1454 GtkTextRealIter *real;
1457 g_return_val_if_fail (iter != NULL, FALSE);
1459 real = gtk_text_iter_make_real (iter);
1461 check_invariants (iter);
1463 /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
1464 * Unicode 3.0; update this if that changes.
1466 #define PARAGRAPH_SEPARATOR 0x2029
1468 wc = gtk_text_iter_get_char (iter);
1470 if (wc == '\r' || wc == PARAGRAPH_SEPARATOR)
1472 else if (wc == '\n')
1474 /* need to determine if a \r precedes the \n, in which case
1475 * we aren't the end of the line
1477 GtkTextIter tmp = *iter;
1478 if (!gtk_text_iter_backward_char (&tmp))
1481 return gtk_text_iter_get_char (&tmp) != '\r';
1488 * gtk_text_iter_is_end:
1489 * @iter: an iterator
1491 * Returns TRUE if @iter is the end iterator, i.e. one past the last
1492 * dereferenceable iterator in the buffer. gtk_text_iter_is_end () is
1493 * the most efficient way to check whether an iterator is the end
1496 * Return value: whether @iter is the end iterator
1499 gtk_text_iter_is_end (const GtkTextIter *iter)
1501 GtkTextRealIter *real;
1503 g_return_val_if_fail (iter != NULL, FALSE);
1505 real = gtk_text_iter_make_surreal (iter);
1510 check_invariants (iter);
1512 return _gtk_text_line_is_last (real->line, real->tree);
1516 * gtk_text_iter_is_start:
1517 * @iter: an iterator
1519 * Returns TRUE if @iter is the first iterator in the buffer, that is
1520 * if @iter has a character offset of 0.
1522 * Return value: whether @iter is the first in the buffer
1525 gtk_text_iter_is_start (const GtkTextIter *iter)
1527 return gtk_text_iter_get_offset (iter) == 0;
1531 * gtk_text_iter_get_chars_in_line:
1532 * @iter: an iterator
1534 * Returns the number of characters in the line containing @iter,
1535 * including the paragraph delimiters.
1537 * Return value: number of characters in the line
1540 gtk_text_iter_get_chars_in_line (const GtkTextIter *iter)
1542 GtkTextRealIter *real;
1544 GtkTextLineSegment *seg;
1546 g_return_val_if_fail (iter != NULL, FALSE);
1548 real = gtk_text_iter_make_surreal (iter);
1553 check_invariants (iter);
1555 if (real->line_char_offset >= 0)
1557 /* We can start at the segments we've already found. */
1558 count = real->line_char_offset - real->segment_char_offset;
1559 seg = _gtk_text_iter_get_indexable_segment (iter);
1563 /* count whole line. */
1564 seg = real->line->segments;
1571 count += seg->char_count;
1580 * gtk_text_iter_get_bytes_in_line:
1581 * @iter: an iterator
1583 * Returns the number of bytes in the line containing @iter,
1584 * including the paragraph delimiters.
1586 * Return value: number of bytes in the line
1589 gtk_text_iter_get_bytes_in_line (const GtkTextIter *iter)
1591 GtkTextRealIter *real;
1593 GtkTextLineSegment *seg;
1595 g_return_val_if_fail (iter != NULL, FALSE);
1597 real = gtk_text_iter_make_surreal (iter);
1602 check_invariants (iter);
1604 if (real->line_byte_offset >= 0)
1606 /* We can start at the segments we've already found. */
1607 count = real->line_byte_offset - real->segment_byte_offset;
1608 seg = _gtk_text_iter_get_indexable_segment (iter);
1612 /* count whole line. */
1613 seg = real->line->segments;
1619 count += seg->byte_count;
1628 * gtk_text_iter_get_attributes:
1629 * @iter: an iterator
1630 * @values: a #GtkTextAttributes to be filled in
1632 * Computes the effect of any tags applied to this spot in the
1633 * text. The @values parameter should be initialized to the default
1634 * settings you wish to use if no tags are in effect. You'd typically
1635 * obtain the defaults from gtk_text_view_get_default_attributes().
1637 * gtk_text_iter_get_attributes () will modify @values, applying the
1638 * effects of any tags present at @iter. If any tags affected @values,
1639 * the function returns %TRUE.
1641 * Return value: %TRUE if @values was modified
1644 gtk_text_iter_get_attributes (const GtkTextIter *iter,
1645 GtkTextAttributes *values)
1650 /* Get the tags at this spot */
1651 tags = _gtk_text_btree_get_tags (iter, &tag_count);
1653 /* No tags, use default style */
1654 if (tags == NULL || tag_count == 0)
1662 /* Sort tags in ascending order of priority */
1663 _gtk_text_tag_array_sort (tags, tag_count);
1665 _gtk_text_attributes_fill_from_tags (values,
1675 * Increments/decrements
1678 /* The return value of this indicates WHETHER WE MOVED.
1679 * The return value of public functions indicates
1680 * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
1683 forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
1685 GtkTextLine *new_line;
1687 new_line = _gtk_text_line_next (real->line);
1689 g_assert (new_line != real->line);
1691 if (new_line != NULL)
1693 real->line = new_line;
1695 real->line_byte_offset = 0;
1696 real->line_char_offset = 0;
1698 real->segment_byte_offset = 0;
1699 real->segment_char_offset = 0;
1701 /* Find first segments in new line */
1702 real->any_segment = real->line->segments;
1703 real->segment = real->any_segment;
1704 while (real->segment->char_count == 0)
1705 real->segment = real->segment->next;
1711 /* There is no way to move forward; we were already
1712 at the "end" index. (the end index is the last
1713 line pointer, segment_byte_offset of 0) */
1715 g_assert (real->line_char_offset == 0 ||
1716 real->line_byte_offset == 0);
1718 /* The only indexable segment allowed on the bogus
1719 line at the end is a single char segment containing
1721 if (real->segments_changed_stamp ==
1722 _gtk_text_btree_get_segments_changed_stamp (real->tree))
1724 g_assert (real->segment->type == >k_text_char_type);
1725 g_assert (real->segment->char_count == 1);
1727 /* We leave real->line as-is */
1734 /* The return value of this indicates WHETHER WE MOVED.
1735 * The return value of public functions indicates
1736 * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
1739 backward_line_leaving_caches_unmodified (GtkTextRealIter *real)
1741 GtkTextLine *new_line;
1743 new_line = _gtk_text_line_previous (real->line);
1745 g_assert (new_line != real->line);
1747 if (new_line != NULL)
1749 real->line = new_line;
1751 real->line_byte_offset = 0;
1752 real->line_char_offset = 0;
1754 real->segment_byte_offset = 0;
1755 real->segment_char_offset = 0;
1757 /* Find first segments in new line */
1758 real->any_segment = real->line->segments;
1759 real->segment = real->any_segment;
1760 while (real->segment->char_count == 0)
1761 real->segment = real->segment->next;
1767 /* There is no way to move backward; we were already
1768 at the first line. */
1770 /* We leave real->line as-is */
1772 /* Note that we didn't clamp to the start of the first line. */
1778 /* The return value indicates (MOVEMENT OCCURRED && NEW ITER IS
1782 forward_char (GtkTextRealIter *real)
1784 GtkTextIter *iter = (GtkTextIter*)real;
1786 check_invariants ((GtkTextIter*)real);
1788 ensure_char_offsets (real);
1790 if ( (real->segment_char_offset + 1) == real->segment->char_count)
1792 /* Need to move to the next segment; if no next segment,
1793 need to move to next line. */
1794 return _gtk_text_iter_forward_indexable_segment (iter);
1798 /* Just moving within a segment. Keep byte count
1799 up-to-date, if it was already up-to-date. */
1801 g_assert (real->segment->type == >k_text_char_type);
1803 if (real->line_byte_offset >= 0)
1806 const char * start =
1807 real->segment->body.chars + real->segment_byte_offset;
1809 bytes = g_utf8_next_char (start) - start;
1811 real->line_byte_offset += bytes;
1812 real->segment_byte_offset += bytes;
1814 g_assert (real->segment_byte_offset < real->segment->byte_count);
1817 real->line_char_offset += 1;
1818 real->segment_char_offset += 1;
1820 adjust_char_index (real, 1);
1822 g_assert (real->segment_char_offset < real->segment->char_count);
1824 /* We moved into the middle of a segment, so the any_segment
1825 must now be the segment we're in the middle of. */
1826 real->any_segment = real->segment;
1828 check_invariants ((GtkTextIter*)real);
1830 if (gtk_text_iter_is_end ((GtkTextIter*)real))
1838 _gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
1840 /* Need to move to the next segment; if no next segment,
1841 need to move to next line. */
1842 GtkTextLineSegment *seg;
1843 GtkTextLineSegment *any_seg;
1844 GtkTextRealIter *real;
1848 g_return_val_if_fail (iter != NULL, FALSE);
1850 real = gtk_text_iter_make_real (iter);
1855 check_invariants (iter);
1857 if (real->line_char_offset >= 0)
1859 chars_skipped = real->segment->char_count - real->segment_char_offset;
1860 g_assert (chars_skipped > 0);
1865 if (real->line_byte_offset >= 0)
1867 bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
1868 g_assert (bytes_skipped > 0);
1873 /* Get first segment of any kind */
1874 any_seg = real->segment->next;
1875 /* skip non-indexable segments, if any */
1877 while (seg != NULL && seg->char_count == 0)
1882 real->any_segment = any_seg;
1883 real->segment = seg;
1885 if (real->line_byte_offset >= 0)
1887 g_assert (bytes_skipped > 0);
1888 real->segment_byte_offset = 0;
1889 real->line_byte_offset += bytes_skipped;
1892 if (real->line_char_offset >= 0)
1894 g_assert (chars_skipped > 0);
1895 real->segment_char_offset = 0;
1896 real->line_char_offset += chars_skipped;
1897 adjust_char_index (real, chars_skipped);
1900 check_invariants (iter);
1906 /* End of the line */
1907 if (forward_line_leaving_caches_unmodified (real))
1909 adjust_line_number (real, 1);
1910 if (real->line_char_offset >= 0)
1911 adjust_char_index (real, chars_skipped);
1913 g_assert (real->line_byte_offset == 0);
1914 g_assert (real->line_char_offset == 0);
1915 g_assert (real->segment_byte_offset == 0);
1916 g_assert (real->segment_char_offset == 0);
1917 g_assert (gtk_text_iter_starts_line (iter));
1919 check_invariants (iter);
1921 if (gtk_text_iter_is_end (iter))
1930 check_invariants (iter);
1938 at_last_indexable_segment (GtkTextRealIter *real)
1940 GtkTextLineSegment *seg;
1942 /* Return TRUE if there are no indexable segments after
1946 seg = real->segment->next;
1949 if (seg->char_count > 0)
1956 /* Goes back to the start of the next segment, even if
1957 * we're not at the start of the current segment (always
1958 * ends up on a different segment if it returns TRUE)
1961 _gtk_text_iter_backward_indexable_segment (GtkTextIter *iter)
1963 /* Move to the start of the previous segment; if no previous
1964 * segment, to the last segment in the previous line. This is
1965 * inherently a bit inefficient due to the singly-linked list and
1966 * tree nodes, but we can't afford the RAM for doubly-linked.
1968 GtkTextRealIter *real;
1969 GtkTextLineSegment *seg;
1970 GtkTextLineSegment *any_seg;
1971 GtkTextLineSegment *prev_seg;
1972 GtkTextLineSegment *prev_any_seg;
1976 g_return_val_if_fail (iter != NULL, FALSE);
1978 real = gtk_text_iter_make_real (iter);
1983 check_invariants (iter);
1985 /* Find first segments in line */
1986 any_seg = real->line->segments;
1988 while (seg->char_count == 0)
1991 if (seg == real->segment)
1993 /* Could probably do this case faster by hand-coding the
1997 /* We were already at the start of a line;
1998 * go back to the previous line.
2000 if (gtk_text_iter_backward_line (iter))
2002 /* Go forward to last indexable segment in line. */
2003 while (!at_last_indexable_segment (real))
2004 _gtk_text_iter_forward_indexable_segment (iter);
2006 check_invariants (iter);
2011 return FALSE; /* We were at the start of the first line. */
2014 /* We must be in the middle of a line; so find the indexable
2015 * segment just before our current segment.
2017 g_assert (seg != real->segment);
2018 while (seg != real->segment)
2021 prev_any_seg = any_seg;
2023 any_seg = seg->next;
2025 while (seg->char_count == 0)
2029 g_assert (prev_seg != NULL);
2030 g_assert (prev_any_seg != NULL);
2031 g_assert (prev_seg->char_count > 0);
2033 /* We skipped the entire previous segment, plus any
2034 * chars we were into the current segment.
2036 if (real->segment_byte_offset >= 0)
2037 bytes_skipped = prev_seg->byte_count + real->segment_byte_offset;
2041 if (real->segment_char_offset >= 0)
2042 chars_skipped = prev_seg->char_count + real->segment_char_offset;
2046 real->segment = prev_seg;
2047 real->any_segment = prev_any_seg;
2048 real->segment_byte_offset = 0;
2049 real->segment_char_offset = 0;
2051 if (bytes_skipped >= 0)
2053 if (real->line_byte_offset >= 0)
2055 real->line_byte_offset -= bytes_skipped;
2056 g_assert (real->line_byte_offset >= 0);
2060 real->line_byte_offset = -1;
2062 if (chars_skipped >= 0)
2064 if (real->line_char_offset >= 0)
2066 real->line_char_offset -= chars_skipped;
2067 g_assert (real->line_char_offset >= 0);
2070 if (real->cached_char_index >= 0)
2072 real->cached_char_index -= chars_skipped;
2073 g_assert (real->cached_char_index >= 0);
2078 real->line_char_offset = -1;
2079 real->cached_char_index = -1;
2082 /* line number is unchanged. */
2084 check_invariants (iter);
2090 * gtk_text_iter_forward_char:
2091 * @iter: an iterator
2093 * Moves @iter forward by one character offset. Note that images
2094 * embedded in the buffer occupy 1 character slot, so
2095 * gtk_text_iter_forward_char () may actually move onto an image instead
2096 * of a character, if you have images in your buffer. If @iter is the
2097 * end iterator or one character before it, @iter will now point at
2098 * the end iterator, and gtk_text_iter_forward_char () returns FALSE for
2099 * convenience when writing loops.
2101 * Return value: whether the new position is the end iterator
2104 gtk_text_iter_forward_char (GtkTextIter *iter)
2106 GtkTextRealIter *real;
2108 g_return_val_if_fail (iter != NULL, FALSE);
2110 real = gtk_text_iter_make_real (iter);
2116 check_invariants (iter);
2117 return forward_char (real);
2122 * gtk_text_iter_backward_char:
2123 * @iter: an iterator
2125 * Moves backward by one character offset. Returns TRUE if movement
2126 * was possible; if @iter was the first in the buffer (character
2127 * offset 0), gtk_text_iter_backward_char () returns FALSE for convenience when
2130 * Return value: whether movement was possible
2133 gtk_text_iter_backward_char (GtkTextIter *iter)
2135 g_return_val_if_fail (iter != NULL, FALSE);
2137 check_invariants (iter);
2139 return gtk_text_iter_backward_chars (iter, 1);
2143 Definitely we should try to linear scan as often as possible for
2144 movement within a single line, because we can't use the BTree to
2145 speed within-line searches up; for movement between lines, we would
2146 like to avoid the linear scan probably.
2148 Instead of using this constant, it might be nice to cache the line
2149 length in the iterator and linear scan if motion is within a single
2152 I guess you'd have to profile the various approaches.
2154 #define MAX_LINEAR_SCAN 150
2158 * gtk_text_iter_forward_chars:
2159 * @iter: an iterator
2160 * @count: number of characters to move, may be negative
2162 * Moves @count characters if possible (if @count would move past the
2163 * start or end of the buffer, moves to the start or end of the
2164 * buffer). The return value indicates whether the new position of
2165 * @iter is different from its original position, and dereferenceable
2166 * (the last iterator in the buffer is not dereferenceable). If @count
2167 * is 0, the function does nothing and returns FALSE.
2169 * Return value: whether @iter moved and is dereferenceable
2172 gtk_text_iter_forward_chars (GtkTextIter *iter, gint count)
2174 GtkTextRealIter *real;
2176 g_return_val_if_fail (iter != NULL, FALSE);
2178 FIX_OVERFLOWS (count);
2180 real = gtk_text_iter_make_real (iter);
2184 else if (count == 0)
2187 return gtk_text_iter_backward_chars (iter, 0 - count);
2188 else if (count < MAX_LINEAR_SCAN)
2190 check_invariants (iter);
2194 if (!forward_char (real))
2199 return forward_char (real);
2203 gint current_char_index;
2204 gint new_char_index;
2206 check_invariants (iter);
2208 current_char_index = gtk_text_iter_get_offset (iter);
2210 if (current_char_index == _gtk_text_btree_char_count (real->tree))
2211 return FALSE; /* can't move forward */
2213 new_char_index = current_char_index + count;
2214 gtk_text_iter_set_offset (iter, new_char_index);
2216 check_invariants (iter);
2218 /* Return FALSE if we're on the non-dereferenceable end
2221 if (gtk_text_iter_is_end (iter))
2229 * gtk_text_iter_backward_chars:
2230 * @iter: an iterator
2231 * @count: number of characters to move
2233 * Moves @count characters backward, if possible (if @count would move
2234 * past the start or end of the buffer, moves to the start or end of
2235 * the buffer). The return value indicates whether the iterator moved
2236 * onto a dereferenceable position; if the iterator didn't move, or
2237 * moved onto the end iterator, then FALSE is returned. If @count is 0,
2238 * the function does nothing and returns FALSE.
2240 * Return value: whether @iter moved and is dereferenceable
2244 gtk_text_iter_backward_chars (GtkTextIter *iter, gint count)
2246 GtkTextRealIter *real;
2248 g_return_val_if_fail (iter != NULL, FALSE);
2250 FIX_OVERFLOWS (count);
2252 real = gtk_text_iter_make_real (iter);
2256 else if (count == 0)
2259 return gtk_text_iter_forward_chars (iter, 0 - count);
2261 ensure_char_offsets (real);
2262 check_invariants (iter);
2264 if (count <= real->segment_char_offset)
2266 /* Optimize the within-segment case */
2267 g_assert (real->segment->char_count > 0);
2268 g_assert (real->segment->type == >k_text_char_type);
2270 real->segment_char_offset -= count;
2271 g_assert (real->segment_char_offset >= 0);
2273 if (real->line_byte_offset >= 0)
2275 gint new_byte_offset;
2278 new_byte_offset = 0;
2280 while (i < real->segment_char_offset)
2282 const char * start = real->segment->body.chars + new_byte_offset;
2283 new_byte_offset += g_utf8_next_char (start) - start;
2288 real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
2289 real->segment_byte_offset = new_byte_offset;
2292 real->line_char_offset -= count;
2294 adjust_char_index (real, 0 - count);
2296 check_invariants (iter);
2302 /* We need to go back into previous segments. For now,
2303 * just keep this really simple. FIXME
2304 * use backward_indexable_segment.
2306 if (TRUE || count > MAX_LINEAR_SCAN)
2308 gint current_char_index;
2309 gint new_char_index;
2311 current_char_index = gtk_text_iter_get_offset (iter);
2313 if (current_char_index == 0)
2314 return FALSE; /* can't move backward */
2316 new_char_index = current_char_index - count;
2317 if (new_char_index < 0)
2319 gtk_text_iter_set_offset (iter, new_char_index);
2321 check_invariants (iter);
2327 /* FIXME backward_indexable_segment here */
2336 /* These two can't be implemented efficiently (always have to use
2337 * a linear scan, since that's the only way to find all the non-text
2342 * gtk_text_iter_forward_text_chars:
2343 * @iter: a #GtkTextIter
2344 * @count: number of chars to move
2346 * Moves forward by @count text characters (pixbufs, widgets,
2347 * etc. do not count as characters for this). Equivalent to moving
2348 * through the results of gtk_text_iter_get_text (), rather than
2349 * gtk_text_iter_get_slice ().
2351 * Return value: whether @iter moved and is dereferenceable
2354 gtk_text_iter_forward_text_chars (GtkTextIter *iter,
2363 * gtk_text_iter_forward_text_chars:
2364 * @iter: a #GtkTextIter
2365 * @count: number of chars to move
2367 * Moves backward by @count text characters (pixbufs, widgets,
2368 * etc. do not count as characters for this). Equivalent to moving
2369 * through the results of gtk_text_iter_get_text (), rather than
2370 * gtk_text_iter_get_slice ().
2372 * Return value: whether @iter moved and is dereferenceable
2375 gtk_text_iter_backward_text_chars (GtkTextIter *iter,
2384 * gtk_text_iter_forward_line:
2385 * @iter: an iterator
2387 * Moves @iter to the start of the next line. Returns TRUE if there
2388 * was a next line to move to, and FALSE if @iter was simply moved to
2389 * the end of the buffer and is now not dereferenceable, or if @iter was
2390 * already at the end of the buffer.
2392 * Return value: whether @iter can be dereferenced
2395 gtk_text_iter_forward_line (GtkTextIter *iter)
2397 GtkTextRealIter *real;
2399 g_return_val_if_fail (iter != NULL, FALSE);
2401 real = gtk_text_iter_make_real (iter);
2406 check_invariants (iter);
2408 if (forward_line_leaving_caches_unmodified (real))
2410 invalidate_char_index (real);
2411 adjust_line_number (real, 1);
2413 check_invariants (iter);
2415 if (gtk_text_iter_is_end (iter))
2422 check_invariants (iter);
2428 * gtk_text_iter_backward_line:
2429 * @iter: an iterator
2431 * Moves @iter to the start of the previous line. Returns TRUE if
2432 * @iter could be moved; i.e. if @iter was at character offset 0, this
2433 * function returns FALSE. Therefore if @iter was already on line 0,
2434 * but not at the start of the line, @iter is snapped to the start of
2435 * the line and the function returns TRUE. (Note that this implies that
2436 * in a loop calling this function, the line number may not change on
2437 * every iteration, if your first iteration is on line 0.)
2439 * Return value: whether @iter moved
2442 gtk_text_iter_backward_line (GtkTextIter *iter)
2444 GtkTextLine *new_line;
2445 GtkTextRealIter *real;
2446 gboolean offset_will_change;
2449 g_return_val_if_fail (iter != NULL, FALSE);
2451 real = gtk_text_iter_make_real (iter);
2456 check_invariants (iter);
2458 new_line = _gtk_text_line_previous (real->line);
2460 offset_will_change = FALSE;
2461 if (real->line_char_offset > 0)
2462 offset_will_change = TRUE;
2464 if (new_line != NULL)
2466 real->line = new_line;
2468 adjust_line_number (real, -1);
2472 if (!offset_will_change)
2476 invalidate_char_index (real);
2478 real->line_byte_offset = 0;
2479 real->line_char_offset = 0;
2481 real->segment_byte_offset = 0;
2482 real->segment_char_offset = 0;
2484 /* Find first segment in line */
2485 real->any_segment = real->line->segments;
2486 real->segment = _gtk_text_line_byte_to_segment (real->line,
2489 g_assert (offset == 0);
2491 /* Note that if we are on the first line, we snap to the start of
2492 * the first line and return TRUE, so TRUE means the iterator
2493 * changed, not that the line changed; this is maybe a bit
2494 * weird. I'm not sure there's an obvious right thing to do though.
2497 check_invariants (iter);
2503 gtk_text_iter_forward_lines (GtkTextIter *iter, gint count)
2505 FIX_OVERFLOWS (count);
2508 return gtk_text_iter_backward_lines (iter, 0 - count);
2509 else if (count == 0)
2511 else if (count == 1)
2513 check_invariants (iter);
2514 return gtk_text_iter_forward_line (iter);
2520 old_line = gtk_text_iter_get_line (iter);
2522 gtk_text_iter_set_line (iter, old_line + count);
2524 check_invariants (iter);
2526 /* return whether it moved, and is dereferenceable. */
2528 (gtk_text_iter_get_line (iter) != old_line) &&
2529 !gtk_text_iter_is_end (iter);
2534 gtk_text_iter_backward_lines (GtkTextIter *iter, gint count)
2536 FIX_OVERFLOWS (count);
2539 return gtk_text_iter_forward_lines (iter, 0 - count);
2540 else if (count == 0)
2542 else if (count == 1)
2544 return gtk_text_iter_backward_line (iter);
2550 old_line = gtk_text_iter_get_line (iter);
2552 gtk_text_iter_set_line (iter, MAX (old_line - count, 0));
2554 return (gtk_text_iter_get_line (iter) != old_line);
2558 typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
2563 gboolean already_moved_initially);
2565 typedef gboolean (* TestLogAttrFunc) (const PangoLogAttr *attrs,
2573 find_word_end_func (const PangoLogAttr *attrs,
2578 gboolean already_moved_initially)
2580 if (!already_moved_initially)
2583 /* Find end of next word */
2584 while (offset < min_offset + len &&
2585 !attrs[offset].is_word_end)
2588 *found_offset = offset;
2590 return offset < min_offset + len;
2594 is_word_end_func (const PangoLogAttr *attrs,
2599 return attrs[offset].is_word_end;
2603 find_word_start_func (const PangoLogAttr *attrs,
2608 gboolean already_moved_initially)
2610 if (!already_moved_initially)
2613 /* Find start of prev word */
2614 while (offset >= min_offset &&
2615 !attrs[offset].is_word_start)
2618 *found_offset = offset;
2620 return offset >= min_offset;
2624 is_word_start_func (const PangoLogAttr *attrs,
2629 return attrs[offset].is_word_start;
2633 inside_word_func (const PangoLogAttr *attrs,
2638 /* Find next word start or end */
2639 while (offset >= min_offset &&
2640 !(attrs[offset].is_word_start || attrs[offset].is_word_end))
2643 return attrs[offset].is_word_start;
2646 /* Sentence funcs */
2649 find_sentence_end_func (const PangoLogAttr *attrs,
2654 gboolean already_moved_initially)
2656 if (!already_moved_initially)
2659 /* Find end of next sentence */
2660 while (offset < min_offset + len &&
2661 !attrs[offset].is_sentence_end)
2664 *found_offset = offset;
2666 return offset < min_offset + len;
2670 is_sentence_end_func (const PangoLogAttr *attrs,
2675 return attrs[offset].is_sentence_end;
2679 find_sentence_start_func (const PangoLogAttr *attrs,
2684 gboolean already_moved_initially)
2686 if (!already_moved_initially)
2689 /* Find start of prev sentence */
2690 while (offset >= min_offset &&
2691 !attrs[offset].is_sentence_start)
2694 *found_offset = offset;
2696 return offset >= min_offset;
2700 is_sentence_start_func (const PangoLogAttr *attrs,
2705 return attrs[offset].is_sentence_start;
2709 inside_sentence_func (const PangoLogAttr *attrs,
2714 /* Find next sentence start or end */
2715 while (offset >= min_offset &&
2716 !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
2719 return attrs[offset].is_sentence_start;
2723 test_log_attrs (const GtkTextIter *iter,
2724 TestLogAttrFunc func)
2727 const PangoLogAttr *attrs;
2729 gboolean result = FALSE;
2731 g_return_val_if_fail (iter != NULL, FALSE);
2733 attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2736 offset = gtk_text_iter_get_line_offset (iter);
2738 g_assert (char_len > 0);
2740 if (offset < char_len)
2741 result = (* func) (attrs, offset, 0, char_len);
2747 find_line_log_attrs (const GtkTextIter *iter,
2748 FindLogAttrFunc func,
2750 gboolean already_moved_initially)
2753 const PangoLogAttr *attrs;
2755 gboolean result = FALSE;
2757 g_return_val_if_fail (iter != NULL, FALSE);
2759 attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2762 offset = gtk_text_iter_get_line_offset (iter);
2764 g_assert (char_len > 0);
2766 if (offset < char_len)
2767 result = (* func) (attrs, offset, 0, char_len, found_offset,
2768 already_moved_initially);
2773 /* FIXME this function is very, very gratuitously slow */
2775 find_by_log_attrs (GtkTextIter *iter,
2776 FindLogAttrFunc func,
2778 gboolean already_moved_initially)
2782 gboolean found = FALSE;
2784 g_return_val_if_fail (iter != NULL, FALSE);
2788 found = find_line_log_attrs (iter, func, &offset, already_moved_initially);
2794 if (gtk_text_iter_forward_line (iter))
2795 return find_by_log_attrs (iter, func, forward,
2802 /* go to end of previous line */
2803 gtk_text_iter_set_line_offset (iter, 0);
2805 if (gtk_text_iter_backward_char (iter))
2806 return find_by_log_attrs (iter, func, forward,
2814 gtk_text_iter_set_line_offset (iter, offset);
2817 !gtk_text_iter_equal (iter, &orig) &&
2818 !gtk_text_iter_is_end (iter);
2823 * gtk_text_iter_forward_word_end:
2824 * @iter: a #GtkTextIter
2826 * Moves forward to the next word end. (If @iter is currently on a
2827 * word end, moves forward to the next one after that.) Word breaks
2828 * are determined by Pango and should be correct for nearly any
2829 * language (if not, the correct fix would be to the Pango word break
2832 * Return value: %TRUE if @iter moved and is not the end iterator
2835 gtk_text_iter_forward_word_end (GtkTextIter *iter)
2837 return find_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
2841 * gtk_text_iter_forward_word_end:
2842 * @iter: a #GtkTextIter
2844 * Moves backward to the next word start. (If @iter is currently on a
2845 * word start, moves backward to the next one after that.) Word breaks
2846 * are determined by Pango and should be correct for nearly any
2847 * language (if not, the correct fix would be to the Pango word break
2850 * Return value: %TRUE if @iter moved and is not the end iterator
2853 gtk_text_iter_backward_word_start (GtkTextIter *iter)
2855 return find_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
2858 /* FIXME a loop around a truly slow function means
2859 * a truly spectacularly slow function.
2863 * gtk_text_iter_forward_word_ends:
2864 * @iter: a #GtkTextIter
2865 * @count: number of times to move
2867 * Calls gtk_text_iter_forward_word_end() up to @count times.
2869 * Return value: %TRUE if @iter moved and is not the end iterator
2872 gtk_text_iter_forward_word_ends (GtkTextIter *iter,
2875 g_return_val_if_fail (iter != NULL, FALSE);
2877 FIX_OVERFLOWS (count);
2883 return gtk_text_iter_backward_word_starts (iter, -count);
2885 if (!gtk_text_iter_forward_word_end (iter))
2891 if (!gtk_text_iter_forward_word_end (iter))
2899 * gtk_text_iter_backward_word_starts
2900 * @iter: a #GtkTextIter
2901 * @count: number of times to move
2903 * Calls gtk_text_iter_backward_word_starts() up to @count times.
2905 * Return value: %TRUE if @iter moved and is not the end iterator
2908 gtk_text_iter_backward_word_starts (GtkTextIter *iter,
2911 g_return_val_if_fail (iter != NULL, FALSE);
2913 FIX_OVERFLOWS (count);
2916 return gtk_text_iter_forward_word_ends (iter, -count);
2918 if (!gtk_text_iter_backward_word_start (iter))
2924 if (!gtk_text_iter_backward_word_start (iter))
2932 * gtk_text_iter_starts_word:
2933 * @iter: a #GtkTextIter
2935 * Determines whether @iter begins a natural-language word. Word
2936 * breaks are determined by Pango and should be correct for nearly any
2937 * language (if not, the correct fix would be to the Pango word break
2940 * Return value: %TRUE if @iter is at the start of a word
2943 gtk_text_iter_starts_word (const GtkTextIter *iter)
2945 return test_log_attrs (iter, is_word_start_func);
2949 * gtk_text_iter_ends_word:
2950 * @iter: a #GtkTextIter
2952 * Determines whether @iter ends a natural-language word. Word breaks
2953 * are determined by Pango and should be correct for nearly any
2954 * language (if not, the correct fix would be to the Pango word break
2957 * Return value: %TRUE if @iter is at the end of a word
2960 gtk_text_iter_ends_word (const GtkTextIter *iter)
2962 return test_log_attrs (iter, is_word_end_func);
2966 * gtk_text_iter_inside_word:
2967 * @iter: a #GtkTextIter
2969 * Determines whether @iter is inside a natural-language word (as
2970 * opposed to say inside some whitespace). Word breaks are determined
2971 * by Pango and should be correct for nearly any language (if not, the
2972 * correct fix would be to the Pango word break algorithms).
2974 * Return value: %TRUE if @iter is inside a word
2977 gtk_text_iter_inside_word (const GtkTextIter *iter)
2979 return test_log_attrs (iter, inside_word_func);
2983 * gtk_text_iter_starts_sentence:
2984 * @iter: a #GtkTextIter
2986 * Determines whether @iter begins a sentence. Sentence boundaries are
2987 * determined by Pango and should be correct for nearly any language
2988 * (if not, the correct fix would be to the Pango text boundary
2991 * Return value: %TRUE if @iter is at the start of a sentence.
2994 gtk_text_iter_starts_sentence (const GtkTextIter *iter)
2996 return test_log_attrs (iter, is_sentence_start_func);
3000 * gtk_text_iter_ends_sentence:
3001 * @iter: a #GtkTextIter
3003 * Determines whether @iter ends a sentence. Sentence boundaries are
3004 * determined by Pango and should be correct for nearly any language
3005 * (if not, the correct fix would be to the Pango text boundary
3008 * Return value: %TRUE if @iter is at the end of a sentence.
3011 gtk_text_iter_ends_sentence (const GtkTextIter *iter)
3013 return test_log_attrs (iter, is_sentence_end_func);
3017 * gtk_text_iter_inside_sentence:
3018 * @iter: a #GtkTextIter
3020 * Determines whether @iter is inside a sentence (as opposed to in
3021 * between two sentences, e.g. after a period and before the first
3022 * letter of the next sentence). Sentence boundaries are determined
3023 * by Pango and should be correct for nearly any language (if not, the
3024 * correct fix would be to the Pango text boundary algorithms).
3026 * Return value: %TRUE if @iter is inside a sentence.
3029 gtk_text_iter_inside_sentence (const GtkTextIter *iter)
3031 return test_log_attrs (iter, inside_sentence_func);
3035 * gtk_text_iter_forward_sentence_end:
3036 * @iter: a #GtkTextIter
3038 * Moves forward to the next sentence end. (If @iter is at the end of
3039 * a sentence, moves to the next end of sentence.) Sentence
3040 * boundaries are determined by Pango and should be correct for nearly
3041 * any language (if not, the correct fix would be to the Pango text
3042 * boundary algorithms).
3044 * Return value: %TRUE if @iter moved and is not the end iterator
3047 gtk_text_iter_forward_sentence_end (GtkTextIter *iter)
3049 return find_by_log_attrs (iter, find_sentence_end_func, TRUE, FALSE);
3053 * gtk_text_iter_backward_sentence_start:
3054 * @iter: a #GtkTextIter
3056 * Moves backward to the next sentence start; if @iter is already at
3057 * the start of a sentence, moves backward to the next one. Sentence
3058 * boundaries are determined by Pango and should be correct for nearly
3059 * any language (if not, the correct fix would be to the Pango text
3060 * boundary algorithms).
3062 * Return value: %TRUE if @iter moved and is not the end iterator
3065 gtk_text_iter_backward_sentence_start (GtkTextIter *iter)
3067 return find_by_log_attrs (iter, find_sentence_start_func, FALSE, FALSE);
3070 /* FIXME a loop around a truly slow function means
3071 * a truly spectacularly slow function.
3074 * gtk_text_iter_forward_sentence_ends:
3075 * @iter: a #GtkTextIter
3076 * @count: number of sentences to move
3078 * Calls gtk_text_iter_forward_sentence_end() up to @count times.
3080 * Return value: %TRUE if @iter moved and is not the end iterator
3083 gtk_text_iter_forward_sentence_ends (GtkTextIter *iter,
3086 g_return_val_if_fail (iter != NULL, FALSE);
3092 return gtk_text_iter_backward_sentence_starts (iter, -count);
3094 if (!gtk_text_iter_forward_sentence_end (iter))
3100 if (!gtk_text_iter_forward_sentence_end (iter))
3108 * gtk_text_iter_forward_sentence_ends:
3109 * @iter: a #GtkTextIter
3110 * @count: number of sentences to move
3112 * Calls gtk_text_iter_backward_sentence_start() up to @count times.
3114 * Return value: %TRUE if @iter moved and is not the end iterator
3117 gtk_text_iter_backward_sentence_starts (GtkTextIter *iter,
3120 g_return_val_if_fail (iter != NULL, FALSE);
3123 return gtk_text_iter_forward_sentence_ends (iter, -count);
3125 if (!gtk_text_iter_backward_sentence_start (iter))
3131 if (!gtk_text_iter_backward_sentence_start (iter))
3139 find_forward_cursor_pos_func (const PangoLogAttr *attrs,
3144 gboolean already_moved_initially)
3146 if (!already_moved_initially)
3149 while (offset < (min_offset + len) &&
3150 !attrs[offset].is_cursor_position)
3153 *found_offset = offset;
3155 return offset < (min_offset + len);
3159 find_backward_cursor_pos_func (const PangoLogAttr *attrs,
3164 gboolean already_moved_initially)
3166 if (!already_moved_initially)
3169 while (offset > min_offset &&
3170 !attrs[offset].is_cursor_position)
3173 *found_offset = offset;
3175 return offset >= min_offset;
3179 is_cursor_pos_func (const PangoLogAttr *attrs,
3184 return attrs[offset].is_cursor_position;
3188 * gtk_text_iter_forward_cursor_position:
3189 * @iter: a #GtkTextIter
3191 * Moves @iter forward by a single cursor position. Cursor positions
3192 * are (unsurprisingly) positions where the cursor can appear. Perhaps
3193 * surprisingly, there may not be a cursor position between all
3194 * characters. The most common example for European languages would be
3195 * a carriage return/newline sequence. For some Unicode characters,
3196 * the equivalent of say the letter "a" with an accent mark will be
3197 * represented as two characters, first the letter then a "combining
3198 * mark" that causes the accent to be rendered; so the cursor can't go
3199 * between those two characters. See also the #PangoLogAttr structure and
3200 * pango_break() function.
3202 * Return value: %TRUE if we moved and the new position is dereferenceable
3205 gtk_text_iter_forward_cursor_position (GtkTextIter *iter)
3207 return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
3211 * gtk_text_iter_backward_cursor_position:
3212 * @iter: a #GtkTextIter
3214 * Like gtk_text_iter_forward_cursor_position(), but moves backward.
3216 * Return value: %TRUE if we moved and the new position is dereferenceable
3219 gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
3221 return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
3225 * gtk_text_iter_forward_cursor_positions:
3226 * @iter: a #GtkTextIter
3227 * @count: number of positions to move
3229 * Moves up to @count cursor positions. See
3230 * gtk_text_iter_forward_cursor_position() for details.
3232 * Return value: %TRUE if we moved and the new position is dereferenceable
3235 gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
3238 g_return_val_if_fail (iter != NULL, FALSE);
3240 FIX_OVERFLOWS (count);
3246 return gtk_text_iter_backward_cursor_positions (iter, -count);
3248 if (!gtk_text_iter_forward_cursor_position (iter))
3254 if (!gtk_text_iter_forward_cursor_position (iter))
3262 * gtk_text_iter_backward_cursor_positions:
3263 * @iter: a #GtkTextIter
3264 * @count: number of positions to move
3266 * Moves up to @count cursor positions. See
3267 * gtk_text_iter_forward_cursor_position() for details.
3269 * Return value: %TRUE if we moved and the new position is dereferenceable
3272 gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
3275 g_return_val_if_fail (iter != NULL, FALSE);
3277 FIX_OVERFLOWS (count);
3283 return gtk_text_iter_forward_cursor_positions (iter, -count);
3285 if (!gtk_text_iter_backward_cursor_position (iter))
3291 if (!gtk_text_iter_backward_cursor_position (iter))
3299 * gtk_text_iter_is_cursor_position:
3300 * @iter: a #GtkTextIter
3302 * See gtk_text_iter_forward_cursor_position() or #PangoLogAttr or
3303 * pango_break() for details on what a cursor position is.
3305 * Return value: %TRUE if the cursor can be placed at @iter
3308 gtk_text_iter_is_cursor_position (const GtkTextIter *iter)
3310 return test_log_attrs (iter, is_cursor_pos_func);
3314 * gtk_text_iter_set_line_offset:
3315 * @iter: a #GtkTextIter
3316 * @char_on_line: a character offset relative to the start of @iter's current line
3318 * Moves @iter within a line, to a new <emphasis>character</emphasis>
3319 * (not byte) offset. The given character offset must be less than or
3320 * equal to the number of characters in the line; if equal, @iter
3321 * moves to the start of the next line. See
3322 * gtk_text_iter_set_line_index() if you have a byte index rather than
3323 * a character offset.
3327 gtk_text_iter_set_line_offset (GtkTextIter *iter,
3330 GtkTextRealIter *real;
3333 g_return_if_fail (iter != NULL);
3335 real = gtk_text_iter_make_surreal (iter);
3340 check_invariants (iter);
3342 chars_in_line = gtk_text_iter_get_chars_in_line (iter);
3344 g_return_if_fail (char_on_line <= chars_in_line);
3346 if (char_on_line < chars_in_line)
3347 iter_set_from_char_offset (real, real->line, char_on_line);
3349 gtk_text_iter_forward_line (iter); /* set to start of next line */
3351 check_invariants (iter);
3355 * gtk_text_iter_set_line_index:
3356 * @iter: a #GtkTextIter
3357 * @byte_on_line: a byte index relative to the start of @iter's current line
3359 * Same as gtk_text_iter_set_line_offset(), but works with a
3360 * <emphasis>byte</emphasis> index. The given byte index must be at
3361 * the start of a character, it can't be in the middle of a UTF-8
3362 * encoded character.
3366 gtk_text_iter_set_line_index (GtkTextIter *iter,
3369 GtkTextRealIter *real;
3372 g_return_if_fail (iter != NULL);
3374 real = gtk_text_iter_make_surreal (iter);
3379 check_invariants (iter);
3381 bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
3383 g_return_if_fail (byte_on_line <= bytes_in_line);
3385 if (byte_on_line < bytes_in_line)
3386 iter_set_from_byte_offset (real, real->line, byte_on_line);
3388 gtk_text_iter_forward_line (iter);
3390 if (real->segment->type == >k_text_char_type &&
3391 (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
3392 g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
3393 "character; this will crash the text buffer. "
3394 "Byte indexes must refer to the start of a character.",
3395 G_STRLOC, byte_on_line);
3397 check_invariants (iter);
3402 * gtk_text_iter_set_visible_line_offset:
3403 * @iter: a #GtkTextIter
3404 * @char_on_line: a character offset
3406 * Like gtk_text_iter_set_line_offset(), but the offset is in visible
3407 * characters, i.e. text with a tag making it invisible is not
3408 * counted in the offset.
3411 gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
3414 gint chars_seen = 0;
3417 g_return_if_fail (iter != NULL);
3421 /* For now we use a ludicrously slow implementation */
3422 while (chars_seen < char_on_line)
3424 if (!_gtk_text_btree_char_is_invisible (&pos))
3427 if (!gtk_text_iter_forward_char (&pos))
3430 if (chars_seen == char_on_line)
3434 if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3437 gtk_text_iter_forward_line (iter);
3441 bytes_in_char (GtkTextIter *iter)
3443 return g_unichar_to_utf8 (gtk_text_iter_get_char (iter), NULL);
3447 * gtk_text_iter_set_visible_line_index:
3448 * @iter: a #GtkTextIter
3449 * @byte_on_line: a byte index
3451 * Like gtk_text_iter_set_line_index(), but the index is in visible
3452 * bytes, i.e. text with a tag making it invisible is not counted
3456 gtk_text_iter_set_visible_line_index (GtkTextIter *iter,
3459 gint bytes_seen = 0;
3462 g_return_if_fail (iter != NULL);
3466 /* For now we use a ludicrously slow implementation */
3467 while (bytes_seen < byte_on_line)
3469 if (!_gtk_text_btree_char_is_invisible (&pos))
3470 bytes_seen += bytes_in_char (&pos);
3472 if (!gtk_text_iter_forward_char (&pos))
3475 if (bytes_seen >= byte_on_line)
3479 if (bytes_seen > byte_on_line)
3480 g_warning ("%s: Incorrect visible byte index %d falls in the middle of a UTF-8 "
3481 "character; this will crash the text buffer. "
3482 "Byte indexes must refer to the start of a character.",
3483 G_STRLOC, byte_on_line);
3485 if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3488 gtk_text_iter_forward_line (iter);
3492 * gtk_text_iter_set_line:
3493 * @iter: a #GtkTextIter
3494 * @line_number: line number (counted from 0)
3496 * Moves iterator @iter to the start of the line @line_number.
3500 gtk_text_iter_set_line (GtkTextIter *iter,
3505 GtkTextRealIter *real;
3507 g_return_if_fail (iter != NULL);
3509 real = gtk_text_iter_make_surreal (iter);
3514 check_invariants (iter);
3516 line = _gtk_text_btree_get_line (real->tree, line_number, &real_line);
3518 iter_set_from_char_offset (real, line, 0);
3520 /* We might as well cache this, since we know it. */
3521 real->cached_line_number = real_line;
3523 check_invariants (iter);
3527 * gtk_text_iter_set_offset:
3528 * @iter: a #GtkTextIter
3529 * @char_offset: a character number
3531 * Sets @iter to point to @char_offset. @char_offset counts from the start
3532 * of the entire text buffer, starting with 0.
3536 gtk_text_iter_set_offset (GtkTextIter *iter,
3540 GtkTextRealIter *real;
3542 gint real_char_index;
3544 g_return_if_fail (iter != NULL);
3546 real = gtk_text_iter_make_surreal (iter);
3551 check_invariants (iter);
3553 if (real->cached_char_index >= 0 &&
3554 real->cached_char_index == char_offset)
3557 line = _gtk_text_btree_get_line_at_char (real->tree,
3562 iter_set_from_char_offset (real, line, real_char_index - line_start);
3564 /* Go ahead and cache this since we have it. */
3565 real->cached_char_index = real_char_index;
3567 check_invariants (iter);
3571 * gtk_text_iter_forward_to_end:
3572 * @iter: a #GtkTextIter
3574 * Moves @iter forward to the "end iterator," which points one past the last
3575 * valid character in the buffer. gtk_text_iter_get_char() called on the
3576 * end iterator returns 0, which is convenient for writing loops.
3580 gtk_text_iter_forward_to_end (GtkTextIter *iter)
3582 GtkTextBuffer *buffer;
3583 GtkTextRealIter *real;
3585 g_return_if_fail (iter != NULL);
3587 real = gtk_text_iter_make_surreal (iter);
3592 buffer = _gtk_text_btree_get_buffer (real->tree);
3594 gtk_text_buffer_get_end_iter (buffer, iter);
3598 * gtk_text_iter_forward_to_line_end:
3599 * @iter: a #GtkTextIter
3601 * Moves the iterator to point to the paragraph delimiter characters,
3602 * which will be either a newline, a carriage return, a carriage
3603 * return/newline in sequence, or the Unicode paragraph separator
3604 * character. If the iterator is already at the paragraph delimiter
3605 * characters, moves to the paragraph delimiter characters for the
3608 * Return value: %TRUE if we moved and the new location is not the end iterator
3611 gtk_text_iter_forward_to_line_end (GtkTextIter *iter)
3613 gint current_offset;
3616 g_return_val_if_fail (iter != NULL, FALSE);
3618 current_offset = gtk_text_iter_get_line_offset (iter);
3619 /* FIXME assumption that line ends in a newline; broken */
3620 new_offset = gtk_text_iter_get_chars_in_line (iter) - 1;
3622 if (current_offset < new_offset)
3624 /* Move to end of this line. */
3625 gtk_text_iter_set_line_offset (iter, new_offset);
3630 /* Move to end of next line. */
3631 if (gtk_text_iter_forward_line (iter))
3633 /* We don't want to move past all
3636 if (!gtk_text_iter_ends_line (iter))
3637 gtk_text_iter_forward_to_line_end (iter);
3646 * gtk_text_iter_forward_to_tag_toggle:
3647 * @iter: a #GtkTextIter
3648 * @tag: a #GtkTextTag, or NULL
3650 * Moves forward to the next toggle (on or off) of the
3651 * #GtkTextTag @tag, or to the next toggle of any tag if
3652 * @tag is NULL. If no matching tag toggles are found,
3653 * returns FALSE, otherwise TRUE. Does not return toggles
3654 * located at @iter, only toggles after @iter. Sets @iter to
3655 * the location of the toggle, or to the end of the buffer
3656 * if no toggle is found.
3658 * Return value: whether we found a tag toggle after @iter
3661 gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
3664 GtkTextLine *next_line;
3665 GtkTextLine *current_line;
3666 GtkTextRealIter *real;
3668 g_return_val_if_fail (iter != NULL, FALSE);
3670 real = gtk_text_iter_make_real (iter);
3675 check_invariants (iter);
3677 current_line = real->line;
3678 next_line = _gtk_text_line_next_could_contain_tag (current_line,
3681 while (_gtk_text_iter_forward_indexable_segment (iter))
3683 /* If we went forward to a line that couldn't contain a toggle
3684 for the tag, then skip forward to a line that could contain
3685 it. This potentially skips huge hunks of the tree, so we
3686 aren't a purely linear search. */
3687 if (real->line != current_line)
3689 if (next_line == NULL)
3691 /* End of search. Set to end of buffer. */
3692 _gtk_text_btree_get_end_iter (real->tree, iter);
3696 if (real->line != next_line)
3697 iter_set_from_byte_offset (real, next_line, 0);
3699 current_line = real->line;
3700 next_line = _gtk_text_line_next_could_contain_tag (current_line,
3705 if (gtk_text_iter_toggles_tag (iter, tag))
3707 /* If there's a toggle here, it isn't indexable so
3708 any_segment can't be the indexable segment. */
3709 g_assert (real->any_segment != real->segment);
3714 /* Check end iterator for tags */
3715 if (gtk_text_iter_toggles_tag (iter, tag))
3717 /* If there's a toggle here, it isn't indexable so
3718 any_segment can't be the indexable segment. */
3719 g_assert (real->any_segment != real->segment);
3723 /* Reached end of buffer */
3728 * gtk_text_iter_backward_to_tag_toggle:
3729 * @iter: a #GtkTextIter
3730 * @tag: a #GtkTextTag, or NULL
3732 * Moves backward to the next toggle (on or off) of the
3733 * #GtkTextTag @tag, or to the next toggle of any tag if
3734 * @tag is NULL. If no matching tag toggles are found,
3735 * returns FALSE, otherwise TRUE. Does not return toggles
3736 * located at @iter, only toggles before @iter. Sets @iter
3737 * to the location of the toggle, or the start of the buffer
3738 * if no toggle is found.
3740 * Return value: whether we found a tag toggle before @iter
3743 gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
3746 GtkTextLine *prev_line;
3747 GtkTextLine *current_line;
3748 GtkTextRealIter *real;
3750 g_return_val_if_fail (iter != NULL, FALSE);
3752 real = gtk_text_iter_make_real (iter);
3757 check_invariants (iter);
3759 current_line = real->line;
3760 prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3764 /* If we're at segment start, go to the previous segment;
3765 * if mid-segment, snap to start of current segment.
3767 if (is_segment_start (real))
3769 if (!_gtk_text_iter_backward_indexable_segment (iter))
3774 ensure_char_offsets (real);
3776 if (!gtk_text_iter_backward_chars (iter, real->segment_char_offset))
3782 /* If we went backward to a line that couldn't contain a toggle
3783 * for the tag, then skip backward further to a line that
3784 * could contain it. This potentially skips huge hunks of the
3785 * tree, so we aren't a purely linear search.
3787 if (real->line != current_line)
3789 if (prev_line == NULL)
3791 /* End of search. Set to start of buffer. */
3792 _gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
3796 if (real->line != prev_line)
3798 /* Set to last segment in prev_line (could do this
3801 iter_set_from_byte_offset (real, prev_line, 0);
3803 while (!at_last_indexable_segment (real))
3804 _gtk_text_iter_forward_indexable_segment (iter);
3807 current_line = real->line;
3808 prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3813 if (gtk_text_iter_toggles_tag (iter, tag))
3815 /* If there's a toggle here, it isn't indexable so
3816 * any_segment can't be the indexable segment.
3818 g_assert (real->any_segment != real->segment);
3822 while (_gtk_text_iter_backward_indexable_segment (iter));
3824 /* Reached front of buffer */
3829 matches_pred (GtkTextIter *iter,
3830 GtkTextCharPredicate pred,
3835 ch = gtk_text_iter_get_char (iter);
3837 return (*pred) (ch, user_data);
3841 * gtk_text_iter_forward_find_char:
3842 * @iter: a #GtkTextIter
3843 * @pred: a function to be called on each character
3844 * @user_data: user data for @pred
3845 * @limit: search limit, or %NULL for none
3847 * Advances @iter, calling @pred on each character. If
3848 * @pred returns %TRUE, returns %TRUE and stops scanning.
3849 * If @pred never returns %TRUE, @iter is set to @limit if
3850 * @limit is non-%NULL, otherwise to the end iterator.
3852 * Return value: whether a match was found
3855 gtk_text_iter_forward_find_char (GtkTextIter *iter,
3856 GtkTextCharPredicate pred,
3858 const GtkTextIter *limit)
3860 g_return_val_if_fail (iter != NULL, FALSE);
3861 g_return_val_if_fail (pred != NULL, FALSE);
3864 gtk_text_iter_compare (iter, limit) >= 0)
3867 while ((limit == NULL ||
3868 !gtk_text_iter_equal (limit, iter)) &&
3869 gtk_text_iter_forward_char (iter))
3871 if (matches_pred (iter, pred, user_data))
3879 * gtk_text_iter_backward_find_char:
3880 * @iter: a #GtkTextIter
3881 * @pred: function to be called on each character
3882 * @user_data: user data for @pred
3883 * @limit: search limit, or %NULL for none
3885 * Same as gtk_text_iter_forward_find_char(), but goes backward from @iter.
3887 * Return value: whether a match was found
3890 gtk_text_iter_backward_find_char (GtkTextIter *iter,
3891 GtkTextCharPredicate pred,
3893 const GtkTextIter *limit)
3895 g_return_val_if_fail (iter != NULL, FALSE);
3896 g_return_val_if_fail (pred != NULL, FALSE);
3899 gtk_text_iter_compare (iter, limit) <= 0)
3902 while ((limit == NULL ||
3903 !gtk_text_iter_equal (limit, iter)) &&
3904 gtk_text_iter_backward_char (iter))
3906 if (matches_pred (iter, pred, user_data))
3914 forward_chars_with_skipping (GtkTextIter *iter,
3916 gboolean skip_invisible,
3917 gboolean skip_nontext)
3922 g_return_if_fail (count >= 0);
3928 gboolean ignored = FALSE;
3931 gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
3936 _gtk_text_btree_char_is_invisible (iter))
3939 gtk_text_iter_forward_char (iter);
3947 lines_match (const GtkTextIter *start,
3948 const gchar **lines,
3949 gboolean visible_only,
3951 GtkTextIter *match_start,
3952 GtkTextIter *match_end)
3959 if (*lines == NULL || **lines == '\0')
3962 *match_start = *start;
3965 *match_end = *start;
3970 gtk_text_iter_forward_line (&next);
3972 /* No more text in buffer, but *lines is nonempty */
3973 if (gtk_text_iter_equal (start, &next))
3981 line_text = gtk_text_iter_get_visible_slice (start, &next);
3983 line_text = gtk_text_iter_get_slice (start, &next);
3988 line_text = gtk_text_iter_get_visible_text (start, &next);
3990 line_text = gtk_text_iter_get_text (start, &next);
3993 if (match_start) /* if this is the first line we're matching */
3994 found = strstr (line_text, *lines);
3997 /* If it's not the first line, we have to match from the
3998 * start of the line.
4000 if (strncmp (line_text, *lines, strlen (*lines)) == 0)
4012 /* Get offset to start of search string */
4013 offset = g_utf8_strlen (line_text, found - line_text);
4017 /* If match start needs to be returned, set it to the
4018 * start of the search string.
4022 *match_start = next;
4024 forward_chars_with_skipping (match_start, offset,
4025 visible_only, !slice);
4028 /* Go to end of search string */
4029 offset += g_utf8_strlen (*lines, -1);
4031 forward_chars_with_skipping (&next, offset,
4032 visible_only, !slice);
4041 /* pass NULL for match_start, since we don't need to find the
4044 return lines_match (&next, lines, visible_only, slice, NULL, match_end);
4047 /* strsplit () that retains the delimiter as part of the string. */
4049 strbreakup (const char *string,
4050 const char *delimiter,
4053 GSList *string_list = NULL, *slist;
4054 gchar **str_array, *s;
4057 g_return_val_if_fail (string != NULL, NULL);
4058 g_return_val_if_fail (delimiter != NULL, NULL);
4061 max_tokens = G_MAXINT;
4063 s = strstr (string, delimiter);
4066 guint delimiter_len = strlen (delimiter);
4073 len = s - string + delimiter_len;
4074 new_string = g_new (gchar, len + 1);
4075 strncpy (new_string, string, len);
4076 new_string[len] = 0;
4077 string_list = g_slist_prepend (string_list, new_string);
4079 string = s + delimiter_len;
4080 s = strstr (string, delimiter);
4082 while (--max_tokens && s);
4087 string_list = g_slist_prepend (string_list, g_strdup (string));
4090 str_array = g_new (gchar*, n);
4094 str_array[i--] = NULL;
4095 for (slist = string_list; slist; slist = slist->next)
4096 str_array[i--] = slist->data;
4098 g_slist_free (string_list);
4104 * gtk_text_iter_forward_search:
4105 * @iter: start of search
4106 * @str: a search string
4107 * @visible_only: if %TRUE, search only visible text
4108 * @slice: if %TRUE, @str contains 0xFFFC when we want to match widgets, pixbufs
4109 * @match_start: return location for start of match, or %NULL
4110 * @match_end: return location for end of match, or %NULL
4111 * @limit: bound for the search, or %NULL for the end of the buffer
4113 * Searches forward for @str. Any match is returned as the range @match_start,
4114 * @match_end. If you specify @visible_only or @slice, the match may have
4115 * invisible text, pixbufs, or child widgets interspersed in @str.
4117 * Return value: whether a match was found
4120 gtk_text_iter_forward_search (const GtkTextIter *iter,
4122 gboolean visible_only,
4124 GtkTextIter *match_start,
4125 GtkTextIter *match_end,
4126 const GtkTextIter *limit)
4128 gchar **lines = NULL;
4130 gboolean retval = FALSE;
4133 g_return_val_if_fail (iter != NULL, FALSE);
4134 g_return_val_if_fail (str != NULL, FALSE);
4137 gtk_text_iter_compare (iter, limit) >= 0)
4142 /* If we can move one char, return the empty string there */
4145 if (gtk_text_iter_forward_char (&match))
4148 gtk_text_iter_equal (&match, limit))
4152 *match_start = match;
4161 /* locate all lines */
4163 lines = strbreakup (str, "\n", -1);
4169 /* This loop has an inefficient worst-case, where
4170 * gtk_text_iter_get_text () is called repeatedly on
4176 gtk_text_iter_compare (&search, limit) >= 0)
4179 if (lines_match (&search, (const gchar**)lines,
4180 visible_only, slice, &match, &end))
4182 if (limit == NULL ||
4184 gtk_text_iter_compare (&end, limit) < 0))
4189 *match_start = match;
4198 while (gtk_text_iter_forward_line (&search));
4200 g_strfreev ((gchar**)lines);
4206 vectors_equal_ignoring_trailing (gchar **vec1,
4209 /* Ignores trailing chars in vec2's last line */
4218 if (strcmp (*i1, *i2) != 0)
4220 if (*(i2 + 1) == NULL) /* if this is the last line */
4222 gint len1 = strlen (*i1);
4223 gint len2 = strlen (*i2);
4226 strncmp (*i1, *i2, len1) == 0)
4228 /* We matched ignoring the trailing stuff in vec2 */
4253 typedef struct _LinesWindow LinesWindow;
4259 GtkTextIter first_line_start;
4260 GtkTextIter first_line_end;
4262 gboolean visible_only;
4266 lines_window_init (LinesWindow *win,
4267 const GtkTextIter *start)
4270 GtkTextIter line_start;
4271 GtkTextIter line_end;
4273 /* If we start on line 1, there are 2 lines to search (0 and 1), so
4276 if (gtk_text_iter_is_start (start) ||
4277 gtk_text_iter_get_line (start) + 1 < win->n_lines)
4279 /* Already at the end, or not enough lines to match */
4280 win->lines = g_new0 (gchar*, 1);
4285 line_start = *start;
4288 /* Move to start iter to start of line */
4289 gtk_text_iter_set_line_offset (&line_start, 0);
4291 if (gtk_text_iter_equal (&line_start, &line_end))
4293 /* we were already at the start; so go back one line */
4294 gtk_text_iter_backward_line (&line_start);
4297 win->first_line_start = line_start;
4298 win->first_line_end = line_end;
4300 win->lines = g_new0 (gchar*, win->n_lines + 1);
4302 i = win->n_lines - 1;
4309 if (win->visible_only)
4310 line_text = gtk_text_iter_get_visible_slice (&line_start, &line_end);
4312 line_text = gtk_text_iter_get_slice (&line_start, &line_end);
4316 if (win->visible_only)
4317 line_text = gtk_text_iter_get_visible_text (&line_start, &line_end);
4319 line_text = gtk_text_iter_get_text (&line_start, &line_end);
4322 win->lines[i] = line_text;
4324 line_end = line_start;
4325 gtk_text_iter_backward_line (&line_start);
4332 lines_window_back (LinesWindow *win)
4334 GtkTextIter new_start;
4337 new_start = win->first_line_start;
4339 if (!gtk_text_iter_backward_line (&new_start))
4343 win->first_line_start = new_start;
4344 win->first_line_end = new_start;
4346 gtk_text_iter_forward_line (&win->first_line_end);
4351 if (win->visible_only)
4352 line_text = gtk_text_iter_get_visible_slice (&win->first_line_start,
4353 &win->first_line_end);
4355 line_text = gtk_text_iter_get_slice (&win->first_line_start,
4356 &win->first_line_end);
4360 if (win->visible_only)
4361 line_text = gtk_text_iter_get_visible_text (&win->first_line_start,
4362 &win->first_line_end);
4364 line_text = gtk_text_iter_get_text (&win->first_line_start,
4365 &win->first_line_end);
4368 /* Move lines to make room for first line. */
4369 g_memmove (win->lines + 1, win->lines, win->n_lines * sizeof (gchar*));
4371 *win->lines = line_text;
4373 /* Free old last line and NULL-terminate */
4374 g_free (win->lines[win->n_lines]);
4375 win->lines[win->n_lines] = NULL;
4381 lines_window_free (LinesWindow *win)
4383 g_strfreev (win->lines);
4387 my_strrstr (const gchar *haystack,
4388 const gchar *needle)
4390 /* FIXME GLib should have a nice implementation in it, this
4394 gint haystack_len = strlen (haystack);
4395 gint needle_len = strlen (needle);
4396 const gchar *needle_end = needle + needle_len;
4397 const gchar *haystack_rend = haystack - 1;
4398 const gchar *needle_rend = needle - 1;
4401 p = haystack + haystack_len;
4402 while (p != haystack)
4404 const gchar *n = needle_end - 1;
4405 const gchar *s = p - 1;
4406 while (s != haystack_rend &&
4414 if (n == needle_rend)
4424 * gtk_text_iter_backward_search:
4425 * @iter: a #GtkTextIter where the search begins
4426 * @str: search string
4427 * @visible_only: if %TRUE search only visible text
4428 * @slice: if %TRUE the search string contains 0xFFFC to match pixbufs, widgets
4429 * @match_start: return location for start of match, or %NULL
4430 * @match_end: return location for end of match, or %NULL
4431 * @limit: location of last possible @match_start, or %NULL for start of buffer
4433 * Same as gtk_text_iter_forward_search(), but moves backward.
4435 * Return value: whether a match was found
4438 gtk_text_iter_backward_search (const GtkTextIter *iter,
4440 gboolean visible_only,
4442 GtkTextIter *match_start,
4443 GtkTextIter *match_end,
4444 const GtkTextIter *limit)
4446 gchar **lines = NULL;
4450 gboolean retval = FALSE;
4452 g_return_val_if_fail (iter != NULL, FALSE);
4453 g_return_val_if_fail (str != NULL, FALSE);
4456 gtk_text_iter_compare (limit, iter) > 0)
4461 /* If we can move one char, return the empty string there */
4462 GtkTextIter match = *iter;
4464 if (limit && gtk_text_iter_equal (limit, &match))
4467 if (gtk_text_iter_backward_char (&match))
4470 *match_start = match;
4479 /* locate all lines */
4481 lines = strbreakup (str, "\n", -1);
4491 win.n_lines = n_lines;
4493 win.visible_only = visible_only;
4495 lines_window_init (&win, iter);
4497 if (*win.lines == NULL)
4502 gchar *first_line_match;
4505 gtk_text_iter_compare (limit, &win.first_line_end) > 0)
4507 /* We're now before the search limit, abort. */
4511 /* If there are multiple lines, the first line will
4512 * end in '\n', so this will only match at the
4513 * end of the first line, which is correct.
4515 first_line_match = my_strrstr (*win.lines, *lines);
4517 if (first_line_match &&
4518 vectors_equal_ignoring_trailing (lines + 1, win.lines + 1))
4523 GtkTextIter start_tmp;
4525 /* Offset to start of search string */
4526 offset = g_utf8_strlen (*win.lines, first_line_match - *win.lines);
4528 next = win.first_line_start;
4530 forward_chars_with_skipping (&start_tmp, offset,
4531 visible_only, !slice);
4534 gtk_text_iter_compare (limit, &start_tmp) > 0)
4535 goto out; /* match was bogus */
4538 *match_start = start_tmp;
4540 /* Go to end of search string */
4544 offset += g_utf8_strlen (*l, -1);
4548 forward_chars_with_skipping (&next, offset,
4549 visible_only, !slice);
4558 while (lines_window_back (&win));
4561 lines_window_free (&win);
4572 * gtk_text_iter_equal:
4573 * @lhs: a #GtkTextIter
4574 * @rhs: another #GtkTextIter
4576 * Tests whether two iterators are equal, using the fastest possible
4577 * mechanism. This function is very fast; you can expect it to perform
4578 * better than e.g. getting the character offset for each iterator and
4579 * comparing the offsets yourself. Also, it's a bit faster than
4580 * gtk_text_iter_compare().
4582 * Return value: %TRUE if the iterators point to the same place in the buffer
4585 gtk_text_iter_equal (const GtkTextIter *lhs,
4586 const GtkTextIter *rhs)
4588 GtkTextRealIter *real_lhs;
4589 GtkTextRealIter *real_rhs;
4591 real_lhs = (GtkTextRealIter*)lhs;
4592 real_rhs = (GtkTextRealIter*)rhs;
4594 check_invariants (lhs);
4595 check_invariants (rhs);
4597 if (real_lhs->line != real_rhs->line)
4599 else if (real_lhs->line_byte_offset >= 0 &&
4600 real_rhs->line_byte_offset >= 0)
4601 return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
4604 /* the ensure_char_offsets () calls do nothing if the char offsets
4605 are already up-to-date. */
4606 ensure_char_offsets (real_lhs);
4607 ensure_char_offsets (real_rhs);
4608 return real_lhs->line_char_offset == real_rhs->line_char_offset;
4613 * gtk_text_iter_compare:
4614 * @lhs: a #GtkTextIter
4615 * @rhs: another #GtkTextIter
4617 * A qsort()-style function that returns negative if @lhs is less than
4618 * @rhs, positive if @lhs is greater than @rhs, and 0 if they're equal.
4619 * Ordering is in character offset order, i.e. the first character in the buffer
4620 * is less than the second character in the buffer.
4622 * Return value: -1 if @lhs is less than @rhs, 1 if @lhs is greater, 0 if they are equal
4625 gtk_text_iter_compare (const GtkTextIter *lhs,
4626 const GtkTextIter *rhs)
4628 GtkTextRealIter *real_lhs;
4629 GtkTextRealIter *real_rhs;
4631 real_lhs = gtk_text_iter_make_surreal (lhs);
4632 real_rhs = gtk_text_iter_make_surreal (rhs);
4634 if (real_lhs == NULL ||
4636 return -1; /* why not */
4638 check_invariants (lhs);
4639 check_invariants (rhs);
4641 if (real_lhs->line == real_rhs->line)
4643 gint left_index, right_index;
4645 if (real_lhs->line_byte_offset >= 0 &&
4646 real_rhs->line_byte_offset >= 0)
4648 left_index = real_lhs->line_byte_offset;
4649 right_index = real_rhs->line_byte_offset;
4653 /* the ensure_char_offsets () calls do nothing if
4654 the offsets are already up-to-date. */
4655 ensure_char_offsets (real_lhs);
4656 ensure_char_offsets (real_rhs);
4657 left_index = real_lhs->line_char_offset;
4658 right_index = real_rhs->line_char_offset;
4661 if (left_index < right_index)
4663 else if (left_index > right_index)
4672 line1 = gtk_text_iter_get_line (lhs);
4673 line2 = gtk_text_iter_get_line (rhs);
4676 else if (line1 > line2)
4684 * gtk_text_iter_in_range:
4685 * @iter: a #GtkTextIter
4686 * @start: start of range
4687 * @end: end of range
4689 * @start and @end must be in order, unlike most text buffer
4690 * functions, for efficiency reasons. The function returns %TRUE if
4691 * @iter falls in the range [@start, @end).
4693 * Return value: %TRUE if @iter is in the range
4696 gtk_text_iter_in_range (const GtkTextIter *iter,
4697 const GtkTextIter *start,
4698 const GtkTextIter *end)
4700 return gtk_text_iter_compare (iter, start) >= 0 &&
4701 gtk_text_iter_compare (iter, end) < 0;
4705 * gtk_text_iter_order:
4706 * @first: a #GtkTextIter
4707 * @second: another #GtkTextIter
4709 * Swaps the value of @first and @second if @second comes before
4710 * @first in the buffer. That is, ensures that @first and @second are
4711 * in sequence. Most text buffer functions that take a range call this
4712 * automatically on your behalf, so there's no real reason to call it yourself
4713 * in those cases. There are some exceptions, such as gtk_text_iter_in_range(),
4714 * that expect a pre-sorted range.
4718 gtk_text_iter_order (GtkTextIter *first,
4719 GtkTextIter *second)
4721 g_return_if_fail (first != NULL);
4722 g_return_if_fail (second != NULL);
4724 if (gtk_text_iter_compare (first, second) > 0)
4735 * Init iterators from the BTree
4739 _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
4743 GtkTextRealIter *real = (GtkTextRealIter*)iter;
4744 gint real_char_index;
4748 g_return_if_fail (iter != NULL);
4749 g_return_if_fail (tree != NULL);
4751 line = _gtk_text_btree_get_line_at_char (tree, char_index,
4752 &line_start, &real_char_index);
4754 iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
4756 real->cached_char_index = real_char_index;
4758 check_invariants (iter);
4762 _gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
4767 GtkTextRealIter *real = (GtkTextRealIter*)iter;
4771 g_return_if_fail (iter != NULL);
4772 g_return_if_fail (tree != NULL);
4774 line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4776 iter_init_from_char_offset (iter, tree, line, char_on_line);
4778 /* We might as well cache this, since we know it. */
4779 real->cached_line_number = real_line;
4781 check_invariants (iter);
4785 _gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree,
4790 GtkTextRealIter *real = (GtkTextRealIter*)iter;
4794 g_return_if_fail (iter != NULL);
4795 g_return_if_fail (tree != NULL);
4797 line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4799 iter_init_from_byte_offset (iter, tree, line, byte_index);
4801 /* We might as well cache this, since we know it. */
4802 real->cached_line_number = real_line;
4804 check_invariants (iter);
4808 _gtk_text_btree_get_iter_at_line (GtkTextBTree *tree,
4813 g_return_if_fail (iter != NULL);
4814 g_return_if_fail (tree != NULL);
4815 g_return_if_fail (line != NULL);
4817 iter_init_from_byte_offset (iter, tree, line, byte_offset);
4819 check_invariants (iter);
4823 _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree,
4829 g_return_val_if_fail (iter != NULL, FALSE);
4830 g_return_val_if_fail (tree != NULL, FALSE);
4832 line = _gtk_text_btree_first_could_contain_tag (tree, tag);
4836 /* Set iter to last in tree */
4837 _gtk_text_btree_get_end_iter (tree, iter);
4838 check_invariants (iter);
4843 iter_init_from_byte_offset (iter, tree, line, 0);
4844 gtk_text_iter_forward_to_tag_toggle (iter, tag);
4845 check_invariants (iter);
4851 _gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree,
4855 g_return_val_if_fail (iter != NULL, FALSE);
4856 g_return_val_if_fail (tree != NULL, FALSE);
4858 _gtk_text_btree_get_end_iter (tree, iter);
4859 gtk_text_iter_backward_to_tag_toggle (iter, tag);
4860 check_invariants (iter);
4866 _gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
4868 const gchar *mark_name)
4872 g_return_val_if_fail (iter != NULL, FALSE);
4873 g_return_val_if_fail (tree != NULL, FALSE);
4875 mark = _gtk_text_btree_get_mark_by_name (tree, mark_name);
4881 _gtk_text_btree_get_iter_at_mark (tree, iter, mark);
4882 check_invariants (iter);
4888 _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
4892 GtkTextLineSegment *seg;
4894 g_return_if_fail (iter != NULL);
4895 g_return_if_fail (tree != NULL);
4896 g_return_if_fail (GTK_IS_TEXT_MARK (mark));
4898 seg = mark->segment;
4900 iter_init_from_segment (iter, tree,
4901 seg->body.mark.line, seg);
4902 g_assert (seg->body.mark.line == _gtk_text_iter_get_text_line (iter));
4903 check_invariants (iter);
4907 _gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree *tree,
4909 GtkTextChildAnchor *anchor)
4911 GtkTextLineSegment *seg;
4913 g_return_if_fail (iter != NULL);
4914 g_return_if_fail (tree != NULL);
4915 g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
4917 seg = anchor->segment;
4919 iter_init_from_segment (iter, tree,
4920 seg->body.child.line, seg);
4921 g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter));
4922 check_invariants (iter);
4926 _gtk_text_btree_get_end_iter (GtkTextBTree *tree,
4929 g_return_if_fail (iter != NULL);
4930 g_return_if_fail (tree != NULL);
4932 _gtk_text_btree_get_iter_at_char (tree,
4934 _gtk_text_btree_char_count (tree));
4935 check_invariants (iter);
4939 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
4941 GtkTextRealIter *real = (GtkTextRealIter*)iter;
4943 g_return_if_fail (iter != NULL);
4945 if (real->chars_changed_stamp != _gtk_text_btree_get_chars_changed_stamp (real->tree))
4946 g_print (" %20s: <invalidated iterator>\n", desc);
4949 check_invariants (iter);
4950 g_print (" %20s: line %d / char %d / line char %d / line byte %d\n",
4952 gtk_text_iter_get_line (iter),
4953 gtk_text_iter_get_offset (iter),
4954 gtk_text_iter_get_line_offset (iter),
4955 gtk_text_iter_get_line_index (iter));
4956 check_invariants (iter);
4961 _gtk_text_iter_check (const GtkTextIter *iter)
4963 const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
4964 gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
4965 GtkTextLineSegment *byte_segment = NULL;
4966 GtkTextLineSegment *byte_any_segment = NULL;
4967 GtkTextLineSegment *char_segment = NULL;
4968 GtkTextLineSegment *char_any_segment = NULL;
4969 gboolean segments_updated;
4971 /* This function checks our class invariants for the Iter class. */
4973 g_assert (sizeof (GtkTextIter) == sizeof (GtkTextRealIter));
4975 if (real->chars_changed_stamp !=
4976 _gtk_text_btree_get_chars_changed_stamp (real->tree))
4977 g_error ("iterator check failed: invalid iterator");
4979 if (real->line_char_offset < 0 && real->line_byte_offset < 0)
4980 g_error ("iterator check failed: both char and byte offsets are invalid");
4982 segments_updated = (real->segments_changed_stamp ==
4983 _gtk_text_btree_get_segments_changed_stamp (real->tree));
4986 printf ("checking iter, segments %s updated, byte %d char %d\n",
4987 segments_updated ? "are" : "aren't",
4988 real->line_byte_offset,
4989 real->line_char_offset);
4992 if (segments_updated)
4994 if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
4995 g_error ("iterator check failed: both char and byte segment offsets are invalid");
4997 if (real->segment->char_count == 0)
4998 g_error ("iterator check failed: segment is not indexable.");
5000 if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
5001 g_error ("segment char offset is not properly up-to-date");
5003 if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
5004 g_error ("segment byte offset is not properly up-to-date");
5006 if (real->segment_byte_offset >= 0 &&
5007 real->segment_byte_offset >= real->segment->byte_count)
5008 g_error ("segment byte offset is too large.");
5010 if (real->segment_char_offset >= 0 &&
5011 real->segment_char_offset >= real->segment->char_count)
5012 g_error ("segment char offset is too large.");
5015 if (real->line_byte_offset >= 0)
5017 _gtk_text_line_byte_locate (real->line, real->line_byte_offset,
5018 &byte_segment, &byte_any_segment,
5019 &seg_byte_offset, &line_byte_offset);
5021 if (line_byte_offset != real->line_byte_offset)
5022 g_error ("wrong byte offset was stored in iterator");
5024 if (segments_updated)
5026 if (real->segment != byte_segment)
5027 g_error ("wrong segment was stored in iterator");
5029 if (real->any_segment != byte_any_segment)
5030 g_error ("wrong any_segment was stored in iterator");
5032 if (seg_byte_offset != real->segment_byte_offset)
5033 g_error ("wrong segment byte offset was stored in iterator");
5035 if (byte_segment->type == >k_text_char_type)
5038 p = byte_segment->body.chars + seg_byte_offset;
5040 if (!gtk_text_byte_begins_utf8_char (p))
5041 g_error ("broken iterator byte index pointed into the middle of a character");
5046 if (real->line_char_offset >= 0)
5048 _gtk_text_line_char_locate (real->line, real->line_char_offset,
5049 &char_segment, &char_any_segment,
5050 &seg_char_offset, &line_char_offset);
5052 if (line_char_offset != real->line_char_offset)
5053 g_error ("wrong char offset was stored in iterator");
5055 if (segments_updated)
5057 if (real->segment != char_segment)
5058 g_error ("wrong segment was stored in iterator");
5060 if (real->any_segment != char_any_segment)
5061 g_error ("wrong any_segment was stored in iterator");
5063 if (seg_char_offset != real->segment_char_offset)
5064 g_error ("wrong segment char offset was stored in iterator");
5066 if (char_segment->type == >k_text_char_type)
5069 p = g_utf8_offset_to_pointer (char_segment->body.chars,
5072 /* hmm, not likely to happen eh */
5073 if (!gtk_text_byte_begins_utf8_char (p))
5074 g_error ("broken iterator char offset pointed into the middle of a character");
5079 if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
5081 if (byte_segment != char_segment)
5082 g_error ("char and byte offsets did not point to the same segment");
5084 if (byte_any_segment != char_any_segment)
5085 g_error ("char and byte offsets did not point to the same any segment");
5087 /* Make sure the segment offsets are equivalent, if it's a char
5089 if (char_segment->type == >k_text_char_type)
5091 gint byte_offset = 0;
5092 gint char_offset = 0;
5093 while (char_offset < seg_char_offset)
5095 const char * start = char_segment->body.chars + byte_offset;
5096 byte_offset += g_utf8_next_char (start) - start;
5100 if (byte_offset != seg_byte_offset)
5101 g_error ("byte offset did not correspond to char offset");
5104 g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
5106 if (char_offset != seg_char_offset)
5107 g_error ("char offset did not correspond to byte offset");
5109 if (!gtk_text_byte_begins_utf8_char (char_segment->body.chars + seg_byte_offset))
5110 g_error ("byte index for iterator does not index the start of a character");
5114 if (real->cached_line_number >= 0)
5118 should_be = _gtk_text_line_get_number (real->line);
5119 if (real->cached_line_number != should_be)
5120 g_error ("wrong line number was cached");
5123 if (real->cached_char_index >= 0)
5125 if (real->line_char_offset >= 0) /* only way we can check it
5126 efficiently, not a real
5131 char_index = _gtk_text_line_char_index (real->line);
5132 char_index += real->line_char_offset;
5134 if (real->cached_char_index != char_index)
5135 g_error ("wrong char index was cached");