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