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