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