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