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