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