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