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