]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
if we move to the front of a segment, use the "slow" technique for moving
[~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   /* <, not <=, because if count == segment_char_offset
2351    * we're going to the front of the segment and the any_segment
2352    * might change
2353    */
2354   if (count < real->segment_char_offset)
2355     {
2356       /* Optimize the within-segment case */
2357       g_assert (real->segment->char_count > 0);
2358       g_assert (real->segment->type == &gtk_text_char_type);
2359
2360       real->segment_char_offset -= count;
2361       g_assert (real->segment_char_offset >= 0);
2362
2363       if (real->line_byte_offset >= 0)
2364         {
2365           gint new_byte_offset;
2366           gint i;
2367
2368           new_byte_offset = 0;
2369           i = 0;
2370           while (i < real->segment_char_offset)
2371             {
2372               const char * start = real->segment->body.chars + new_byte_offset;
2373               new_byte_offset += g_utf8_next_char (start) - start;
2374
2375               ++i;
2376             }
2377
2378           real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
2379           real->segment_byte_offset = new_byte_offset;
2380         }
2381
2382       real->line_char_offset -= count;
2383
2384       adjust_char_index (real, 0 - count);
2385
2386       check_invariants (iter);
2387
2388       return TRUE;
2389     }
2390   else
2391     {
2392       /* We need to go back into previous segments. For now,
2393        * just keep this really simple. FIXME
2394        * use backward_indexable_segment.
2395        */
2396       if (TRUE || count > MAX_LINEAR_SCAN)
2397         {
2398           gint current_char_index;
2399           gint new_char_index;
2400
2401           current_char_index = gtk_text_iter_get_offset (iter);
2402
2403           if (current_char_index == 0)
2404             return FALSE; /* can't move backward */
2405
2406           new_char_index = current_char_index - count;
2407           if (new_char_index < 0)
2408             new_char_index = 0;
2409
2410           gtk_text_iter_set_offset (iter, new_char_index);
2411
2412           check_invariants (iter);
2413
2414           return TRUE;
2415         }
2416       else
2417         {
2418           /* FIXME backward_indexable_segment here */
2419
2420           return FALSE;
2421         }
2422     }
2423 }
2424
2425 #if 0
2426
2427 /* These two can't be implemented efficiently (always have to use
2428  * a linear scan, since that's the only way to find all the non-text
2429  * segments)
2430  */
2431
2432 /**
2433  * gtk_text_iter_forward_text_chars:
2434  * @iter: a #GtkTextIter
2435  * @count: number of chars to move
2436  *
2437  * Moves forward by @count text characters (pixbufs, widgets,
2438  * etc. do not count as characters for this). Equivalent to moving
2439  * through the results of gtk_text_iter_get_text (), rather than
2440  * gtk_text_iter_get_slice ().
2441  *
2442  * Return value: whether @iter moved and is dereferenceable
2443  **/
2444 gboolean
2445 gtk_text_iter_forward_text_chars  (GtkTextIter *iter,
2446                                    gint         count)
2447 {
2448
2449
2450
2451 }
2452
2453 /**
2454  * gtk_text_iter_forward_text_chars:
2455  * @iter: a #GtkTextIter
2456  * @count: number of chars to move
2457  *
2458  * Moves backward by @count text characters (pixbufs, widgets,
2459  * etc. do not count as characters for this). Equivalent to moving
2460  * through the results of gtk_text_iter_get_text (), rather than
2461  * gtk_text_iter_get_slice ().
2462  *
2463  * Return value: whether @iter moved and is dereferenceable
2464  **/
2465 gboolean
2466 gtk_text_iter_backward_text_chars (GtkTextIter *iter,
2467                                    gint         count)
2468 {
2469
2470
2471 }
2472 #endif
2473
2474 /**
2475  * gtk_text_iter_forward_line:
2476  * @iter: an iterator
2477  *
2478  * Moves @iter to the start of the next line. Returns TRUE if there
2479  * was a next line to move to, and FALSE if @iter was simply moved to
2480  * the end of the buffer and is now not dereferenceable, or if @iter was
2481  * already at the end of the buffer.
2482  *
2483  * Return value: whether @iter can be dereferenced
2484  **/
2485 gboolean
2486 gtk_text_iter_forward_line (GtkTextIter *iter)
2487 {
2488   GtkTextRealIter *real;
2489
2490   g_return_val_if_fail (iter != NULL, FALSE);
2491   
2492   real = gtk_text_iter_make_real (iter);
2493
2494   if (real == NULL)
2495     return FALSE;
2496
2497   check_invariants (iter);
2498
2499   if (forward_line_leaving_caches_unmodified (real))
2500     {
2501       invalidate_char_index (real);
2502       adjust_line_number (real, 1);
2503
2504       check_invariants (iter);
2505
2506       if (gtk_text_iter_is_end (iter))
2507         return FALSE;
2508       else
2509         return TRUE;
2510     }
2511   else
2512     {
2513       /* On the last line, move to end of it */
2514       
2515       if (!gtk_text_iter_is_end (iter))
2516         gtk_text_iter_forward_to_end (iter);
2517       
2518       check_invariants (iter);
2519       return FALSE;
2520     }
2521 }
2522
2523 /**
2524  * gtk_text_iter_backward_line:
2525  * @iter: an iterator
2526  *
2527  * Moves @iter to the start of the previous line. Returns TRUE if
2528  * @iter could be moved; i.e. if @iter was at character offset 0, this
2529  * function returns FALSE. Therefore if @iter was already on line 0,
2530  * but not at the start of the line, @iter is snapped to the start of
2531  * the line and the function returns TRUE. (Note that this implies that
2532  * in a loop calling this function, the line number may not change on
2533  * every iteration, if your first iteration is on line 0.)
2534  *
2535  * Return value: whether @iter moved
2536  **/
2537 gboolean
2538 gtk_text_iter_backward_line (GtkTextIter *iter)
2539 {
2540   GtkTextLine *new_line;
2541   GtkTextRealIter *real;
2542   gboolean offset_will_change;
2543   gint offset;
2544
2545   g_return_val_if_fail (iter != NULL, FALSE);
2546
2547   real = gtk_text_iter_make_real (iter);
2548
2549   if (real == NULL)
2550     return FALSE;
2551
2552   check_invariants (iter);
2553
2554   new_line = _gtk_text_line_previous (real->line);
2555
2556   offset_will_change = FALSE;
2557   if (real->line_char_offset > 0)
2558     offset_will_change = TRUE;
2559
2560   if (new_line != NULL)
2561     {
2562       real->line = new_line;
2563
2564       adjust_line_number (real, -1);
2565     }
2566   else
2567     {
2568       if (!offset_will_change)
2569         return FALSE;
2570     }
2571
2572   invalidate_char_index (real);
2573
2574   real->line_byte_offset = 0;
2575   real->line_char_offset = 0;
2576
2577   real->segment_byte_offset = 0;
2578   real->segment_char_offset = 0;
2579
2580   /* Find first segment in line */
2581   real->any_segment = real->line->segments;
2582   real->segment = _gtk_text_line_byte_to_segment (real->line,
2583                                                   0, &offset);
2584
2585   g_assert (offset == 0);
2586
2587   /* Note that if we are on the first line, we snap to the start of
2588    * the first line and return TRUE, so TRUE means the iterator
2589    * changed, not that the line changed; this is maybe a bit
2590    * weird. I'm not sure there's an obvious right thing to do though.
2591    */
2592
2593   check_invariants (iter);
2594
2595   return TRUE;
2596 }
2597
2598
2599 /**
2600  * gtk_text_iter_forward_lines:
2601  * @iter: a #GtkTextIter
2602  * @count: number of lines to move forward
2603  *
2604  * Moves @count lines forward, if possible (if @count would move
2605  * past the start or end of the buffer, moves to the start or end of
2606  * the buffer).  The return value indicates whether the iterator moved
2607  * onto a dereferenceable position; if the iterator didn't move, or
2608  * moved onto the end iterator, then FALSE is returned. If @count is 0,
2609  * the function does nothing and returns FALSE. If @count is negative,
2610  * moves backward by 0 - @count lines.
2611  *
2612  * Return value: whether @iter moved and is dereferenceable
2613  **/
2614 gboolean
2615 gtk_text_iter_forward_lines (GtkTextIter *iter, gint count)
2616 {
2617   FIX_OVERFLOWS (count);
2618   
2619   if (count < 0)
2620     return gtk_text_iter_backward_lines (iter, 0 - count);
2621   else if (count == 0)
2622     return FALSE;
2623   else if (count == 1)
2624     {
2625       check_invariants (iter);
2626       return gtk_text_iter_forward_line (iter);
2627     }
2628   else
2629     {
2630       gint old_line;
2631
2632       if (gtk_text_iter_is_end (iter))
2633         return FALSE;
2634       
2635       old_line = gtk_text_iter_get_line (iter);
2636
2637       gtk_text_iter_set_line (iter, old_line + count);
2638
2639       if ((gtk_text_iter_get_line (iter) - old_line) < count)
2640         {
2641           /* count went past the last line, so move to end of last line */
2642           if (!gtk_text_iter_is_end (iter))
2643             gtk_text_iter_forward_to_end (iter);
2644         }
2645       
2646       return !gtk_text_iter_is_end (iter);
2647     }
2648 }
2649
2650 /**
2651  * gtk_text_iter_backward_lines:
2652  * @iter: a #GtkTextIter
2653  * @count: number of lines to move backward
2654  *
2655  * Moves @count lines backward, if possible (if @count would move
2656  * past the start or end of the buffer, moves to the start or end of
2657  * the buffer).  The return value indicates whether the iterator moved
2658  * onto a dereferenceable position; if the iterator didn't move, or
2659  * moved onto the end iterator, then FALSE is returned. If @count is 0,
2660  * the function does nothing and returns FALSE. If @count is negative,
2661  * moves forward by 0 - @count lines.
2662  *
2663  * Return value: whether @iter moved and is dereferenceable
2664  **/
2665 gboolean
2666 gtk_text_iter_backward_lines (GtkTextIter *iter, gint count)
2667 {
2668   FIX_OVERFLOWS (count);
2669   
2670   if (count < 0)
2671     return gtk_text_iter_forward_lines (iter, 0 - count);
2672   else if (count == 0)
2673     return FALSE;
2674   else if (count == 1)
2675     {
2676       return gtk_text_iter_backward_line (iter);
2677     }
2678   else
2679     {
2680       gint old_line;
2681
2682       old_line = gtk_text_iter_get_line (iter);
2683
2684       gtk_text_iter_set_line (iter, MAX (old_line - count, 0));
2685
2686       return (gtk_text_iter_get_line (iter) != old_line);
2687     }
2688 }
2689
2690 typedef gboolean (* FindLogAttrFunc) (const PangoLogAttr *attrs,
2691                                       gint                offset,
2692                                       gint                min_offset,
2693                                       gint                len,
2694                                       gint               *found_offset,
2695                                       gboolean            already_moved_initially);
2696
2697 typedef gboolean (* TestLogAttrFunc) (const PangoLogAttr *attrs,
2698                                       gint                offset,
2699                                       gint                min_offset,
2700                                       gint                len);
2701
2702 /* Word funcs */
2703
2704 static gboolean
2705 find_word_end_func (const PangoLogAttr *attrs,
2706                     gint          offset,
2707                     gint          min_offset,
2708                     gint          len,
2709                     gint         *found_offset,
2710                     gboolean      already_moved_initially)
2711 {
2712   if (!already_moved_initially)
2713     ++offset;
2714
2715   /* Find end of next word */
2716   while (offset < min_offset + len &&
2717          !attrs[offset].is_word_end)
2718     ++offset;
2719
2720   *found_offset = offset;
2721
2722   return offset < min_offset + len;
2723 }
2724
2725 static gboolean
2726 is_word_end_func (const PangoLogAttr *attrs,
2727                   gint          offset,
2728                   gint          min_offset,
2729                   gint          len)
2730 {
2731   return attrs[offset].is_word_end;
2732 }
2733
2734 static gboolean
2735 find_word_start_func (const PangoLogAttr *attrs,
2736                       gint          offset,
2737                       gint          min_offset,
2738                       gint          len,
2739                       gint         *found_offset,
2740                       gboolean      already_moved_initially)
2741 {
2742   if (!already_moved_initially)
2743     --offset;
2744
2745   /* Find start of prev word */
2746   while (offset >= min_offset &&
2747          !attrs[offset].is_word_start)
2748     --offset;
2749
2750   *found_offset = offset;
2751
2752   return offset >= min_offset;
2753 }
2754
2755 static gboolean
2756 is_word_start_func (const PangoLogAttr *attrs,
2757                     gint          offset,
2758                     gint          min_offset,
2759                     gint          len)
2760 {
2761   return attrs[offset].is_word_start;
2762 }
2763
2764 static gboolean
2765 inside_word_func (const PangoLogAttr *attrs,
2766                   gint          offset,
2767                   gint          min_offset,
2768                   gint          len)
2769 {
2770   /* Find next word start or end */
2771   while (offset >= min_offset &&
2772          !(attrs[offset].is_word_start || attrs[offset].is_word_end))
2773     --offset;
2774
2775   return attrs[offset].is_word_start;
2776 }
2777
2778 /* Sentence funcs */
2779
2780 static gboolean
2781 find_sentence_end_func (const PangoLogAttr *attrs,
2782                         gint          offset,
2783                         gint          min_offset,
2784                         gint          len,
2785                         gint         *found_offset,
2786                         gboolean      already_moved_initially)
2787 {
2788   if (!already_moved_initially)
2789     ++offset;
2790
2791   /* Find end of next sentence */
2792   while (offset < min_offset + len &&
2793          !attrs[offset].is_sentence_end)
2794     ++offset;
2795
2796   *found_offset = offset;
2797
2798   return offset < min_offset + len;
2799 }
2800
2801 static gboolean
2802 is_sentence_end_func (const PangoLogAttr *attrs,
2803                       gint          offset,
2804                       gint          min_offset,
2805                       gint          len)
2806 {
2807   return attrs[offset].is_sentence_end;
2808 }
2809
2810 static gboolean
2811 find_sentence_start_func (const PangoLogAttr *attrs,
2812                           gint          offset,
2813                           gint          min_offset,
2814                           gint          len,
2815                           gint         *found_offset,
2816                           gboolean      already_moved_initially)
2817 {
2818   if (!already_moved_initially)
2819     --offset;
2820
2821   /* Find start of prev sentence */
2822   while (offset >= min_offset &&
2823          !attrs[offset].is_sentence_start)
2824     --offset;
2825
2826   *found_offset = offset;
2827
2828   return offset >= min_offset;
2829 }
2830
2831 static gboolean
2832 is_sentence_start_func (const PangoLogAttr *attrs,
2833                         gint          offset,
2834                         gint          min_offset,
2835                         gint          len)
2836 {
2837   return attrs[offset].is_sentence_start;
2838 }
2839
2840 static gboolean
2841 inside_sentence_func (const PangoLogAttr *attrs,
2842                       gint          offset,
2843                       gint          min_offset,
2844                       gint          len)
2845 {
2846   /* Find next sentence start or end */
2847   while (offset >= min_offset &&
2848          !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
2849     --offset;
2850
2851   return attrs[offset].is_sentence_start;
2852 }
2853
2854 static gboolean
2855 test_log_attrs (const GtkTextIter *iter,
2856                 TestLogAttrFunc    func)
2857 {
2858   gint char_len;
2859   const PangoLogAttr *attrs;
2860   int offset;
2861   gboolean result = FALSE;
2862
2863   g_return_val_if_fail (iter != NULL, FALSE);
2864
2865   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2866                                                iter, &char_len);
2867
2868   offset = gtk_text_iter_get_line_offset (iter);
2869
2870   /* char_len may be 0 and attrs will be NULL if so, if
2871    * iter is the end iter and the last line is empty.
2872    * 
2873    * offset may be equal to char_len, since attrs contains an entry
2874    * for one past the end
2875    */
2876   
2877   if (attrs && offset <= char_len)
2878     result = (* func) (attrs, offset, 0, char_len);
2879
2880   return result;
2881 }
2882
2883 static gboolean
2884 find_line_log_attrs (const GtkTextIter *iter,
2885                      FindLogAttrFunc    func,
2886                      gint              *found_offset,
2887                      gboolean           already_moved_initially)
2888 {
2889   gint char_len;
2890   const PangoLogAttr *attrs;
2891   int offset;
2892   gboolean result = FALSE;
2893
2894   g_return_val_if_fail (iter != NULL, FALSE);
2895   
2896   attrs = _gtk_text_buffer_get_line_log_attrs (gtk_text_iter_get_buffer (iter),
2897                                                iter, &char_len);      
2898
2899   offset = gtk_text_iter_get_line_offset (iter);
2900   
2901   /* char_len may be 0 and attrs will be NULL if so, if
2902    * iter is the end iter and the last line is empty
2903    */
2904   
2905   if (attrs)
2906     result = (* func) (attrs, offset, 0, char_len, found_offset,
2907                        already_moved_initially);
2908
2909   return result;
2910 }
2911
2912 /* FIXME this function is very, very gratuitously slow */
2913 static gboolean
2914 find_by_log_attrs (GtkTextIter    *iter,
2915                    FindLogAttrFunc func,
2916                    gboolean        forward,
2917                    gboolean        already_moved_initially)
2918 {
2919   GtkTextIter orig;
2920   gint offset = 0;
2921   gboolean found = FALSE;
2922
2923   g_return_val_if_fail (iter != NULL, FALSE);
2924
2925   orig = *iter;
2926   
2927   found = find_line_log_attrs (iter, func, &offset, already_moved_initially);
2928   
2929   if (!found)
2930     {
2931       if (forward)
2932         {
2933           if (gtk_text_iter_forward_line (iter))
2934             return find_by_log_attrs (iter, func, forward,
2935                                       TRUE);
2936           else
2937             return FALSE;
2938         }
2939       else
2940         {                    
2941           /* go to end of previous line. need to check that
2942            * line is > 0 because backward_line snaps to start of
2943            * line 0 if it's on line 0
2944            */
2945           if (gtk_text_iter_get_line (iter) > 0 && 
2946               gtk_text_iter_backward_line (iter))
2947             {
2948               if (!gtk_text_iter_ends_line (iter))
2949                 gtk_text_iter_forward_to_line_end (iter);
2950               
2951               return find_by_log_attrs (iter, func, forward,
2952                                         TRUE);
2953             }
2954           else
2955             return FALSE;
2956         }
2957     }
2958   else
2959     {      
2960       gtk_text_iter_set_line_offset (iter, offset);
2961
2962       return
2963         (already_moved_initially || !gtk_text_iter_equal (iter, &orig)) &&
2964         !gtk_text_iter_is_end (iter);
2965     }
2966 }
2967
2968 /**
2969  * gtk_text_iter_forward_word_end:
2970  * @iter: a #GtkTextIter
2971  * 
2972  * Moves forward to the next word end. (If @iter is currently on a
2973  * word end, moves forward to the next one after that.) Word breaks
2974  * are determined by Pango and should be correct for nearly any
2975  * language (if not, the correct fix would be to the Pango word break
2976  * algorithms).
2977  * 
2978  * Return value: %TRUE if @iter moved and is not the end iterator 
2979  **/
2980 gboolean
2981 gtk_text_iter_forward_word_end (GtkTextIter *iter)
2982 {
2983   return find_by_log_attrs (iter, find_word_end_func, TRUE, FALSE);
2984 }
2985
2986 /**
2987  * gtk_text_iter_backward_word_start:
2988  * @iter: a #GtkTextIter
2989  * 
2990  * Moves backward to the next word start. (If @iter is currently on a
2991  * word start, moves backward to the next one after that.) Word breaks
2992  * are determined by Pango and should be correct for nearly any
2993  * language (if not, the correct fix would be to the Pango word break
2994  * algorithms).
2995  * 
2996  * Return value: %TRUE if @iter moved and is not the end iterator 
2997  **/
2998 gboolean
2999 gtk_text_iter_backward_word_start (GtkTextIter      *iter)
3000 {
3001   return find_by_log_attrs (iter, find_word_start_func, FALSE, FALSE);
3002 }
3003
3004 /* FIXME a loop around a truly slow function means
3005  * a truly spectacularly slow function.
3006  */
3007
3008 /**
3009  * gtk_text_iter_forward_word_ends:
3010  * @iter: a #GtkTextIter
3011  * @count: number of times to move
3012  * 
3013  * Calls gtk_text_iter_forward_word_end() up to @count times.
3014  *
3015  * Return value: %TRUE if @iter moved and is not the end iterator 
3016  **/
3017 gboolean
3018 gtk_text_iter_forward_word_ends (GtkTextIter      *iter,
3019                                  gint              count)
3020 {
3021   g_return_val_if_fail (iter != NULL, FALSE);
3022
3023   FIX_OVERFLOWS (count);
3024   
3025   if (count == 0)
3026     return FALSE;
3027
3028   if (count < 0)
3029     return gtk_text_iter_backward_word_starts (iter, -count);
3030
3031   if (!gtk_text_iter_forward_word_end (iter))
3032     return FALSE;
3033   --count;
3034
3035   while (count > 0)
3036     {
3037       if (!gtk_text_iter_forward_word_end (iter))
3038         break;
3039       --count;
3040     }
3041   
3042   return !gtk_text_iter_is_end (iter);
3043 }
3044
3045 /**
3046  * gtk_text_iter_backward_word_starts
3047  * @iter: a #GtkTextIter
3048  * @count: number of times to move
3049  * 
3050  * Calls gtk_text_iter_backward_word_starts() up to @count times.
3051  *
3052  * Return value: %TRUE if @iter moved and is not the end iterator 
3053  **/
3054 gboolean
3055 gtk_text_iter_backward_word_starts (GtkTextIter      *iter,
3056                                     gint               count)
3057 {
3058   g_return_val_if_fail (iter != NULL, FALSE);
3059
3060   FIX_OVERFLOWS (count);
3061   
3062   if (count < 0)
3063     return gtk_text_iter_forward_word_ends (iter, -count);
3064
3065   if (!gtk_text_iter_backward_word_start (iter))
3066     return FALSE;
3067   --count;
3068
3069   while (count > 0)
3070     {
3071       if (!gtk_text_iter_backward_word_start (iter))
3072         break;
3073       --count;
3074     }
3075
3076   return !gtk_text_iter_is_end (iter);
3077 }
3078
3079 /**
3080  * gtk_text_iter_starts_word:
3081  * @iter: a #GtkTextIter
3082  * 
3083  * Determines whether @iter begins a natural-language word.  Word
3084  * breaks are determined by Pango and should be correct for nearly any
3085  * language (if not, the correct fix would be to the Pango word break
3086  * algorithms).
3087  *
3088  * Return value: %TRUE if @iter is at the start of a word
3089  **/
3090 gboolean
3091 gtk_text_iter_starts_word (const GtkTextIter *iter)
3092 {
3093   return test_log_attrs (iter, is_word_start_func);
3094 }
3095
3096 /**
3097  * gtk_text_iter_ends_word:
3098  * @iter: a #GtkTextIter
3099  * 
3100  * Determines whether @iter ends a natural-language word.  Word breaks
3101  * are determined by Pango and should be correct for nearly any
3102  * language (if not, the correct fix would be to the Pango word break
3103  * algorithms).
3104  *
3105  * Return value: %TRUE if @iter is at the end of a word
3106  **/
3107 gboolean
3108 gtk_text_iter_ends_word (const GtkTextIter *iter)
3109 {
3110   return test_log_attrs (iter, is_word_end_func);
3111 }
3112
3113 /**
3114  * gtk_text_iter_inside_word:
3115  * @iter: a #GtkTextIter
3116  * 
3117  * Determines whether @iter is inside a natural-language word (as
3118  * opposed to say inside some whitespace).  Word breaks are determined
3119  * by Pango and should be correct for nearly any language (if not, the
3120  * correct fix would be to the Pango word break algorithms).
3121  * 
3122  * Return value: %TRUE if @iter is inside a word
3123  **/
3124 gboolean
3125 gtk_text_iter_inside_word (const GtkTextIter *iter)
3126 {
3127   return test_log_attrs (iter, inside_word_func);
3128 }
3129
3130 /**
3131  * gtk_text_iter_starts_sentence:
3132  * @iter: a #GtkTextIter
3133  * 
3134  * Determines whether @iter begins a sentence.  Sentence boundaries are
3135  * determined by Pango and should be correct for nearly any language
3136  * (if not, the correct fix would be to the Pango text boundary
3137  * algorithms).
3138  * 
3139  * Return value: %TRUE if @iter is at the start of a sentence.
3140  **/
3141 gboolean
3142 gtk_text_iter_starts_sentence (const GtkTextIter *iter)
3143 {
3144   return test_log_attrs (iter, is_sentence_start_func);
3145 }
3146
3147 /**
3148  * gtk_text_iter_ends_sentence:
3149  * @iter: a #GtkTextIter
3150  * 
3151  * Determines whether @iter ends a sentence.  Sentence boundaries are
3152  * determined by Pango and should be correct for nearly any language
3153  * (if not, the correct fix would be to the Pango text boundary
3154  * algorithms).
3155  * 
3156  * Return value: %TRUE if @iter is at the end of a sentence.
3157  **/
3158 gboolean
3159 gtk_text_iter_ends_sentence (const GtkTextIter *iter)
3160 {
3161   return test_log_attrs (iter, is_sentence_end_func);
3162 }
3163
3164 /**
3165  * gtk_text_iter_inside_sentence:
3166  * @iter: a #GtkTextIter
3167  * 
3168  * Determines whether @iter is inside a sentence (as opposed to in
3169  * between two sentences, e.g. after a period and before the first
3170  * letter of the next sentence).  Sentence boundaries are determined
3171  * by Pango and should be correct for nearly any language (if not, the
3172  * correct fix would be to the Pango text boundary algorithms).
3173  * 
3174  * Return value: %TRUE if @iter is inside a sentence.
3175  **/
3176 gboolean
3177 gtk_text_iter_inside_sentence (const GtkTextIter *iter)
3178 {
3179   return test_log_attrs (iter, inside_sentence_func);
3180 }
3181
3182 /**
3183  * gtk_text_iter_forward_sentence_end:
3184  * @iter: a #GtkTextIter
3185  * 
3186  * Moves forward to the next sentence end. (If @iter is at the end of
3187  * a sentence, moves to the next end of sentence.)  Sentence
3188  * boundaries are determined by Pango and should be correct for nearly
3189  * any language (if not, the correct fix would be to the Pango text
3190  * boundary algorithms).
3191  * 
3192  * Return value: %TRUE if @iter moved and is not the end iterator
3193  **/
3194 gboolean
3195 gtk_text_iter_forward_sentence_end (GtkTextIter *iter)
3196 {
3197   return find_by_log_attrs (iter, find_sentence_end_func, TRUE, FALSE);
3198 }
3199
3200 /**
3201  * gtk_text_iter_backward_sentence_start:
3202  * @iter: a #GtkTextIter
3203  * 
3204  * Moves backward to the next sentence start; if @iter is already at
3205  * the start of a sentence, moves backward to the next one.  Sentence
3206  * boundaries are determined by Pango and should be correct for nearly
3207  * any language (if not, the correct fix would be to the Pango text
3208  * boundary algorithms).
3209  * 
3210  * Return value: %TRUE if @iter moved and is not the end iterator
3211  **/
3212 gboolean
3213 gtk_text_iter_backward_sentence_start (GtkTextIter      *iter)
3214 {
3215   return find_by_log_attrs (iter, find_sentence_start_func, FALSE, FALSE);
3216 }
3217
3218 /* FIXME a loop around a truly slow function means
3219  * a truly spectacularly slow function.
3220  */
3221 /**
3222  * gtk_text_iter_forward_sentence_ends:
3223  * @iter: a #GtkTextIter
3224  * @count: number of sentences to move
3225  * 
3226  * Calls gtk_text_iter_forward_sentence_end() @count times (or until
3227  * gtk_text_iter_forward_sentence_end() returns %FALSE). If @count is
3228  * negative, moves backward instead of forward.
3229  * 
3230  * Return value: %TRUE if @iter moved and is not the end iterator
3231  **/
3232 gboolean
3233 gtk_text_iter_forward_sentence_ends (GtkTextIter      *iter,
3234                                      gint              count)
3235 {
3236   g_return_val_if_fail (iter != NULL, FALSE);
3237
3238   if (count == 0)
3239     return FALSE;
3240
3241   if (count < 0)
3242     return gtk_text_iter_backward_sentence_starts (iter, -count);
3243
3244   if (!gtk_text_iter_forward_sentence_end (iter))
3245     return FALSE;
3246   --count;
3247
3248   while (count > 0)
3249     {
3250       if (!gtk_text_iter_forward_sentence_end (iter))
3251         break;
3252       --count;
3253     }
3254
3255   return !gtk_text_iter_is_end (iter);
3256 }
3257
3258 /**
3259  * gtk_text_iter_backward_sentence_starts:
3260  * @iter: a #GtkTextIter
3261  * @count: number of sentences to move
3262  * 
3263  * Calls gtk_text_iter_backward_sentence_start() up to @count times,
3264  * or until it returns %FALSE. If @count is negative, moves forward
3265  * instead of backward.
3266  * 
3267  * Return value: %TRUE if @iter moved and is not the end iterator
3268  **/
3269 gboolean
3270 gtk_text_iter_backward_sentence_starts (GtkTextIter      *iter,
3271                                         gint               count)
3272 {
3273   g_return_val_if_fail (iter != NULL, FALSE);
3274
3275   if (count < 0)
3276     return gtk_text_iter_forward_sentence_ends (iter, -count);
3277
3278   if (!gtk_text_iter_backward_sentence_start (iter))
3279     return FALSE;
3280   --count;
3281
3282   while (count > 0)
3283     {
3284       if (!gtk_text_iter_backward_sentence_start (iter))
3285         break;
3286       --count;
3287     }
3288
3289   return !gtk_text_iter_is_end (iter);
3290 }
3291
3292 static gboolean
3293 find_forward_cursor_pos_func (const PangoLogAttr *attrs,
3294                               gint          offset,
3295                               gint          min_offset,
3296                               gint          len,
3297                               gint         *found_offset,
3298                               gboolean      already_moved_initially)
3299 {
3300   if (!already_moved_initially)
3301     ++offset;
3302
3303   while (offset < (min_offset + len) &&
3304          !attrs[offset].is_cursor_position)
3305     ++offset;
3306
3307   *found_offset = offset;
3308
3309   return offset < (min_offset + len);
3310 }
3311
3312 static gboolean
3313 find_backward_cursor_pos_func (const PangoLogAttr *attrs,
3314                                gint          offset,
3315                                gint          min_offset,
3316                                gint          len,
3317                                gint         *found_offset,
3318                                gboolean      already_moved_initially)
3319 {  
3320   if (!already_moved_initially)
3321     --offset;
3322
3323   while (offset > min_offset &&
3324          !attrs[offset].is_cursor_position)
3325     --offset;
3326
3327   *found_offset = offset;
3328   
3329   return offset >= min_offset;
3330 }
3331
3332 static gboolean
3333 is_cursor_pos_func (const PangoLogAttr *attrs,
3334                     gint          offset,
3335                     gint          min_offset,
3336                     gint          len)
3337 {
3338   return attrs[offset].is_cursor_position;
3339 }
3340
3341 /**
3342  * gtk_text_iter_forward_cursor_position:
3343  * @iter: a #GtkTextIter
3344  * 
3345  * Moves @iter forward by a single cursor position. Cursor positions
3346  * are (unsurprisingly) positions where the cursor can appear. Perhaps
3347  * surprisingly, there may not be a cursor position between all
3348  * characters. The most common example for European languages would be
3349  * a carriage return/newline sequence. For some Unicode characters,
3350  * the equivalent of say the letter "a" with an accent mark will be
3351  * represented as two characters, first the letter then a "combining
3352  * mark" that causes the accent to be rendered; so the cursor can't go
3353  * between those two characters. See also the #PangoLogAttr structure and
3354  * pango_break() function.
3355  * 
3356  * Return value: %TRUE if we moved and the new position is dereferenceable
3357  **/
3358 gboolean
3359 gtk_text_iter_forward_cursor_position (GtkTextIter *iter)
3360 {
3361   return find_by_log_attrs (iter, find_forward_cursor_pos_func, TRUE, FALSE);
3362 }
3363
3364 /**
3365  * gtk_text_iter_backward_cursor_position:
3366  * @iter: a #GtkTextIter
3367  * 
3368  * Like gtk_text_iter_forward_cursor_position(), but moves backward.
3369  * 
3370  * Return value: %TRUE if we moved and the new position is dereferenceable
3371  **/
3372 gboolean
3373 gtk_text_iter_backward_cursor_position (GtkTextIter *iter)
3374 {
3375   return find_by_log_attrs (iter, find_backward_cursor_pos_func, FALSE, FALSE);
3376 }
3377
3378 /**
3379  * gtk_text_iter_forward_cursor_positions:
3380  * @iter: a #GtkTextIter
3381  * @count: number of positions to move
3382  * 
3383  * Moves up to @count cursor positions. See
3384  * gtk_text_iter_forward_cursor_position() for details.
3385  * 
3386  * Return value: %TRUE if we moved and the new position is dereferenceable
3387  **/
3388 gboolean
3389 gtk_text_iter_forward_cursor_positions (GtkTextIter *iter,
3390                                         gint         count)
3391 {
3392   g_return_val_if_fail (iter != NULL, FALSE);
3393
3394   FIX_OVERFLOWS (count);
3395   
3396   if (count == 0)
3397     return FALSE;
3398   
3399   if (count < 0)
3400     return gtk_text_iter_backward_cursor_positions (iter, -count);
3401   
3402   if (!gtk_text_iter_forward_cursor_position (iter))
3403     return FALSE;
3404   --count;
3405
3406   while (count > 0)
3407     {
3408       if (!gtk_text_iter_forward_cursor_position (iter))
3409         break;
3410       --count;
3411     }
3412   
3413   return !gtk_text_iter_is_end (iter);
3414 }
3415
3416 /**
3417  * gtk_text_iter_backward_cursor_positions:
3418  * @iter: a #GtkTextIter
3419  * @count: number of positions to move
3420  *
3421  * Moves up to @count cursor positions. See
3422  * gtk_text_iter_forward_cursor_position() for details.
3423  * 
3424  * Return value: %TRUE if we moved and the new position is dereferenceable
3425  **/
3426 gboolean
3427 gtk_text_iter_backward_cursor_positions (GtkTextIter *iter,
3428                                          gint         count)
3429 {
3430   g_return_val_if_fail (iter != NULL, FALSE);
3431
3432   FIX_OVERFLOWS (count);
3433   
3434   if (count == 0)
3435     return FALSE;
3436
3437   if (count < 0)
3438     return gtk_text_iter_forward_cursor_positions (iter, -count);
3439   
3440   if (!gtk_text_iter_backward_cursor_position (iter))
3441     return FALSE;
3442   --count;
3443
3444   while (count > 0)
3445     {
3446       if (!gtk_text_iter_backward_cursor_position (iter))
3447         break;
3448       --count;
3449     }
3450
3451   return !gtk_text_iter_is_end (iter);
3452 }
3453
3454 /**
3455  * gtk_text_iter_is_cursor_position:
3456  * @iter: a #GtkTextIter
3457  * 
3458  * See gtk_text_iter_forward_cursor_position() or #PangoLogAttr or
3459  * pango_break() for details on what a cursor position is.
3460  * 
3461  * Return value: %TRUE if the cursor can be placed at @iter
3462  **/
3463 gboolean
3464 gtk_text_iter_is_cursor_position (const GtkTextIter *iter)
3465 {
3466   return test_log_attrs (iter, is_cursor_pos_func);
3467 }
3468
3469 /**
3470  * gtk_text_iter_set_line_offset:
3471  * @iter: a #GtkTextIter 
3472  * @char_on_line: a character offset relative to the start of @iter's current line
3473  * 
3474  * Moves @iter within a line, to a new <emphasis>character</emphasis>
3475  * (not byte) offset. The given character offset must be less than or
3476  * equal to the number of characters in the line; if equal, @iter
3477  * moves to the start of the next line. See
3478  * gtk_text_iter_set_line_index() if you have a byte index rather than
3479  * a character offset.
3480  *
3481  **/
3482 void
3483 gtk_text_iter_set_line_offset (GtkTextIter *iter,
3484                                gint         char_on_line)
3485 {
3486   GtkTextRealIter *real;
3487   gint chars_in_line;
3488   
3489   g_return_if_fail (iter != NULL);
3490
3491   real = gtk_text_iter_make_surreal (iter);
3492
3493   if (real == NULL)
3494     return;
3495   
3496   check_invariants (iter);
3497
3498   chars_in_line = gtk_text_iter_get_chars_in_line (iter);
3499
3500   g_return_if_fail (char_on_line <= chars_in_line);
3501
3502   if (char_on_line < chars_in_line)
3503     iter_set_from_char_offset (real, real->line, char_on_line);
3504   else
3505     gtk_text_iter_forward_line (iter); /* set to start of next line */
3506   
3507   check_invariants (iter);
3508 }
3509
3510 /**
3511  * gtk_text_iter_set_line_index:
3512  * @iter: a #GtkTextIter
3513  * @byte_on_line: a byte index relative to the start of @iter's current line
3514  *
3515  * Same as gtk_text_iter_set_line_offset(), but works with a
3516  * <emphasis>byte</emphasis> index. The given byte index must be at
3517  * the start of a character, it can't be in the middle of a UTF-8
3518  * encoded character.
3519  * 
3520  **/
3521 void
3522 gtk_text_iter_set_line_index (GtkTextIter *iter,
3523                               gint         byte_on_line)
3524 {
3525   GtkTextRealIter *real;
3526   gint bytes_in_line;
3527   
3528   g_return_if_fail (iter != NULL);
3529
3530   real = gtk_text_iter_make_surreal (iter);
3531
3532   if (real == NULL)
3533     return;
3534
3535   check_invariants (iter);
3536
3537   bytes_in_line = gtk_text_iter_get_bytes_in_line (iter);
3538
3539   g_return_if_fail (byte_on_line <= bytes_in_line);
3540   
3541   if (byte_on_line < bytes_in_line)
3542     iter_set_from_byte_offset (real, real->line, byte_on_line);
3543   else
3544     gtk_text_iter_forward_line (iter);
3545
3546   if (real->segment->type == &gtk_text_char_type &&
3547       (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
3548     g_warning ("%s: Incorrect byte offset %d falls in the middle of a UTF-8 "
3549                "character; this will crash the text buffer. "
3550                "Byte indexes must refer to the start of a character.",
3551                G_STRLOC, byte_on_line);
3552
3553   check_invariants (iter);
3554 }
3555
3556
3557 /**
3558  * gtk_text_iter_set_visible_line_offset:
3559  * @iter: a #GtkTextIter
3560  * @char_on_line: a character offset
3561  * 
3562  * Like gtk_text_iter_set_line_offset(), but the offset is in visible
3563  * characters, i.e. text with a tag making it invisible is not
3564  * counted in the offset.
3565  **/
3566 void
3567 gtk_text_iter_set_visible_line_offset (GtkTextIter *iter,
3568                                        gint         char_on_line)
3569 {
3570   gint chars_seen = 0;
3571   GtkTextIter pos;
3572
3573   g_return_if_fail (iter != NULL);
3574   
3575   pos = *iter;
3576
3577   /* For now we use a ludicrously slow implementation */
3578   while (chars_seen < char_on_line)
3579     {
3580       if (!_gtk_text_btree_char_is_invisible (&pos))
3581         ++chars_seen;
3582
3583       if (!gtk_text_iter_forward_char (&pos))
3584         break;
3585
3586       if (chars_seen == char_on_line)
3587         break;
3588     }
3589   
3590   if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3591     *iter = pos;
3592   else
3593     gtk_text_iter_forward_line (iter);
3594 }
3595
3596 static gint
3597 bytes_in_char (GtkTextIter *iter)
3598 {
3599   return g_unichar_to_utf8 (gtk_text_iter_get_char (iter), NULL);
3600 }
3601
3602 /**
3603  * gtk_text_iter_set_visible_line_index:
3604  * @iter: a #GtkTextIter
3605  * @byte_on_line: a byte index
3606  * 
3607  * Like gtk_text_iter_set_line_index(), but the index is in visible
3608  * bytes, i.e. text with a tag making it invisible is not counted
3609  * in the index.
3610  **/
3611 void
3612 gtk_text_iter_set_visible_line_index  (GtkTextIter *iter,
3613                                        gint         byte_on_line)
3614 {
3615   gint bytes_seen = 0;
3616   GtkTextIter pos;
3617
3618   g_return_if_fail (iter != NULL);
3619   
3620   pos = *iter;
3621
3622   /* For now we use a ludicrously slow implementation */
3623   while (bytes_seen < byte_on_line)
3624     {
3625       if (!_gtk_text_btree_char_is_invisible (&pos))
3626         bytes_seen += bytes_in_char (&pos);
3627
3628       if (!gtk_text_iter_forward_char (&pos))
3629         break;
3630
3631       if (bytes_seen >= byte_on_line)
3632         break;
3633     }
3634
3635   if (bytes_seen > byte_on_line)
3636     g_warning ("%s: Incorrect visible byte index %d falls in the middle of a UTF-8 "
3637                "character; this will crash the text buffer. "
3638                "Byte indexes must refer to the start of a character.",
3639                G_STRLOC, byte_on_line);
3640   
3641   if (_gtk_text_iter_get_text_line (&pos) == _gtk_text_iter_get_text_line (iter))
3642     *iter = pos;
3643   else
3644     gtk_text_iter_forward_line (iter);
3645 }
3646
3647 /**
3648  * gtk_text_iter_set_line:
3649  * @iter: a #GtkTextIter
3650  * @line_number: line number (counted from 0)
3651  *
3652  * Moves iterator @iter to the start of the line @line_number.  If
3653  * @line_number is negative or larger than the number of lines in the
3654  * buffer, moves @iter to the start of the last line in the buffer.
3655  * 
3656  **/
3657 void
3658 gtk_text_iter_set_line (GtkTextIter *iter,
3659                         gint         line_number)
3660 {
3661   GtkTextLine *line;
3662   gint real_line;
3663   GtkTextRealIter *real;
3664
3665   g_return_if_fail (iter != NULL);
3666
3667   real = gtk_text_iter_make_surreal (iter);
3668
3669   if (real == NULL)
3670     return;
3671
3672   check_invariants (iter);
3673
3674   line = _gtk_text_btree_get_line_no_last (real->tree, line_number, &real_line);
3675
3676   iter_set_from_char_offset (real, line, 0);
3677
3678   /* We might as well cache this, since we know it. */
3679   real->cached_line_number = real_line;
3680
3681   check_invariants (iter);
3682 }
3683
3684 /**
3685  * gtk_text_iter_set_offset:
3686  * @iter: a #GtkTextIter
3687  * @char_offset: a character number
3688  *
3689  * Sets @iter to point to @char_offset. @char_offset counts from the start
3690  * of the entire text buffer, starting with 0.
3691  * 
3692  **/
3693 void
3694 gtk_text_iter_set_offset (GtkTextIter *iter,
3695                           gint         char_offset)
3696 {
3697   GtkTextLine *line;
3698   GtkTextRealIter *real;
3699   gint line_start;
3700   gint real_char_index;
3701
3702   g_return_if_fail (iter != NULL);
3703
3704   real = gtk_text_iter_make_surreal (iter);
3705
3706   if (real == NULL)
3707     return;
3708
3709   check_invariants (iter);
3710
3711   if (real->cached_char_index >= 0 &&
3712       real->cached_char_index == char_offset)
3713     return;
3714
3715   line = _gtk_text_btree_get_line_at_char (real->tree,
3716                                            char_offset,
3717                                            &line_start,
3718                                            &real_char_index);
3719
3720   iter_set_from_char_offset (real, line, real_char_index - line_start);
3721
3722   /* Go ahead and cache this since we have it. */
3723   real->cached_char_index = real_char_index;
3724
3725   check_invariants (iter);
3726 }
3727
3728 /**
3729  * gtk_text_iter_forward_to_end:
3730  * @iter: a #GtkTextIter
3731  *
3732  * Moves @iter forward to the "end iterator," which points one past the last
3733  * valid character in the buffer. gtk_text_iter_get_char() called on the
3734  * end iterator returns 0, which is convenient for writing loops.
3735  * 
3736  **/
3737 void
3738 gtk_text_iter_forward_to_end  (GtkTextIter *iter)
3739 {
3740   GtkTextBuffer *buffer;
3741   GtkTextRealIter *real;
3742
3743   g_return_if_fail (iter != NULL);
3744
3745   real = gtk_text_iter_make_surreal (iter);
3746
3747   if (real == NULL)
3748     return;
3749
3750   buffer = _gtk_text_btree_get_buffer (real->tree);
3751
3752   gtk_text_buffer_get_end_iter (buffer, iter);
3753 }
3754
3755 /* FIXME this and gtk_text_iter_forward_to_line_end() could be cleaned up
3756  * and made faster. Look at iter_ends_line() for inspiration, perhaps.
3757  * If all else fails we could cache the para delimiter pos in the iter.
3758  * I think forward_to_line_end() actually gets called fairly often.
3759  */
3760 static int
3761 find_paragraph_delimiter_for_line (GtkTextIter *iter)
3762 {
3763   GtkTextIter end;
3764   end = *iter;
3765
3766   /* if we aren't on the last line, go forward to start of next line, then scan
3767    * back for the delimiters on the previous line
3768    */
3769   if (gtk_text_iter_forward_line (&end))
3770     {
3771       gtk_text_iter_backward_char (&end);
3772       while (!gtk_text_iter_ends_line (&end))
3773         gtk_text_iter_backward_char (&end);
3774     }
3775
3776   return gtk_text_iter_get_line_offset (&end);
3777 }
3778
3779 /**
3780  * gtk_text_iter_forward_to_line_end:
3781  * @iter: a #GtkTextIter
3782  * 
3783  * Moves the iterator to point to the paragraph delimiter characters,
3784  * which will be either a newline, a carriage return, a carriage
3785  * return/newline in sequence, or the Unicode paragraph separator
3786  * character. If the iterator is already at the paragraph delimiter
3787  * characters, moves to the paragraph delimiter characters for the
3788  * next line. If @iter is on the last line in the buffer, which does
3789  * not end in paragraph delimiters, moves to the end iterator (end of
3790  * the last line), and returns %FALSE.
3791  * 
3792  * Return value: %TRUE if we moved and the new location is not the end iterator
3793  **/
3794 gboolean
3795 gtk_text_iter_forward_to_line_end (GtkTextIter *iter)
3796 {
3797   gint current_offset;
3798   gint new_offset;
3799
3800   
3801   g_return_val_if_fail (iter != NULL, FALSE);
3802
3803   current_offset = gtk_text_iter_get_line_offset (iter);
3804   new_offset = find_paragraph_delimiter_for_line (iter);
3805   
3806   if (current_offset < new_offset)
3807     {
3808       /* Move to end of this line. */
3809       gtk_text_iter_set_line_offset (iter, new_offset);
3810       return TRUE;
3811     }
3812   else
3813     {
3814       /* Move to end of next line. */
3815       if (gtk_text_iter_forward_line (iter))
3816         {
3817           /* We don't want to move past all
3818            * empty lines.
3819            */
3820           if (!gtk_text_iter_ends_line (iter))
3821             gtk_text_iter_forward_to_line_end (iter);
3822           return TRUE;
3823         }
3824       else
3825         return FALSE;
3826     }
3827 }
3828
3829 /**
3830  * gtk_text_iter_forward_to_tag_toggle:
3831  * @iter: a #GtkTextIter
3832  * @tag: a #GtkTextTag, or NULL
3833  *
3834  * Moves forward to the next toggle (on or off) of the
3835  * #GtkTextTag @tag, or to the next toggle of any tag if
3836  * @tag is NULL. If no matching tag toggles are found,
3837  * returns FALSE, otherwise TRUE. Does not return toggles
3838  * located at @iter, only toggles after @iter. Sets @iter to
3839  * the location of the toggle, or to the end of the buffer
3840  * if no toggle is found.
3841  *
3842  * Return value: whether we found a tag toggle after @iter
3843  **/
3844 gboolean
3845 gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
3846                                      GtkTextTag  *tag)
3847 {
3848   GtkTextLine *next_line;
3849   GtkTextLine *current_line;
3850   GtkTextRealIter *real;
3851
3852   g_return_val_if_fail (iter != NULL, FALSE);
3853
3854   real = gtk_text_iter_make_real (iter);
3855
3856   if (real == NULL)
3857     return FALSE;
3858
3859   check_invariants (iter);
3860
3861   current_line = real->line;
3862   next_line = _gtk_text_line_next_could_contain_tag (current_line,
3863                                                      real->tree, tag);
3864
3865   while (_gtk_text_iter_forward_indexable_segment (iter))
3866     {
3867       /* If we went forward to a line that couldn't contain a toggle
3868          for the tag, then skip forward to a line that could contain
3869          it. This potentially skips huge hunks of the tree, so we
3870          aren't a purely linear search. */
3871       if (real->line != current_line)
3872         {
3873           if (next_line == NULL)
3874             {
3875               /* End of search. Set to end of buffer. */
3876               _gtk_text_btree_get_end_iter (real->tree, iter);
3877               return FALSE;
3878             }
3879
3880           if (real->line != next_line)
3881             iter_set_from_byte_offset (real, next_line, 0);
3882
3883           current_line = real->line;
3884           next_line = _gtk_text_line_next_could_contain_tag (current_line,
3885                                                              real->tree,
3886                                                              tag);
3887         }
3888
3889       if (gtk_text_iter_toggles_tag (iter, tag))
3890         {
3891           /* If there's a toggle here, it isn't indexable so
3892              any_segment can't be the indexable segment. */
3893           g_assert (real->any_segment != real->segment);
3894           return TRUE;
3895         }
3896     }
3897
3898   /* Check end iterator for tags */
3899   if (gtk_text_iter_toggles_tag (iter, tag))
3900     {
3901       /* If there's a toggle here, it isn't indexable so
3902          any_segment can't be the indexable segment. */
3903       g_assert (real->any_segment != real->segment);
3904       return TRUE;
3905     }
3906
3907   /* Reached end of buffer */
3908   return FALSE;
3909 }
3910
3911 /**
3912  * gtk_text_iter_backward_to_tag_toggle:
3913  * @iter: a #GtkTextIter
3914  * @tag: a #GtkTextTag, or NULL
3915  *
3916  * Moves backward to the next toggle (on or off) of the
3917  * #GtkTextTag @tag, or to the next toggle of any tag if
3918  * @tag is NULL. If no matching tag toggles are found,
3919  * returns FALSE, otherwise TRUE. Does not return toggles
3920  * located at @iter, only toggles before @iter. Sets @iter
3921  * to the location of the toggle, or the start of the buffer
3922  * if no toggle is found.
3923  *
3924  * Return value: whether we found a tag toggle before @iter
3925  **/
3926 gboolean
3927 gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
3928                                       GtkTextTag  *tag)
3929 {
3930   GtkTextLine *prev_line;
3931   GtkTextLine *current_line;
3932   GtkTextRealIter *real;
3933
3934   g_return_val_if_fail (iter != NULL, FALSE);
3935
3936   real = gtk_text_iter_make_real (iter);
3937
3938   if (real == NULL)
3939     return FALSE;
3940
3941   check_invariants (iter);
3942
3943   current_line = real->line;
3944   prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3945                                                         real->tree, tag);
3946
3947
3948   /* If we're at segment start, go to the previous segment;
3949    * if mid-segment, snap to start of current segment.
3950    */
3951   if (is_segment_start (real))
3952     {
3953       if (!_gtk_text_iter_backward_indexable_segment (iter))
3954         return FALSE;
3955     }
3956   else
3957     {
3958       ensure_char_offsets (real);
3959
3960       if (!gtk_text_iter_backward_chars (iter, real->segment_char_offset))
3961         return FALSE;
3962     }
3963
3964   do
3965     {
3966       /* If we went backward to a line that couldn't contain a toggle
3967        * for the tag, then skip backward further to a line that
3968        * could contain it. This potentially skips huge hunks of the
3969        * tree, so we aren't a purely linear search.
3970        */
3971       if (real->line != current_line)
3972         {
3973           if (prev_line == NULL)
3974             {
3975               /* End of search. Set to start of buffer. */
3976               _gtk_text_btree_get_iter_at_char (real->tree, iter, 0);
3977               return FALSE;
3978             }
3979
3980           if (real->line != prev_line)
3981             {
3982               /* Set to last segment in prev_line (could do this
3983                * more quickly)
3984                */
3985               iter_set_from_byte_offset (real, prev_line, 0);
3986
3987               while (!at_last_indexable_segment (real))
3988                 _gtk_text_iter_forward_indexable_segment (iter);
3989             }
3990
3991           current_line = real->line;
3992           prev_line = _gtk_text_line_previous_could_contain_tag (current_line,
3993                                                                 real->tree,
3994                                                                 tag);
3995         }
3996
3997       if (gtk_text_iter_toggles_tag (iter, tag))
3998         {
3999           /* If there's a toggle here, it isn't indexable so
4000            * any_segment can't be the indexable segment.
4001            */
4002           g_assert (real->any_segment != real->segment);
4003           return TRUE;
4004         }
4005     }
4006   while (_gtk_text_iter_backward_indexable_segment (iter));
4007
4008   /* Reached front of buffer */
4009   return FALSE;
4010 }
4011
4012 static gboolean
4013 matches_pred (GtkTextIter *iter,
4014               GtkTextCharPredicate pred,
4015               gpointer user_data)
4016 {
4017   gint ch;
4018
4019   ch = gtk_text_iter_get_char (iter);
4020
4021   return (*pred) (ch, user_data);
4022 }
4023
4024 /**
4025  * gtk_text_iter_forward_find_char:
4026  * @iter: a #GtkTextIter
4027  * @pred: a function to be called on each character
4028  * @user_data: user data for @pred
4029  * @limit: search limit, or %NULL for none 
4030  * 
4031  * Advances @iter, calling @pred on each character. If
4032  * @pred returns %TRUE, returns %TRUE and stops scanning.
4033  * If @pred never returns %TRUE, @iter is set to @limit if
4034  * @limit is non-%NULL, otherwise to the end iterator.
4035  * 
4036  * Return value: whether a match was found
4037  **/
4038 gboolean
4039 gtk_text_iter_forward_find_char (GtkTextIter         *iter,
4040                                  GtkTextCharPredicate pred,
4041                                  gpointer             user_data,
4042                                  const GtkTextIter   *limit)
4043 {
4044   g_return_val_if_fail (iter != NULL, FALSE);
4045   g_return_val_if_fail (pred != NULL, FALSE);
4046
4047   if (limit &&
4048       gtk_text_iter_compare (iter, limit) >= 0)
4049     return FALSE;
4050   
4051   while ((limit == NULL ||
4052           !gtk_text_iter_equal (limit, iter)) &&
4053          gtk_text_iter_forward_char (iter))
4054     {      
4055       if (matches_pred (iter, pred, user_data))
4056         return TRUE;
4057     }
4058
4059   return FALSE;
4060 }
4061
4062 /**
4063  * gtk_text_iter_backward_find_char:
4064  * @iter: a #GtkTextIter
4065  * @pred: function to be called on each character
4066  * @user_data: user data for @pred
4067  * @limit: search limit, or %NULL for none
4068  * 
4069  * Same as gtk_text_iter_forward_find_char(), but goes backward from @iter.
4070  * 
4071  * Return value: whether a match was found
4072  **/
4073 gboolean
4074 gtk_text_iter_backward_find_char (GtkTextIter         *iter,
4075                                   GtkTextCharPredicate pred,
4076                                   gpointer             user_data,
4077                                   const GtkTextIter   *limit)
4078 {
4079   g_return_val_if_fail (iter != NULL, FALSE);
4080   g_return_val_if_fail (pred != NULL, FALSE);
4081
4082   if (limit &&
4083       gtk_text_iter_compare (iter, limit) <= 0)
4084     return FALSE;
4085   
4086   while ((limit == NULL ||
4087           !gtk_text_iter_equal (limit, iter)) &&
4088          gtk_text_iter_backward_char (iter))
4089     {
4090       if (matches_pred (iter, pred, user_data))
4091         return TRUE;
4092     }
4093
4094   return FALSE;
4095 }
4096
4097 static void
4098 forward_chars_with_skipping (GtkTextIter *iter,
4099                              gint         count,
4100                              gboolean     skip_invisible,
4101                              gboolean     skip_nontext)
4102 {
4103
4104   gint i;
4105
4106   g_return_if_fail (count >= 0);
4107
4108   i = count;
4109
4110   while (i > 0)
4111     {
4112       gboolean ignored = FALSE;
4113
4114       if (skip_nontext &&
4115           gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
4116         ignored = TRUE;
4117
4118       if (!ignored &&
4119           skip_invisible &&
4120           _gtk_text_btree_char_is_invisible (iter))
4121         ignored = TRUE;
4122
4123       gtk_text_iter_forward_char (iter);
4124
4125       if (!ignored)
4126         --i;
4127     }
4128 }
4129
4130 static gboolean
4131 lines_match (const GtkTextIter *start,
4132              const gchar **lines,
4133              gboolean visible_only,
4134              gboolean slice,
4135              GtkTextIter *match_start,
4136              GtkTextIter *match_end)
4137 {
4138   GtkTextIter next;
4139   gchar *line_text;
4140   const gchar *found;
4141   gint offset;
4142
4143   if (*lines == NULL || **lines == '\0')
4144     {
4145       if (match_start)
4146         *match_start = *start;
4147
4148       if (match_end)
4149         *match_end = *start;
4150       return TRUE;
4151     }
4152
4153   next = *start;
4154   gtk_text_iter_forward_line (&next);
4155
4156   /* No more text in buffer, but *lines is nonempty */
4157   if (gtk_text_iter_equal (start, &next))
4158     {
4159       return FALSE;
4160     }
4161
4162   if (slice)
4163     {
4164       if (visible_only)
4165         line_text = gtk_text_iter_get_visible_slice (start, &next);
4166       else
4167         line_text = gtk_text_iter_get_slice (start, &next);
4168     }
4169   else
4170     {
4171       if (visible_only)
4172         line_text = gtk_text_iter_get_visible_text (start, &next);
4173       else
4174         line_text = gtk_text_iter_get_text (start, &next);
4175     }
4176
4177   if (match_start) /* if this is the first line we're matching */
4178     found = strstr (line_text, *lines);
4179   else
4180     {
4181       /* If it's not the first line, we have to match from the
4182        * start of the line.
4183        */
4184       if (strncmp (line_text, *lines, strlen (*lines)) == 0)
4185         found = line_text;
4186       else
4187         found = NULL;
4188     }
4189
4190   if (found == NULL)
4191     {
4192       g_free (line_text);
4193       return FALSE;
4194     }
4195
4196   /* Get offset to start of search string */
4197   offset = g_utf8_strlen (line_text, found - line_text);
4198
4199   next = *start;
4200
4201   /* If match start needs to be returned, set it to the
4202    * start of the search string.
4203    */
4204   if (match_start)
4205     {
4206       *match_start = next;
4207
4208       forward_chars_with_skipping (match_start, offset,
4209                                    visible_only, !slice);
4210     }
4211
4212   /* Go to end of search string */
4213   offset += g_utf8_strlen (*lines, -1);
4214
4215   forward_chars_with_skipping (&next, offset,
4216                                visible_only, !slice);
4217
4218   g_free (line_text);
4219
4220   ++lines;
4221
4222   if (match_end)
4223     *match_end = next;
4224
4225   /* pass NULL for match_start, since we don't need to find the
4226    * start again.
4227    */
4228   return lines_match (&next, lines, visible_only, slice, NULL, match_end);
4229 }
4230
4231 /* strsplit () that retains the delimiter as part of the string. */
4232 static gchar **
4233 strbreakup (const char *string,
4234             const char *delimiter,
4235             gint        max_tokens)
4236 {
4237   GSList *string_list = NULL, *slist;
4238   gchar **str_array, *s;
4239   guint i, n = 1;
4240
4241   g_return_val_if_fail (string != NULL, NULL);
4242   g_return_val_if_fail (delimiter != NULL, NULL);
4243
4244   if (max_tokens < 1)
4245     max_tokens = G_MAXINT;
4246
4247   s = strstr (string, delimiter);
4248   if (s)
4249     {
4250       guint delimiter_len = strlen (delimiter);
4251
4252       do
4253         {
4254           guint len;
4255           gchar *new_string;
4256
4257           len = s - string + delimiter_len;
4258           new_string = g_new (gchar, len + 1);
4259           strncpy (new_string, string, len);
4260           new_string[len] = 0;
4261           string_list = g_slist_prepend (string_list, new_string);
4262           n++;
4263           string = s + delimiter_len;
4264           s = strstr (string, delimiter);
4265         }
4266       while (--max_tokens && s);
4267     }
4268   if (*string)
4269     {
4270       n++;
4271       string_list = g_slist_prepend (string_list, g_strdup (string));
4272     }
4273
4274   str_array = g_new (gchar*, n);
4275
4276   i = n - 1;
4277
4278   str_array[i--] = NULL;
4279   for (slist = string_list; slist; slist = slist->next)
4280     str_array[i--] = slist->data;
4281
4282   g_slist_free (string_list);
4283
4284   return str_array;
4285 }
4286
4287 /**
4288  * gtk_text_iter_forward_search:
4289  * @iter: start of search
4290  * @str: a search string
4291  * @flags: flags affecting how the search is done
4292  * @match_start: return location for start of match, or %NULL
4293  * @match_end: return location for end of match, or %NULL
4294  * @limit: bound for the search, or %NULL for the end of the buffer
4295  * 
4296  * Searches forward for @str. Any match is returned as the range
4297  * @match_start, @match_end. The search will not continue past
4298  * @limit. Note that a search is a linear or O(n) operation, so you
4299  * may wish to use @limit to avoid locking up your UI on large
4300  * buffers.
4301  * 
4302  * If the #GTK_TEXT_SEARCH_VISIBLE_ONLY flag is present, the match may
4303  * have invisible text interspersed in @str. i.e. @str will be a
4304  * possibly-noncontiguous subsequence of the matched range. similarly,
4305  * if you specify #GTK_TEXT_SEARCH_TEXT_ONLY, the match may have
4306  * pixbufs or child widgets mixed inside the matched range. If these
4307  * flags are not given, the match must be exact; the special 0xFFFC
4308  * character in @str will match embedded pixbufs or child widgets.
4309  *
4310  * Return value: whether a match was found
4311  **/
4312 gboolean
4313 gtk_text_iter_forward_search (const GtkTextIter *iter,
4314                               const gchar       *str,
4315                               GtkTextSearchFlags flags,
4316                               GtkTextIter       *match_start,
4317                               GtkTextIter       *match_end,
4318                               const GtkTextIter *limit)
4319 {
4320   gchar **lines = NULL;
4321   GtkTextIter match;
4322   gboolean retval = FALSE;
4323   GtkTextIter search;
4324   gboolean visible_only;
4325   gboolean slice;
4326   
4327   g_return_val_if_fail (iter != NULL, FALSE);
4328   g_return_val_if_fail (str != NULL, FALSE);
4329
4330   if (limit &&
4331       gtk_text_iter_compare (iter, limit) >= 0)
4332     return FALSE;
4333   
4334   if (*str == '\0')
4335     {
4336       /* If we can move one char, return the empty string there */
4337       match = *iter;
4338       
4339       if (gtk_text_iter_forward_char (&match))
4340         {
4341           if (limit &&
4342               gtk_text_iter_equal (&match, limit))
4343             return FALSE;
4344           
4345           if (match_start)
4346             *match_start = match;
4347           if (match_end)
4348             *match_end = match;
4349           return TRUE;
4350         }
4351       else
4352         return FALSE;
4353     }
4354
4355   visible_only = (flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
4356   slice = (flags & GTK_TEXT_SEARCH_TEXT_ONLY) == 0;
4357   
4358   /* locate all lines */
4359
4360   lines = strbreakup (str, "\n", -1);
4361
4362   search = *iter;
4363
4364   do
4365     {
4366       /* This loop has an inefficient worst-case, where
4367        * gtk_text_iter_get_text () is called repeatedly on
4368        * a single line.
4369        */
4370       GtkTextIter end;
4371
4372       if (limit &&
4373           gtk_text_iter_compare (&search, limit) >= 0)
4374         break;
4375       
4376       if (lines_match (&search, (const gchar**)lines,
4377                        visible_only, slice, &match, &end))
4378         {
4379           if (limit == NULL ||
4380               (limit &&
4381                gtk_text_iter_compare (&end, limit) < 0))
4382             {
4383               retval = TRUE;
4384               
4385               if (match_start)
4386                 *match_start = match;
4387               
4388               if (match_end)
4389                 *match_end = end;
4390             }
4391           
4392           break;
4393         }
4394     }
4395   while (gtk_text_iter_forward_line (&search));
4396
4397   g_strfreev ((gchar**)lines);
4398
4399   return retval;
4400 }
4401
4402 static gboolean
4403 vectors_equal_ignoring_trailing (gchar **vec1,
4404                                  gchar **vec2)
4405 {
4406   /* Ignores trailing chars in vec2's last line */
4407
4408   gchar **i1, **i2;
4409
4410   i1 = vec1;
4411   i2 = vec2;
4412
4413   while (*i1 && *i2)
4414     {
4415       if (strcmp (*i1, *i2) != 0)
4416         {
4417           if (*(i2 + 1) == NULL) /* if this is the last line */
4418             {
4419               gint len1 = strlen (*i1);
4420               gint len2 = strlen (*i2);
4421
4422               if (len2 >= len1 &&
4423                   strncmp (*i1, *i2, len1) == 0)
4424                 {
4425                   /* We matched ignoring the trailing stuff in vec2 */
4426                   return TRUE;
4427                 }
4428               else
4429                 {
4430                   return FALSE;
4431                 }
4432             }
4433           else
4434             {
4435               return FALSE;
4436             }
4437         }
4438       ++i1;
4439       ++i2;
4440     }
4441
4442   if (*i1 || *i2)
4443     {
4444       return FALSE;
4445     }
4446   else
4447     return TRUE;
4448 }
4449
4450 typedef struct _LinesWindow LinesWindow;
4451
4452 struct _LinesWindow
4453 {
4454   gint n_lines;
4455   gchar **lines;
4456   GtkTextIter first_line_start;
4457   GtkTextIter first_line_end;
4458   gboolean slice;
4459   gboolean visible_only;
4460 };
4461
4462 static void
4463 lines_window_init (LinesWindow       *win,
4464                    const GtkTextIter *start)
4465 {
4466   gint i;
4467   GtkTextIter line_start;
4468   GtkTextIter line_end;
4469
4470   /* If we start on line 1, there are 2 lines to search (0 and 1), so
4471    * n_lines can be 2.
4472    */
4473   if (gtk_text_iter_is_start (start) ||
4474       gtk_text_iter_get_line (start) + 1 < win->n_lines)
4475     {
4476       /* Already at the end, or not enough lines to match */
4477       win->lines = g_new0 (gchar*, 1);
4478       *win->lines = NULL;
4479       return;
4480     }
4481
4482   line_start = *start;
4483   line_end = *start;
4484
4485   /* Move to start iter to start of line */
4486   gtk_text_iter_set_line_offset (&line_start, 0);
4487
4488   if (gtk_text_iter_equal (&line_start, &line_end))
4489     {
4490       /* we were already at the start; so go back one line */
4491       gtk_text_iter_backward_line (&line_start);
4492     }
4493
4494   win->first_line_start = line_start;
4495   win->first_line_end = line_end;
4496
4497   win->lines = g_new0 (gchar*, win->n_lines + 1);
4498
4499   i = win->n_lines - 1;
4500   while (i >= 0)
4501     {
4502       gchar *line_text;
4503
4504       if (win->slice)
4505         {
4506           if (win->visible_only)
4507             line_text = gtk_text_iter_get_visible_slice (&line_start, &line_end);
4508           else
4509             line_text = gtk_text_iter_get_slice (&line_start, &line_end);
4510         }
4511       else
4512         {
4513           if (win->visible_only)
4514             line_text = gtk_text_iter_get_visible_text (&line_start, &line_end);
4515           else
4516             line_text = gtk_text_iter_get_text (&line_start, &line_end);
4517         }
4518
4519       win->lines[i] = line_text;
4520
4521       line_end = line_start;
4522       gtk_text_iter_backward_line (&line_start);
4523
4524       --i;
4525     }
4526 }
4527
4528 static gboolean
4529 lines_window_back (LinesWindow *win)
4530 {
4531   GtkTextIter new_start;
4532   gchar *line_text;
4533
4534   new_start = win->first_line_start;
4535
4536   if (!gtk_text_iter_backward_line (&new_start))
4537     return FALSE;
4538   else
4539     {
4540       win->first_line_start = new_start;
4541       win->first_line_end = new_start;
4542
4543       gtk_text_iter_forward_line (&win->first_line_end);
4544     }
4545
4546   if (win->slice)
4547     {
4548       if (win->visible_only)
4549         line_text = gtk_text_iter_get_visible_slice (&win->first_line_start,
4550                                                      &win->first_line_end);
4551       else
4552         line_text = gtk_text_iter_get_slice (&win->first_line_start,
4553                                              &win->first_line_end);
4554     }
4555   else
4556     {
4557       if (win->visible_only)
4558         line_text = gtk_text_iter_get_visible_text (&win->first_line_start,
4559                                                     &win->first_line_end);
4560       else
4561         line_text = gtk_text_iter_get_text (&win->first_line_start,
4562                                             &win->first_line_end);
4563     }
4564
4565   /* Move lines to make room for first line. */
4566   g_memmove (win->lines + 1, win->lines, win->n_lines * sizeof (gchar*));
4567
4568   *win->lines = line_text;
4569
4570   /* Free old last line and NULL-terminate */
4571   g_free (win->lines[win->n_lines]);
4572   win->lines[win->n_lines] = NULL;
4573
4574   return TRUE;
4575 }
4576
4577 static void
4578 lines_window_free (LinesWindow *win)
4579 {
4580   g_strfreev (win->lines);
4581 }
4582
4583 static gchar*
4584 my_strrstr (const gchar *haystack,
4585             const gchar *needle)
4586 {
4587   /* FIXME GLib should have a nice implementation in it, this
4588    * is slow-ass crap.
4589    */
4590
4591   gint haystack_len = strlen (haystack);
4592   gint needle_len = strlen (needle);
4593   const gchar *needle_end = needle + needle_len;
4594   const gchar *haystack_rend = haystack - 1;
4595   const gchar *needle_rend = needle - 1;
4596   const gchar *p;
4597
4598   p = haystack + haystack_len;
4599   while (p != haystack)
4600     {
4601       const gchar *n = needle_end - 1;
4602       const gchar *s = p - 1;
4603       while (s != haystack_rend &&
4604              n != needle_rend &&
4605              *s == *n)
4606         {
4607           --n;
4608           --s;
4609         }
4610
4611       if (n == needle_rend)
4612         return (gchar*)++s;
4613
4614       --p;
4615     }
4616
4617   return NULL;
4618 }
4619
4620 /**
4621  * gtk_text_iter_backward_search:
4622  * @iter: a #GtkTextIter where the search begins
4623  * @str: search string
4624  * @flags: bitmask of flags affecting the search
4625  * @match_start: return location for start of match, or %NULL
4626  * @match_end: return location for end of match, or %NULL
4627  * @limit: location of last possible @match_start, or %NULL for start of buffer
4628  * 
4629  * Same as gtk_text_iter_forward_search(), but moves backward.
4630  * 
4631  * Return value: whether a match was found
4632  **/
4633 gboolean
4634 gtk_text_iter_backward_search (const GtkTextIter *iter,
4635                                const gchar       *str,
4636                                GtkTextSearchFlags flags,
4637                                GtkTextIter       *match_start,
4638                                GtkTextIter       *match_end,
4639                                const GtkTextIter *limit)
4640 {
4641   gchar **lines = NULL;
4642   gchar **l;
4643   gint n_lines;
4644   LinesWindow win;
4645   gboolean retval = FALSE;
4646   gboolean visible_only;
4647   gboolean slice;
4648   
4649   g_return_val_if_fail (iter != NULL, FALSE);
4650   g_return_val_if_fail (str != NULL, FALSE);
4651
4652   if (limit &&
4653       gtk_text_iter_compare (limit, iter) > 0)
4654     return FALSE;
4655   
4656   if (*str == '\0')
4657     {
4658       /* If we can move one char, return the empty string there */
4659       GtkTextIter match = *iter;
4660
4661       if (limit && gtk_text_iter_equal (limit, &match))
4662         return FALSE;
4663       
4664       if (gtk_text_iter_backward_char (&match))
4665         {
4666           if (match_start)
4667             *match_start = match;
4668           if (match_end)
4669             *match_end = match;
4670           return TRUE;
4671         }
4672       else
4673         return FALSE;
4674     }
4675
4676   visible_only = (flags & GTK_TEXT_SEARCH_VISIBLE_ONLY) != 0;
4677   slice = (flags & GTK_TEXT_SEARCH_TEXT_ONLY) == 0;
4678   
4679   /* locate all lines */
4680
4681   lines = strbreakup (str, "\n", -1);
4682
4683   l = lines;
4684   n_lines = 0;
4685   while (*l)
4686     {
4687       ++n_lines;
4688       ++l;
4689     }
4690
4691   win.n_lines = n_lines;
4692   win.slice = slice;
4693   win.visible_only = visible_only;
4694
4695   lines_window_init (&win, iter);
4696
4697   if (*win.lines == NULL)
4698     goto out;
4699
4700   do
4701     {
4702       gchar *first_line_match;
4703
4704       if (limit &&
4705           gtk_text_iter_compare (limit, &win.first_line_end) > 0)
4706         {
4707           /* We're now before the search limit, abort. */
4708           goto out;
4709         }
4710       
4711       /* If there are multiple lines, the first line will
4712        * end in '\n', so this will only match at the
4713        * end of the first line, which is correct.
4714        */
4715       first_line_match = my_strrstr (*win.lines, *lines);
4716
4717       if (first_line_match &&
4718           vectors_equal_ignoring_trailing (lines + 1, win.lines + 1))
4719         {
4720           /* Match! */
4721           gint offset;
4722           GtkTextIter next;
4723           GtkTextIter start_tmp;
4724           
4725           /* Offset to start of search string */
4726           offset = g_utf8_strlen (*win.lines, first_line_match - *win.lines);
4727
4728           next = win.first_line_start;
4729           start_tmp = next;
4730           forward_chars_with_skipping (&start_tmp, offset,
4731                                        visible_only, !slice);
4732
4733           if (limit &&
4734               gtk_text_iter_compare (limit, &start_tmp) > 0)
4735             goto out; /* match was bogus */
4736           
4737           if (match_start)
4738             *match_start = start_tmp;
4739
4740           /* Go to end of search string */
4741           l = lines;
4742           while (*l)
4743             {
4744               offset += g_utf8_strlen (*l, -1);
4745               ++l;
4746             }
4747
4748           forward_chars_with_skipping (&next, offset,
4749                                        visible_only, !slice);
4750
4751           if (match_end)
4752             *match_end = next;
4753
4754           retval = TRUE;
4755           goto out;
4756         }
4757     }
4758   while (lines_window_back (&win));
4759
4760  out:
4761   lines_window_free (&win);
4762   g_strfreev (lines);
4763   
4764   return retval;
4765 }
4766
4767 /*
4768  * Comparisons
4769  */
4770
4771 /**
4772  * gtk_text_iter_equal:
4773  * @lhs: a #GtkTextIter
4774  * @rhs: another #GtkTextIter
4775  * 
4776  * Tests whether two iterators are equal, using the fastest possible
4777  * mechanism. This function is very fast; you can expect it to perform
4778  * better than e.g. getting the character offset for each iterator and
4779  * comparing the offsets yourself. Also, it's a bit faster than
4780  * gtk_text_iter_compare().
4781  * 
4782  * Return value: %TRUE if the iterators point to the same place in the buffer
4783  **/
4784 gboolean
4785 gtk_text_iter_equal (const GtkTextIter *lhs,
4786                      const GtkTextIter *rhs)
4787 {
4788   GtkTextRealIter *real_lhs;
4789   GtkTextRealIter *real_rhs;
4790
4791   real_lhs = (GtkTextRealIter*)lhs;
4792   real_rhs = (GtkTextRealIter*)rhs;
4793
4794   check_invariants (lhs);
4795   check_invariants (rhs);
4796
4797   if (real_lhs->line != real_rhs->line)
4798     return FALSE;
4799   else if (real_lhs->line_byte_offset >= 0 &&
4800            real_rhs->line_byte_offset >= 0)
4801     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
4802   else
4803     {
4804       /* the ensure_char_offsets () calls do nothing if the char offsets
4805          are already up-to-date. */
4806       ensure_char_offsets (real_lhs);
4807       ensure_char_offsets (real_rhs);
4808       return real_lhs->line_char_offset == real_rhs->line_char_offset;
4809     }
4810 }
4811
4812 /**
4813  * gtk_text_iter_compare:
4814  * @lhs: a #GtkTextIter
4815  * @rhs: another #GtkTextIter
4816  * 
4817  * A qsort()-style function that returns negative if @lhs is less than
4818  * @rhs, positive if @lhs is greater than @rhs, and 0 if they're equal.
4819  * Ordering is in character offset order, i.e. the first character in the buffer
4820  * is less than the second character in the buffer.
4821  * 
4822  * Return value: -1 if @lhs is less than @rhs, 1 if @lhs is greater, 0 if they are equal
4823  **/
4824 gint
4825 gtk_text_iter_compare (const GtkTextIter *lhs,
4826                        const GtkTextIter *rhs)
4827 {
4828   GtkTextRealIter *real_lhs;
4829   GtkTextRealIter *real_rhs;
4830
4831   real_lhs = gtk_text_iter_make_surreal (lhs);
4832   real_rhs = gtk_text_iter_make_surreal (rhs);
4833
4834   if (real_lhs == NULL ||
4835       real_rhs == NULL)
4836     return -1; /* why not */
4837
4838   check_invariants (lhs);
4839   check_invariants (rhs);
4840   
4841   if (real_lhs->line == real_rhs->line)
4842     {
4843       gint left_index, right_index;
4844
4845       if (real_lhs->line_byte_offset >= 0 &&
4846           real_rhs->line_byte_offset >= 0)
4847         {
4848           left_index = real_lhs->line_byte_offset;
4849           right_index = real_rhs->line_byte_offset;
4850         }
4851       else
4852         {
4853           /* the ensure_char_offsets () calls do nothing if
4854              the offsets are already up-to-date. */
4855           ensure_char_offsets (real_lhs);
4856           ensure_char_offsets (real_rhs);
4857           left_index = real_lhs->line_char_offset;
4858           right_index = real_rhs->line_char_offset;
4859         }
4860
4861       if (left_index < right_index)
4862         return -1;
4863       else if (left_index > right_index)
4864         return 1;
4865       else
4866         return 0;
4867     }
4868   else
4869     {
4870       gint line1, line2;
4871
4872       line1 = gtk_text_iter_get_line (lhs);
4873       line2 = gtk_text_iter_get_line (rhs);
4874       if (line1 < line2)
4875         return -1;
4876       else if (line1 > line2)
4877         return 1;
4878       else
4879         return 0;
4880     }
4881 }
4882
4883 /**
4884  * gtk_text_iter_in_range:
4885  * @iter: a #GtkTextIter
4886  * @start: start of range
4887  * @end: end of range
4888  * 
4889  * Checks whether @iter falls in the range [@start, @end).
4890  * @start and @end must be in ascending order.
4891  * 
4892  * Return value: %TRUE if @iter is in the range
4893  **/
4894 gboolean
4895 gtk_text_iter_in_range (const GtkTextIter *iter,
4896                         const GtkTextIter *start,
4897                         const GtkTextIter *end)
4898 {
4899   g_return_val_if_fail (iter != NULL, FALSE);
4900   g_return_val_if_fail (start != NULL, FALSE);
4901   g_return_val_if_fail (end != NULL, FALSE);
4902   g_return_val_if_fail (gtk_text_iter_compare (start, end) <= 0, FALSE);
4903   
4904   return gtk_text_iter_compare (iter, start) >= 0 &&
4905     gtk_text_iter_compare (iter, end) < 0;
4906 }
4907
4908 /**
4909  * gtk_text_iter_order:
4910  * @first: a #GtkTextIter
4911  * @second: another #GtkTextIter
4912  *
4913  * Swaps the value of @first and @second if @second comes before
4914  * @first in the buffer. That is, ensures that @first and @second are
4915  * in sequence. Most text buffer functions that take a range call this
4916  * automatically on your behalf, so there's no real reason to call it yourself
4917  * in those cases. There are some exceptions, such as gtk_text_iter_in_range(),
4918  * that expect a pre-sorted range.
4919  * 
4920  **/
4921 void
4922 gtk_text_iter_order (GtkTextIter *first,
4923                      GtkTextIter *second)
4924 {
4925   g_return_if_fail (first != NULL);
4926   g_return_if_fail (second != NULL);
4927
4928   if (gtk_text_iter_compare (first, second) > 0)
4929     {
4930       GtkTextIter tmp;
4931
4932       tmp = *first;
4933       *first = *second;
4934       *second = tmp;
4935     }
4936 }
4937
4938 /*
4939  * Init iterators from the BTree
4940  */
4941
4942 void
4943 _gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
4944                                   GtkTextIter *iter,
4945                                   gint char_index)
4946 {
4947   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4948   gint real_char_index;
4949   gint line_start;
4950   GtkTextLine *line;
4951
4952   g_return_if_fail (iter != NULL);
4953   g_return_if_fail (tree != NULL);
4954
4955   line = _gtk_text_btree_get_line_at_char (tree, char_index,
4956                                            &line_start, &real_char_index);
4957
4958   iter_init_from_char_offset (iter, tree, line, real_char_index - line_start);
4959
4960   real->cached_char_index = real_char_index;
4961
4962   check_invariants (iter);
4963 }
4964
4965 void
4966 _gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
4967                                        GtkTextIter  *iter,
4968                                        gint          line_number,
4969                                        gint          char_on_line)
4970 {
4971   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4972   GtkTextLine *line;
4973   gint real_line;
4974
4975   g_return_if_fail (iter != NULL);
4976   g_return_if_fail (tree != NULL);
4977
4978   line = _gtk_text_btree_get_line_no_last (tree, line_number, &real_line);
4979   
4980   iter_init_from_char_offset (iter, tree, line, char_on_line);
4981
4982   /* We might as well cache this, since we know it. */
4983   real->cached_line_number = real_line;
4984
4985   check_invariants (iter);
4986 }
4987
4988 void
4989 _gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
4990                                        GtkTextIter    *iter,
4991                                        gint            line_number,
4992                                        gint            byte_index)
4993 {
4994   GtkTextRealIter *real = (GtkTextRealIter*)iter;
4995   GtkTextLine *line;
4996   gint real_line;
4997
4998   g_return_if_fail (iter != NULL);
4999   g_return_if_fail (tree != NULL);
5000
5001   line = _gtk_text_btree_get_line_no_last (tree, line_number, &real_line);
5002
5003   iter_init_from_byte_offset (iter, tree, line, byte_index);
5004
5005   /* We might as well cache this, since we know it. */
5006   real->cached_line_number = real_line;
5007
5008   check_invariants (iter);
5009 }
5010
5011 void
5012 _gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
5013                                        GtkTextIter    *iter,
5014                                        GtkTextLine    *line,
5015                                        gint            byte_offset)
5016 {
5017   g_return_if_fail (iter != NULL);
5018   g_return_if_fail (tree != NULL);
5019   g_return_if_fail (line != NULL);
5020
5021   iter_init_from_byte_offset (iter, tree, line, byte_offset);
5022
5023   check_invariants (iter);
5024 }
5025
5026 gboolean
5027 _gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
5028                                           GtkTextIter    *iter,
5029                                           GtkTextTag     *tag)
5030 {
5031   GtkTextLine *line;
5032
5033   g_return_val_if_fail (iter != NULL, FALSE);
5034   g_return_val_if_fail (tree != NULL, FALSE);
5035
5036   line = _gtk_text_btree_first_could_contain_tag (tree, tag);
5037
5038   if (line == NULL)
5039     {
5040       /* Set iter to last in tree */
5041       _gtk_text_btree_get_end_iter (tree, iter);
5042       check_invariants (iter);
5043       return FALSE;
5044     }
5045   else
5046     {
5047       iter_init_from_byte_offset (iter, tree, line, 0);
5048       gtk_text_iter_forward_to_tag_toggle (iter, tag);
5049       check_invariants (iter);
5050       return TRUE;
5051     }
5052 }
5053
5054 gboolean
5055 _gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
5056                                           GtkTextIter    *iter,
5057                                           GtkTextTag     *tag)
5058 {
5059   g_return_val_if_fail (iter != NULL, FALSE);
5060   g_return_val_if_fail (tree != NULL, FALSE);
5061
5062   _gtk_text_btree_get_end_iter (tree, iter);
5063   gtk_text_iter_backward_to_tag_toggle (iter, tag);
5064   check_invariants (iter);
5065   
5066   return TRUE;
5067 }
5068
5069 gboolean
5070 _gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
5071                                        GtkTextIter *iter,
5072                                        const gchar *mark_name)
5073 {
5074   GtkTextMark *mark;
5075
5076   g_return_val_if_fail (iter != NULL, FALSE);
5077   g_return_val_if_fail (tree != NULL, FALSE);
5078
5079   mark = _gtk_text_btree_get_mark_by_name (tree, mark_name);
5080
5081   if (mark == NULL)
5082     return FALSE;
5083   else
5084     {
5085       _gtk_text_btree_get_iter_at_mark (tree, iter, mark);
5086       check_invariants (iter);
5087       return TRUE;
5088     }
5089 }
5090
5091 void
5092 _gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
5093                                   GtkTextIter *iter,
5094                                   GtkTextMark *mark)
5095 {
5096   GtkTextLineSegment *seg;
5097
5098   g_return_if_fail (iter != NULL);
5099   g_return_if_fail (tree != NULL);
5100   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
5101
5102   seg = mark->segment;
5103
5104   iter_init_from_segment (iter, tree,
5105                           seg->body.mark.line, seg);
5106   g_assert (seg->body.mark.line == _gtk_text_iter_get_text_line (iter));
5107   check_invariants (iter);
5108 }
5109
5110 void
5111 _gtk_text_btree_get_iter_at_child_anchor (GtkTextBTree       *tree,
5112                                           GtkTextIter        *iter,
5113                                           GtkTextChildAnchor *anchor)
5114 {
5115   GtkTextLineSegment *seg;
5116
5117   g_return_if_fail (iter != NULL);
5118   g_return_if_fail (tree != NULL);
5119   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
5120
5121   seg = anchor->segment;
5122   
5123   iter_init_from_segment (iter, tree,
5124                           seg->body.child.line, seg);
5125   g_assert (seg->body.child.line == _gtk_text_iter_get_text_line (iter));
5126   check_invariants (iter);
5127 }
5128
5129 void
5130 _gtk_text_btree_get_end_iter         (GtkTextBTree   *tree,
5131                                       GtkTextIter    *iter)
5132 {
5133   g_return_if_fail (iter != NULL);
5134   g_return_if_fail (tree != NULL);
5135
5136   _gtk_text_btree_get_iter_at_char (tree,
5137                                    iter,
5138                                    _gtk_text_btree_char_count (tree));
5139   check_invariants (iter);
5140 }
5141
5142 void
5143 _gtk_text_iter_check (const GtkTextIter *iter)
5144 {
5145   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
5146   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
5147   GtkTextLineSegment *byte_segment = NULL;
5148   GtkTextLineSegment *byte_any_segment = NULL;
5149   GtkTextLineSegment *char_segment = NULL;
5150   GtkTextLineSegment *char_any_segment = NULL;
5151   gboolean segments_updated;
5152
5153   /* This function checks our class invariants for the Iter class. */
5154
5155   g_assert (sizeof (GtkTextIter) == sizeof (GtkTextRealIter));
5156
5157   if (real->chars_changed_stamp !=
5158       _gtk_text_btree_get_chars_changed_stamp (real->tree))
5159     g_error ("iterator check failed: invalid iterator");
5160
5161   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
5162     g_error ("iterator check failed: both char and byte offsets are invalid");
5163
5164   segments_updated = (real->segments_changed_stamp ==
5165                       _gtk_text_btree_get_segments_changed_stamp (real->tree));
5166
5167 #if 0
5168   printf ("checking iter, segments %s updated, byte %d char %d\n",
5169           segments_updated ? "are" : "aren't",
5170           real->line_byte_offset,
5171           real->line_char_offset);
5172 #endif
5173
5174   if (segments_updated)
5175     {
5176       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
5177         g_error ("iterator check failed: both char and byte segment offsets are invalid");
5178
5179       if (real->segment->char_count == 0)
5180         g_error ("iterator check failed: segment is not indexable.");
5181
5182       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
5183         g_error ("segment char offset is not properly up-to-date");
5184
5185       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
5186         g_error ("segment byte offset is not properly up-to-date");
5187
5188       if (real->segment_byte_offset >= 0 &&
5189           real->segment_byte_offset >= real->segment->byte_count)
5190         g_error ("segment byte offset is too large.");
5191
5192       if (real->segment_char_offset >= 0 &&
5193           real->segment_char_offset >= real->segment->char_count)
5194         g_error ("segment char offset is too large.");
5195     }
5196
5197   if (real->line_byte_offset >= 0)
5198     {
5199       _gtk_text_line_byte_locate (real->line, real->line_byte_offset,
5200                                   &byte_segment, &byte_any_segment,
5201                                   &seg_byte_offset, &line_byte_offset);
5202
5203       if (line_byte_offset != real->line_byte_offset)
5204         g_error ("wrong byte offset was stored in iterator");
5205
5206       if (segments_updated)
5207         {
5208           if (real->segment != byte_segment)
5209             g_error ("wrong segment was stored in iterator");
5210
5211           if (real->any_segment != byte_any_segment)
5212             g_error ("wrong any_segment was stored in iterator");
5213
5214           if (seg_byte_offset != real->segment_byte_offset)
5215             g_error ("wrong segment byte offset was stored in iterator");
5216
5217           if (byte_segment->type == &gtk_text_char_type)
5218             {
5219               const gchar *p;
5220               p = byte_segment->body.chars + seg_byte_offset;
5221               
5222               if (!gtk_text_byte_begins_utf8_char (p))
5223                 g_error ("broken iterator byte index pointed into the middle of a character");
5224             }
5225         }
5226     }
5227
5228   if (real->line_char_offset >= 0)
5229     {
5230       _gtk_text_line_char_locate (real->line, real->line_char_offset,
5231                                   &char_segment, &char_any_segment,
5232                                   &seg_char_offset, &line_char_offset);
5233
5234       if (line_char_offset != real->line_char_offset)
5235         g_error ("wrong char offset was stored in iterator");
5236
5237       if (segments_updated)
5238         {          
5239           if (real->segment != char_segment)
5240             g_error ("wrong segment was stored in iterator");
5241
5242           if (real->any_segment != char_any_segment)
5243             g_error ("wrong any_segment was stored in iterator");
5244
5245           if (seg_char_offset != real->segment_char_offset)
5246             g_error ("wrong segment char offset was stored in iterator");
5247
5248           if (char_segment->type == &gtk_text_char_type)
5249             {
5250               const gchar *p;
5251               p = g_utf8_offset_to_pointer (char_segment->body.chars,
5252                                             seg_char_offset);
5253
5254               /* hmm, not likely to happen eh */
5255               if (!gtk_text_byte_begins_utf8_char (p))
5256                 g_error ("broken iterator char offset pointed into the middle of a character");
5257             }
5258         }
5259     }
5260
5261   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
5262     {
5263       if (byte_segment != char_segment)
5264         g_error ("char and byte offsets did not point to the same segment");
5265
5266       if (byte_any_segment != char_any_segment)
5267         g_error ("char and byte offsets did not point to the same any segment");
5268
5269       /* Make sure the segment offsets are equivalent, if it's a char
5270          segment. */
5271       if (char_segment->type == &gtk_text_char_type)
5272         {
5273           gint byte_offset = 0;
5274           gint char_offset = 0;
5275           while (char_offset < seg_char_offset)
5276             {
5277               const char * start = char_segment->body.chars + byte_offset;
5278               byte_offset += g_utf8_next_char (start) - start;
5279               char_offset += 1;
5280             }
5281
5282           if (byte_offset != seg_byte_offset)
5283             g_error ("byte offset did not correspond to char offset");
5284
5285           char_offset =
5286             g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
5287
5288           if (char_offset != seg_char_offset)
5289             g_error ("char offset did not correspond to byte offset");
5290
5291           if (!gtk_text_byte_begins_utf8_char (char_segment->body.chars + seg_byte_offset))
5292             g_error ("byte index for iterator does not index the start of a character");
5293         }
5294     }
5295
5296   if (real->cached_line_number >= 0)
5297     {
5298       gint should_be;
5299
5300       should_be = _gtk_text_line_get_number (real->line);
5301       if (real->cached_line_number != should_be)
5302         g_error ("wrong line number was cached");
5303     }
5304
5305   if (real->cached_char_index >= 0)
5306     {
5307       if (real->line_char_offset >= 0) /* only way we can check it
5308                                           efficiently, not a real
5309                                           invariant. */
5310         {
5311           gint char_index;
5312
5313           char_index = _gtk_text_line_char_index (real->line);
5314           char_index += real->line_char_offset;
5315
5316           if (real->cached_char_index != char_index)
5317             g_error ("wrong char index was cached");
5318         }
5319     }
5320
5321   if (_gtk_text_line_is_last (real->line, real->tree))
5322     g_error ("Iterator was on last line (past the end iterator)");
5323 }
5324