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