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