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