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