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