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