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