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