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