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