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 == &gtk_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 == &gtk_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 != &gtk_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 != &gtk_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 == &gtk_text_left_mark_type ||
1060           seg->type == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_text_toggle_off_type ||
1253             seg->type == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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 == &gtk_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