]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
use gtk_text_iter_can_insert
[~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_iter_is_end (iter));
2007           
2008           check_invariants (iter);
2009
2010           return FALSE;
2011         }
2012     }
2013 }
2014
2015 static gboolean
2016 at_last_indexable_segment (GtkTextRealIter *real)
2017 {
2018   GtkTextLineSegment *seg;
2019
2020   /* Return TRUE if there are no indexable segments after
2021    * this iterator.
2022    */
2023
2024   seg = real->segment->next;
2025   while (seg)
2026     {
2027       if (seg->char_count > 0)
2028         return FALSE;
2029       seg = seg->next;
2030     }
2031   return TRUE;
2032 }
2033
2034 /* Goes back to the start of the next segment, even if
2035  * we're not at the start of the current segment (always
2036  * ends up on a different segment if it returns TRUE)
2037  */
2038 gboolean
2039 _gtk_text_iter_backward_indexable_segment (GtkTextIter *iter)
2040 {
2041   /* Move to the start of the previous segment; if no previous
2042    * segment, to the last segment in the previous line. This is
2043    * inherently a bit inefficient due to the singly-linked list and
2044    * tree nodes, but we can't afford the RAM for doubly-linked.
2045    */
2046   GtkTextRealIter *real;
2047   GtkTextLineSegment *seg;
2048   GtkTextLineSegment *any_seg;
2049   GtkTextLineSegment *prev_seg;
2050   GtkTextLineSegment *prev_any_seg;
2051   gint bytes_skipped;
2052   gint chars_skipped;
2053
2054   g_return_val_if_fail (iter != NULL, FALSE);
2055
2056   real = gtk_text_iter_make_real (iter);
2057
2058   if (real == NULL)
2059     return FALSE;
2060
2061   check_invariants (iter);
2062
2063   /* Find first segments in line */
2064   any_seg = real->line->segments;
2065   seg = any_seg;
2066   while (seg->char_count == 0)
2067     seg = seg->next;
2068
2069   if (seg == real->segment)
2070     {
2071       /* Could probably do this case faster by hand-coding the
2072        * iteration.
2073        */
2074
2075       /* We were already at the start of a line;
2076        * go back to the previous line.
2077        */
2078       if (gtk_text_iter_backward_line (iter))
2079         {
2080           /* Go forward to last indexable segment in line. */
2081           while (!at_last_indexable_segment (real))
2082             _gtk_text_iter_forward_indexable_segment (iter);
2083
2084           check_invariants (iter);
2085
2086           return TRUE;
2087         }
2088       else
2089         return FALSE; /* We were at the start of the first line. */
2090     }
2091
2092   /* We must be in the middle of a line; so find the indexable
2093    * segment just before our current segment.
2094    */
2095   g_assert (seg != real->segment);
2096   while (seg != real->segment)
2097     {
2098       prev_seg = seg;
2099       prev_any_seg = any_seg;
2100
2101       any_seg = seg->next;
2102       seg = any_seg;
2103       while (seg->char_count == 0)
2104         seg = seg->next;
2105     }
2106
2107   g_assert (prev_seg != NULL);
2108   g_assert (prev_any_seg != NULL);
2109   g_assert (prev_seg->char_count > 0);
2110
2111   /* We skipped the entire previous segment, plus any
2112    * chars we were into the current segment.
2113    */
2114   if (real->segment_byte_offset >= 0)
2115     bytes_skipped = prev_seg->byte_count + real->segment_byte_offset;
2116   else
2117     bytes_skipped = -1;
2118
2119   if (real->segment_char_offset >= 0)
2120     chars_skipped = prev_seg->char_count + real->segment_char_offset;
2121   else
2122     chars_skipped = -1;
2123
2124   real->segment = prev_seg;
2125   real->any_segment = prev_any_seg;
2126   real->segment_byte_offset = 0;
2127   real->segment_char_offset = 0;
2128
2129   if (bytes_skipped >= 0)
2130     {
2131       if (real->line_byte_offset >= 0)
2132         {
2133           real->line_byte_offset -= bytes_skipped;
2134           g_assert (real->line_byte_offset >= 0);
2135         }
2136     }
2137   else
2138     real->line_byte_offset = -1;
2139
2140   if (chars_skipped >= 0)
2141     {
2142       if (real->line_char_offset >= 0)
2143         {
2144           real->line_char_offset -= chars_skipped;
2145           g_assert (real->line_char_offset >= 0);
2146         }
2147
2148       if (real->cached_char_index >= 0)
2149         {
2150           real->cached_char_index -= chars_skipped;
2151           g_assert (real->cached_char_index >= 0);
2152         }
2153     }
2154   else
2155     {
2156       real->line_char_offset = -1;
2157       real->cached_char_index = -1;
2158     }
2159
2160   /* line number is unchanged. */
2161
2162   check_invariants (iter);
2163
2164   return TRUE;
2165 }
2166
2167 /**
2168  * gtk_text_iter_forward_char:
2169  * @iter: an iterator
2170  *
2171  * Moves @iter forward by one character offset. Note that images
2172  * embedded in the buffer occupy 1 character slot, so
2173  * gtk_text_iter_forward_char () may actually move onto an image instead
2174  * of a character, if you have images in your buffer.  If @iter is the
2175  * end iterator or one character before it, @iter will now point at
2176  * the end iterator, and gtk_text_iter_forward_char () returns FALSE for
2177  * convenience when writing loops.
2178  *
2179  * Return value: whether the new position is the end iterator
2180  **/
2181 gboolean
2182 gtk_text_iter_forward_char (GtkTextIter *iter)
2183 {
2184   GtkTextRealIter *real;
2185
2186   g_return_val_if_fail (iter != NULL, FALSE);
2187
2188   real = gtk_text_iter_make_real (iter);
2189
2190   if (real == NULL)
2191     return FALSE;
2192   else
2193     {
2194       check_invariants (iter);
2195       return forward_char (real);
2196     }
2197 }
2198
2199 /**
2200  * gtk_text_iter_backward_char:
2201  * @iter: an iterator
2202  *
2203  * Moves backward by one character offset. Returns TRUE if movement
2204  * was possible; if @iter was the first in the buffer (character
2205  * offset 0), gtk_text_iter_backward_char () returns FALSE for convenience when
2206  * writing loops.
2207  *
2208  * Return value: whether movement was possible
2209  **/
2210 gboolean
2211 gtk_text_iter_backward_char (GtkTextIter *iter)
2212 {
2213   g_return_val_if_fail (iter != NULL, FALSE);
2214
2215   check_invariants (iter);
2216
2217   return gtk_text_iter_backward_chars (iter, 1);
2218 }
2219
2220 /*
2221   Definitely we should try to linear scan as often as possible for
2222   movement within a single line, because we can't use the BTree to
2223   speed within-line searches up; for movement between lines, we would
2224   like to avoid the linear scan probably.
2225
2226   Instead of using this constant, it might be nice to cache the line
2227   length in the iterator and linear scan if motion is within a single
2228   line.
2229
2230   I guess you'd have to profile the various approaches.
2231 */
2232 #define MAX_LINEAR_SCAN 150
2233
2234
2235 /**
2236  * gtk_text_iter_forward_chars:
2237  * @iter: an iterator
2238  * @count: number of characters to move, may be negative
2239  *
2240  * Moves @count characters if possible (if @count would move past the
2241  * start or end of the buffer, moves to the start or end of the
2242  * buffer). The return value indicates whether the new position of
2243  * @iter is different from its original position, and dereferenceable
2244  * (the last iterator in the buffer is not dereferenceable). If @count
2245  * is 0, the function does nothing and returns FALSE.
2246  *
2247  * Return value: whether @iter moved and is dereferenceable
2248  **/
2249 gboolean
2250 gtk_text_iter_forward_chars (GtkTextIter *iter, gint count)
2251 {
2252   GtkTextRealIter *real;
2253
2254   g_return_val_if_fail (iter != NULL, FALSE);
2255
2256   FIX_OVERFLOWS (count);
2257   
2258   real = gtk_text_iter_make_real (iter);
2259
2260   if (real == NULL)
2261     return FALSE;
2262   else if (count == 0)
2263     return FALSE;
2264   else if (count < 0)
2265     return gtk_text_iter_backward_chars (iter, 0 - count);
2266   else if (count < MAX_LINEAR_SCAN)
2267     {
2268       check_invariants (iter);
2269
2270       while (count > 1)
2271         {
2272           if (!forward_char (real))
2273             return FALSE;
2274           --count;
2275         }
2276
2277       return forward_char (real);
2278     }
2279   else
2280     {
2281       gint current_char_index;
2282       gint new_char_index;
2283
2284       check_invariants (iter);
2285
2286       current_char_index = gtk_text_iter_get_offset (iter);
2287
2288       if (current_char_index == _gtk_text_btree_char_count (real->tree))
2289         return FALSE; /* can't move forward */
2290
2291       new_char_index = current_char_index + count;
2292       gtk_text_iter_set_offset (iter, new_char_index);
2293
2294       check_invariants (iter);
2295
2296       /* Return FALSE if we're on the non-dereferenceable end
2297        * iterator.
2298        */
2299       if (gtk_text_iter_is_end (iter))
2300         return FALSE;
2301       else
2302         return TRUE;
2303     }
2304 }
2305
2306 /**
2307  * gtk_text_iter_backward_chars:
2308  * @iter: an iterator
2309  * @count: number of characters to move
2310  *
2311  * Moves @count characters backward, if possible (if @count would move
2312  * past the start or end of the buffer, moves to the start or end of
2313  * the buffer).  The return value indicates whether the iterator moved
2314  * onto a dereferenceable position; if the iterator didn't move, or
2315  * moved onto the end iterator, then FALSE is returned. If @count is 0,
2316  * the function does nothing and returns FALSE.
2317  *
2318  * Return value: whether @iter moved and is dereferenceable
2319  *
2320  **/
2321 gboolean
2322 gtk_text_iter_backward_chars (GtkTextIter *iter, gint count)
2323 {
2324   GtkTextRealIter *real;
2325
2326   g_return_val_if_fail (iter != NULL, FALSE);
2327
2328   FIX_OVERFLOWS (count);
2329   
2330   real = gtk_text_iter_make_real (iter);
2331
2332   if (real == NULL)
2333     return FALSE;
2334   else if (count == 0)
2335     return FALSE;
2336   else if (count < 0)
2337     return gtk_text_iter_forward_chars (iter, 0 - count);
2338
2339   ensure_char_offsets (real);
2340   check_invariants (iter);
2341
2342   if (count <= real->segment_char_offset)
2343     {
2344       /* Optimize the within-segment case */
2345       g_assert (real->segment->char_count > 0);
2346       g_assert (real->segment->type == &gtk_text_char_type);
2347
2348       real->segment_char_offset -= count;
2349       g_assert (real->segment_char_offset >= 0);
2350
2351       if (real->line_byte_offset >= 0)
2352         {
2353           gint new_byte_offset;
2354           gint i;
2355
2356           new_byte_offset = 0;
2357           i = 0;
2358           while (i < real->segment_char_offset)
2359             {
2360               const char * start = real->segment->body.chars + new_byte_offset;
2361               new_byte_offset += g_utf8_next_char (start) - start;
2362
2363               ++i;
2364             }
2365
2366           real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
2367           real->segment_byte_offset = new_byte_offset;
2368         }
2369
2370       real->line_char_offset -= count;
2371
2372       adjust_char_index (real, 0 - count);
2373
2374       check_invariants (iter);
2375
2376       return TRUE;
2377     }
2378   else
2379     {
2380       /* We need to go back into previous segments. For now,
2381        * just keep this really simple. FIXME
2382        * use backward_indexable_segment.
2383        */
2384       if (TRUE || count > MAX_LINEAR_SCAN)
2385         {
2386           gint current_char_index;
2387           gint new_char_index;
2388
2389           current_char_index = gtk_text_iter_get_offset (iter);
2390
2391           if (current_char_index == 0)
2392             return FALSE; /* can't move backward */
2393
2394           new_char_index = current_char_index - count;
2395           if (new_char_index < 0)
2396             new_char_index = 0;
2397           gtk_text_iter_set_offset (iter, new_char_index);
2398
2399           check_invariants (iter);
2400
2401           return TRUE;
2402         }
2403       else
2404         {
2405           /* FIXME backward_indexable_segment here */
2406
2407           return FALSE;
2408         }
2409     }
2410 }
2411
2412 #if 0
2413
2414 /* These two can't be implemented efficiently (always have to use
2415  * a linear scan, since that's the only way to find all the non-text
2416  * segments)
2417  */
2418
2419 /**
2420  * gtk_text_iter_forward_text_chars:
2421  * @iter: a #GtkTextIter
2422  * @count: number of chars to move
2423  *
2424  * Moves forward by @count text characters (pixbufs, widgets,
2425  * etc. do not count as characters for this). Equivalent to moving
2426  * through the results of gtk_text_iter_get_text (), rather than
2427  * gtk_text_iter_get_slice ().
2428  *
2429  * Return value: whether @iter moved and is dereferenceable
2430  **/
2431 gboolean
2432 gtk_text_iter_forward_text_chars  (GtkTextIter *iter,
2433                                    gint         count)
2434 {
2435
2436
2437
2438 }
2439
2440 /**
2441  * gtk_text_iter_forward_text_chars:
2442  * @iter: a #GtkTextIter
2443  * @count: number of chars to move
2444  *
2445  * Moves backward by @count text characters (pixbufs, widgets,
2446  * etc. do not count as characters for this). Equivalent to moving
2447  * through the results of gtk_text_iter_get_text (), rather than
2448  * gtk_text_iter_get_slice ().
2449  *
2450  * Return value: whether @iter moved and is dereferenceable
2451  **/
2452 gboolean
2453 gtk_text_iter_backward_text_chars (GtkTextIter *iter,
2454                                    gint         count)
2455 {
2456
2457
2458 }
2459 #endif
2460
2461 /**
2462  * gtk_text_iter_forward_line:
2463  * @iter: an iterator
2464  *
2465  * Moves @iter to the start of the next line. Returns TRUE if there
2466  * was a next line to move to, and FALSE if @iter was simply moved to
2467  * the end of the buffer and is now not dereferenceable, or if @iter was
2468  * already at the end of the buffer.
2469  *
2470  * Return value: whether @iter can be dereferenced
2471  **/
2472 gboolean
2473 gtk_text_iter_forward_line (GtkTextIter *iter)
2474 {
2475   GtkTextRealIter *real;
2476
2477   g_return_val_if_fail (iter != NULL, FALSE);
2478   
2479   real = gtk_text_iter_make_real (iter);
2480
2481   if (real == NULL)
2482     return FALSE;
2483
2484   check_invariants (iter);
2485
2486   if (forward_line_leaving_caches_unmodified (real))
2487     {
2488       invalidate_char_index (real);
2489       adjust_line_number (real, 1);
2490
2491       check_invariants (iter);
2492
2493       if (gtk_text_iter_is_end (iter))
2494         return FALSE;
2495       else
2496         return TRUE;
2497     }
2498   else
2499     {
2500       /* On the last line, move to end of it */
2501       
2502       if (!gtk_text_iter_is_end (iter))
2503         gtk_text_iter_forward_to_end (iter);
2504       
2505       check_invariants (iter);
2506       return FALSE;
2507     }
2508 }
2509
2510 /**
2511  * gtk_text_iter_backward_line:
2512  * @iter: an iterator
2513  *
2514  * Moves @iter to the start of the previous line. Returns TRUE if
2515  * @iter could be moved; i.e. if @iter was at character offset 0, this
2516  * function returns FALSE. Therefore if @iter was already on line 0,
2517  * but not at the start of the line, @iter is snapped to the start of
2518  * the line and the function returns TRUE. (Note that this implies that
2519  * in a loop calling this function, the line number may not change on
2520  * every iteration, if your first iteration is on line 0.)
2521  *
2522  * Return value: whether @iter moved
2523  **/
2524 gboolean
2525 gtk_text_iter_backward_line (GtkTextIter *iter)
2526 {
2527   GtkTextLine *new_line;
2528   GtkTextRealIter *real;
2529   gboolean offset_will_change;
2530   gint offset;
2531
2532   g_return_val_if_fail (iter != NULL, FALSE);
2533
2534   real = gtk_text_iter_make_real (iter);
2535
2536   if (real == NULL)
2537     return FALSE;
2538
2539   check_invariants (iter);
2540
2541   new_line = _gtk_text_line_previous (real->line);
2542
2543   offset_will_change = FALSE;
2544   if (real->line_char_offset > 0)
2545     offset_will_change = TRUE;
2546
2547   if (new_line != NULL)
2548     {
2549       real->line = new_line;
2550
2551       adjust_line_number (real, -1);
2552     }
2553   else
2554     {
2555       if (!offset_will_change)
2556         return FALSE;
2557     }
2558
2559   invalidate_char_index (real);
2560
2561   real->line_byte_offset = 0;
2562   real->line_char_offset = 0;
2563
2564   real->segment_byte_offset = 0;
2565   real->segment_char_offset = 0;
2566
2567   /* Find first segment in line */
2568   real->any_segment = real->line->segments;
2569   real->segment = _gtk_text_line_byte_to_segment (real->line,
2570                                                  0, &offset);
2571
2572   g_assert (offset == 0);
2573
2574   /* Note that if we are on the first line, we snap to the start of
2575    * the first line and return TRUE, so TRUE means the iterator
2576    * changed, not that the line changed; this is maybe a bit
2577    * weird. I'm not sure there's an obvious right thing to do though.
2578    */
2579
2580   check_invariants (iter);
2581
2582   return TRUE;
2583 }
2584
2585
2586 /**
2587  * gtk_text_iter_forward_lines:
2588  * @iter: a #GtkTextIter
2589  * @count: number of lines to move forward
2590  *
2591  * Moves @count lines forward, if possible (if @count would move
2592  * past the start or end of the buffer, moves to the start or end of
2593  * the buffer).  The return value indicates whether the iterator moved
2594  * onto a dereferenceable position; if the iterator didn't move, or
2595  * moved onto the end iterator, then FALSE is returned. If @count is 0,
2596  * the function does nothing and returns FALSE. If @count is negative,
2597  * moves backward by 0 - @count lines.
2598  *
2599  * Return value: whether @iter moved and is dereferenceable
2600  **/
2601 gboolean
2602 gtk_text_iter_forward_lines (GtkTextIter *iter, gint count)
2603 {
2604   FIX_OVERFLOWS (count);
2605   
2606   if (count < 0)
2607     return gtk_text_iter_backward_lines (iter, 0 - count);
2608   else if (count == 0)
2609     return FALSE;
2610   else if (count == 1)
2611     {
2612       check_invariants (iter);
2613       return gtk_text_iter_forward_line (iter);
2614     }
2615   else
2616     {
2617       gint old_line;
2618
2619       old_line = gtk_text_iter_get_line (iter);
2620
2621       gtk_text_iter_set_line (iter, old_line + count);
2622
2623       check_invariants (iter);
2624
2625       /* return whether it moved, and is dereferenceable. */
2626       return
2627         (gtk_text_iter_get_line (iter) != old_line) &&
2628         !gtk_text_iter_is_end (iter);
2629     }
2630 }
2631
2632 /**
2633  * gtk_text_iter_backward_lines:
2634  * @iter: a #GtkTextIter
2635  * @count: number of lines to move backward
2636  *
2637  * Moves @count lines backward, if possible (if @count would move
2638  * past the start or end of the buffer, moves to the start or end of
2639  * the buffer).  The return value indicates whether the iterator moved
2640  * onto a dereferenceable position; if the iterator didn't move, or
2641  * moved onto the end iterator, then FALSE is returned. If @count is 0,
2642  * the function does nothing and returns FALSE. If @count is negative,
2643  * moves forward by 0 - @count lines.
2644  *
2645  * Return value: whether @iter moved and is dereferenceable
2646  **/
2647 gboolean
2648 gtk_text_iter_backward_lines (GtkTextIter *iter, gint count)
2649 {
2650   FIX_OVERFLOWS (count);
2651   
2652   if (count < 0)
2653     return gtk_text_iter_forward_lines (iter, 0 - count);
2654   else if (count == 0)
2655     return FALSE;
2656   else if (count == 1)
2657     {
2658       return gtk_text_iter_backward_line (iter);
2659     }
2660   else
2661     {
2662       gint old_line;
2663
2664       old_line = gtk_text_iter_get_line (iter);
2665
2666       gtk_text_iter_set_line (iter, MAX (old_line - count, 0));
2667
2668       return (gtk_text_iter_get_line (iter) != old_line);
2669     }
2670 }
2671
2672 typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
2673                                       gint                offset,
2674                                       gint                min_offset,
2675                                       gint                len,
2676                                       gint               *found_offset,
2677                                       gboolean            already_moved_initially);
2678
2679 typedef gboolean (* TestLogAttrFunc) (const PangoLogAttr *attrs,
2680                                       gint                offset,
2681                                       gint                min_offset,
2682                                       gint                len);
2683
2684 /* Word funcs */
2685
2686 static gboolean
2687 find_word_end_func (const PangoLogAttr *attrs,
2688                     gint          offset,
2689                     gint          min_offset,
2690                     gint          len,
2691                     gint         *found_offset,
2692                     gboolean      already_moved_initially)
2693 {
2694   if (!already_moved_initially)
2695     ++offset;
2696
2697   /* Find end of next word */
2698   while (offset < min_offset + len &&
2699          !attrs[offset].is_word_end)
2700     ++offset;
2701
2702   *found_offset = offset;
2703
2704   return offset < min_offset + len;
2705 }
2706
2707 static gboolean
2708 is_word_end_func (const PangoLogAttr *attrs,
2709                   gint          offset,
2710                   gint          min_offset,
2711                   gint          len)
2712 {
2713   return attrs[offset].is_word_end;
2714 }
2715
2716 static gboolean
2717 find_word_start_func (const PangoLogAttr *attrs,
2718                       gint          offset,
2719                       gint          min_offset,
2720                       gint          len,
2721                       gint         *found_offset,
2722                       gboolean      already_moved_initially)
2723 {
2724   if (!already_moved_initially)
2725     --offset;
2726
2727   /* Find start of prev word */
2728   while (offset >= min_offset &&
2729          !attrs[offset].is_word_start)
2730     --offset;
2731
2732   *found_offset = offset;
2733
2734   return offset >= min_offset;
2735 }
2736
2737 static gboolean
2738 is_word_start_func (const PangoLogAttr *attrs,
2739                     gint          offset,
2740                     gint          min_offset,
2741                     gint          len)
2742 {
2743   return attrs[offset].is_word_start;
2744 }
2745
2746 static gboolean
2747 inside_word_func (const PangoLogAttr *attrs,
2748                   gint          offset,
2749                   gint          min_offset,
2750                   gint          len)
2751 {
2752   /* Find next word start or end */
2753   while (offset >= min_offset &&
2754          !(attrs[offset].is_word_start || attrs[offset].is_word_end))
2755     --offset;
2756
2757   return attrs[offset].is_word_start;
2758 }
2759
2760 /* Sentence funcs */
2761
2762 static gboolean
2763 find_sentence_end_func (const PangoLogAttr *attrs,
2764                         gint          offset,
2765                         gint          min_offset,
2766                         gint          len,
2767                         gint         *found_offset,
2768                         gboolean      already_moved_initially)
2769 {
2770   if (!already_moved_initially)
2771     ++offset;
2772
2773   /* Find end of next sentence */
2774   while (offset < min_offset + len &&
2775          !attrs[offset].is_sentence_end)
2776     ++offset;
2777
2778   *found_offset = offset;
2779
2780   return offset < min_offset + len;
2781 }
2782
2783 static gboolean
2784 is_sentence_end_func (const PangoLogAttr *attrs,
2785                       gint          offset,
2786                       gint          min_offset,
2787                       gint          len)
2788 {
2789   return attrs[offset].is_sentence_end;
2790 }
2791
2792 static gboolean
2793 find_sentence_start_func (const PangoLogAttr *attrs,
2794                           gint          offset,
2795                           gint          min_offset,
2796                           gint          len,
2797                           gint         *found_offset,
2798                           gboolean      already_moved_initially)
2799 {
2800   if (!already_moved_initially)
2801     --offset;
2802
2803   /* Find start of prev sentence */
2804   while (offset >= min_offset &&
2805          !attrs[offset].is_sentence_start)
2806     --offset;
2807
2808   *found_offset = offset;
2809
2810   return offset >= min_offset;
2811 }
2812
2813 static gboolean
2814 is_sentence_start_func (const PangoLogAttr *attrs,
2815                         gint          offset,
2816                         gint          min_offset,
2817                         gint          len)
2818 {
2819   return attrs[offset].is_sentence_start;
2820 }
2821
2822 static gboolean
2823 inside_sentence_func (const PangoLogAttr *attrs,
2824                       gint          offset,
2825                       gint          min_offset,
2826                       gint          len)
2827 {
2828   /* Find next sentence start or end */
2829   while (offset >= min_offset &&
2830          !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
2831     --offset;
2832
2833   return attrs[offset].is_sentence_start;
2834 }
2835
2836 static gboolean
2837 test_log_attrs (const GtkTextIter *iter,
2838                 TestLogAttrFunc    func)
2839 {
2840   gint char_len;
2841   const PangoLogAttr *attrs;
2842   int offset;
2843   gboolean result = FALSE;
2844
2845   g_return_val_if_fail (iter != NULL, FALSE);
2846
2847   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2848                                                iter, &char_len);
2849
2850   offset = gtk_text_iter_get_line_offset (iter);
2851
2852   /* char_len may be 0 and attrs will be NULL if so, if
2853    * iter is the end iter and the last line is empty
2854    */
2855   
2856   if (offset < char_len)
2857     result = (* func) (attrs, offset, 0, char_len);
2858
2859   return result;
2860 }
2861
2862 static gboolean
2863 find_line_log_attrs (const GtkTextIter *iter,
2864                      FindLogAttrFunc    func,
2865                      gint              *found_offset,
2866                      gboolean           already_moved_initially)
2867 {
2868   gint char_len;
2869   const PangoLogAttr *attrs;
2870   int offset;
2871   gboolean result = FALSE;
2872
2873   g_return_val_if_fail (iter != NULL, FALSE);
2874   
2875   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2876                                                iter, &char_len);      
2877
2878   offset = gtk_text_iter_get_line_offset (iter);
2879   
2880   /* char_len may be 0 and attrs will be NULL if so, if
2881    * iter is the end iter and the last line is empty
2882    */
2883   
2884   if (attrs)
2885     result = (* func) (attrs, offset, 0, char_len, found_offset,
2886                        already_moved_initially);
2887
2888   return result;
2889 }
2890
2891 /* FIXME this function is very, very gratuitously slow */
2892 static gboolean
2893 find_by_log_attrs (GtkTextIter    *iter,
2894                    FindLogAttrFunc func,
2895                    gboolean        forward,
2896                    gboolean        already_moved_initially)
2897 {
2898   GtkTextIter orig;
2899   gint offset = 0;
2900   gboolean found = FALSE;
2901
2902   g_return_val_if_fail (iter != NULL, FALSE);
2903
2904   orig = *iter;
2905   
2906   found = find_line_log_attrs (iter, func, &offset, already_moved_initially);
2907   
2908   if (!found)
2909     {
2910       if (forward)
2911         {
2912           if (gtk_text_iter_forward_line (iter))
2913             return find_by_log_attrs (iter, func, forward,
2914                                       TRUE);
2915           else
2916             return FALSE;
2917         }
2918       else
2919         {                    
2920           /* go to end of previous line. need to check that
2921            * line is > 0 because backward_line snaps to start of
2922            * line 0 if it's on line 0
2923            */
2924           if (gtk_text_iter_get_line (iter) > 0 && 
2925               gtk_text_iter_backward_line (iter))
2926             {
2927               if (!gtk_text_iter_ends_line (iter))
2928                 gtk_text_iter_forward_to_line_end (iter);
2929               
2930               return find_by_log_attrs (iter, func, forward,
2931                                         TRUE);
2932             }
2933           else
2934             return FALSE;
2935         }
2936     }
2937   else
2938     {      
2939       gtk_text_iter_set_line_offset (iter, offset);
2940
2941       return
2942         (already_moved_initially || !gtk_text_iter_equal (iter, &orig)) &&
2943         !gtk_text_iter_is_end (iter);
2944     }
2945 }
2946
2947 /**
2948  * gtk_text_iter_forward_word_end:
2949  * @iter: a #GtkTextIter
2950  * 
2951  * Moves forward to the next word end. (If @iter is currently on a
2952  * word end, moves forward to the next one after that.) Word breaks
2953  * are determined by Pango and should be correct for nearly any
2954  * language (if not, the correct fix would be to the Pango word break
2955  * algorithms).
2956  * 
2957  * Return value: %TRUE if @iter moved and is not the end iterator 
2958  **/
2959 gboolean
2960 gtk_text_iter_forward_word_end (GtkTextIter *iter)
2961 {
2962   return find_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
2963 }
2964
2965 /**
2966  * gtk_text_iter_backward_word_start:
2967  * @iter: a #GtkTextIter
2968  * 
2969  * Moves backward to the next word start. (If @iter is currently on a
2970  * word start, moves backward to the next one after that.) Word breaks
2971  * are determined by Pango and should be correct for nearly any
2972  * language (if not, the correct fix would be to the Pango word break
2973  * algorithms).
2974  * 
2975  * Return value: %TRUE if @iter moved and is not the end iterator 
2976  **/
2977 gboolean
2978 gtk_text_iter_backward_word_start (GtkTextIter      *iter)
2979 {
2980   return find_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
2981 }
2982
2983 /* FIXME a loop around a truly slow function means
2984  * a truly spectacularly slow function.
2985  */
2986
2987 /**
2988  * gtk_text_iter_forward_word_ends:
2989  * @iter: a #GtkTextIter
2990  * @count: number of times to move
2991  * 
2992  * Calls gtk_text_iter_forward_word_end() up to @count times.
2993  *
2994  * Return value: %TRUE if @iter moved and is not the end iterator 
2995  **/
2996 gboolean
2997 gtk_text_iter_forward_word_ends (GtkTextIter      *iter,
2998                                  gint              count)
2999 {
3000   g_return_val_if_fail (iter != NULL, FALSE);
3001
3002   FIX_OVERFLOWS (count);
3003   
3004   if (count == 0)
3005     return FALSE;
3006
3007   if (count < 0)
3008     return gtk_text_iter_backward_word_starts (iter, -count);
3009
3010   if (!gtk_text_iter_forward_word_end (iter))
3011     return FALSE;
3012   --count;
3013
3014   while (count > 0)
3015     {
3016       if (!gtk_text_iter_forward_word_end (iter))
3017         break;
3018       --count;
3019     }
3020   return TRUE;
3021 }
3022
3023 /**
3024  * gtk_text_iter_backward_word_starts
3025  * @iter: a #GtkTextIter
3026  * @count: number of times to move
3027  * 
3028  * Calls gtk_text_iter_backward_word_starts() up to @count times.
3029  *
3030  * Return value: %TRUE if @iter moved and is not the end iterator 
3031  **/
3032 gboolean
3033 gtk_text_iter_backward_word_starts (GtkTextIter      *iter,
3034                                     gint               count)
3035 {
3036   g_return_val_if_fail (iter != NULL, FALSE);
3037
3038   FIX_OVERFLOWS (count);
3039   
3040   if (count < 0)
3041     return gtk_text_iter_forward_word_ends (iter, -count);
3042
3043   if (!gtk_text_iter_backward_word_start (iter))
3044     return FALSE;
3045   --count;
3046
3047   while (count > 0)
3048     {
3049       if (!gtk_text_iter_backward_word_start (iter))
3050         break;
3051       --count;
3052     }
3053   return TRUE;
3054 }
3055
3056 /**
3057  * gtk_text_iter_starts_word:
3058  * @iter: a #GtkTextIter
3059  * 
3060  * Determines whether @iter begins a natural-language word.  Word
3061  * breaks are determined by Pango and should be correct for nearly any
3062  * language (if not, the correct fix would be to the Pango word break
3063  * algorithms).
3064  *
3065  * Return value: %TRUE if @iter is at the start of a word
3066  **/
3067 gboolean
3068 gtk_text_iter_starts_word (const GtkTextIter *iter)
3069 {
3070   return test_log_attrs (iter, is_word_start_func);
3071 }
3072
3073 /**
3074  * gtk_text_iter_ends_word:
3075  * @iter: a #GtkTextIter
3076  * 
3077  * Determines whether @iter ends a natural-language word.  Word breaks
3078  * are determined by Pango and should be correct for nearly any
3079  * language (if not, the correct fix would be to the Pango word break
3080  * algorithms).
3081  *
3082  * Return value: %TRUE if @iter is at the end of a word
3083  **/
3084 gboolean
3085 gtk_text_iter_ends_word (const GtkTextIter *iter)
3086 {
3087   return test_log_attrs (iter, is_word_end_func);
3088 }
3089
3090 /**
3091  * gtk_text_iter_inside_word:
3092  * @iter: a #GtkTextIter
3093  * 
3094  * Determines whether @iter is inside a natural-language word (as
3095  * opposed to say inside some whitespace).  Word breaks are determined
3096  * by Pango and should be correct for nearly any language (if not, the
3097  * correct fix would be to the Pango word break algorithms).
3098  * 
3099  * Return value: %TRUE if @iter is inside a word
3100  **/
3101 gboolean
3102 gtk_text_iter_inside_word (const GtkTextIter *iter)
3103 {
3104   return test_log_attrs (iter, inside_word_func);
3105 }
3106
3107 /**
3108  * gtk_text_iter_starts_sentence:
3109  * @iter: a #GtkTextIter
3110  * 
3111  * Determines whether @iter begins a sentence.  Sentence boundaries are
3112  * determined by Pango and should be correct for nearly any language
3113  * (if not, the correct fix would be to the Pango text boundary
3114  * algorithms).
3115  * 
3116  * Return value: %TRUE if @iter is at the start of a sentence.
3117  **/
3118 gboolean
3119 gtk_text_iter_starts_sentence (const GtkTextIter *iter)
3120 {
3121   return test_log_attrs (iter, is_sentence_start_func);
3122 }
3123
3124 /**
3125  * gtk_text_iter_ends_sentence:
3126  * @iter: a #GtkTextIter
3127  * 
3128  * Determines whether @iter ends a sentence.  Sentence boundaries are
3129  * determined by Pango and should be correct for nearly any language
3130  * (if not, the correct fix would be to the Pango text boundary
3131  * algorithms).
3132  * 
3133  * Return value: %TRUE if @iter is at the end of a sentence.
3134  **/
3135 gboolean
3136 gtk_text_iter_ends_sentence (const GtkTextIter *iter)
3137 {
3138   return test_log_attrs (iter, is_sentence_end_func);
3139 }
3140
3141 /**
3142  * gtk_text_iter_inside_sentence:
3143  * @iter: a #GtkTextIter
3144  * 
3145  * Determines whether @iter is inside a sentence (as opposed to in
3146  * between two sentences, e.g. after a period and before the first
3147  * letter of the next sentence).  Sentence boundaries are determined
3148  * by Pango and should be correct for nearly any language (if not, the
3149  * correct fix would be to the Pango text boundary algorithms).
3150  * 
3151  * Return value: %TRUE if @iter is inside a sentence.
3152  **/
3153 gboolean
3154 gtk_text_iter_inside_sentence (const GtkTextIter *iter)
3155 {
3156   return test_log_attrs (iter, inside_sentence_func);
3157 }
3158
3159 /**
3160  * gtk_text_iter_forward_sentence_end:
3161  * @iter: a #GtkTextIter
3162  * 
3163  * Moves forward to the next sentence end. (If @iter is at the end of
3164  * a sentence, moves to the next end of sentence.)  Sentence
3165  * boundaries are determined by Pango and should be correct for nearly
3166  * any language (if not, the correct fix would be to the Pango text
3167  * boundary algorithms).
3168  * 
3169  * Return value: %TRUE if @iter moved and is not the end iterator
3170  **/
3171 gboolean
3172 gtk_text_iter_forward_sentence_end (GtkTextIter *iter)
3173 {
3174   return find_by_log_attrs (iter, find_sentence_end_func, TRUE, FALSE);
3175 }
3176
3177 /**
3178  * gtk_text_iter_backward_sentence_start:
3179  * @iter: a #GtkTextIter
3180  * 
3181  * Moves backward to the next sentence start; if @iter is already at
3182  * the start of a sentence, moves backward to the next one.  Sentence
3183  * boundaries are determined by Pango and should be correct for nearly
3184  * any language (if not, the correct fix would be to the Pango text
3185  * boundary algorithms).
3186  * 
3187  * Return value: %TRUE if @iter moved and is not the end iterator
3188  **/
3189 gboolean
3190 gtk_text_iter_backward_sentence_start (GtkTextIter      *iter)
3191 {
3192   return find_by_log_attrs (iter, find_sentence_start_func, FALSE, FALSE);
3193 }
3194
3195 /* FIXME a loop around a truly slow function means
3196  * a truly spectacularly slow function.
3197  */
3198 /**
3199  * gtk_text_iter_forward_sentence_ends:
3200  * @iter: a #GtkTextIter
3201  * @count: number of sentences to move
3202  * 
3203  * Calls gtk_text_iter_forward_sentence_end() @count times (or until
3204  * gtk_text_iter_forward_sentence_end() returns %FALSE). If @count is
3205  * negative, moves backward instead of forward.
3206  * 
3207  * Return value: %TRUE if @iter moved and is not the end iterator
3208  **/
3209 gboolean
3210 gtk_text_iter_forward_sentence_ends (GtkTextIter      *iter,
3211                                      gint              count)
3212 {
3213   g_return_val_if_fail (iter != NULL, FALSE);
3214
3215   if (count == 0)
3216     return FALSE;
3217
3218   if (count < 0)
3219     return gtk_text_iter_backward_sentence_starts (iter, -count);
3220
3221   if (!gtk_text_iter_forward_sentence_end (iter))
3222     return FALSE;
3223   --count;
3224
3225   while (count > 0)
3226     {
3227       if (!gtk_text_iter_forward_sentence_end (iter))
3228         break;
3229       --count;
3230     }
3231   return TRUE;
3232 }
3233
3234 /**
3235  * gtk_text_iter_backward_sentence_starts:
3236  * @iter: a #GtkTextIter
3237  * @count: number of sentences to move
3238  * 
3239  * Calls gtk_text_iter_backward_sentence_start() up to @count times,
3240  * or until it returns %FALSE. If @count is negative, moves forward
3241  * instead of backward.
3242  * 
3243  * Return value: %TRUE if @iter moved and is not the end iterator
3244  **/
3245 gboolean
3246 gtk_text_iter_backward_sentence_starts (GtkTextIter      *iter,
3247                                         gint               count)
3248 {
3249   g_return_val_if_fail (iter != NULL, FALSE);
3250
3251   if (count < 0)
3252     return gtk_text_iter_forward_sentence_ends (iter, -count);
3253
3254   if (!gtk_text_iter_backward_sentence_start (iter))
3255     return FALSE;
3256   --count;
3257
3258   while (count > 0)
3259     {
3260       if (!gtk_text_iter_backward_sentence_start (iter))
3261         break;
3262       --count;
3263     }
3264   return TRUE;
3265 }
3266
3267 static gboolean
3268 find_forward_cursor_pos_func (const PangoLogAttr *attrs,
3269                               gint          offset,
3270                               gint          min_offset,
3271                               gint          len,
3272                               gint         *found_offset,
3273                               gboolean      already_moved_initially)
3274 {
3275   if (!already_moved_initially)
3276     ++offset;
3277
3278   while (offset < (min_offset + len) &&
3279          !attrs[offset].is_cursor_position)
3280     ++offset;
3281
3282   *found_offset = offset;
3283
3284   return offset < (min_offset + len);
3285 }
3286
3287 static gboolean
3288 find_backward_cursor_pos_func (const PangoLogAttr *attrs,
3289                                gint          offset,
3290                                gint          min_offset,
3291                                gint          len,
3292                                gint         *found_offset,
3293                                gboolean      already_moved_initially)
3294 {  
3295   if (!already_moved_initially)
3296     --offset;
3297
3298   while (offset > min_offset &&
3299          !attrs[offset].is_cursor_position)
3300     --offset;
3301
3302   *found_offset = offset;
3303   
3304   return offset >= min_offset;
3305 }
3306
3307 static gboolean
3308 is_cursor_pos_func (const PangoLogAttr *attrs,
3309                     gint          offset,
3310                     gint          min_offset,
3311                     gint          len)
3312 {
3313   return attrs[offset].is_cursor_position;
3314 }
3315
3316 /**
3317  * gtk_text_iter_forward_cursor_position:
3318  * @iter: a #GtkTextIter
3319  * 
3320  * Moves @iter forward by a single cursor position. Cursor positions
3321  * are (unsurprisingly) positions where the cursor can appear. Perhaps
3322  * surprisingly, there may not be a cursor position between all
3323  * characters. The most common example for European languages would be
3324  * a carriage return/newline sequence. For some Unicode characters,
3325  * the equivalent of say the letter "a" with an accent mark will be
3326  * represented as two characters, first the letter then a "combining
3327  * mark" that causes the accent to be rendered; so the cursor can't go
3328  * between those two characters. See also the #PangoLogAttr structure and
3329  * pango_break() function.
3330  * 
3331  * Return value: %TRUE if we moved and the new position is dereferenceable
3332  **/
3333 gboolean
3334 gtk_text_iter_forward_cursor_position (GtkTextIter *iter)
3335 {
3336   return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
3337 }
3338
3339 /**
3340  * gtk_text_iter_backward_cursor_position:
3341  * @iter: a #GtkTextIter
3342  * 
3343  * Like gtk_text_iter_forward_cursor_position(), but moves backward.
3344  * 
3345  * Return value: %TRUE if we moved and the new position is dereferenceable
3346  **/
3347 gboolean
3348 gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
3349 {
3350   return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
3351 }
3352
3353 /**
3354  * gtk_text_iter_forward_cursor_positions:
3355  * @iter: a #GtkTextIter
3356  * @count: number of positions to move
3357  * 
3358  * Moves up to @count cursor positions. See
3359  * gtk_text_iter_forward_cursor_position() for details.
3360  * 
3361  * Return value: %TRUE if we moved and the new position is dereferenceable
3362  **/
3363 gboolean
3364 gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
3365                                         gint         count)
3366 {
3367   g_return_val_if_fail (iter != NULL, FALSE);
3368
3369   FIX_OVERFLOWS (count);
3370   
3371   if (count == 0)
3372     return FALSE;
3373   
3374   if (count < 0)
3375     return gtk_text_iter_backward_cursor_positions (iter, -count);
3376   
3377   if (!gtk_text_iter_forward_cursor_position (iter))
3378     return FALSE;
3379   --count;
3380
3381   while (count > 0)
3382     {
3383       if (!gtk_text_iter_forward_cursor_position (iter))
3384         break;
3385       --count;
3386     }
3387   return TRUE;
3388 }
3389
3390 /**
3391  * gtk_text_iter_backward_cursor_positions:
3392  * @iter: a #GtkTextIter
3393  * @count: number of positions to move
3394  *
3395  * Moves up to @count cursor positions. See
3396  * gtk_text_iter_forward_cursor_position() for details.
3397  * 
3398  * Return value: %TRUE if we moved and the new position is dereferenceable
3399  **/
3400 gboolean
3401 gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
3402                                          gint         count)
3403 {
3404   g_return_val_if_fail (iter != NULL, FALSE);
3405
3406   FIX_OVERFLOWS (count);
3407   
3408   if (count == 0)
3409     return FALSE;
3410
3411   if (count < 0)
3412     return gtk_text_iter_forward_cursor_positions (iter, -count);
3413   
3414   if (!gtk_text_iter_backward_cursor_position (iter))
3415     return FALSE;
3416   --count;
3417
3418   while (count > 0)
3419     {
3420       if (!gtk_text_iter_backward_cursor_position (iter))
3421         break;
3422       --count;
3423     }
3424   return TRUE;
3425 }
3426
3427 /**
3428  * gtk_text_iter_is_cursor_position:
3429  * @iter: a #GtkTextIter
3430  * 
3431  * See gtk_text_iter_forward_cursor_position() or #PangoLogAttr or
3432  * pango_break() for details on what a cursor position is.
3433  * 
3434  * Return value: %TRUE if the cursor can be placed at @iter
3435  **/
3436 gboolean
3437 gtk_text_iter_is_cursor_position (const GtkTextIter *iter)
3438 {
3439   return test_log_attrs (iter, is_cursor_pos_func);
3440 }
3441
3442 /**
3443  * gtk_text_iter_set_line_offset:
3444  * @iter: a #GtkTextIter 
3445  * @char_on_line: a character offset relative to the start of @iter's current line
3446  * 
3447  * Moves @iter within a line, to a new <emphasis>character</emphasis>
3448  * (not byte) offset. The given character offset must be less than or
3449  * equal to the number of characters in the line; if equal, @iter
3450  * moves to the start of the next line. See
3451  * gtk_text_iter_set_line_index() if you have a byte index rather than
3452  * a character offset.
3453  *
3454  **/
3455 void
3456 gtk_text_iter_set_line_offset (GtkTextIter *iter,
3457                                gint         char_on_line)
3458 {
3459   GtkTextRealIter *real;
3460   gint chars_in_line;
3461   
3462   g_return_if_fail (iter != NULL);
3463
3464   real = gtk_text_iter_make_surreal (iter);
3465
3466   if (real == NULL)
3467     return;
3468   
3469   check_invariants (iter);
3470
3471   chars_in_line = gtk_text_iter_get_chars_in_line (iter);
3472
3473   g_return_if_fail (char_on_line <= chars_in_line);
3474
3475   if (char_on_line < chars_in_line)
3476     iter_set_from_char_offset (real, real->line, char_on_line);
3477   else
3478     gtk_text_iter_forward_line (iter); /* set to start of next line */
3479
3480   check_invariants (iter);
3481 }
3482
3483 /**
3484  * gtk_text_iter_set_line_index:
3485  * @iter: a #GtkTextIter
3486  * @byte_on_line: a byte index relative to the start of @iter's current line
3487  *
3488  * Same as gtk_text_iter_set_line_offset(), but works with a
3489  * <emphasis>byte</emphasis> index. The given byte index must be at
3490  * the start of a character, it can't be in the middle of a UTF-8
3491  * encoded character.
3492  * 
3493  **/
3494 void
3495 gtk_text_iter_set_line_index (GtkTextIter *iter,
3496                               gint         byte_on_line)
3497 {
3498   GtkTextRealIter *real;
3499   gint bytes_in_line;
3500   
3501   g_return_if_fail (iter != NULL);
3502
3503   real = gtk_text_iter_make_surreal (iter);
3504
3505   if (real == NULL)
3506     return;
3507
3508   check_invariants (iter);
3509
3510   bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
3511
3512   g_return_if_fail (byte_on_line <= bytes_in_line);
3513   
3514   if (byte_on_line < bytes_in_line)
3515     iter_set_from_byte_offset (real, real->line, byte_on_line);
3516   else
3517     gtk_text_iter_forward_line (iter);
3518
3519   if (real->segment->type == &gtk_text_char_type &&
3520       (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
3521     g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
3522                "character; this will crash the text buffer. "
3523                "Byte indexes must refer to the start of a character.",
3524                G_STRLOC, byte_on_line);
3525
3526   check_invariants (iter);
3527 }
3528
3529
3530 /**
3531  * gtk_text_iter_set_visible_line_offset:
3532  * @iter: a #GtkTextIter
3533  * @char_on_line: a character offset
3534  * 
3535  * Like gtk_text_iter_set_line_offset(), but the offset is in visible
3536  * characters, i.e. text with a tag making it invisible is not
3537  * counted in the offset.
3538  **/
3539 void
3540 gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
3541                                        gint         char_on_line)
3542 {
3543   gint chars_seen = 0;
3544   GtkTextIter pos;
3545
3546   g_return_if_fail (iter != NULL);
3547   
3548   pos = *iter;
3549
3550   /* For now we use a ludicrously slow implementation */
3551   while (chars_seen < char_on_line)
3552     {
3553       if (!_gtk_text_btree_char_is_invisible (&pos))
3554         ++chars_seen;
3555
3556       if (!gtk_text_iter_forward_char (&pos))
3557         break;
3558
3559       if (chars_seen == char_on_line)
3560         break;
3561     }
3562   
3563   if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3564     *iter = pos;
3565   else
3566     gtk_text_iter_forward_line (iter);
3567 }
3568
3569 static gint
3570 bytes_in_char (GtkTextIter *iter)
3571 {
3572   return g_unichar_to_utf8 (gtk_text_iter_get_char (iter), NULL);
3573 }
3574
3575 /**
3576  * gtk_text_iter_set_visible_line_index:
3577  * @iter: a #GtkTextIter
3578  * @byte_on_line: a byte index
3579  * 
3580  * Like gtk_text_iter_set_line_index(), but the index is in visible
3581  * bytes, i.e. text with a tag making it invisible is not counted
3582  * in the index.
3583  **/
3584 void
3585 gtk_text_iter_set_visible_line_index  (GtkTextIter *iter,
3586                                        gint         byte_on_line)
3587 {
3588   gint bytes_seen = 0;
3589   GtkTextIter pos;
3590
3591   g_return_if_fail (iter != NULL);
3592   
3593   pos = *iter;
3594
3595   /* For now we use a ludicrously slow implementation */
3596   while (bytes_seen < byte_on_line)
3597     {
3598       if (!_gtk_text_btree_char_is_invisible (&pos))
3599         bytes_seen += bytes_in_char (&pos);
3600
3601       if (!gtk_text_iter_forward_char (&pos))
3602         break;
3603
3604       if (bytes_seen >= byte_on_line)
3605         break;
3606     }
3607
3608   if (bytes_seen > byte_on_line)
3609     g_warning ("%s: Incorrect visible byte index %d falls in the middle of a UTF-8 "
3610                "character; this will crash the text buffer. "
3611                "Byte indexes must refer to the start of a character.",
3612                G_STRLOC, byte_on_line);
3613   
3614   if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3615     *iter = pos;
3616   else
3617     gtk_text_iter_forward_line (iter);
3618 }
3619
3620 /**
3621  * gtk_text_iter_set_line:
3622  * @iter: a #GtkTextIter
3623  * @line_number: line number (counted from 0)
3624  *
3625  * Moves iterator @iter to the start of the line @line_number.
3626  * 
3627  **/
3628 void
3629 gtk_text_iter_set_line (GtkTextIter *iter,
3630                         gint         line_number)
3631 {
3632   GtkTextLine *line;
3633   gint real_line;
3634   GtkTextRealIter *real;
3635
3636   g_return_if_fail (iter != NULL);
3637
3638   real = gtk_text_iter_make_surreal (iter);
3639
3640   if (real == NULL)
3641     return;
3642
3643   check_invariants (iter);
3644
3645   line = _gtk_text_btree_get_line (real->tree, line_number, &real_line);
3646
3647   iter_set_from_char_offset (real, line, 0);
3648
3649   /* We might as well cache this, since we know it. */
3650   real->cached_line_number = real_line;
3651
3652   check_invariants (iter);
3653 }
3654
3655 /**
3656  * gtk_text_iter_set_offset:
3657  * @iter: a #GtkTextIter
3658  * @char_offset: a character number
3659  *
3660  * Sets @iter to point to @char_offset. @char_offset counts from the start
3661  * of the entire text buffer, starting with 0.
3662  * 
3663  **/
3664 void
3665 gtk_text_iter_set_offset (GtkTextIter *iter,
3666                           gint         char_offset)
3667 {
3668   GtkTextLine *line;
3669   GtkTextRealIter *real;
3670   gint line_start;
3671   gint real_char_index;
3672
3673   g_return_if_fail (iter != NULL);
3674
3675   real = gtk_text_iter_make_surreal (iter);
3676
3677   if (real == NULL)
3678     return;
3679
3680   check_invariants (iter);
3681
3682   if (real->cached_char_index >= 0 &&
3683       real->cached_char_index == char_offset)
3684     return;
3685
3686   line = _gtk_text_btree_get_line_at_char (real->tree,
3687                                            char_offset,
3688                                            &line_start,
3689                                            &real_char_index);
3690
3691   iter_set_from_char_offset (real, line, real_char_index - line_start);
3692
3693   /* Go ahead and cache this since we have it. */
3694   real->cached_char_index = real_char_index;
3695
3696   check_invariants (iter);
3697 }
3698
3699 /**
3700  * gtk_text_iter_forward_to_end:
3701  * @iter: a #GtkTextIter
3702  *
3703  * Moves @iter forward to the "end iterator," which points one past the last
3704  * valid character in the buffer. gtk_text_iter_get_char() called on the
3705  * end iterator returns 0, which is convenient for writing loops.
3706  * 
3707  **/
3708 void
3709 gtk_text_iter_forward_to_end  (GtkTextIter *iter)
3710 {
3711   GtkTextBuffer *buffer;
3712   GtkTextRealIter *real;
3713
3714   g_return_if_fail (iter != NULL);
3715
3716   real = gtk_text_iter_make_surreal (iter);
3717
3718   if (real == NULL)
3719     return;
3720
3721   buffer = _gtk_text_btree_get_buffer (real->tree);
3722
3723   gtk_text_buffer_get_end_iter (buffer, iter);
3724 }
3725
3726 /**
3727  * gtk_text_iter_forward_to_line_end:
3728  * @iter: a #GtkTextIter
3729  * 
3730  * Moves the iterator to point to the paragraph delimiter characters,
3731  * which will be either a newline, a carriage return, a carriage
3732  * return/newline in sequence, or the Unicode paragraph separator
3733  * character. If the iterator is already at the paragraph delimiter
3734  * characters, moves to the paragraph delimiter characters for the
3735  * next line.
3736  * 
3737  * Return value: %TRUE if we moved and the new location is not the end iterator
3738  **/
3739 gboolean
3740 gtk_text_iter_forward_to_line_end (GtkTextIter *iter)
3741 {
3742   gint current_offset;
3743   gint new_offset;
3744
3745   g_return_val_if_fail (iter != NULL, FALSE);
3746
3747   current_offset = gtk_text_iter_get_line_offset (iter);
3748   /* FIXME assumption that line ends in a newline; broken */
3749   new_offset = gtk_text_iter_get_chars_in_line (iter) - 1;
3750
3751   if (current_offset < new_offset)
3752     {
3753       /* Move to end of this line. */
3754       gtk_text_iter_set_line_offset (iter, new_offset);
3755       return TRUE;
3756     }
3757   else
3758     {
3759       /* Move to end of next line. */
3760       if (gtk_text_iter_forward_line (iter))
3761         {
3762           /* We don't want to move past all
3763            * empty lines.
3764            */
3765           if (!gtk_text_iter_ends_line (iter))
3766             gtk_text_iter_forward_to_line_end (iter);
3767           return TRUE;
3768         }
3769       else
3770         return FALSE;
3771     }
3772 }
3773
3774 /**
3775  * gtk_text_iter_forward_to_tag_toggle:
3776  * @iter: a #GtkTextIter
3777  * @tag: a #GtkTextTag, or NULL
3778  *
3779  * Moves forward to the next toggle (on or off) of the
3780  * #GtkTextTag @tag, or to the next toggle of any tag if
3781  * @tag is NULL. If no matching tag toggles are found,
3782  * returns FALSE, otherwise TRUE. Does not return toggles
3783  * located at @iter, only toggles after @iter. Sets @iter to
3784  * the location of the toggle, or to the end of the buffer
3785  * if no toggle is found.
3786  *
3787  * Return value: whether we found a tag toggle after @iter
3788  **/
3789 gboolean
3790 gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
3791                                      GtkTextTag  *tag)
3792 {
3793   GtkTextLine *next_line;
3794   GtkTextLine *current_line;
3795   GtkTextRealIter *real;
3796
3797   g_return_val_if_fail (iter != NULL, FALSE);
3798
3799   real = gtk_text_iter_make_real (iter);
3800
3801   if (real == NULL)
3802     return FALSE;
3803
3804   check_invariants (iter);
3805
3806   current_line = real->line;
3807   next_line = _gtk_text_line_next_could_contain_tag (current_line,
3808                                                      real->tree, tag);
3809
3810   while (_gtk_text_iter_forward_indexable_segment (iter))
3811     {
3812       /* If we went forward to a line that couldn't contain a toggle
3813          for the tag, then skip forward to a line that could contain
3814          it. This potentially skips huge hunks of the tree, so we
3815          aren't a purely linear search. */
3816       if (real->line != current_line)
3817         {
3818           if (next_line == NULL)
3819             {
3820               /* End of search. Set to end of buffer. */
3821               _gtk_text_btree_get_end_iter (real->tree, iter);
3822               return FALSE;
3823             }
3824
3825           if (real->line != next_line)
3826             iter_set_from_byte_offset (real, next_line, 0);
3827
3828           current_line = real->line;
3829           next_line = _gtk_text_line_next_could_contain_tag (current_line,
3830                                                              real->tree,
3831                                                              tag);
3832         }
3833
3834       if (gtk_text_iter_toggles_tag (iter, tag))
3835         {
3836           /* If there's a toggle here, it isn't indexable so
3837              any_segment can't be the indexable segment. */
3838           g_assert (real->any_segment != real->segment);
3839           return TRUE;
3840         }
3841     }
3842
3843   /* Check end iterator for tags */
3844   if (gtk_text_iter_toggles_tag (iter, tag))
3845     {
3846       /* If there's a toggle here, it isn't indexable so
3847          any_segment can't be the indexable segment. */
3848       g_assert (real->any_segment != real->segment);
3849       return TRUE;
3850     }
3851
3852   /* Reached end of buffer */
3853   return FALSE;
3854 }
3855
3856 /**
3857  * gtk_text_iter_backward_to_tag_toggle:
3858  * @iter: a #GtkTextIter
3859  * @tag: a #GtkTextTag, or NULL
3860  *
3861  * Moves backward to the next toggle (on or off) of the
3862  * #GtkTextTag @tag, or to the next toggle of any tag if
3863  * @tag is NULL. If no matching tag toggles are found,
3864  * returns FALSE, otherwise TRUE. Does not return toggles
3865  * located at @iter, only toggles before @iter. Sets @iter
3866  * to the location of the toggle, or the start of the buffer
3867  * if no toggle is found.
3868  *
3869  * Return value: whether we found a tag toggle before @iter
3870  **/
3871 gboolean
3872 gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
3873                                       GtkTextTag  *tag)
3874 {
3875   GtkTextLine *prev_line;
3876   GtkTextLine *current_line;
3877   GtkTextRealIter *real;
3878
3879   g_return_val_if_fail (iter != NULL, FALSE);
3880
3881   real = gtk_text_iter_make_real (iter);
3882
3883   if (real == NULL)
3884     return FALSE;
3885
3886   check_invariants (iter);
3887
3888   current_line = real->line;
3889   prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3890                                                         real->tree, tag);
3891
3892
3893   /* If we're at segment start, go to the previous segment;
3894    * if mid-segment, snap to start of current segment.
3895    */
3896   if (is_segment_start (real))
3897     {
3898       if (!_gtk_text_iter_backward_indexable_segment (iter))
3899         return FALSE;
3900     }
3901   else
3902     {
3903       ensure_char_offsets (real);
3904
3905       if (!gtk_text_iter_backward_chars (iter, real->segment_char_offset))
3906         return FALSE;
3907     }
3908
3909   do
3910     {
3911       /* If we went backward to a line that couldn't contain a toggle
3912        * for the tag, then skip backward further to a line that
3913        * could contain it. This potentially skips huge hunks of the
3914        * tree, so we aren't a purely linear search.
3915        */
3916       if (real->line != current_line)
3917         {
3918           if (prev_line == NULL)
3919             {
3920               /* End of search. Set to start of buffer. */
3921               _gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
3922               return FALSE;
3923             }
3924
3925           if (real->line != prev_line)
3926             {
3927               /* Set to last segment in prev_line (could do this
3928                * more quickly)
3929                */
3930               iter_set_from_byte_offset (real, prev_line, 0);
3931
3932               while (!at_last_indexable_segment (real))
3933                 _gtk_text_iter_forward_indexable_segment (iter);
3934             }
3935
3936           current_line = real->line;
3937           prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3938                                                                 real->tree,
3939                                                                 tag);
3940         }
3941
3942       if (gtk_text_iter_toggles_tag (iter, tag))
3943         {
3944           /* If there's a toggle here, it isn't indexable so
3945            * any_segment can't be the indexable segment.
3946            */
3947           g_assert (real->any_segment != real->segment);
3948           return TRUE;
3949         }
3950     }
3951   while (_gtk_text_iter_backward_indexable_segment (iter));
3952
3953   /* Reached front of buffer */
3954   return FALSE;
3955 }
3956
3957 static gboolean
3958 matches_pred (GtkTextIter *iter,
3959               GtkTextCharPredicate pred,
3960               gpointer user_data)
3961 {
3962   gint ch;
3963
3964   ch = gtk_text_iter_get_char (iter);
3965
3966   return (*pred) (ch, user_data);
3967 }
3968
3969 /**
3970  * gtk_text_iter_forward_find_char:
3971  * @iter: a #GtkTextIter
3972  * @pred: a function to be called on each character
3973  * @user_data: user data for @pred
3974  * @limit: search limit, or %NULL for none 
3975  * 
3976  * Advances @iter, calling @pred on each character. If
3977  * @pred returns %TRUE, returns %TRUE and stops scanning.
3978  * If @pred never returns %TRUE, @iter is set to @limit if
3979  * @limit is non-%NULL, otherwise to the end iterator.
3980  * 
3981  * Return value: whether a match was found
3982  **/
3983 gboolean
3984 gtk_text_iter_forward_find_char (GtkTextIter         *iter,
3985                                  GtkTextCharPredicate pred,
3986                                  gpointer             user_data,
3987                                  const GtkTextIter   *limit)
3988 {
3989   g_return_val_if_fail (iter != NULL, FALSE);
3990   g_return_val_if_fail (pred != NULL, FALSE);
3991
3992   if (limit &&
3993       gtk_text_iter_compare (iter, limit) >= 0)
3994     return FALSE;
3995   
3996   while ((limit == NULL ||
3997           !gtk_text_iter_equal (limit, iter)) &&
3998          gtk_text_iter_forward_char (iter))
3999     {      
4000       if (matches_pred (iter, pred, user_data))
4001         return TRUE;
4002     }
4003
4004   return FALSE;
4005 }
4006
4007 /**
4008  * gtk_text_iter_backward_find_char:
4009  * @iter: a #GtkTextIter
4010  * @pred: function to be called on each character
4011  * @user_data: user data for @pred
4012  * @limit: search limit, or %NULL for none
4013  * 
4014  * Same as gtk_text_iter_forward_find_char(), but goes backward from @iter.
4015  * 
4016  * Return value: whether a match was found
4017  **/
4018 gboolean
4019 gtk_text_iter_backward_find_char (GtkTextIter         *iter,
4020                                   GtkTextCharPredicate pred,
4021                                   gpointer             user_data,
4022                                   const GtkTextIter   *limit)
4023 {
4024   g_return_val_if_fail (iter != NULL, FALSE);
4025   g_return_val_if_fail (pred != NULL, FALSE);
4026
4027   if (limit &&
4028       gtk_text_iter_compare (iter, limit) <= 0)
4029     return FALSE;
4030   
4031   while ((limit == NULL ||
4032           !gtk_text_iter_equal (limit, iter)) &&
4033          gtk_text_iter_backward_char (iter))
4034     {
4035       if (matches_pred (iter, pred, user_data))
4036         return TRUE;
4037     }
4038
4039   return FALSE;
4040 }
4041
4042 static void
4043 forward_chars_with_skipping (GtkTextIter *iter,
4044                              gint         count,
4045                              gboolean     skip_invisible,
4046                              gboolean     skip_nontext)
4047 {
4048
4049   gint i;
4050
4051   g_return_if_fail (count >= 0);
4052
4053   i = count;
4054
4055   while (i > 0)
4056     {
4057       gboolean ignored = FALSE;
4058
4059       if (skip_nontext &&
4060           gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
4061         ignored = TRUE;
4062
4063       if (!ignored &&
4064           skip_invisible &&
4065           _gtk_text_btree_char_is_invisible (iter))
4066         ignored = TRUE;
4067
4068       gtk_text_iter_forward_char (iter);
4069
4070       if (!ignored)
4071         --i;
4072     }
4073 }
4074
4075 static gboolean
4076 lines_match (const GtkTextIter *start,
4077              const gchar **lines,
4078              gboolean visible_only,
4079              gboolean slice,
4080              GtkTextIter *match_start,
4081              GtkTextIter *match_end)
4082 {
4083   GtkTextIter next;
4084   gchar *line_text;
4085   const gchar *found;
4086   gint offset;
4087
4088   if (*lines == NULL || **lines == '\0')
4089     {
4090       if (match_start)
4091         *match_start = *start;
4092
4093       if (match_end)
4094         *match_end = *start;
4095       return TRUE;
4096     }
4097
4098   next = *start;
4099   gtk_text_iter_forward_line (&next);
4100
4101   /* No more text in buffer, but *lines is nonempty */
4102   if (gtk_text_iter_equal (start, &next))
4103     {
4104       return FALSE;
4105     }
4106
4107   if (slice)
4108     {
4109       if (visible_only)
4110         line_text = gtk_text_iter_get_visible_slice (start, &next);
4111       else
4112         line_text = gtk_text_iter_get_slice (start, &next);
4113     }
4114   else
4115     {
4116       if (visible_only)
4117         line_text = gtk_text_iter_get_visible_text (start, &next);
4118       else
4119         line_text = gtk_text_iter_get_text (start, &next);
4120     }
4121
4122   if (match_start) /* if this is the first line we're matching */
4123     found = strstr (line_text, *lines);
4124   else
4125     {
4126       /* If it's not the first line, we have to match from the
4127        * start of the line.
4128        */
4129       if (strncmp (line_text, *lines, strlen (*lines)) == 0)
4130         found = line_text;
4131       else
4132         found = NULL;
4133     }
4134
4135   if (found == NULL)
4136     {
4137       g_free (line_text);
4138       return FALSE;
4139     }
4140
4141   /* Get offset to start of search string */
4142   offset = g_utf8_strlen (line_text, found - line_text);
4143
4144   next = *start;
4145
4146   /* If match start needs to be returned, set it to the
4147    * start of the search string.
4148    */
4149   if (match_start)
4150     {
4151       *match_start = next;
4152
4153       forward_chars_with_skipping (match_start, offset,
4154                                    visible_only, !slice);
4155     }
4156
4157   /* Go to end of search string */
4158   offset += g_utf8_strlen (*lines, -1);
4159
4160   forward_chars_with_skipping (&next, offset,
4161                                visible_only, !slice);
4162
4163   g_free (line_text);
4164
4165   ++lines;
4166
4167   if (match_end)
4168     *match_end = next;
4169
4170   /* pass NULL for match_start, since we don't need to find the
4171    * start again.
4172    */
4173   return lines_match (&next, lines, visible_only, slice, NULL, match_end);
4174 }
4175
4176 /* strsplit () that retains the delimiter as part of the string. */
4177 static gchar **
4178 strbreakup (const char *string,
4179             const char *delimiter,
4180             gint        max_tokens)
4181 {
4182   GSList *string_list = NULL, *slist;
4183   gchar **str_array, *s;
4184   guint i, n = 1;
4185
4186   g_return_val_if_fail (string != NULL, NULL);
4187   g_return_val_if_fail (delimiter != NULL, NULL);
4188
4189   if (max_tokens < 1)
4190     max_tokens = G_MAXINT;
4191
4192   s = strstr (string, delimiter);
4193   if (s)
4194     {
4195       guint delimiter_len = strlen (delimiter);
4196
4197       do
4198         {
4199           guint len;
4200           gchar *new_string;
4201
4202           len = s - string + delimiter_len;
4203           new_string = g_new (gchar, len + 1);
4204           strncpy (new_string, string, len);
4205           new_string[len] = 0;
4206           string_list = g_slist_prepend (string_list, new_string);
4207           n++;
4208           string = s + delimiter_len;
4209           s = strstr (string, delimiter);
4210         }
4211       while (--max_tokens && s);
4212     }
4213   if (*string)
4214     {
4215       n++;
4216       string_list = g_slist_prepend (string_list, g_strdup (string));
4217     }
4218
4219   str_array = g_new (gchar*, n);
4220
4221   i = n - 1;
4222
4223   str_array[i--] = NULL;
4224   for (slist = string_list; slist; slist = slist->next)
4225     str_array[i--] = slist->data;
4226
4227   g_slist_free (string_list);
4228
4229   return str_array;
4230 }
4231
4232 /**
4233  * gtk_text_iter_forward_search:
4234  * @iter: start of search
4235  * @str: a search string
4236  * @visible_only: if %TRUE, search only visible text
4237  * @slice: if %TRUE, @str contains 0xFFFC when we want to match widgets, pixbufs
4238  * @match_start: return location for start of match, or %NULL
4239  * @match_end: return location for end of match, or %NULL
4240  * @limit: bound for the search, or %NULL for the end of the buffer
4241  * 
4242  * Searches forward for @str. Any match is returned as the range @match_start,
4243  * @match_end. If you specify @visible_only or @slice, the match may have
4244  * invisible text, pixbufs, or child widgets interspersed in @str.
4245  * 
4246  * Return value: whether a match was found
4247  **/
4248 gboolean
4249 gtk_text_iter_forward_search (const GtkTextIter *iter,
4250                               const gchar       *str,
4251                               gboolean           visible_only,
4252                               gboolean           slice,
4253                               GtkTextIter       *match_start,
4254                               GtkTextIter       *match_end,
4255                               const GtkTextIter *limit)
4256 {
4257   gchar **lines = NULL;
4258   GtkTextIter match;
4259   gboolean retval = FALSE;
4260   GtkTextIter search;
4261
4262   g_return_val_if_fail (iter != NULL, FALSE);
4263   g_return_val_if_fail (str != NULL, FALSE);
4264
4265   if (limit &&
4266       gtk_text_iter_compare (iter, limit) >= 0)
4267     return FALSE;
4268   
4269   if (*str == '\0')
4270     {
4271       /* If we can move one char, return the empty string there */
4272       match = *iter;
4273       
4274       if (gtk_text_iter_forward_char (&match))
4275         {
4276           if (limit &&
4277               gtk_text_iter_equal (&match, limit))
4278             return FALSE;
4279           
4280           if (match_start)
4281             *match_start = match;
4282           if (match_end)
4283             *match_end = match;
4284           return TRUE;
4285         }
4286       else
4287         return FALSE;
4288     }
4289
4290   /* locate all lines */
4291
4292   lines = strbreakup (str, "\n", -1);
4293
4294   search = *iter;
4295
4296   do
4297     {
4298       /* This loop has an inefficient worst-case, where
4299        * gtk_text_iter_get_text () is called repeatedly on
4300        * a single line.
4301        */
4302       GtkTextIter end;
4303
4304       if (limit &&
4305           gtk_text_iter_compare (&search, limit) >= 0)
4306         break;
4307       
4308       if (lines_match (&search, (const gchar**)lines,
4309                        visible_only, slice, &match, &end))
4310         {
4311           if (limit == NULL ||
4312               (limit &&
4313                gtk_text_iter_compare (&end, limit) < 0))
4314             {
4315               retval = TRUE;
4316               
4317               if (match_start)
4318                 *match_start = match;
4319               
4320               if (match_end)
4321                 *match_end = end;
4322             }
4323           
4324           break;
4325         }
4326     }
4327   while (gtk_text_iter_forward_line (&search));
4328
4329   g_strfreev ((gchar**)lines);
4330
4331   return retval;
4332 }
4333
4334 static gboolean
4335 vectors_equal_ignoring_trailing (gchar **vec1,
4336                                  gchar **vec2)
4337 {
4338   /* Ignores trailing chars in vec2's last line */
4339
4340   gchar **i1, **i2;
4341
4342   i1 = vec1;
4343   i2 = vec2;
4344
4345   while (*i1 && *i2)
4346     {
4347       if (strcmp (*i1, *i2) != 0)
4348         {
4349           if (*(i2 + 1) == NULL) /* if this is the last line */
4350             {
4351               gint len1 = strlen (*i1);
4352               gint len2 = strlen (*i2);
4353
4354               if (len2 >= len1 &&
4355                   strncmp (*i1, *i2, len1) == 0)
4356                 {
4357                   /* We matched ignoring the trailing stuff in vec2 */
4358                   return TRUE;
4359                 }
4360               else
4361                 {
4362                   return FALSE;
4363                 }
4364             }
4365           else
4366             {
4367               return FALSE;
4368             }
4369         }
4370       ++i1;
4371       ++i2;
4372     }
4373
4374   if (*i1 || *i2)
4375     {
4376       return FALSE;
4377     }
4378   else
4379     return TRUE;
4380 }
4381
4382 typedef struct _LinesWindow LinesWindow;
4383
4384 struct _LinesWindow
4385 {
4386   gint n_lines;
4387   gchar **lines;
4388   GtkTextIter first_line_start;
4389   GtkTextIter first_line_end;
4390   gboolean slice;
4391   gboolean visible_only;
4392 };
4393
4394 static void
4395 lines_window_init (LinesWindow       *win,
4396                    const GtkTextIter *start)
4397 {
4398   gint i;
4399   GtkTextIter line_start;
4400   GtkTextIter line_end;
4401
4402   /* If we start on line 1, there are 2 lines to search (0 and 1), so
4403    * n_lines can be 2.
4404    */
4405   if (gtk_text_iter_is_start (start) ||
4406       gtk_text_iter_get_line (start) + 1 < win->n_lines)
4407     {
4408       /* Already at the end, or not enough lines to match */
4409       win->lines = g_new0 (gchar*, 1);
4410       *win->lines = NULL;
4411       return;
4412     }
4413
4414   line_start = *start;
4415   line_end = *start;
4416
4417   /* Move to start iter to start of line */
4418   gtk_text_iter_set_line_offset (&line_start, 0);
4419
4420   if (gtk_text_iter_equal (&line_start, &line_end))
4421     {
4422       /* we were already at the start; so go back one line */
4423       gtk_text_iter_backward_line (&line_start);
4424     }
4425
4426   win->first_line_start = line_start;
4427   win->first_line_end = line_end;
4428
4429   win->lines = g_new0 (gchar*, win->n_lines + 1);
4430
4431   i = win->n_lines - 1;
4432   while (i >= 0)
4433     {
4434       gchar *line_text;
4435
4436       if (win->slice)
4437         {
4438           if (win->visible_only)
4439             line_text = gtk_text_iter_get_visible_slice (&line_start, &line_end);
4440           else
4441             line_text = gtk_text_iter_get_slice (&line_start, &line_end);
4442         }
4443       else
4444         {
4445           if (win->visible_only)
4446             line_text = gtk_text_iter_get_visible_text (&line_start, &line_end);
4447           else
4448             line_text = gtk_text_iter_get_text (&line_start, &line_end);
4449         }
4450
4451       win->lines[i] = line_text;
4452
4453       line_end = line_start;
4454       gtk_text_iter_backward_line (&line_start);
4455
4456       --i;
4457     }
4458 }
4459
4460 static gboolean
4461 lines_window_back (LinesWindow *win)
4462 {
4463   GtkTextIter new_start;
4464   gchar *line_text;
4465
4466   new_start = win->first_line_start;
4467
4468   if (!gtk_text_iter_backward_line (&new_start))
4469     return FALSE;
4470   else
4471     {
4472       win->first_line_start = new_start;
4473       win->first_line_end = new_start;
4474
4475       gtk_text_iter_forward_line (&win->first_line_end);
4476     }
4477
4478   if (win->slice)
4479     {
4480       if (win->visible_only)
4481         line_text = gtk_text_iter_get_visible_slice (&win->first_line_start,
4482                                                      &win->first_line_end);
4483       else
4484         line_text = gtk_text_iter_get_slice (&win->first_line_start,
4485                                              &win->first_line_end);
4486     }
4487   else
4488     {
4489       if (win->visible_only)
4490         line_text = gtk_text_iter_get_visible_text (&win->first_line_start,
4491                                                     &win->first_line_end);
4492       else
4493         line_text = gtk_text_iter_get_text (&win->first_line_start,
4494                                             &win->first_line_end);
4495     }
4496
4497   /* Move lines to make room for first line. */
4498   g_memmove (win->lines + 1, win->lines, win->n_lines * sizeof (gchar*));
4499
4500   *win->lines = line_text;
4501
4502   /* Free old last line and NULL-terminate */
4503   g_free (win->lines[win->n_lines]);
4504   win->lines[win->n_lines] = NULL;
4505
4506   return TRUE;
4507 }
4508
4509 static void
4510 lines_window_free (LinesWindow *win)
4511 {
4512   g_strfreev (win->lines);
4513 }
4514
4515 static gchar*
4516 my_strrstr (const gchar *haystack,
4517             const gchar *needle)
4518 {
4519   /* FIXME GLib should have a nice implementation in it, this
4520    * is slow-ass crap.
4521    */
4522
4523   gint haystack_len = strlen (haystack);
4524   gint needle_len = strlen (needle);
4525   const gchar *needle_end = needle + needle_len;
4526   const gchar *haystack_rend = haystack - 1;
4527   const gchar *needle_rend = needle - 1;
4528   const gchar *p;
4529
4530   p = haystack + haystack_len;
4531   while (p != haystack)
4532     {
4533       const gchar *n = needle_end - 1;
4534       const gchar *s = p - 1;
4535       while (s != haystack_rend &&
4536              n != needle_rend &&
4537              *s == *n)
4538         {
4539           --n;
4540           --s;
4541         }
4542
4543       if (n == needle_rend)
4544         return (gchar*)++s;
4545
4546       --p;
4547     }
4548
4549   return NULL;
4550 }
4551
4552 /**
4553  * gtk_text_iter_backward_search:
4554  * @iter: a #GtkTextIter where the search begins
4555  * @str: search string
4556  * @visible_only: if %TRUE search only visible text
4557  * @slice: if %TRUE the search string contains 0xFFFC to match pixbufs, widgets
4558  * @match_start: return location for start of match, or %NULL
4559  * @match_end: return location for end of match, or %NULL
4560  * @limit: location of last possible @match_start, or %NULL for start of buffer
4561  * 
4562  * Same as gtk_text_iter_forward_search(), but moves backward.
4563  * 
4564  * Return value: whether a match was found
4565  **/
4566 gboolean
4567 gtk_text_iter_backward_search (const GtkTextIter *iter,
4568                                const gchar       *str,
4569                                gboolean           visible_only,
4570                                gboolean           slice,
4571                                GtkTextIter       *match_start,
4572                                GtkTextIter       *match_end,
4573                                const GtkTextIter *limit)
4574 {
4575   gchar **lines = NULL;
4576   gchar **l;
4577   gint n_lines;
4578   LinesWindow win;
4579   gboolean retval = FALSE;
4580
4581   g_return_val_if_fail (iter != NULL, FALSE);
4582   g_return_val_if_fail (str != NULL, FALSE);
4583
4584   if (limit &&
4585       gtk_text_iter_compare (limit, iter) > 0)
4586     return FALSE;
4587   
4588   if (*str == '\0')
4589     {
4590       /* If we can move one char, return the empty string there */
4591       GtkTextIter match = *iter;
4592
4593       if (limit && gtk_text_iter_equal (limit, &match))
4594         return FALSE;
4595       
4596       if (gtk_text_iter_backward_char (&match))
4597         {
4598           if (match_start)
4599             *match_start = match;
4600           if (match_end)
4601             *match_end = match;
4602           return TRUE;
4603         }
4604       else
4605         return FALSE;
4606     }
4607
4608   /* locate all lines */
4609
4610   lines = strbreakup (str, "\n", -1);
4611
4612   l = lines;
4613   n_lines = 0;
4614   while (*l)
4615     {
4616       ++n_lines;
4617       ++l;
4618     }
4619
4620   win.n_lines = n_lines;
4621   win.slice = slice;
4622   win.visible_only = visible_only;
4623
4624   lines_window_init (&win, iter);
4625
4626   if (*win.lines == NULL)
4627     goto out;
4628
4629   do
4630     {
4631       gchar *first_line_match;
4632
4633       if (limit &&
4634           gtk_text_iter_compare (limit, &win.first_line_end) > 0)
4635         {
4636           /* We're now before the search limit, abort. */
4637           goto out;
4638         }
4639       
4640       /* If there are multiple lines, the first line will
4641        * end in '\n', so this will only match at the
4642        * end of the first line, which is correct.
4643        */
4644       first_line_match = my_strrstr (*win.lines, *lines);
4645
4646       if (first_line_match &&
4647           vectors_equal_ignoring_trailing (lines + 1, win.lines + 1))
4648         {
4649           /* Match! */
4650           gint offset;
4651           GtkTextIter next;
4652           GtkTextIter start_tmp;
4653           
4654           /* Offset to start of search string */
4655           offset = g_utf8_strlen (*win.lines, first_line_match - *win.lines);
4656
4657           next = win.first_line_start;
4658           start_tmp = next;
4659           forward_chars_with_skipping (&start_tmp, offset,
4660                                        visible_only, !slice);
4661
4662           if (limit &&
4663               gtk_text_iter_compare (limit, &start_tmp) > 0)
4664             goto out; /* match was bogus */
4665           
4666           if (match_start)
4667             *match_start = start_tmp;
4668
4669           /* Go to end of search string */
4670           l = lines;
4671           while (*l)
4672             {
4673               offset += g_utf8_strlen (*l, -1);
4674               ++l;
4675             }
4676
4677           forward_chars_with_skipping (&next, offset,
4678                                        visible_only, !slice);
4679
4680           if (match_end)
4681             *match_end = next;
4682
4683           retval = TRUE;
4684           goto out;
4685         }
4686     }
4687   while (lines_window_back (&win));
4688
4689  out:
4690   lines_window_free (&win);
4691   g_strfreev (lines);
4692   
4693   return retval;
4694 }
4695
4696 /*
4697  * Comparisons
4698  */
4699
4700 /**
4701  * gtk_text_iter_equal:
4702  * @lhs: a #GtkTextIter
4703  * @rhs: another #GtkTextIter
4704  * 
4705  * Tests whether two iterators are equal, using the fastest possible
4706  * mechanism. This function is very fast; you can expect it to perform
4707  * better than e.g. getting the character offset for each iterator and
4708  * comparing the offsets yourself. Also, it's a bit faster than
4709  * gtk_text_iter_compare().
4710  * 
4711  * Return value: %TRUE if the iterators point to the same place in the buffer
4712  **/
4713 gboolean
4714 gtk_text_iter_equal (const GtkTextIter *lhs,
4715                      const GtkTextIter *rhs)
4716 {
4717   GtkTextRealIter *real_lhs;
4718   GtkTextRealIter *real_rhs;
4719
4720   real_lhs = (GtkTextRealIter*)lhs;
4721   real_rhs = (GtkTextRealIter*)rhs;
4722
4723   check_invariants (lhs);
4724   check_invariants (rhs);
4725
4726   if (real_lhs->line != real_rhs->line)
4727     return FALSE;
4728   else if (real_lhs->line_byte_offset >= 0 &&
4729            real_rhs->line_byte_offset >= 0)
4730     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
4731   else
4732     {
4733       /* the ensure_char_offsets () calls do nothing if the char offsets
4734          are already up-to-date. */
4735       ensure_char_offsets (real_lhs);
4736       ensure_char_offsets (real_rhs);
4737       return real_lhs->line_char_offset == real_rhs->line_char_offset;
4738     }
4739 }
4740
4741 /**
4742  * gtk_text_iter_compare:
4743  * @lhs: a #GtkTextIter
4744  * @rhs: another #GtkTextIter
4745  * 
4746  * A qsort()-style function that returns negative if @lhs is less than
4747  * @rhs, positive if @lhs is greater than @rhs, and 0 if they're equal.
4748  * Ordering is in character offset order, i.e. the first character in the buffer
4749  * is less than the second character in the buffer.
4750  * 
4751  * Return value: -1 if @lhs is less than @rhs, 1 if @lhs is greater, 0 if they are equal
4752  **/
4753 gint
4754 gtk_text_iter_compare (const GtkTextIter *lhs,
4755                        const GtkTextIter *rhs)
4756 {
4757   GtkTextRealIter *real_lhs;
4758   GtkTextRealIter *real_rhs;
4759
4760   real_lhs = gtk_text_iter_make_surreal (lhs);
4761   real_rhs = gtk_text_iter_make_surreal (rhs);
4762
4763   if (real_lhs == NULL ||
4764       real_rhs == NULL)
4765     return -1; /* why not */
4766
4767   check_invariants (lhs);
4768   check_invariants (rhs);
4769   
4770   if (real_lhs->line == real_rhs->line)
4771     {
4772       gint left_index, right_index;
4773
4774       if (real_lhs->line_byte_offset >= 0 &&
4775           real_rhs->line_byte_offset >= 0)
4776         {
4777           left_index = real_lhs->line_byte_offset;
4778           right_index = real_rhs->line_byte_offset;
4779         }
4780       else
4781         {
4782           /* the ensure_char_offsets () calls do nothing if
4783              the offsets are already up-to-date. */
4784           ensure_char_offsets (real_lhs);
4785           ensure_char_offsets (real_rhs);
4786           left_index = real_lhs->line_char_offset;
4787           right_index = real_rhs->line_char_offset;
4788         }
4789
4790       if (left_index < right_index)
4791         return -1;
4792       else if (left_index > right_index)
4793         return 1;
4794       else
4795         return 0;
4796     }
4797   else
4798     {
4799       gint line1, line2;
4800
4801       line1 = gtk_text_iter_get_line (lhs);
4802       line2 = gtk_text_iter_get_line (rhs);
4803       if (line1 < line2)
4804         return -1;
4805       else if (line1 > line2)
4806         return 1;
4807       else
4808         return 0;
4809     }
4810 }
4811
4812 /**
4813  * gtk_text_iter_in_range:
4814  * @iter: a #GtkTextIter
4815  * @start: start of range
4816  * @end: end of range
4817  * 
4818  * Checks whether @iter falls in the range [@start, @end).
4819  * @start and @end must be in ascending order.
4820  * 
4821  * Return value: %TRUE if @iter is in the range
4822  **/
4823 gboolean
4824 gtk_text_iter_in_range (const GtkTextIter *iter,
4825                         const GtkTextIter *start,
4826                         const GtkTextIter *end)
4827 {
4828   g_return_val_if_fail (iter != NULL, FALSE);
4829   g_return_val_if_fail (start != NULL, FALSE);
4830   g_return_val_if_fail (end != NULL, FALSE);
4831   g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE);
4832   
4833   return gtk_text_iter_compare (iter, start) >= 0 &&
4834     gtk_text_iter_compare (iter, end) < 0;
4835 }
4836
4837 /**
4838  * gtk_text_iter_order:
4839  * @first: a #GtkTextIter
4840  * @second: another #GtkTextIter
4841  *
4842  * Swaps the value of @first and @second if @second comes before
4843  * @first in the buffer. That is, ensures that @first and @second are
4844  * in sequence. Most text buffer functions that take a range call this
4845  * automatically on your behalf, so there's no real reason to call it yourself
4846  * in those cases. There are some exceptions, such as gtk_text_iter_in_range(),
4847  * that expect a pre-sorted range.
4848  * 
4849  **/
4850 void
4851 gtk_text_iter_order (GtkTextIter *first,
4852                      GtkTextIter *second)
4853 {
4854   g_return_if_fail (first != NULL);
4855   g_return_if_fail (second != NULL);
4856
4857   if (gtk_text_iter_compare (first, second) > 0)
4858     {
4859       GtkTextIter tmp;
4860
4861       tmp = *first;
4862       *first = *second;
4863       *second = tmp;
4864     }
4865 }
4866
4867 /*
4868  * Init iterators from the BTree
4869  */
4870
4871 void
4872 _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
4873                                   GtkTextIter *iter,
4874                                   gint char_index)
4875 {
4876   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4877   gint real_char_index;
4878   gint line_start;
4879   GtkTextLine *line;
4880
4881   g_return_if_fail (iter != NULL);
4882   g_return_if_fail (tree != NULL);
4883
4884   line = _gtk_text_btree_get_line_at_char (tree, char_index,
4885                                           &line_start, &real_char_index);
4886
4887   iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
4888
4889   real->cached_char_index = real_char_index;
4890
4891   check_invariants (iter);
4892 }
4893
4894 void
4895 _gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
4896                                        GtkTextIter *iter,
4897                                        gint line_number,
4898                                        gint char_on_line)
4899 {
4900   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4901   GtkTextLine *line;
4902   gint real_line;
4903
4904   g_return_if_fail (iter != NULL);
4905   g_return_if_fail (tree != NULL);
4906
4907   line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4908   
4909   iter_init_from_char_offset (iter, tree, line, char_on_line);
4910
4911   /* We might as well cache this, since we know it. */
4912   real->cached_line_number = real_line;
4913
4914   check_invariants (iter);
4915 }
4916
4917 void
4918 _gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
4919                                        GtkTextIter    *iter,
4920                                        gint            line_number,
4921                                        gint            byte_index)
4922 {
4923   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4924   GtkTextLine *line;
4925   gint real_line;
4926
4927   g_return_if_fail (iter != NULL);
4928   g_return_if_fail (tree != NULL);
4929
4930   line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4931
4932   iter_init_from_byte_offset (iter, tree, line, byte_index);
4933
4934   /* We might as well cache this, since we know it. */
4935   real->cached_line_number = real_line;
4936
4937   check_invariants (iter);
4938 }
4939
4940 void
4941 _gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
4942                                        GtkTextIter    *iter,
4943                                        GtkTextLine    *line,
4944                                        gint            byte_offset)
4945 {
4946   g_return_if_fail (iter != NULL);
4947   g_return_if_fail (tree != NULL);
4948   g_return_if_fail (line != NULL);
4949
4950   iter_init_from_byte_offset (iter, tree, line, byte_offset);
4951
4952   check_invariants (iter);
4953 }
4954
4955 gboolean
4956 _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
4957                                           GtkTextIter    *iter,
4958                                           GtkTextTag     *tag)
4959 {
4960   GtkTextLine *line;
4961
4962   g_return_val_if_fail (iter != NULL, FALSE);
4963   g_return_val_if_fail (tree != NULL, FALSE);
4964
4965   line = _gtk_text_btree_first_could_contain_tag (tree, tag);
4966
4967   if (line == NULL)
4968     {
4969       /* Set iter to last in tree */
4970       _gtk_text_btree_get_end_iter (tree, iter);
4971       check_invariants (iter);
4972       return FALSE;
4973     }
4974   else
4975     {
4976       iter_init_from_byte_offset (iter, tree, line, 0);
4977       gtk_text_iter_forward_to_tag_toggle (iter, tag);
4978       check_invariants (iter);
4979       return TRUE;
4980     }
4981 }
4982
4983 gboolean
4984 _gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
4985                                           GtkTextIter    *iter,
4986                                           GtkTextTag     *tag)
4987 {
4988   g_return_val_if_fail (iter != NULL, FALSE);
4989   g_return_val_if_fail (tree != NULL, FALSE);
4990
4991   _gtk_text_btree_get_end_iter (tree, iter);
4992   gtk_text_iter_backward_to_tag_toggle (iter, tag);
4993   check_invariants (iter);
4994   
4995   return TRUE;
4996 }
4997
4998 gboolean
4999 _gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
5000                                        GtkTextIter *iter,
5001                                        const gchar *mark_name)
5002 {
5003   GtkTextMark *mark;
5004
5005   g_return_val_if_fail (iter != NULL, FALSE);
5006   g_return_val_if_fail (tree != NULL, FALSE);
5007
5008   mark = _gtk_text_btree_get_mark_by_name (tree, mark_name);
5009
5010   if (mark == NULL)
5011     return FALSE;
5012   else
5013     {
5014       _gtk_text_btree_get_iter_at_mark (tree, iter, mark);
5015       check_invariants (iter);
5016       return TRUE;
5017     }
5018 }
5019
5020 void
5021 _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
5022                                   GtkTextIter *iter,
5023                                   GtkTextMark *mark)
5024 {
5025   GtkTextLineSegment *seg;
5026
5027   g_return_if_fail (iter != NULL);
5028   g_return_if_fail (tree != NULL);
5029   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
5030
5031   seg = mark->segment;
5032
5033   iter_init_from_segment (iter, tree,
5034                           seg->body.mark.line, seg);
5035   g_assert (seg->body.mark.line == _gtk_text_iter_get_text_line (iter));
5036   check_invariants (iter);
5037 }
5038
5039 void
5040 _gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree       *tree,
5041                                           GtkTextIter        *iter,
5042                                           GtkTextChildAnchor *anchor)
5043 {
5044   GtkTextLineSegment *seg;
5045
5046   g_return_if_fail (iter != NULL);
5047   g_return_if_fail (tree != NULL);
5048   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
5049
5050   seg = anchor->segment;
5051   
5052   iter_init_from_segment (iter, tree,
5053                           seg->body.child.line, seg);
5054   g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter));
5055   check_invariants (iter);
5056 }
5057
5058 void
5059 _gtk_text_btree_get_end_iter         (GtkTextBTree   *tree,
5060                                       GtkTextIter    *iter)
5061 {
5062   g_return_if_fail (iter != NULL);
5063   g_return_if_fail (tree != NULL);
5064
5065   _gtk_text_btree_get_iter_at_char (tree,
5066                                    iter,
5067                                    _gtk_text_btree_char_count (tree));
5068   check_invariants (iter);
5069 }
5070
5071 void
5072 _gtk_text_iter_check (const GtkTextIter *iter)
5073 {
5074   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
5075   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
5076   GtkTextLineSegment *byte_segment = NULL;
5077   GtkTextLineSegment *byte_any_segment = NULL;
5078   GtkTextLineSegment *char_segment = NULL;
5079   GtkTextLineSegment *char_any_segment = NULL;
5080   gboolean segments_updated;
5081
5082   /* This function checks our class invariants for the Iter class. */
5083
5084   g_assert (sizeof (GtkTextIter) == sizeof (GtkTextRealIter));
5085
5086   if (real->chars_changed_stamp !=
5087       _gtk_text_btree_get_chars_changed_stamp (real->tree))
5088     g_error ("iterator check failed: invalid iterator");
5089
5090   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
5091     g_error ("iterator check failed: both char and byte offsets are invalid");
5092
5093   segments_updated = (real->segments_changed_stamp ==
5094                       _gtk_text_btree_get_segments_changed_stamp (real->tree));
5095
5096 #if 0
5097   printf ("checking iter, segments %s updated, byte %d char %d\n",
5098           segments_updated ? "are" : "aren't",
5099           real->line_byte_offset,
5100           real->line_char_offset);
5101 #endif
5102
5103   if (segments_updated)
5104     {
5105       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
5106         g_error ("iterator check failed: both char and byte segment offsets are invalid");
5107
5108       if (real->segment->char_count == 0)
5109         g_error ("iterator check failed: segment is not indexable.");
5110
5111       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
5112         g_error ("segment char offset is not properly up-to-date");
5113
5114       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
5115         g_error ("segment byte offset is not properly up-to-date");
5116
5117       if (real->segment_byte_offset >= 0 &&
5118           real->segment_byte_offset >= real->segment->byte_count)
5119         g_error ("segment byte offset is too large.");
5120
5121       if (real->segment_char_offset >= 0 &&
5122           real->segment_char_offset >= real->segment->char_count)
5123         g_error ("segment char offset is too large.");
5124     }
5125
5126   if (real->line_byte_offset >= 0)
5127     {
5128       _gtk_text_line_byte_locate (real->line, real->line_byte_offset,
5129                                   &byte_segment, &byte_any_segment,
5130                                   &seg_byte_offset, &line_byte_offset);
5131
5132       if (line_byte_offset != real->line_byte_offset)
5133         g_error ("wrong byte offset was stored in iterator");
5134
5135       if (segments_updated)
5136         {
5137           if (real->segment != byte_segment)
5138             g_error ("wrong segment was stored in iterator");
5139
5140           if (real->any_segment != byte_any_segment)
5141             g_error ("wrong any_segment was stored in iterator");
5142
5143           if (seg_byte_offset != real->segment_byte_offset)
5144             g_error ("wrong segment byte offset was stored in iterator");
5145
5146           if (byte_segment->type == &gtk_text_char_type)
5147             {
5148               const gchar *p;
5149               p = byte_segment->body.chars + seg_byte_offset;
5150               
5151               if (!gtk_text_byte_begins_utf8_char (p))
5152                 g_error ("broken iterator byte index pointed into the middle of a character");
5153             }
5154         }
5155     }
5156
5157   if (real->line_char_offset >= 0)
5158     {
5159       _gtk_text_line_char_locate (real->line, real->line_char_offset,
5160                                   &char_segment, &char_any_segment,
5161                                   &seg_char_offset, &line_char_offset);
5162
5163       if (line_char_offset != real->line_char_offset)
5164         g_error ("wrong char offset was stored in iterator");
5165
5166       if (segments_updated)
5167         {          
5168           if (real->segment != char_segment)
5169             g_error ("wrong segment was stored in iterator");
5170
5171           if (real->any_segment != char_any_segment)
5172             g_error ("wrong any_segment was stored in iterator");
5173
5174           if (seg_char_offset != real->segment_char_offset)
5175             g_error ("wrong segment char offset was stored in iterator");
5176
5177           if (char_segment->type == &gtk_text_char_type)
5178             {
5179               const gchar *p;
5180               p = g_utf8_offset_to_pointer (char_segment->body.chars,
5181                                             seg_char_offset);
5182
5183               /* hmm, not likely to happen eh */
5184               if (!gtk_text_byte_begins_utf8_char (p))
5185                 g_error ("broken iterator char offset pointed into the middle of a character");
5186             }
5187         }
5188     }
5189
5190   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
5191     {
5192       if (byte_segment != char_segment)
5193         g_error ("char and byte offsets did not point to the same segment");
5194
5195       if (byte_any_segment != char_any_segment)
5196         g_error ("char and byte offsets did not point to the same any segment");
5197
5198       /* Make sure the segment offsets are equivalent, if it's a char
5199          segment. */
5200       if (char_segment->type == &gtk_text_char_type)
5201         {
5202           gint byte_offset = 0;
5203           gint char_offset = 0;
5204           while (char_offset < seg_char_offset)
5205             {
5206               const char * start = char_segment->body.chars + byte_offset;
5207               byte_offset += g_utf8_next_char (start) - start;
5208               char_offset += 1;
5209             }
5210
5211           if (byte_offset != seg_byte_offset)
5212             g_error ("byte offset did not correspond to char offset");
5213
5214           char_offset =
5215             g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
5216
5217           if (char_offset != seg_char_offset)
5218             g_error ("char offset did not correspond to byte offset");
5219
5220           if (!gtk_text_byte_begins_utf8_char (char_segment->body.chars + seg_byte_offset))
5221             g_error ("byte index for iterator does not index the start of a character");
5222         }
5223     }
5224
5225   if (real->cached_line_number >= 0)
5226     {
5227       gint should_be;
5228
5229       should_be = _gtk_text_line_get_number (real->line);
5230       if (real->cached_line_number != should_be)
5231         g_error ("wrong line number was cached");
5232     }
5233
5234   if (real->cached_char_index >= 0)
5235     {
5236       if (real->line_char_offset >= 0) /* only way we can check it
5237                                           efficiently, not a real
5238                                           invariant. */
5239         {
5240           gint char_index;
5241
5242           char_index = _gtk_text_line_char_index (real->line);
5243           char_index += real->line_char_offset;
5244
5245           if (real->cached_char_index != char_index)
5246             g_error ("wrong char index was cached");
5247         }
5248     }
5249
5250   if (_gtk_text_line_is_last (real->line, real->tree))
5251     g_error ("Iterator was on last line (past the end iterator)");
5252 }
5253