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