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