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