]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
hack on this a bit
[~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 static gboolean
2451 find_word_end_func (const PangoLogAttr *attrs,
2452                     gint          offset,
2453                     gint          min_offset,
2454                     gint          len,
2455                     gint         *found_offset,
2456                     gboolean      already_moved_initially)
2457 {
2458   if (!already_moved_initially)
2459     ++offset;
2460
2461   /* Find end of next word */
2462   while (offset < min_offset + len &&
2463          !attrs[offset].is_word_end)
2464     ++offset;
2465
2466   *found_offset = offset;
2467
2468   return offset < min_offset + len;
2469 }
2470
2471 static gboolean
2472 is_word_end_func (const PangoLogAttr *attrs,
2473                   gint          offset,
2474                   gint          min_offset,
2475                   gint          len)
2476 {
2477   return attrs[offset].is_word_end;
2478 }
2479
2480 static gboolean
2481 find_word_start_func (const PangoLogAttr *attrs,
2482                       gint          offset,
2483                       gint          min_offset,
2484                       gint          len,
2485                       gint         *found_offset,
2486                       gboolean      already_moved_initially)
2487 {
2488   if (!already_moved_initially)
2489     --offset;
2490
2491   /* Find start of prev word */
2492   while (offset >= min_offset &&
2493          !attrs[offset].is_word_start)
2494     --offset;
2495
2496   *found_offset = offset;
2497
2498   return offset >= min_offset;
2499 }
2500
2501 static gboolean
2502 is_word_start_func (const PangoLogAttr *attrs,
2503                     gint          offset,
2504                     gint          min_offset,
2505                     gint          len)
2506 {
2507   return attrs[offset].is_word_start;
2508 }
2509
2510 static gboolean
2511 inside_word_func (const PangoLogAttr *attrs,
2512                   gint          offset,
2513                   gint          min_offset,
2514                   gint          len)
2515 {
2516   /* Find next word start or end */
2517   while (offset >= min_offset &&
2518          !(attrs[offset].is_word_start || attrs[offset].is_word_end))
2519     --offset;
2520
2521   return attrs[offset].is_word_start;
2522 }
2523
2524 static gboolean
2525 test_log_attrs (const GtkTextIter *iter,
2526                 TestLogAttrFunc    func)
2527 {
2528   gint char_len;
2529   const PangoLogAttr *attrs;
2530   int offset;
2531   gboolean result = FALSE;
2532
2533   g_return_val_if_fail (iter != NULL, FALSE);
2534
2535   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2536                                                iter, &char_len);
2537
2538   offset = gtk_text_iter_get_line_offset (iter);
2539
2540   g_assert (char_len > 0);
2541   
2542   if (offset < char_len)
2543     result = (* func) (attrs, offset, 0, char_len);
2544
2545   return result;
2546 }
2547
2548 static gboolean
2549 find_line_log_attrs (const GtkTextIter *iter,
2550                      FindLogAttrFunc    func,
2551                      gint              *found_offset,
2552                      gboolean           already_moved_initially)
2553 {
2554   gint char_len;
2555   const PangoLogAttr *attrs;
2556   int offset;
2557   gboolean result = FALSE;
2558
2559   g_return_val_if_fail (iter != NULL, FALSE);
2560   
2561   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2562                                                iter, &char_len);      
2563
2564   offset = gtk_text_iter_get_line_offset (iter);
2565   
2566   g_assert (char_len > 0);
2567   
2568   if (offset < char_len)
2569     result = (* func) (attrs, offset, 0, char_len, found_offset,
2570                        already_moved_initially);
2571
2572   return result;
2573 }
2574
2575 /* FIXME this function is very, very gratuitously slow */
2576 static gboolean
2577 find_by_log_attrs (GtkTextIter    *iter,
2578                    FindLogAttrFunc func,
2579                    gboolean        forward,
2580                    gboolean        already_moved_initially)
2581 {
2582   GtkTextIter orig;
2583   gint offset = 0;
2584   gboolean found = FALSE;
2585
2586   g_return_val_if_fail (iter != NULL, FALSE);
2587
2588   orig = *iter;
2589   
2590   found = find_line_log_attrs (iter, func, &offset, already_moved_initially);
2591   
2592   if (!found)
2593     {
2594       if (forward)
2595         {
2596           if (gtk_text_iter_forward_line (iter))
2597             return find_by_log_attrs (iter, func, forward,
2598                                       TRUE);
2599           else
2600             return FALSE;
2601         }
2602       else
2603         {                    
2604           /* go to end of previous line */
2605           gtk_text_iter_set_line_offset (iter, 0);
2606           
2607           if (gtk_text_iter_backward_char (iter))
2608             return find_by_log_attrs (iter, func, forward,
2609                                       TRUE);
2610           else
2611             return FALSE;
2612         }
2613     }
2614   else
2615     {
2616       gtk_text_iter_set_line_offset (iter, offset);
2617
2618       return
2619         !gtk_text_iter_equal (iter, &orig) &&
2620         !gtk_text_iter_is_last (iter);
2621     }
2622 }
2623
2624 gboolean
2625 gtk_text_iter_forward_word_end (GtkTextIter *iter)
2626 {
2627   return find_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
2628 }
2629
2630 gboolean
2631 gtk_text_iter_backward_word_start (GtkTextIter      *iter)
2632 {
2633   return find_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
2634 }
2635
2636 /* FIXME a loop around a truly slow function means
2637  * a truly spectacularly slow function.
2638  */
2639 gboolean
2640 gtk_text_iter_forward_word_ends (GtkTextIter      *iter,
2641                                  gint              count)
2642 {
2643   g_return_val_if_fail (iter != NULL, FALSE);
2644
2645   if (count == 0)
2646     return FALSE;
2647
2648   if (count < 0)
2649     return gtk_text_iter_backward_word_starts (iter, -count);
2650
2651   if (!gtk_text_iter_forward_word_end (iter))
2652     return FALSE;
2653   --count;
2654
2655   while (count > 0)
2656     {
2657       if (!gtk_text_iter_forward_word_end (iter))
2658         break;
2659       --count;
2660     }
2661   return TRUE;
2662 }
2663
2664 gboolean
2665 gtk_text_iter_backward_word_starts (GtkTextIter      *iter,
2666                                     gint               count)
2667 {
2668   g_return_val_if_fail (iter != NULL, FALSE);
2669
2670   if (count < 0)
2671     return gtk_text_iter_forward_word_ends (iter, -count);
2672
2673   if (!gtk_text_iter_backward_word_start (iter))
2674     return FALSE;
2675   --count;
2676
2677   while (count > 0)
2678     {
2679       if (!gtk_text_iter_backward_word_start (iter))
2680         break;
2681       --count;
2682     }
2683   return TRUE;
2684 }
2685
2686
2687 gboolean
2688 gtk_text_iter_starts_word (const GtkTextIter *iter)
2689 {
2690   return test_log_attrs (iter, is_word_start_func);
2691 }
2692
2693 gboolean
2694 gtk_text_iter_ends_word (const GtkTextIter *iter)
2695 {
2696   return test_log_attrs (iter, is_word_end_func);
2697 }
2698
2699 gboolean
2700 gtk_text_iter_inside_word (const GtkTextIter *iter)
2701 {
2702   return test_log_attrs (iter, inside_word_func);
2703 }
2704
2705 static gboolean
2706 find_forward_cursor_pos_func (const PangoLogAttr *attrs,
2707                               gint          offset,
2708                               gint          min_offset,
2709                               gint          len,
2710                               gint         *found_offset,
2711                               gboolean      already_moved_initially)
2712 {
2713   if (!already_moved_initially)
2714     ++offset;
2715
2716   while (offset < (min_offset + len) &&
2717          !attrs[offset].is_cursor_position)
2718     ++offset;
2719
2720   *found_offset = offset;
2721
2722   return offset < (min_offset + len);
2723 }
2724
2725 static gboolean
2726 find_backward_cursor_pos_func (const PangoLogAttr *attrs,
2727                                gint          offset,
2728                                gint          min_offset,
2729                                gint          len,
2730                                gint         *found_offset,
2731                                gboolean      already_moved_initially)
2732 {
2733   if (!already_moved_initially)
2734     --offset;
2735
2736   while (offset > min_offset &&
2737          !attrs[offset].is_cursor_position)
2738     --offset;
2739
2740   *found_offset = offset;
2741   
2742   return offset >= min_offset;
2743 }
2744
2745 static gboolean
2746 is_cursor_pos_func (const PangoLogAttr *attrs,
2747                     gint          offset,
2748                     gint          min_offset,
2749                     gint          len)
2750 {
2751   return attrs[offset].is_cursor_position;
2752 }
2753
2754 gboolean
2755 gtk_text_iter_forward_cursor_position (GtkTextIter *iter)
2756 {
2757   return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
2758 }
2759
2760 gboolean
2761 gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
2762 {
2763   return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
2764 }
2765
2766 gboolean
2767 gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
2768                                         gint         count)
2769 {
2770   g_return_val_if_fail (iter != NULL, FALSE);
2771
2772   if (count == 0)
2773     return FALSE;
2774   
2775   if (count < 0)
2776     return gtk_text_iter_backward_cursor_positions (iter, -count);
2777   
2778   if (!gtk_text_iter_forward_cursor_position (iter))
2779     return FALSE;
2780   --count;
2781
2782   while (count > 0)
2783     {
2784       if (!gtk_text_iter_forward_cursor_position (iter))
2785         break;
2786       --count;
2787     }
2788   return TRUE;
2789 }
2790
2791 gboolean
2792 gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
2793                                          gint         count)
2794 {
2795   g_return_val_if_fail (iter != NULL, FALSE);
2796
2797   if (count == 0)
2798     return FALSE;
2799
2800   if (count < 0)
2801     return gtk_text_iter_forward_cursor_positions (iter, -count);
2802   
2803   if (!gtk_text_iter_backward_cursor_position (iter))
2804     return FALSE;
2805   --count;
2806
2807   while (count > 0)
2808     {
2809       if (!gtk_text_iter_backward_cursor_position (iter))
2810         break;
2811       --count;
2812     }
2813   return TRUE;
2814 }
2815
2816 gboolean
2817 gtk_text_iter_is_cursor_position (const GtkTextIter *iter)
2818 {
2819   return test_log_attrs (iter, is_cursor_pos_func);
2820 }
2821
2822 void
2823 gtk_text_iter_set_line_offset (GtkTextIter *iter,
2824                                gint         char_on_line)
2825 {
2826   GtkTextRealIter *real;
2827   gint chars_in_line;
2828   
2829   g_return_if_fail (iter != NULL);
2830
2831   real = gtk_text_iter_make_surreal (iter);
2832
2833   if (real == NULL)
2834     return;
2835   
2836   check_invariants (iter);
2837
2838   chars_in_line = gtk_text_iter_get_chars_in_line (iter);
2839
2840   g_return_if_fail (char_on_line <= chars_in_line);
2841
2842   if (char_on_line < chars_in_line)
2843     iter_set_from_char_offset (real, real->line, char_on_line);
2844   else
2845     gtk_text_iter_forward_line (iter); /* set to start of next line */
2846
2847   check_invariants (iter);
2848 }
2849
2850 void
2851 gtk_text_iter_set_line_index (GtkTextIter *iter,
2852                               gint         byte_on_line)
2853 {
2854   GtkTextRealIter *real;
2855   gint bytes_in_line;
2856   
2857   g_return_if_fail (iter != NULL);
2858
2859   real = gtk_text_iter_make_surreal (iter);
2860
2861   if (real == NULL)
2862     return;
2863
2864   check_invariants (iter);
2865
2866   bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
2867
2868   g_return_if_fail (byte_on_line <= bytes_in_line);
2869   
2870   if (byte_on_line < bytes_in_line)
2871     iter_set_from_byte_offset (real, real->line, byte_on_line);
2872   else
2873     gtk_text_iter_forward_line (iter);
2874
2875   if (real->segment->type == &gtk_text_char_type &&
2876       (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
2877     g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
2878                "character; this will crash the text buffer. "
2879                "Byte indexes must refer to the start of a character.",
2880                G_STRLOC, byte_on_line);
2881
2882   check_invariants (iter);
2883 }
2884
2885 void
2886 gtk_text_iter_set_line (GtkTextIter *iter,
2887                         gint         line_number)
2888 {
2889   GtkTextLine *line;
2890   gint real_line;
2891   GtkTextRealIter *real;
2892
2893   g_return_if_fail (iter != NULL);
2894
2895   real = gtk_text_iter_make_surreal (iter);
2896
2897   if (real == NULL)
2898     return;
2899
2900   check_invariants (iter);
2901
2902   line = _gtk_text_btree_get_line (real->tree, line_number, &real_line);
2903
2904   iter_set_from_char_offset (real, line, 0);
2905
2906   /* We might as well cache this, since we know it. */
2907   real->cached_line_number = real_line;
2908
2909   check_invariants (iter);
2910 }
2911
2912 void
2913 gtk_text_iter_set_offset (GtkTextIter *iter, gint char_index)
2914 {
2915   GtkTextLine *line;
2916   GtkTextRealIter *real;
2917   gint line_start;
2918   gint real_char_index;
2919
2920   g_return_if_fail (iter != NULL);
2921
2922   real = gtk_text_iter_make_surreal (iter);
2923
2924   if (real == NULL)
2925     return;
2926
2927   check_invariants (iter);
2928
2929   if (real->cached_char_index >= 0 &&
2930       real->cached_char_index == char_index)
2931     return;
2932
2933   line = _gtk_text_btree_get_line_at_char (real->tree,
2934                                            char_index,
2935                                            &line_start,
2936                                            &real_char_index);
2937
2938   iter_set_from_char_offset (real, line, real_char_index - line_start);
2939
2940   /* Go ahead and cache this since we have it. */
2941   real->cached_char_index = real_char_index;
2942
2943   check_invariants (iter);
2944 }
2945
2946 void
2947 gtk_text_iter_forward_to_end  (GtkTextIter       *iter)
2948 {
2949   GtkTextBuffer *buffer;
2950   GtkTextRealIter *real;
2951
2952   g_return_if_fail (iter != NULL);
2953
2954   real = gtk_text_iter_make_surreal (iter);
2955
2956   if (real == NULL)
2957     return;
2958
2959   buffer = _gtk_text_btree_get_buffer (real->tree);
2960
2961   gtk_text_buffer_get_last_iter (buffer, iter);
2962 }
2963
2964 /**
2965  * gtk_text_iter_forward_to_line_end:
2966  * @iter: a #GtkTextIter
2967  * 
2968  * Moves the iterator to point to the paragraph delimiter characters,
2969  * which will be either a newline, a carriage return, a carriage
2970  * return/newline in sequence, or the Unicode paragraph separator
2971  * character. If the iterator is already at the paragraph delimiter
2972  * characters, moves to the paragraph delimiter characters for the
2973  * next line.
2974  * 
2975  * Return value: %TRUE if we moved and the new location is not the end iterator
2976  **/
2977 gboolean
2978 gtk_text_iter_forward_to_line_end (GtkTextIter *iter)
2979 {
2980   gint current_offset;
2981   gint new_offset;
2982
2983   g_return_val_if_fail (iter != NULL, FALSE);
2984
2985   current_offset = gtk_text_iter_get_line_offset (iter);
2986   /* FIXME assumption that line ends in a newline; broken */
2987   new_offset = gtk_text_iter_get_chars_in_line (iter) - 1;
2988
2989   if (current_offset < new_offset)
2990     {
2991       /* Move to end of this line. */
2992       gtk_text_iter_set_line_offset (iter, new_offset);
2993       return TRUE;
2994     }
2995   else
2996     {
2997       /* Move to end of next line. */
2998       if (gtk_text_iter_forward_line (iter))
2999         {
3000           /* We don't want to move past all
3001            * empty lines.
3002            */
3003           if (!gtk_text_iter_ends_line (iter))
3004             gtk_text_iter_forward_to_line_end (iter);
3005           return TRUE;
3006         }
3007       else
3008         return FALSE;
3009     }
3010 }
3011
3012 /**
3013  * gtk_text_iter_forward_to_tag_toggle:
3014  * @iter: a #GtkTextIter
3015  * @tag: a #GtkTextTag, or NULL
3016  *
3017  * Moves forward to the next toggle (on or off) of the
3018  * #GtkTextTag @tag, or to the next toggle of any tag if
3019  * @tag is NULL. If no matching tag toggles are found,
3020  * returns FALSE, otherwise TRUE. Does not return toggles
3021  * located at @iter, only toggles after @iter. Sets @iter to
3022  * the location of the toggle, or to the end of the buffer
3023  * if no toggle is found.
3024  *
3025  * Return value: whether we found a tag toggle after @iter
3026  **/
3027 gboolean
3028 gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
3029                                      GtkTextTag  *tag)
3030 {
3031   GtkTextLine *next_line;
3032   GtkTextLine *current_line;
3033   GtkTextRealIter *real;
3034
3035   g_return_val_if_fail (iter != NULL, FALSE);
3036
3037   real = gtk_text_iter_make_real (iter);
3038
3039   if (real == NULL)
3040     return FALSE;
3041
3042   check_invariants (iter);
3043
3044   current_line = real->line;
3045   next_line = _gtk_text_line_next_could_contain_tag (current_line,
3046                                                     real->tree, tag);
3047
3048   while (_gtk_text_iter_forward_indexable_segment (iter))
3049     {
3050       /* If we went forward to a line that couldn't contain a toggle
3051          for the tag, then skip forward to a line that could contain
3052          it. This potentially skips huge hunks of the tree, so we
3053          aren't a purely linear search. */
3054       if (real->line != current_line)
3055         {
3056           if (next_line == NULL)
3057             {
3058               /* End of search. Set to end of buffer. */
3059               _gtk_text_btree_get_last_iter (real->tree, iter);
3060               return FALSE;
3061             }
3062
3063           if (real->line != next_line)
3064             iter_set_from_byte_offset (real, next_line, 0);
3065
3066           current_line = real->line;
3067           next_line = _gtk_text_line_next_could_contain_tag (current_line,
3068                                                             real->tree,
3069                                                             tag);
3070         }
3071
3072       if (gtk_text_iter_toggles_tag (iter, tag))
3073         {
3074           /* If there's a toggle here, it isn't indexable so
3075              any_segment can't be the indexable segment. */
3076           g_assert (real->any_segment != real->segment);
3077           return TRUE;
3078         }
3079     }
3080
3081   /* Check end iterator for tags */
3082   if (gtk_text_iter_toggles_tag (iter, tag))
3083     {
3084       /* If there's a toggle here, it isn't indexable so
3085          any_segment can't be the indexable segment. */
3086       g_assert (real->any_segment != real->segment);
3087       return TRUE;
3088     }
3089
3090   /* Reached end of buffer */
3091   return FALSE;
3092 }
3093
3094 /**
3095  * gtk_text_iter_backward_to_tag_toggle:
3096  * @iter: a #GtkTextIter
3097  * @tag: a #GtkTextTag, or NULL
3098  *
3099  * Moves backward to the next toggle (on or off) of the
3100  * #GtkTextTag @tag, or to the next toggle of any tag if
3101  * @tag is NULL. If no matching tag toggles are found,
3102  * returns FALSE, otherwise TRUE. Does not return toggles
3103  * located at @iter, only toggles before @iter. Sets @iter
3104  * to the location of the toggle, or the start of the buffer
3105  * if no toggle is found.
3106  *
3107  * Return value: whether we found a tag toggle before @iter
3108  **/
3109 gboolean
3110 gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
3111                                       GtkTextTag  *tag)
3112 {
3113   GtkTextLine *prev_line;
3114   GtkTextLine *current_line;
3115   GtkTextRealIter *real;
3116
3117   g_return_val_if_fail (iter != NULL, FALSE);
3118
3119   real = gtk_text_iter_make_real (iter);
3120
3121   if (real == NULL)
3122     return FALSE;
3123
3124   check_invariants (iter);
3125
3126   current_line = real->line;
3127   prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3128                                                         real->tree, tag);
3129
3130
3131   /* If we're at segment start, go to the previous segment;
3132    * if mid-segment, snap to start of current segment.
3133    */
3134   if (is_segment_start (real))
3135     {
3136       if (!_gtk_text_iter_backward_indexable_segment (iter))
3137         return FALSE;
3138     }
3139   else
3140     {
3141       ensure_char_offsets (real);
3142
3143       if (!gtk_text_iter_backward_chars (iter, real->segment_char_offset))
3144         return FALSE;
3145     }
3146
3147   do
3148     {
3149       /* If we went backward to a line that couldn't contain a toggle
3150        * for the tag, then skip backward further to a line that
3151        * could contain it. This potentially skips huge hunks of the
3152        * tree, so we aren't a purely linear search.
3153        */
3154       if (real->line != current_line)
3155         {
3156           if (prev_line == NULL)
3157             {
3158               /* End of search. Set to start of buffer. */
3159               _gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
3160               return FALSE;
3161             }
3162
3163           if (real->line != prev_line)
3164             {
3165               /* Set to last segment in prev_line (could do this
3166                * more quickly)
3167                */
3168               iter_set_from_byte_offset (real, prev_line, 0);
3169
3170               while (!at_last_indexable_segment (real))
3171                 _gtk_text_iter_forward_indexable_segment (iter);
3172             }
3173
3174           current_line = real->line;
3175           prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3176                                                                 real->tree,
3177                                                                 tag);
3178         }
3179
3180       if (gtk_text_iter_toggles_tag (iter, tag))
3181         {
3182           /* If there's a toggle here, it isn't indexable so
3183            * any_segment can't be the indexable segment.
3184            */
3185           g_assert (real->any_segment != real->segment);
3186           return TRUE;
3187         }
3188     }
3189   while (_gtk_text_iter_backward_indexable_segment (iter));
3190
3191   /* Reached front of buffer */
3192   return FALSE;
3193 }
3194
3195 static gboolean
3196 matches_pred (GtkTextIter *iter,
3197               GtkTextCharPredicate pred,
3198               gpointer user_data)
3199 {
3200   gint ch;
3201
3202   ch = gtk_text_iter_get_char (iter);
3203
3204   return (*pred) (ch, user_data);
3205 }
3206
3207 /**
3208  * gtk_text_iter_forward_find_char:
3209  * @iter: a #GtkTextIter
3210  * @pred: a function to be called on each character
3211  * @user_data: user data for @pred
3212  * @limit: search limit, or %NULL for none 
3213  * 
3214  * Advances @iter, calling @pred on each character. If
3215  * @pred returns %TRUE, returns %TRUE and stops scanning.
3216  * If @pred never returns %TRUE, @iter is set to @limit if
3217  * @limit is non-%NULL, otherwise to the end iterator.
3218  * 
3219  * Return value: whether a match was found
3220  **/
3221 gboolean
3222 gtk_text_iter_forward_find_char (GtkTextIter         *iter,
3223                                  GtkTextCharPredicate pred,
3224                                  gpointer             user_data,
3225                                  const GtkTextIter   *limit)
3226 {
3227   g_return_val_if_fail (iter != NULL, FALSE);
3228   g_return_val_if_fail (pred != NULL, FALSE);
3229
3230   if (limit &&
3231       gtk_text_iter_compare (iter, limit) >= 0)
3232     return FALSE;
3233   
3234   while ((limit == NULL ||
3235           !gtk_text_iter_equal (limit, iter)) &&
3236          gtk_text_iter_forward_char (iter))
3237     {      
3238       if (matches_pred (iter, pred, user_data))
3239         return TRUE;
3240     }
3241
3242   return FALSE;
3243 }
3244
3245 gboolean
3246 gtk_text_iter_backward_find_char (GtkTextIter         *iter,
3247                                   GtkTextCharPredicate pred,
3248                                   gpointer             user_data,
3249                                   const GtkTextIter   *limit)
3250 {
3251   g_return_val_if_fail (iter != NULL, FALSE);
3252   g_return_val_if_fail (pred != NULL, FALSE);
3253
3254   if (limit &&
3255       gtk_text_iter_compare (iter, limit) <= 0)
3256     return FALSE;
3257   
3258   while ((limit == NULL ||
3259           !gtk_text_iter_equal (limit, iter)) &&
3260          gtk_text_iter_backward_char (iter))
3261     {
3262       if (matches_pred (iter, pred, user_data))
3263         return TRUE;
3264     }
3265
3266   return FALSE;
3267 }
3268
3269 static void
3270 forward_chars_with_skipping (GtkTextIter *iter,
3271                              gint         count,
3272                              gboolean     skip_invisible,
3273                              gboolean     skip_nontext)
3274 {
3275
3276   gint i;
3277
3278   g_return_if_fail (count >= 0);
3279
3280   i = count;
3281
3282   while (i > 0)
3283     {
3284       gboolean ignored = FALSE;
3285
3286       if (skip_nontext &&
3287           gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
3288         ignored = TRUE;
3289
3290       if (!ignored &&
3291           skip_invisible &&
3292           _gtk_text_btree_char_is_invisible (iter))
3293         ignored = TRUE;
3294
3295       gtk_text_iter_forward_char (iter);
3296
3297       if (!ignored)
3298         --i;
3299     }
3300 }
3301
3302 static gboolean
3303 lines_match (const GtkTextIter *start,
3304              const gchar **lines,
3305              gboolean visible_only,
3306              gboolean slice,
3307              GtkTextIter *match_start,
3308              GtkTextIter *match_end)
3309 {
3310   GtkTextIter next;
3311   gchar *line_text;
3312   const gchar *found;
3313   gint offset;
3314
3315   if (*lines == NULL || **lines == '\0')
3316     {
3317       if (match_start)
3318         *match_start = *start;
3319
3320       if (match_end)
3321         *match_end = *start;
3322       return TRUE;
3323     }
3324
3325   next = *start;
3326   gtk_text_iter_forward_line (&next);
3327
3328   /* No more text in buffer, but *lines is nonempty */
3329   if (gtk_text_iter_equal (start, &next))
3330     {
3331       return FALSE;
3332     }
3333
3334   if (slice)
3335     {
3336       if (visible_only)
3337         line_text = gtk_text_iter_get_visible_slice (start, &next);
3338       else
3339         line_text = gtk_text_iter_get_slice (start, &next);
3340     }
3341   else
3342     {
3343       if (visible_only)
3344         line_text = gtk_text_iter_get_visible_text (start, &next);
3345       else
3346         line_text = gtk_text_iter_get_text (start, &next);
3347     }
3348
3349   if (match_start) /* if this is the first line we're matching */
3350     found = strstr (line_text, *lines);
3351   else
3352     {
3353       /* If it's not the first line, we have to match from the
3354        * start of the line.
3355        */
3356       if (strncmp (line_text, *lines, strlen (*lines)) == 0)
3357         found = line_text;
3358       else
3359         found = NULL;
3360     }
3361
3362   if (found == NULL)
3363     {
3364       g_free (line_text);
3365       return FALSE;
3366     }
3367
3368   /* Get offset to start of search string */
3369   offset = g_utf8_strlen (line_text, found - line_text);
3370
3371   next = *start;
3372
3373   /* If match start needs to be returned, set it to the
3374    * start of the search string.
3375    */
3376   if (match_start)
3377     {
3378       *match_start = next;
3379
3380       forward_chars_with_skipping (match_start, offset,
3381                                    visible_only, !slice);
3382     }
3383
3384   /* Go to end of search string */
3385   offset += g_utf8_strlen (*lines, -1);
3386
3387   forward_chars_with_skipping (&next, offset,
3388                                visible_only, !slice);
3389
3390   g_free (line_text);
3391
3392   ++lines;
3393
3394   if (match_end)
3395     *match_end = next;
3396
3397   /* pass NULL for match_start, since we don't need to find the
3398    * start again.
3399    */
3400   return lines_match (&next, lines, visible_only, slice, NULL, match_end);
3401 }
3402
3403 /* strsplit () that retains the delimiter as part of the string. */
3404 static gchar **
3405 strbreakup (const char *string,
3406             const char *delimiter,
3407             gint        max_tokens)
3408 {
3409   GSList *string_list = NULL, *slist;
3410   gchar **str_array, *s;
3411   guint i, n = 1;
3412
3413   g_return_val_if_fail (string != NULL, NULL);
3414   g_return_val_if_fail (delimiter != NULL, NULL);
3415
3416   if (max_tokens < 1)
3417     max_tokens = G_MAXINT;
3418
3419   s = strstr (string, delimiter);
3420   if (s)
3421     {
3422       guint delimiter_len = strlen (delimiter);
3423
3424       do
3425         {
3426           guint len;
3427           gchar *new_string;
3428
3429           len = s - string + delimiter_len;
3430           new_string = g_new (gchar, len + 1);
3431           strncpy (new_string, string, len);
3432           new_string[len] = 0;
3433           string_list = g_slist_prepend (string_list, new_string);
3434           n++;
3435           string = s + delimiter_len;
3436           s = strstr (string, delimiter);
3437         }
3438       while (--max_tokens && s);
3439     }
3440   if (*string)
3441     {
3442       n++;
3443       string_list = g_slist_prepend (string_list, g_strdup (string));
3444     }
3445
3446   str_array = g_new (gchar*, n);
3447
3448   i = n - 1;
3449
3450   str_array[i--] = NULL;
3451   for (slist = string_list; slist; slist = slist->next)
3452     str_array[i--] = slist->data;
3453
3454   g_slist_free (string_list);
3455
3456   return str_array;
3457 }
3458
3459 /**
3460  * gtk_text_iter_forward_search:
3461  * @iter: start of search
3462  * @str: a search string
3463  * @visible_only: if %TRUE, search only visible text
3464  * @slice: if %TRUE, @str contains 0xFFFC when we want to match widgets, pixbufs
3465  * @match_start: return location for start of match, or %NULL
3466  * @match_end: return location for end of match, or %NULL
3467  * @limit: bound for the search, or %NULL for the end of the buffer
3468  * 
3469  * 
3470  * 
3471  * Return value: whether a match was found
3472  **/
3473 gboolean
3474 gtk_text_iter_forward_search (const GtkTextIter *iter,
3475                               const gchar       *str,
3476                               gboolean           visible_only,
3477                               gboolean           slice,
3478                               GtkTextIter       *match_start,
3479                               GtkTextIter       *match_end,
3480                               const GtkTextIter *limit)
3481 {
3482   gchar **lines = NULL;
3483   GtkTextIter match;
3484   gboolean retval = FALSE;
3485   GtkTextIter search;
3486
3487   g_return_val_if_fail (iter != NULL, FALSE);
3488   g_return_val_if_fail (str != NULL, FALSE);
3489
3490   if (limit &&
3491       gtk_text_iter_compare (iter, limit) >= 0)
3492     return FALSE;
3493   
3494   if (*str == '\0')
3495     {
3496       /* If we can move one char, return the empty string there */
3497       match = *iter;
3498       
3499       if (gtk_text_iter_forward_char (&match))
3500         {
3501           if (limit &&
3502               gtk_text_iter_equal (&match, limit))
3503             return FALSE;
3504           
3505           if (match_start)
3506             *match_start = match;
3507           if (match_end)
3508             *match_end = match;
3509           return TRUE;
3510         }
3511       else
3512         return FALSE;
3513     }
3514
3515   /* locate all lines */
3516
3517   lines = strbreakup (str, "\n", -1);
3518
3519   search = *iter;
3520
3521   do
3522     {
3523       /* This loop has an inefficient worst-case, where
3524        * gtk_text_iter_get_text () is called repeatedly on
3525        * a single line.
3526        */
3527       GtkTextIter end;
3528
3529       if (limit &&
3530           gtk_text_iter_compare (&search, limit) >= 0)
3531         break;
3532       
3533       if (lines_match (&search, (const gchar**)lines,
3534                        visible_only, slice, &match, &end))
3535         {
3536           if (limit == NULL ||
3537               (limit &&
3538                gtk_text_iter_compare (&end, limit) < 0))
3539             {
3540               retval = TRUE;
3541               
3542               if (match_start)
3543                 *match_start = match;
3544               
3545               if (match_end)
3546                 *match_end = end;
3547             }
3548           
3549           break;
3550         }
3551     }
3552   while (gtk_text_iter_forward_line (&search));
3553
3554   g_strfreev ((gchar**)lines);
3555
3556   return retval;
3557 }
3558
3559 static gboolean
3560 vectors_equal_ignoring_trailing (gchar **vec1,
3561                                  gchar **vec2)
3562 {
3563   /* Ignores trailing chars in vec2's last line */
3564
3565   gchar **i1, **i2;
3566
3567   i1 = vec1;
3568   i2 = vec2;
3569
3570   while (*i1 && *i2)
3571     {
3572       if (strcmp (*i1, *i2) != 0)
3573         {
3574           if (*(i2 + 1) == NULL) /* if this is the last line */
3575             {
3576               gint len1 = strlen (*i1);
3577               gint len2 = strlen (*i2);
3578
3579               if (len2 >= len1 &&
3580                   strncmp (*i1, *i2, len1) == 0)
3581                 {
3582                   /* We matched ignoring the trailing stuff in vec2 */
3583                   return TRUE;
3584                 }
3585               else
3586                 {
3587                   return FALSE;
3588                 }
3589             }
3590           else
3591             {
3592               return FALSE;
3593             }
3594         }
3595       ++i1;
3596       ++i2;
3597     }
3598
3599   if (*i1 || *i2)
3600     {
3601       return FALSE;
3602     }
3603   else
3604     return TRUE;
3605 }
3606
3607 typedef struct _LinesWindow LinesWindow;
3608
3609 struct _LinesWindow
3610 {
3611   gint n_lines;
3612   gchar **lines;
3613   GtkTextIter first_line_start;
3614   GtkTextIter first_line_end;
3615   gboolean slice;
3616   gboolean visible_only;
3617 };
3618
3619 static void
3620 lines_window_init (LinesWindow       *win,
3621                    const GtkTextIter *start)
3622 {
3623   gint i;
3624   GtkTextIter line_start;
3625   GtkTextIter line_end;
3626
3627   /* If we start on line 1, there are 2 lines to search (0 and 1), so
3628    * n_lines can be 2.
3629    */
3630   if (gtk_text_iter_is_first (start) ||
3631       gtk_text_iter_get_line (start) + 1 < win->n_lines)
3632     {
3633       /* Already at the end, or not enough lines to match */
3634       win->lines = g_new0 (gchar*, 1);
3635       *win->lines = NULL;
3636       return;
3637     }
3638
3639   line_start = *start;
3640   line_end = *start;
3641
3642   /* Move to start iter to start of line */
3643   gtk_text_iter_set_line_offset (&line_start, 0);
3644
3645   if (gtk_text_iter_equal (&line_start, &line_end))
3646     {
3647       /* we were already at the start; so go back one line */
3648       gtk_text_iter_backward_line (&line_start);
3649     }
3650
3651   win->first_line_start = line_start;
3652   win->first_line_end = line_end;
3653
3654   win->lines = g_new0 (gchar*, win->n_lines + 1);
3655
3656   i = win->n_lines - 1;
3657   while (i >= 0)
3658     {
3659       gchar *line_text;
3660
3661       if (win->slice)
3662         {
3663           if (win->visible_only)
3664             line_text = gtk_text_iter_get_visible_slice (&line_start, &line_end);
3665           else
3666             line_text = gtk_text_iter_get_slice (&line_start, &line_end);
3667         }
3668       else
3669         {
3670           if (win->visible_only)
3671             line_text = gtk_text_iter_get_visible_text (&line_start, &line_end);
3672           else
3673             line_text = gtk_text_iter_get_text (&line_start, &line_end);
3674         }
3675
3676       win->lines[i] = line_text;
3677
3678       line_end = line_start;
3679       gtk_text_iter_backward_line (&line_start);
3680
3681       --i;
3682     }
3683 }
3684
3685 static gboolean
3686 lines_window_back (LinesWindow *win)
3687 {
3688   GtkTextIter new_start;
3689   gchar *line_text;
3690
3691   new_start = win->first_line_start;
3692
3693   if (!gtk_text_iter_backward_line (&new_start))
3694     return FALSE;
3695   else
3696     {
3697       win->first_line_start = new_start;
3698       win->first_line_end = new_start;
3699
3700       gtk_text_iter_forward_line (&win->first_line_end);
3701     }
3702
3703   if (win->slice)
3704     {
3705       if (win->visible_only)
3706         line_text = gtk_text_iter_get_visible_slice (&win->first_line_start,
3707                                                      &win->first_line_end);
3708       else
3709         line_text = gtk_text_iter_get_slice (&win->first_line_start,
3710                                              &win->first_line_end);
3711     }
3712   else
3713     {
3714       if (win->visible_only)
3715         line_text = gtk_text_iter_get_visible_text (&win->first_line_start,
3716                                                     &win->first_line_end);
3717       else
3718         line_text = gtk_text_iter_get_text (&win->first_line_start,
3719                                             &win->first_line_end);
3720     }
3721
3722   /* Move lines to make room for first line. */
3723   g_memmove (win->lines + 1, win->lines, win->n_lines * sizeof (gchar*));
3724
3725   *win->lines = line_text;
3726
3727   /* Free old last line and NULL-terminate */
3728   g_free (win->lines[win->n_lines]);
3729   win->lines[win->n_lines] = NULL;
3730
3731   return TRUE;
3732 }
3733
3734 static void
3735 lines_window_free (LinesWindow *win)
3736 {
3737   g_strfreev (win->lines);
3738 }
3739
3740 static gchar*
3741 my_strrstr (const gchar *haystack,
3742             const gchar *needle)
3743 {
3744   /* FIXME GLib should have a nice implementation in it, this
3745    * is slow-ass crap.
3746    */
3747
3748   gint haystack_len = strlen (haystack);
3749   gint needle_len = strlen (needle);
3750   const gchar *needle_end = needle + needle_len;
3751   const gchar *haystack_rend = haystack - 1;
3752   const gchar *needle_rend = needle - 1;
3753   const gchar *p;
3754
3755   p = haystack + haystack_len;
3756   while (p != haystack)
3757     {
3758       const gchar *n = needle_end - 1;
3759       const gchar *s = p - 1;
3760       while (s != haystack_rend &&
3761              n != needle_rend &&
3762              *s == *n)
3763         {
3764           --n;
3765           --s;
3766         }
3767
3768       if (n == needle_rend)
3769         return (gchar*)++s;
3770
3771       --p;
3772     }
3773
3774   return NULL;
3775 }
3776
3777 /**
3778  * gtk_text_iter_backward_search:
3779  * @iter: a #GtkTextIter where the search begins
3780  * @str: search string
3781  * @visible_only: if %TRUE search only visible text
3782  * @slice: if %TRUE the search string contains 0xFFFC to match pixbufs, widgets
3783  * @match_start: return location for start of match, or %NULL
3784  * @match_end: return location for end of match, or %NULL
3785  * @limit: location of last possible @match_start, or %NULL for start of buffer
3786  * 
3787  * 
3788  * 
3789  * Return value: whether a match was found
3790  **/
3791 gboolean
3792 gtk_text_iter_backward_search (const GtkTextIter *iter,
3793                                const gchar       *str,
3794                                gboolean           visible_only,
3795                                gboolean           slice,
3796                                GtkTextIter       *match_start,
3797                                GtkTextIter       *match_end,
3798                                const GtkTextIter *limit)
3799 {
3800   gchar **lines = NULL;
3801   gchar **l;
3802   gint n_lines;
3803   LinesWindow win;
3804   gboolean retval = FALSE;
3805
3806   g_return_val_if_fail (iter != NULL, FALSE);
3807   g_return_val_if_fail (str != NULL, FALSE);
3808
3809   if (limit &&
3810       gtk_text_iter_compare (limit, iter) > 0)
3811     return FALSE;
3812   
3813   if (*str == '\0')
3814     {
3815       /* If we can move one char, return the empty string there */
3816       GtkTextIter match = *iter;
3817
3818       if (limit && gtk_text_iter_equal (limit, &match))
3819         return FALSE;
3820       
3821       if (gtk_text_iter_backward_char (&match))
3822         {
3823           if (match_start)
3824             *match_start = match;
3825           if (match_end)
3826             *match_end = match;
3827           return TRUE;
3828         }
3829       else
3830         return FALSE;
3831     }
3832
3833   /* locate all lines */
3834
3835   lines = strbreakup (str, "\n", -1);
3836
3837   l = lines;
3838   n_lines = 0;
3839   while (*l)
3840     {
3841       ++n_lines;
3842       ++l;
3843     }
3844
3845   win.n_lines = n_lines;
3846   win.slice = slice;
3847   win.visible_only = visible_only;
3848
3849   lines_window_init (&win, iter);
3850
3851   if (*win.lines == NULL)
3852     goto out;
3853
3854   do
3855     {
3856       gchar *first_line_match;
3857
3858       if (limit &&
3859           gtk_text_iter_compare (limit, &win.first_line_end) > 0)
3860         {
3861           /* We're now before the search limit, abort. */
3862           goto out;
3863         }
3864       
3865       /* If there are multiple lines, the first line will
3866        * end in '\n', so this will only match at the
3867        * end of the first line, which is correct.
3868        */
3869       first_line_match = my_strrstr (*win.lines, *lines);
3870
3871       if (first_line_match &&
3872           vectors_equal_ignoring_trailing (lines + 1, win.lines + 1))
3873         {
3874           /* Match! */
3875           gint offset;
3876           GtkTextIter next;
3877           GtkTextIter start_tmp;
3878           
3879           /* Offset to start of search string */
3880           offset = g_utf8_strlen (*win.lines, first_line_match - *win.lines);
3881
3882           next = win.first_line_start;
3883           start_tmp = next;
3884           forward_chars_with_skipping (&start_tmp, offset,
3885                                        visible_only, !slice);
3886
3887           if (limit &&
3888               gtk_text_iter_compare (limit, &start_tmp) > 0)
3889             goto out; /* match was bogus */
3890           
3891           if (match_start)
3892             *match_start = start_tmp;
3893
3894           /* Go to end of search string */
3895           l = lines;
3896           while (*l)
3897             {
3898               offset += g_utf8_strlen (*l, -1);
3899               ++l;
3900             }
3901
3902           forward_chars_with_skipping (&next, offset,
3903                                        visible_only, !slice);
3904
3905           if (match_end)
3906             *match_end = next;
3907
3908           retval = TRUE;
3909           goto out;
3910         }
3911     }
3912   while (lines_window_back (&win));
3913
3914  out:
3915   lines_window_free (&win);
3916   g_strfreev (lines);
3917   
3918   return retval;
3919 }
3920
3921 /*
3922  * Comparisons
3923  */
3924
3925 gboolean
3926 gtk_text_iter_equal (const GtkTextIter *lhs,
3927                      const GtkTextIter *rhs)
3928 {
3929   GtkTextRealIter *real_lhs;
3930   GtkTextRealIter *real_rhs;
3931
3932   real_lhs = (GtkTextRealIter*)lhs;
3933   real_rhs = (GtkTextRealIter*)rhs;
3934
3935   check_invariants (lhs);
3936   check_invariants (rhs);
3937
3938   if (real_lhs->line != real_rhs->line)
3939     return FALSE;
3940   else if (real_lhs->line_byte_offset >= 0 &&
3941            real_rhs->line_byte_offset >= 0)
3942     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
3943   else
3944     {
3945       /* the ensure_char_offsets () calls do nothing if the char offsets
3946          are already up-to-date. */
3947       ensure_char_offsets (real_lhs);
3948       ensure_char_offsets (real_rhs);
3949       return real_lhs->line_char_offset == real_rhs->line_char_offset;
3950     }
3951 }
3952
3953 gint
3954 gtk_text_iter_compare (const GtkTextIter *lhs, const GtkTextIter *rhs)
3955 {
3956   GtkTextRealIter *real_lhs;
3957   GtkTextRealIter *real_rhs;
3958
3959   real_lhs = gtk_text_iter_make_surreal (lhs);
3960   real_rhs = gtk_text_iter_make_surreal (rhs);
3961
3962   check_invariants (lhs);
3963   check_invariants (rhs);
3964
3965   if (real_lhs == NULL ||
3966       real_rhs == NULL)
3967     return -1; /* why not */
3968
3969   if (real_lhs->line == real_rhs->line)
3970     {
3971       gint left_index, right_index;
3972
3973       if (real_lhs->line_byte_offset >= 0 &&
3974           real_rhs->line_byte_offset >= 0)
3975         {
3976           left_index = real_lhs->line_byte_offset;
3977           right_index = real_rhs->line_byte_offset;
3978         }
3979       else
3980         {
3981           /* the ensure_char_offsets () calls do nothing if
3982              the offsets are already up-to-date. */
3983           ensure_char_offsets (real_lhs);
3984           ensure_char_offsets (real_rhs);
3985           left_index = real_lhs->line_char_offset;
3986           right_index = real_rhs->line_char_offset;
3987         }
3988
3989       if (left_index < right_index)
3990         return -1;
3991       else if (left_index > right_index)
3992         return 1;
3993       else
3994         return 0;
3995     }
3996   else
3997     {
3998       gint line1, line2;
3999
4000       line1 = gtk_text_iter_get_line (lhs);
4001       line2 = gtk_text_iter_get_line (rhs);
4002       if (line1 < line2)
4003         return -1;
4004       else if (line1 > line2)
4005         return 1;
4006       else
4007         return 0;
4008     }
4009 }
4010
4011 gboolean
4012 gtk_text_iter_in_range (const GtkTextIter *iter,
4013                         const GtkTextIter *start,
4014                         const GtkTextIter *end)
4015 {
4016   return gtk_text_iter_compare (iter, start) >= 0 &&
4017     gtk_text_iter_compare (iter, end) < 0;
4018 }
4019
4020 void
4021 gtk_text_iter_reorder         (GtkTextIter *first,
4022                                GtkTextIter *second)
4023 {
4024   g_return_if_fail (first != NULL);
4025   g_return_if_fail (second != NULL);
4026
4027   if (gtk_text_iter_compare (first, second) > 0)
4028     {
4029       GtkTextIter tmp;
4030
4031       tmp = *first;
4032       *first = *second;
4033       *second = tmp;
4034     }
4035 }
4036
4037 /*
4038  * Init iterators from the BTree
4039  */
4040
4041 void
4042 _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
4043                                   GtkTextIter *iter,
4044                                   gint char_index)
4045 {
4046   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4047   gint real_char_index;
4048   gint line_start;
4049   GtkTextLine *line;
4050
4051   g_return_if_fail (iter != NULL);
4052   g_return_if_fail (tree != NULL);
4053
4054   line = _gtk_text_btree_get_line_at_char (tree, char_index,
4055                                           &line_start, &real_char_index);
4056
4057   iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
4058
4059   real->cached_char_index = real_char_index;
4060
4061   check_invariants (iter);
4062 }
4063
4064 void
4065 _gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
4066                                        GtkTextIter *iter,
4067                                        gint line_number,
4068                                        gint char_on_line)
4069 {
4070   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4071   GtkTextLine *line;
4072   gint real_line;
4073
4074   g_return_if_fail (iter != NULL);
4075   g_return_if_fail (tree != NULL);
4076
4077   line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4078   
4079   iter_init_from_char_offset (iter, tree, line, char_on_line);
4080
4081   /* We might as well cache this, since we know it. */
4082   real->cached_line_number = real_line;
4083
4084   check_invariants (iter);
4085 }
4086
4087 void
4088 _gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
4089                                        GtkTextIter    *iter,
4090                                        gint            line_number,
4091                                        gint            byte_index)
4092 {
4093   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4094   GtkTextLine *line;
4095   gint real_line;
4096
4097   g_return_if_fail (iter != NULL);
4098   g_return_if_fail (tree != NULL);
4099
4100   line = _gtk_text_btree_get_line (tree, line_number, &real_line);
4101
4102   iter_init_from_byte_offset (iter, tree, line, byte_index);
4103
4104   /* We might as well cache this, since we know it. */
4105   real->cached_line_number = real_line;
4106
4107   if (real->segment->type == &gtk_text_char_type &&
4108       (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
4109     g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
4110                "character; this will crash the text buffer. "
4111                "Byte indexes must refer to the start of a character.",
4112                G_STRLOC, byte_index);
4113
4114   check_invariants (iter);
4115 }
4116
4117 void
4118 _gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
4119                                        GtkTextIter    *iter,
4120                                        GtkTextLine    *line,
4121                                        gint            byte_offset)
4122 {
4123   g_return_if_fail (iter != NULL);
4124   g_return_if_fail (tree != NULL);
4125   g_return_if_fail (line != NULL);
4126
4127   iter_init_from_byte_offset (iter, tree, line, byte_offset);
4128
4129   check_invariants (iter);
4130 }
4131
4132 gboolean
4133 _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
4134                                          GtkTextIter    *iter,
4135                                          GtkTextTag     *tag)
4136 {
4137   GtkTextLine *line;
4138
4139   g_return_val_if_fail (iter != NULL, FALSE);
4140   g_return_val_if_fail (tree != NULL, FALSE);
4141
4142   line = _gtk_text_btree_first_could_contain_tag (tree, tag);
4143
4144   if (line == NULL)
4145     {
4146       /* Set iter to last in tree */
4147       _gtk_text_btree_get_last_iter (tree, iter);
4148       check_invariants (iter);
4149       return FALSE;
4150     }
4151   else
4152     {
4153       iter_init_from_byte_offset (iter, tree, line, 0);
4154       gtk_text_iter_forward_to_tag_toggle (iter, tag);
4155       check_invariants (iter);
4156       return TRUE;
4157     }
4158 }
4159
4160 gboolean
4161 _gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
4162                                          GtkTextIter    *iter,
4163                                          GtkTextTag     *tag)
4164 {
4165   GtkTextLine *line;
4166
4167   g_return_val_if_fail (iter != NULL, FALSE);
4168   g_return_val_if_fail (tree != NULL, FALSE);
4169
4170   line = _gtk_text_btree_last_could_contain_tag (tree, tag);
4171
4172   if (line == NULL)
4173     {
4174       /* Set iter to first in tree */
4175       _gtk_text_btree_get_iter_at_line_char (tree, iter, 0, 0);
4176       check_invariants (iter);
4177       return FALSE;
4178     }
4179   else
4180     {
4181       iter_init_from_byte_offset (iter, tree, line, -1);
4182       gtk_text_iter_backward_to_tag_toggle (iter, tag);
4183       check_invariants (iter);
4184       return TRUE;
4185     }
4186 }
4187
4188 gboolean
4189 _gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
4190                                       GtkTextIter *iter,
4191                                       const gchar *mark_name)
4192 {
4193   GtkTextMark *mark;
4194
4195   g_return_val_if_fail (iter != NULL, FALSE);
4196   g_return_val_if_fail (tree != NULL, FALSE);
4197
4198   mark = _gtk_text_btree_get_mark_by_name (tree, mark_name);
4199
4200   if (mark == NULL)
4201     return FALSE;
4202   else
4203     {
4204       _gtk_text_btree_get_iter_at_mark (tree, iter, mark);
4205       check_invariants (iter);
4206       return TRUE;
4207     }
4208 }
4209
4210 void
4211 _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
4212                                  GtkTextIter *iter,
4213                                  GtkTextMark *mark)
4214 {
4215   GtkTextLineSegment *seg;
4216
4217   g_return_if_fail (iter != NULL);
4218   g_return_if_fail (tree != NULL);
4219   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
4220
4221   seg = mark->segment;
4222
4223   iter_init_from_segment (iter, tree,
4224                           seg->body.mark.line, seg);
4225   g_assert (seg->body.mark.line == _gtk_text_iter_get_text_line (iter));
4226   check_invariants (iter);
4227 }
4228
4229 void
4230 _gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree       *tree,
4231                                          GtkTextIter        *iter,
4232                                          GtkTextChildAnchor *anchor)
4233 {
4234   GtkTextLineSegment *seg;
4235
4236   g_return_if_fail (iter != NULL);
4237   g_return_if_fail (tree != NULL);
4238   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
4239
4240   seg = anchor->segment;
4241   
4242   iter_init_from_segment (iter, tree,
4243                           seg->body.child.line, seg);
4244   g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter));
4245   check_invariants (iter);
4246 }
4247
4248 void
4249 _gtk_text_btree_get_last_iter         (GtkTextBTree   *tree,
4250                                       GtkTextIter    *iter)
4251 {
4252   g_return_if_fail (iter != NULL);
4253   g_return_if_fail (tree != NULL);
4254
4255   _gtk_text_btree_get_iter_at_char (tree,
4256                                    iter,
4257                                    _gtk_text_btree_char_count (tree));
4258   check_invariants (iter);
4259 }
4260
4261 void
4262 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
4263 {
4264   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4265
4266   g_return_if_fail (iter != NULL);
4267
4268   if (real->chars_changed_stamp != _gtk_text_btree_get_chars_changed_stamp (real->tree))
4269     g_print (" %20s: <invalidated iterator>\n", desc);
4270   else
4271     {
4272       check_invariants (iter);
4273       g_print (" %20s: line %d / char %d / line char %d / line byte %d\n",
4274                desc,
4275                gtk_text_iter_get_line (iter),
4276                gtk_text_iter_get_offset (iter),
4277                gtk_text_iter_get_line_offset (iter),
4278                gtk_text_iter_get_line_index (iter));
4279       check_invariants (iter);
4280     }
4281 }
4282
4283 void
4284 _gtk_text_iter_check (const GtkTextIter *iter)
4285 {
4286   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
4287   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
4288   GtkTextLineSegment *byte_segment = NULL;
4289   GtkTextLineSegment *byte_any_segment = NULL;
4290   GtkTextLineSegment *char_segment = NULL;
4291   GtkTextLineSegment *char_any_segment = NULL;
4292   gboolean segments_updated;
4293
4294   /* This function checks our class invariants for the Iter class. */
4295
4296   g_assert (sizeof (GtkTextIter) == sizeof (GtkTextRealIter));
4297
4298   if (real->chars_changed_stamp !=
4299       _gtk_text_btree_get_chars_changed_stamp (real->tree))
4300     g_error ("iterator check failed: invalid iterator");
4301
4302   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
4303     g_error ("iterator check failed: both char and byte offsets are invalid");
4304
4305   segments_updated = (real->segments_changed_stamp ==
4306                       _gtk_text_btree_get_segments_changed_stamp (real->tree));
4307
4308 #if 0
4309   printf ("checking iter, segments %s updated, byte %d char %d\n",
4310           segments_updated ? "are" : "aren't",
4311           real->line_byte_offset,
4312           real->line_char_offset);
4313 #endif
4314
4315   if (segments_updated)
4316     {
4317       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
4318         g_error ("iterator check failed: both char and byte segment offsets are invalid");
4319
4320       if (real->segment->char_count == 0)
4321         g_error ("iterator check failed: segment is not indexable.");
4322
4323       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
4324         g_error ("segment char offset is not properly up-to-date");
4325
4326       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
4327         g_error ("segment byte offset is not properly up-to-date");
4328
4329       if (real->segment_byte_offset >= 0 &&
4330           real->segment_byte_offset >= real->segment->byte_count)
4331         g_error ("segment byte offset is too large.");
4332
4333       if (real->segment_char_offset >= 0 &&
4334           real->segment_char_offset >= real->segment->char_count)
4335         g_error ("segment char offset is too large.");
4336     }
4337
4338   if (real->line_byte_offset >= 0)
4339     {
4340       _gtk_text_line_byte_locate (real->line, real->line_byte_offset,
4341                                   &byte_segment, &byte_any_segment,
4342                                   &seg_byte_offset, &line_byte_offset);
4343
4344       if (line_byte_offset != real->line_byte_offset)
4345         g_error ("wrong byte offset was stored in iterator");
4346
4347       if (segments_updated)
4348         {
4349           if (real->segment != byte_segment)
4350             g_error ("wrong segment was stored in iterator");
4351
4352           if (real->any_segment != byte_any_segment)
4353             g_error ("wrong any_segment was stored in iterator");
4354
4355           if (seg_byte_offset != real->segment_byte_offset)
4356             g_error ("wrong segment byte offset was stored in iterator");
4357
4358           if (byte_segment->type == &gtk_text_char_type)
4359             {
4360               const gchar *p;
4361               p = byte_segment->body.chars + seg_byte_offset;
4362               
4363               if (!gtk_text_byte_begins_utf8_char (p))
4364                 g_error ("broken iterator byte index pointed into the middle of a character");
4365             }
4366         }
4367     }
4368
4369   if (real->line_char_offset >= 0)
4370     {
4371       _gtk_text_line_char_locate (real->line, real->line_char_offset,
4372                                   &char_segment, &char_any_segment,
4373                                   &seg_char_offset, &line_char_offset);
4374
4375       if (line_char_offset != real->line_char_offset)
4376         g_error ("wrong char offset was stored in iterator");
4377
4378       if (segments_updated)
4379         {          
4380           if (real->segment != char_segment)
4381             g_error ("wrong segment was stored in iterator");
4382
4383           if (real->any_segment != char_any_segment)
4384             g_error ("wrong any_segment was stored in iterator");
4385
4386           if (seg_char_offset != real->segment_char_offset)
4387             g_error ("wrong segment char offset was stored in iterator");
4388
4389           if (char_segment->type == &gtk_text_char_type)
4390             {
4391               const gchar *p;
4392               p = g_utf8_offset_to_pointer (char_segment->body.chars,
4393                                             seg_char_offset);
4394
4395               /* hmm, not likely to happen eh */
4396               if (!gtk_text_byte_begins_utf8_char (p))
4397                 g_error ("broken iterator char offset pointed into the middle of a character");
4398             }
4399         }
4400     }
4401
4402   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
4403     {
4404       if (byte_segment != char_segment)
4405         g_error ("char and byte offsets did not point to the same segment");
4406
4407       if (byte_any_segment != char_any_segment)
4408         g_error ("char and byte offsets did not point to the same any segment");
4409
4410       /* Make sure the segment offsets are equivalent, if it's a char
4411          segment. */
4412       if (char_segment->type == &gtk_text_char_type)
4413         {
4414           gint byte_offset = 0;
4415           gint char_offset = 0;
4416           while (char_offset < seg_char_offset)
4417             {
4418               const char * start = char_segment->body.chars + byte_offset;
4419               byte_offset += g_utf8_next_char (start) - start;
4420               char_offset += 1;
4421             }
4422
4423           if (byte_offset != seg_byte_offset)
4424             g_error ("byte offset did not correspond to char offset");
4425
4426           char_offset =
4427             g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
4428
4429           if (char_offset != seg_char_offset)
4430             g_error ("char offset did not correspond to byte offset");
4431
4432           if (!gtk_text_byte_begins_utf8_char (char_segment->body.chars + seg_byte_offset))
4433             g_error ("byte index for iterator does not index the start of a character");
4434         }
4435     }
4436
4437   if (real->cached_line_number >= 0)
4438     {
4439       gint should_be;
4440
4441       should_be = _gtk_text_line_get_number (real->line);
4442       if (real->cached_line_number != should_be)
4443         g_error ("wrong line number was cached");
4444     }
4445
4446   if (real->cached_char_index >= 0)
4447     {
4448       if (real->line_char_offset >= 0) /* only way we can check it
4449                                           efficiently, not a real
4450                                           invariant. */
4451         {
4452           gint char_index;
4453
4454           char_index = _gtk_text_line_char_index (real->line);
4455           char_index += real->line_char_offset;
4456
4457           if (real->cached_char_index != char_index)
4458             g_error ("wrong char index was cached");
4459         }
4460     }
4461 }
4462