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