]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
Use gunichar instead of gint when appropriate in the interfaces
[~andy/gtk] / gtk / gtktextiter.c
1 #include "gtktextiter.h"
2 #include "gtktextbtree.h"
3 #include "gtktextiterprivate.h"
4 #include "gtkdebug.h"
5 #include <ctype.h>
6
7 typedef struct _GtkTextRealIter GtkTextRealIter;
8
9 struct _GtkTextRealIter {
10   /* Always-valid information */
11   GtkTextBTree *tree;
12   GtkTextLine *line;
13   /* At least one of these is always valid;
14      if invalid, they are -1.
15
16      If the line byte offset is valid, so is the segment byte offset;
17      and ditto for char offsets. */
18   gint line_byte_offset;
19   gint line_char_offset;
20   /* These two are valid if >= 0 */
21   gint cached_char_index;
22   gint cached_line_number;
23   /* Stamps to detect the buffer changing under us */
24   gint chars_changed_stamp;
25   gint segments_changed_stamp;
26   /* Valid if the segments_changed_stamp is up-to-date */
27   GtkTextLineSegment *segment;     /* indexable segment we index */
28   GtkTextLineSegment *any_segment; /* first segment in our location,
29                                    maybe same as "segment" */
30   /* One of these will always be valid if segments_changed_stamp is
31      up-to-date. If invalid, they are -1.
32
33      If the line byte offset is valid, so is the segment byte offset;
34      and ditto for char offsets. */
35   gint segment_byte_offset;
36   gint segment_char_offset;
37   /* These are here for binary-compatible expansion space. */
38   gpointer pad1;
39   gint pad2;
40 };
41
42 /* These "set" functions should not assume any fields
43    other than the char stamp and the tree are valid.
44 */
45 static void
46 iter_set_common(GtkTextRealIter *iter,
47                 GtkTextLine *line)
48 {
49   /* Update segments stamp */
50   iter->segments_changed_stamp =
51     gtk_text_btree_get_segments_changed_stamp(iter->tree);
52       
53   iter->line = line;
54
55   iter->line_byte_offset = -1;
56   iter->line_char_offset = -1;
57   iter->segment_byte_offset = -1;
58   iter->segment_char_offset = -1;
59   iter->cached_char_index = -1;
60   iter->cached_line_number = -1;
61 }
62
63 static void
64 iter_set_from_byte_offset(GtkTextRealIter *iter,
65                           GtkTextLine *line,
66                           gint byte_offset)
67 {
68   iter_set_common(iter, line);
69
70   gtk_text_line_byte_locate(iter->line,
71                              byte_offset,
72                              &iter->segment,
73                              &iter->any_segment,
74                              &iter->segment_byte_offset,
75                              &iter->line_byte_offset);
76
77 }
78
79 static void
80 iter_set_from_char_offset(GtkTextRealIter *iter,
81                           GtkTextLine *line,
82                           gint char_offset)
83 {
84   iter_set_common(iter, line);
85
86   gtk_text_line_char_locate(iter->line,
87                              char_offset,
88                              &iter->segment,
89                              &iter->any_segment,
90                              &iter->segment_char_offset,
91                              &iter->line_char_offset);
92 }
93
94 static void
95 iter_set_from_segment(GtkTextRealIter *iter,
96                       GtkTextLine *line,
97                       GtkTextLineSegment *segment)
98 {
99   GtkTextLineSegment *seg;
100   gint byte_offset;
101
102   /* This could theoretically be optimized by computing all the iter
103      fields in this same loop, but I'm skipping it for now. */
104   byte_offset = 0;
105   seg = line->segments;
106   while (seg != segment)
107     {
108       byte_offset += seg->byte_count;
109       seg = seg->next;
110     }
111
112   iter_set_from_byte_offset(iter, line, byte_offset);
113 }
114
115 /* This function ensures that the segment-dependent information is
116    truly computed lazily; often we don't need to do the full make_real
117    work. */
118 static GtkTextRealIter*
119 gtk_text_iter_make_surreal(const GtkTextIter *_iter)
120 {
121   GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
122   
123   if (iter->chars_changed_stamp !=
124       gtk_text_btree_get_chars_changed_stamp(iter->tree))
125     {
126       g_warning("Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixmaps/widgets in the buffer have been modified since the iterator was created.\nYou must use marks, character numbers, or line numbers to preserve a position across buffer modifications.\nYou can apply tags and insert marks without invalidating your iterators, however.");
127       return NULL;
128     }
129
130   /* We don't update the segments information since we are becoming
131      only surreal. However we do invalidate the segments information
132      if appropriate, to be sure we segfault if we try to use it and we
133      should have used make_real. */
134
135   if (iter->segments_changed_stamp !=
136       gtk_text_btree_get_segments_changed_stamp(iter->tree))
137     {
138       iter->segment = NULL;
139       iter->any_segment = NULL;
140       /* set to segfault-causing values. */
141       iter->segment_byte_offset = -10000;
142       iter->segment_char_offset = -10000;
143     }
144   
145   return iter;
146 }
147
148 static GtkTextRealIter*
149 gtk_text_iter_make_real(const GtkTextIter *_iter)
150 {
151   GtkTextRealIter *iter;
152   
153   iter = gtk_text_iter_make_surreal(_iter);
154   
155   if (iter->segments_changed_stamp !=
156       gtk_text_btree_get_segments_changed_stamp(iter->tree))
157     {
158       if (iter->line_byte_offset >= 0)
159         {
160           iter_set_from_byte_offset(iter,
161                                     iter->line,
162                                     iter->line_byte_offset);
163         }
164       else
165         {
166           g_assert(iter->line_char_offset >= 0);
167           
168           iter_set_from_char_offset(iter,
169                                     iter->line,
170                                     iter->line_char_offset);
171         }
172     }
173
174   g_assert(iter->segment != NULL);
175   g_assert(iter->any_segment != NULL);
176   g_assert(iter->segment->char_count > 0);
177   
178   return iter;
179 }
180
181 static GtkTextRealIter*
182 iter_init_common(GtkTextIter *_iter,
183                  GtkTextBTree *tree)
184 {
185   GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
186
187   g_return_val_if_fail(iter != NULL, NULL);
188   g_return_val_if_fail(tree != NULL, NULL);
189
190   iter->tree = tree;
191
192   iter->chars_changed_stamp =
193     gtk_text_btree_get_chars_changed_stamp(iter->tree);
194   
195   return iter;
196 }
197
198 static GtkTextRealIter*
199 iter_init_from_segment(GtkTextIter *iter,
200                        GtkTextBTree *tree,
201                        GtkTextLine *line,
202                        GtkTextLineSegment *segment)
203 {
204   GtkTextRealIter *real;
205   
206   g_return_val_if_fail(line != NULL, NULL);
207
208   real = iter_init_common(iter, tree);
209   
210   iter_set_from_segment(real, line, segment);
211   
212   return real;
213 }
214
215 static GtkTextRealIter*
216 iter_init_from_byte_offset(GtkTextIter *iter,
217                            GtkTextBTree *tree,
218                            GtkTextLine *line,
219                            gint line_byte_offset)
220 {
221   GtkTextRealIter *real;
222   
223   g_return_val_if_fail(line != NULL, NULL);
224
225   real = iter_init_common(iter, tree);
226   
227   iter_set_from_byte_offset(real, line, line_byte_offset);
228   
229   return real;
230 }
231
232 static GtkTextRealIter*
233 iter_init_from_char_offset(GtkTextIter *iter,
234                            GtkTextBTree *tree,
235                            GtkTextLine *line,
236                            gint line_char_offset)
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_char_offset(real, line, line_char_offset);
245   
246   return real;
247 }
248
249 static inline void
250 invalidate_segment(GtkTextRealIter *iter)
251 {
252   iter->segments_changed_stamp -= 1;
253 }
254
255 static inline void
256 invalidate_char_index(GtkTextRealIter *iter)
257 {
258   iter->cached_char_index = -1;
259 }
260
261 static inline void
262 invalidate_line_number(GtkTextRealIter *iter)
263 {
264   iter->cached_line_number = -1;
265 }
266
267 static inline void
268 adjust_char_index(GtkTextRealIter *iter, gint count)
269 {
270   if (iter->cached_char_index >= 0)
271     iter->cached_char_index += count;
272 }
273
274 static inline void
275 adjust_line_number(GtkTextRealIter *iter, gint count)
276 {
277   if (iter->cached_line_number >= 0)
278     iter->cached_line_number += count;
279 }
280
281 static inline void
282 adjust_char_offsets(GtkTextRealIter *iter, gint count)
283 {
284   if (iter->line_char_offset >= 0)
285     {
286       iter->line_char_offset += count;
287       g_assert(iter->segment_char_offset >= 0);
288       iter->segment_char_offset += count;
289     }
290 }
291
292 static inline void
293 adjust_byte_offsets(GtkTextRealIter *iter, gint count)
294 {
295   if (iter->line_byte_offset >= 0)
296     {
297       iter->line_byte_offset += count;
298       g_assert(iter->segment_byte_offset >= 0);
299       iter->segment_byte_offset += count;
300     }
301 }
302
303 static inline void
304 ensure_char_offsets(GtkTextRealIter *iter)
305 {
306   if (iter->line_char_offset < 0)
307     {
308       g_assert(iter->line_byte_offset >= 0);
309
310       gtk_text_line_byte_to_char_offsets(iter->line,
311                                           iter->line_byte_offset,
312                                           &iter->line_char_offset,
313                                           &iter->segment_char_offset);
314     }
315 }
316
317 static inline void
318 ensure_byte_offsets(GtkTextRealIter *iter)
319 {
320   if (iter->line_byte_offset < 0)
321     {
322       g_assert(iter->line_char_offset >= 0);
323
324       gtk_text_line_char_to_byte_offsets(iter->line,
325                                           iter->line_char_offset,
326                                           &iter->line_byte_offset,
327                                           &iter->segment_byte_offset);
328     }
329 }
330
331 #if 1
332 static void
333 check_invariants(const GtkTextIter *iter)
334 {
335   if (gtk_debug_flags & GTK_DEBUG_TEXT)
336     gtk_text_iter_check(iter);
337 }
338 #else
339 #define check_invariants(x)
340 #endif
341
342 GtkTextBuffer*
343 gtk_text_iter_get_buffer(const GtkTextIter *iter)
344 {
345   GtkTextRealIter *real;
346
347   g_return_val_if_fail(iter != NULL, NULL);
348   
349   real = gtk_text_iter_make_surreal(iter);
350
351   if (real == NULL)
352     return NULL;
353
354   check_invariants(iter);
355   
356   return gtk_text_btree_get_buffer(real->tree);
357 }
358
359 GtkTextIter*
360 gtk_text_iter_copy(const GtkTextIter *iter)
361 {
362   GtkTextIter *new_iter;
363
364   g_return_val_if_fail(iter != NULL, NULL);
365   
366   new_iter = g_new(GtkTextIter, 1);
367
368   *new_iter = *iter;
369   
370   return new_iter;
371 }
372
373 void
374 gtk_text_iter_free(GtkTextIter *iter)
375 {
376   g_return_if_fail(iter != NULL);
377
378   g_free(iter);
379 }
380
381 GtkTextLineSegment*
382 gtk_text_iter_get_indexable_segment(const GtkTextIter *iter)
383 {
384   GtkTextRealIter *real;
385
386   g_return_val_if_fail(iter != NULL, 0);
387   
388   real = gtk_text_iter_make_real(iter);
389
390   if (real == NULL)
391     return NULL;
392
393   check_invariants(iter);
394   
395   g_assert(real->segment != NULL);
396   
397   return real->segment;
398 }
399
400 GtkTextLineSegment*
401 gtk_text_iter_get_any_segment(const GtkTextIter *iter)
402 {
403   GtkTextRealIter *real;
404
405   g_return_val_if_fail(iter != NULL, 0);
406   
407   real = gtk_text_iter_make_real(iter);
408
409   if (real == NULL)
410     return NULL;
411
412   check_invariants(iter);
413   
414   g_assert(real->any_segment != NULL);
415   
416   return real->any_segment;
417 }
418
419 gint
420 gtk_text_iter_get_segment_byte(const GtkTextIter *iter)
421 {
422   GtkTextRealIter *real;
423
424   g_return_val_if_fail(iter != NULL, 0);
425   
426   real = gtk_text_iter_make_real(iter);
427
428   if (real == NULL)
429     return 0;
430
431   ensure_byte_offsets(real);
432
433   check_invariants(iter);
434   
435   return real->segment_byte_offset;
436 }
437
438 gint
439 gtk_text_iter_get_segment_char(const GtkTextIter *iter)
440 {
441   GtkTextRealIter *real;
442
443   g_return_val_if_fail(iter != NULL, 0);
444   
445   real = gtk_text_iter_make_real(iter);
446
447   if (real == NULL)
448     return 0;
449
450   ensure_char_offsets(real);
451
452   check_invariants(iter);
453   
454   return real->segment_char_offset;
455 }
456
457 /* This function does not require a still-valid
458    iterator */
459 GtkTextLine*
460 gtk_text_iter_get_line(const GtkTextIter *iter)
461 {
462   const GtkTextRealIter *real;
463
464   g_return_val_if_fail(iter != NULL, 0);
465   
466   real = (const GtkTextRealIter*)iter;
467
468   return real->line;
469 }
470
471 /* This function does not require a still-valid
472    iterator */
473 GtkTextBTree*
474 gtk_text_iter_get_btree(const GtkTextIter *iter)
475 {
476   const GtkTextRealIter *real;
477
478   g_return_val_if_fail(iter != NULL, 0);
479   
480   real = (const GtkTextRealIter*)iter;
481
482   return real->tree;
483 }
484
485 /*
486  * Conversions
487  */
488
489 gint
490 gtk_text_iter_get_char_index(const GtkTextIter *iter)
491 {
492   GtkTextRealIter *real;
493
494   g_return_val_if_fail(iter != NULL, 0);
495   
496   real = gtk_text_iter_make_surreal(iter);
497
498   if (real == NULL)
499     return 0;
500
501   if (real->cached_char_index < 0)
502     {
503       real->cached_char_index =
504         gtk_text_line_char_index(real->line);
505       ensure_char_offsets(real);
506       real->cached_char_index += real->line_char_offset;
507     }
508
509   check_invariants(iter);
510   
511   return real->cached_char_index;
512 }
513
514 gint
515 gtk_text_iter_get_line_number(const GtkTextIter *iter)
516 {
517   GtkTextRealIter *real;
518
519   g_return_val_if_fail(iter != NULL, 0);
520   
521   real = gtk_text_iter_make_surreal(iter);
522
523   if (real == NULL)
524     return 0;
525
526   if (real->cached_line_number < 0)
527     real->cached_line_number =
528       gtk_text_line_get_number(real->line);
529
530   check_invariants(iter);
531   
532   return real->cached_line_number;
533 }
534
535 gint
536 gtk_text_iter_get_line_char(const GtkTextIter *iter)
537 {
538
539   GtkTextRealIter *real;
540
541   g_return_val_if_fail(iter != NULL, 0);
542   
543   real = gtk_text_iter_make_surreal(iter);
544
545   if (real == NULL)
546     return 0;
547
548   ensure_char_offsets(real);
549
550   check_invariants(iter);
551   
552   return real->line_char_offset;
553 }
554
555 gint
556 gtk_text_iter_get_line_byte(const GtkTextIter *iter)
557 {
558   GtkTextRealIter *real;
559
560   g_return_val_if_fail(iter != NULL, 0);
561   
562   real = gtk_text_iter_make_surreal(iter);
563
564   if (real == NULL)
565     return 0;
566
567   ensure_byte_offsets(real);
568
569   check_invariants(iter);
570   
571   return real->line_byte_offset;
572 }
573
574 /*
575  * Dereferencing
576  */
577
578 gunichar
579 gtk_text_iter_get_char(const GtkTextIter *iter)
580 {
581   GtkTextRealIter *real;
582
583   g_return_val_if_fail(iter != NULL, 0);
584   
585   real = gtk_text_iter_make_real(iter);
586
587   if (real == NULL)
588     return 0;
589
590   check_invariants(iter);
591   
592   /* FIXME probably want to special-case the end iterator
593      and either have an error or return 0 */
594   
595   if (real->segment->type == &gtk_text_char_type)
596     {
597       ensure_byte_offsets(real);
598       
599       return g_utf8_get_char (real->segment->body.chars +
600                               real->segment_byte_offset);
601     }
602   else
603     {
604       /* Unicode "unknown character" 0xFFFD */
605       return gtk_text_unknown_char;
606     }
607 }
608
609 gchar*
610 gtk_text_iter_get_slice       (const GtkTextIter *start,
611                                 const GtkTextIter *end)
612 {
613   g_return_val_if_fail(start != NULL, NULL);
614   g_return_val_if_fail(end != NULL, NULL);
615
616   check_invariants(start);
617   check_invariants(end);
618   
619   return gtk_text_btree_get_text(start, end, TRUE, TRUE);
620 }
621
622 gchar*
623 gtk_text_iter_get_text       (const GtkTextIter *start,
624                                const GtkTextIter *end)
625 {
626   g_return_val_if_fail(start != NULL, NULL);
627   g_return_val_if_fail(end != NULL, NULL);
628
629   check_invariants(start);
630   check_invariants(end);
631   
632   return gtk_text_btree_get_text(start, end, TRUE, FALSE);
633 }
634
635 gchar*
636 gtk_text_iter_get_visible_slice (const GtkTextIter  *start,
637                                   const GtkTextIter  *end)
638 {
639   g_return_val_if_fail(start != NULL, NULL);
640   g_return_val_if_fail(end != NULL, NULL);
641
642   check_invariants(start);
643   check_invariants(end);
644   
645   return gtk_text_btree_get_text(start, end, FALSE, TRUE);
646 }
647
648 gchar*
649 gtk_text_iter_get_visible_text (const GtkTextIter  *start,
650                                  const GtkTextIter  *end)
651 {
652   g_return_val_if_fail(start != NULL, NULL);
653   g_return_val_if_fail(end != NULL, NULL);
654   
655   check_invariants(start);
656   check_invariants(end);
657   
658   return gtk_text_btree_get_text(start, end, FALSE, FALSE);
659 }
660
661 gboolean
662 gtk_text_iter_get_pixmap      (const GtkTextIter *iter,
663                                 GdkPixmap** pixmap,
664                                 GdkBitmap** mask)
665 {
666   GtkTextRealIter *real;
667
668   g_return_val_if_fail(iter != NULL, FALSE);
669   g_return_val_if_fail(pixmap != NULL, FALSE);
670   g_return_val_if_fail(mask != NULL, FALSE);
671
672   *pixmap = NULL;
673   *mask = NULL;
674   
675   real = gtk_text_iter_make_real(iter);
676
677   if (real == NULL)
678     return FALSE;
679   
680   check_invariants(iter);
681   
682   if (real->segment->type != &gtk_text_pixmap_type)
683     return FALSE;
684   else
685     {
686       *pixmap = real->segment->body.pixmap.pixmap;
687       *mask = real->segment->body.pixmap.pixmap;
688
689       return TRUE;
690     }
691 }
692
693 GSList*
694 gtk_text_iter_get_toggled_tags  (const GtkTextIter  *iter,
695                                   gboolean             toggled_on)
696 {
697   GtkTextRealIter *real;
698   GtkTextLineSegment *seg;
699   GSList *retval;
700   
701   g_return_val_if_fail(iter != NULL, NULL);
702   
703   real = gtk_text_iter_make_real(iter);
704
705   if (real == NULL)
706     return NULL;
707
708   check_invariants(iter);
709   
710   retval = NULL;
711   seg = real->any_segment;
712   while (seg != real->segment)
713     {
714       if (toggled_on)
715         {
716           if (seg->type == &gtk_text_toggle_on_type)
717             {
718               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
719             }
720         }
721       else
722         {
723           if (seg->type == &gtk_text_toggle_off_type)
724             {
725               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
726             }
727         }
728       
729       seg = seg->next;
730     }
731
732   /* The returned list isn't guaranteed to be in any special order,
733      and it isn't. */
734   return retval;
735 }
736
737 gboolean
738 gtk_text_iter_begins_tag    (const GtkTextIter  *iter,
739                               GtkTextTag         *tag)
740 {
741   GtkTextRealIter *real;
742   GtkTextLineSegment *seg;
743   
744   g_return_val_if_fail(iter != NULL, FALSE);
745   
746   real = gtk_text_iter_make_real(iter);
747
748   if (real == NULL)
749     return FALSE;
750
751   check_invariants(iter);
752   
753   seg = real->any_segment;
754   while (seg != real->segment)
755     {
756       if (seg->type == &gtk_text_toggle_on_type)
757         {
758           if (tag == NULL ||
759               seg->body.toggle.info->tag == tag)
760             return TRUE;
761         }
762       
763       seg = seg->next;
764     }
765
766   return FALSE;
767 }
768
769 gboolean
770 gtk_text_iter_ends_tag   (const GtkTextIter  *iter,
771                            GtkTextTag         *tag)
772 {
773   GtkTextRealIter *real;
774   GtkTextLineSegment *seg;
775   
776   g_return_val_if_fail(iter != NULL, FALSE);
777   
778   real = gtk_text_iter_make_real(iter);
779
780   if (real == NULL)
781     return FALSE;
782
783   check_invariants(iter);
784   
785   seg = real->any_segment;
786   while (seg != real->segment)
787     {
788       if (seg->type == &gtk_text_toggle_off_type)
789         {
790           if (tag == NULL ||
791               seg->body.toggle.info->tag == tag)
792             return TRUE;
793         }
794       
795       seg = seg->next;
796     }
797
798   return FALSE;
799 }
800
801 gboolean
802 gtk_text_iter_toggles_tag       (const GtkTextIter  *iter,
803                                   GtkTextTag         *tag)
804 {
805   GtkTextRealIter *real;
806   GtkTextLineSegment *seg;
807   
808   g_return_val_if_fail(iter != NULL, FALSE);
809   
810   real = gtk_text_iter_make_real(iter);
811
812   if (real == NULL)
813     return FALSE;
814
815   check_invariants(iter);
816   
817   seg = real->any_segment;
818   while (seg != real->segment)
819     {
820       if ( (seg->type == &gtk_text_toggle_off_type ||
821             seg->type == &gtk_text_toggle_on_type) &&
822            (tag == NULL ||
823             seg->body.toggle.info->tag == tag) )
824         return TRUE;
825       
826       seg = seg->next;
827     }
828
829   return FALSE;
830 }
831
832 gboolean
833 gtk_text_iter_has_tag           (const GtkTextIter   *iter,
834                                   GtkTextTag          *tag)
835 {
836   GtkTextRealIter *real;
837   
838   g_return_val_if_fail(iter != NULL, FALSE);
839   g_return_val_if_fail(GTK_IS_TEXT_TAG(tag), FALSE);
840   
841   real = gtk_text_iter_make_surreal(iter);
842
843   if (real == NULL)
844     return FALSE; 
845
846   check_invariants(iter);
847   
848   if (real->line_byte_offset >= 0)
849     {
850       return gtk_text_line_byte_has_tag(real->line, real->tree,
851                                          real->line_byte_offset, tag);
852     }
853   else
854     {
855       g_assert(real->line_char_offset >= 0);
856       return gtk_text_line_char_has_tag(real->line, real->tree,
857                                          real->line_char_offset, tag);
858     }
859 }
860
861 gboolean
862 gtk_text_iter_starts_line (const GtkTextIter   *iter)
863 {
864   GtkTextRealIter *real;
865   
866   g_return_val_if_fail(iter != NULL, FALSE);
867   
868   real = gtk_text_iter_make_surreal(iter);
869
870   if (real == NULL)
871     return FALSE;  
872
873   check_invariants(iter);
874   
875   if (real->line_byte_offset >= 0)
876     {
877       return (real->line_byte_offset == 0);
878     }
879   else
880     {
881       g_assert(real->line_char_offset >= 0);
882       return (real->line_char_offset == 0);
883     }
884 }
885
886 gboolean
887 gtk_text_iter_ends_line (const GtkTextIter   *iter)
888 {
889   g_return_val_if_fail(iter != NULL, FALSE);
890
891   check_invariants(iter);
892   
893   return gtk_text_iter_get_char(iter) == '\n';
894 }
895
896 gint
897 gtk_text_iter_get_chars_in_line (const GtkTextIter   *iter)
898 {
899   GtkTextRealIter *real;
900   gint count;
901   GtkTextLineSegment *seg;
902   
903   g_return_val_if_fail(iter != NULL, FALSE);
904   
905   real = gtk_text_iter_make_surreal(iter);
906
907   if (real == NULL)
908     return 0;  
909
910   check_invariants(iter);
911   
912   if (real->line_char_offset >= 0)
913     {
914       /* We can start at the segments we've already found. */
915       count = real->line_char_offset - real->segment_char_offset;
916       seg = gtk_text_iter_get_indexable_segment(iter);
917     }
918   else
919     {
920       /* count whole line. */
921       seg = real->line->segments;
922       count = 0;
923     }
924
925   
926   while (seg != NULL)
927     {
928       count += seg->char_count;
929       
930       seg = seg->next;
931     }
932
933   return count;
934 }
935
936 /*
937  * Increments/decrements
938  */
939
940 static gboolean
941 forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
942 {
943   GtkTextLine *new_line;
944   
945   new_line = gtk_text_line_next(real->line);
946
947   g_assert(new_line != real->line);
948   
949   if (new_line != NULL)
950     {      
951       real->line = new_line;
952
953       real->line_byte_offset = 0;
954       real->line_char_offset = 0;
955       
956       real->segment_byte_offset = 0;
957       real->segment_char_offset = 0;
958       
959       /* Find first segments in new line */
960       real->any_segment = real->line->segments;
961       real->segment = real->any_segment;
962       while (real->segment->char_count == 0)
963         real->segment = real->segment->next;
964       
965       return TRUE;
966     }
967   else
968     {
969       /* There is no way to move forward; we were already
970          at the "end" index. (the end index is the last
971          line pointer, segment_byte_offset of 0) */
972
973       g_assert(real->line_char_offset == 0 ||
974                real->line_byte_offset == 0);
975       
976       /* The only indexable segment allowed on the bogus
977          line at the end is a single char segment containing
978          a newline. */
979       if (real->segments_changed_stamp ==
980           gtk_text_btree_get_segments_changed_stamp(real->tree))
981         {
982           g_assert(real->segment->type == &gtk_text_char_type);
983           g_assert(real->segment->char_count == 1);
984         }
985       /* We leave real->line as-is */
986       
987       return FALSE;
988     }
989 }
990
991 static gboolean
992 forward_char(GtkTextRealIter *real)
993 {
994   GtkTextIter *iter = (GtkTextIter*)real;
995
996   check_invariants((GtkTextIter*)real);
997   
998   ensure_char_offsets(real);
999   
1000   if ( (real->segment_char_offset + 1) == real->segment->char_count)
1001     {
1002       /* Need to move to the next segment; if no next segment,
1003          need to move to next line. */
1004       return gtk_text_iter_forward_indexable_segment(iter);
1005     }
1006   else
1007     {
1008       /* Just moving within a segment. Keep byte count
1009          up-to-date, if it was already up-to-date. */
1010
1011       g_assert(real->segment->type == &gtk_text_char_type);
1012       
1013       if (real->line_byte_offset >= 0)
1014         {
1015           gint bytes;
1016           const char * start =
1017             real->segment->body.chars + real->segment_byte_offset;
1018           
1019           bytes = g_utf8_next_char (start) - start;
1020
1021           real->line_byte_offset += bytes;
1022           real->segment_byte_offset += bytes;
1023
1024           g_assert(real->segment_byte_offset < real->segment->byte_count);
1025         }
1026
1027       real->line_char_offset += 1;
1028       real->segment_char_offset += 1;
1029
1030       adjust_char_index(real, 1);
1031       
1032       g_assert(real->segment_char_offset < real->segment->char_count);
1033
1034       /* We moved into the middle of a segment, so the any_segment
1035          must now be the segment we're in the middle of. */
1036       real->any_segment = real->segment;
1037       
1038       check_invariants((GtkTextIter*)real);
1039       
1040       return TRUE;
1041     }
1042 }
1043
1044 gboolean
1045 gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
1046 {
1047   /* Need to move to the next segment; if no next segment,
1048      need to move to next line. */
1049   GtkTextLineSegment *seg;
1050   GtkTextLineSegment *any_seg;
1051   GtkTextRealIter *real;
1052   gint chars_skipped;
1053   gint bytes_skipped;
1054   
1055   g_return_val_if_fail(iter != NULL, FALSE);
1056   
1057   real = gtk_text_iter_make_real(iter);
1058
1059   if (real == NULL)
1060     return FALSE;
1061
1062   check_invariants(iter);
1063   
1064   if (real->line_char_offset >= 0)
1065     {
1066       chars_skipped = real->segment->char_count - real->segment_char_offset;
1067       g_assert(chars_skipped > 0);
1068     }
1069   else
1070     chars_skipped = 0;
1071
1072   if (real->line_byte_offset >= 0)
1073     {
1074       bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
1075       g_assert(bytes_skipped > 0);
1076     }
1077   else
1078     bytes_skipped = 0;
1079   
1080   /* Get first segment of any kind */
1081   any_seg = real->segment->next;
1082   /* skip non-indexable segments, if any */
1083   seg = any_seg;
1084   while (seg != NULL && seg->char_count == 0)
1085     seg = seg->next;
1086   
1087   if (seg != NULL)
1088     {
1089       real->any_segment = any_seg;
1090       real->segment = seg;
1091
1092       if (real->line_byte_offset >= 0)
1093         {
1094           g_assert(bytes_skipped > 0);
1095           real->segment_byte_offset = 0;
1096           real->line_byte_offset += bytes_skipped;
1097         }
1098
1099       if (real->line_char_offset >= 0)
1100         {
1101           g_assert(chars_skipped > 0);
1102           real->segment_char_offset = 0;
1103           real->line_char_offset += chars_skipped;
1104           adjust_char_index(real, chars_skipped);
1105         }
1106
1107       check_invariants(iter);
1108       
1109       return TRUE;
1110     }
1111   else
1112     {      
1113       /* End of the line */
1114       if (forward_line_leaving_caches_unmodified(real))
1115         {
1116           adjust_line_number(real, 1);
1117           if (real->line_char_offset >= 0)
1118             adjust_char_index(real, chars_skipped);
1119
1120           check_invariants(iter);
1121
1122           g_assert(real->line_byte_offset == 0);
1123           g_assert(real->line_char_offset == 0);
1124           g_assert(real->segment_byte_offset == 0);
1125           g_assert(real->segment_char_offset == 0);
1126           g_assert(gtk_text_iter_starts_line(iter));
1127
1128           check_invariants(iter);
1129           
1130           return TRUE;
1131         }
1132       else
1133         {
1134           /* End of buffer */
1135
1136           check_invariants(iter);
1137           
1138           return FALSE;
1139         }
1140     }
1141 }
1142
1143 gboolean
1144 gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
1145 {
1146   g_warning("FIXME");
1147
1148 }
1149
1150 gboolean
1151 gtk_text_iter_forward_char(GtkTextIter *iter)
1152 {
1153   GtkTextRealIter *real;
1154   
1155   g_return_val_if_fail(iter != NULL, FALSE);
1156   
1157   real = gtk_text_iter_make_real(iter);
1158
1159   if (real == NULL)
1160     return FALSE;
1161   else
1162     {
1163       check_invariants(iter);
1164       return forward_char(real);
1165     }
1166 }
1167
1168 gboolean
1169 gtk_text_iter_backward_char(GtkTextIter *iter)
1170 {
1171   g_return_val_if_fail(iter != NULL, FALSE);
1172
1173   check_invariants(iter);
1174   
1175   return gtk_text_iter_backward_chars(iter, 1);
1176 }
1177
1178 /*
1179   Definitely we should try to linear scan as often as possible for
1180   movement within a single line, because we can't use the BTree to
1181   speed within-line searches up; for movement between lines, we would
1182   like to avoid the linear scan probably.
1183   
1184   Instead of using this constant, it might be nice to cache the line
1185   length in the iterator and linear scan if motion is within a single
1186   line.
1187
1188   I guess you'd have to profile the various approaches.
1189 */
1190 #define MAX_LINEAR_SCAN 300
1191
1192 gboolean
1193 gtk_text_iter_forward_chars(GtkTextIter *iter, gint count)
1194 {
1195   GtkTextRealIter *real;
1196   
1197   g_return_val_if_fail(iter != NULL, FALSE);
1198   
1199   real = gtk_text_iter_make_real(iter);
1200   
1201   if (real == NULL)
1202     return FALSE;
1203   else if (count == 0)
1204     return FALSE;
1205   else if (count < 0)
1206     return gtk_text_iter_backward_chars(iter, 0 - count);
1207   else if (count < MAX_LINEAR_SCAN)
1208     {
1209       check_invariants(iter);
1210       
1211       while (count > 1)
1212         {
1213           if (!forward_char(real))
1214             return FALSE;
1215           --count;
1216         }
1217       
1218       return forward_char(real);
1219     }
1220   else
1221     {
1222       gint current_char_index;
1223       gint new_char_index;
1224
1225       check_invariants(iter);
1226       
1227       current_char_index = gtk_text_iter_get_char_index(iter);
1228
1229       if (current_char_index == gtk_text_btree_char_count(real->tree))
1230         return FALSE; /* can't move forward */
1231       
1232       new_char_index = current_char_index + count;
1233       gtk_text_iter_set_char_index(iter, new_char_index);
1234
1235       check_invariants(iter);
1236
1237       return TRUE;
1238     }
1239 }
1240
1241 gboolean
1242 gtk_text_iter_backward_chars(GtkTextIter *iter, gint count)
1243 {
1244   GtkTextRealIter *real;
1245
1246   g_return_val_if_fail(iter != NULL, FALSE);
1247   
1248   real = gtk_text_iter_make_real(iter);
1249   
1250   if (real == NULL)
1251     return FALSE;
1252   else if (count == 0)
1253     return FALSE;
1254   else if (count < 0)
1255     return gtk_text_iter_forward_chars(iter, 0 - count);
1256
1257   ensure_char_offsets(real);
1258   check_invariants(iter);
1259   
1260   if (count <= real->segment_char_offset)
1261     {
1262       /* Optimize the within-segment case */      
1263       g_assert(real->segment->char_count > 0);
1264       g_assert(real->segment->type == &gtk_text_char_type);
1265
1266       real->segment_char_offset -= count;
1267       g_assert(real->segment_char_offset >= 0);
1268
1269       if (real->line_byte_offset >= 0)
1270         {
1271           gint new_byte_offset;
1272           gint i;
1273
1274           new_byte_offset = 0;
1275           i = 0;
1276           while (i < real->segment_char_offset)
1277             {
1278               const char * start = real->segment->body.chars + new_byte_offset;
1279               new_byte_offset += g_utf8_next_char (start) - start;
1280
1281               ++i;
1282             }
1283       
1284           real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
1285           real->segment_byte_offset = new_byte_offset;
1286         }
1287       
1288       real->line_char_offset -= count;
1289
1290       adjust_char_index(real, 0 - count);
1291
1292       check_invariants(iter);
1293       
1294       return TRUE;
1295     }
1296   else
1297     {
1298       /* We need to go back into previous segments. For now,
1299          just keep this really simple. */
1300       gint current_char_index;
1301       gint new_char_index;
1302       
1303       current_char_index = gtk_text_iter_get_char_index(iter);
1304
1305       if (current_char_index == 0)
1306         return FALSE; /* can't move backward */
1307       
1308       new_char_index = current_char_index - count;
1309       if (new_char_index < 0)
1310         new_char_index = 0;
1311       gtk_text_iter_set_char_index(iter, new_char_index);
1312
1313       check_invariants(iter);
1314       
1315       return TRUE;
1316     }
1317 }
1318
1319 gboolean
1320 gtk_text_iter_forward_line(GtkTextIter *iter)
1321 {
1322   GtkTextRealIter *real;
1323   
1324   g_return_val_if_fail(iter != NULL, FALSE);
1325   
1326   real = gtk_text_iter_make_real(iter);
1327   
1328   if (real == NULL)
1329     return FALSE;
1330
1331   check_invariants(iter);
1332   
1333   if (forward_line_leaving_caches_unmodified(real))
1334     {
1335       invalidate_char_index(real);
1336       adjust_line_number(real, 1);
1337
1338       check_invariants(iter);
1339       
1340       return TRUE;
1341     }
1342   else
1343     {
1344       check_invariants(iter);
1345       return FALSE;
1346     }
1347 }
1348
1349 gboolean
1350 gtk_text_iter_backward_line(GtkTextIter *iter)
1351 {
1352   GtkTextLine *new_line;
1353   GtkTextRealIter *real;
1354   gboolean offset_will_change;
1355   gint offset;
1356   
1357   g_return_val_if_fail(iter != NULL, FALSE);
1358   
1359   real = gtk_text_iter_make_real(iter);
1360   
1361   if (real == NULL)
1362     return FALSE;
1363
1364   check_invariants(iter);
1365   
1366   new_line = gtk_text_line_previous(real->line);
1367
1368   offset_will_change = FALSE;
1369   if (real->line_char_offset > 0)
1370     offset_will_change = TRUE;
1371           
1372   if (new_line != NULL)
1373     {
1374       real->line = new_line;
1375       
1376       adjust_line_number(real, -1);
1377     }
1378   else
1379     {
1380       if (!offset_will_change)
1381         return FALSE;
1382     }
1383
1384   invalidate_char_index(real);
1385   
1386   real->line_byte_offset = 0;
1387   real->line_char_offset = 0;
1388
1389   real->segment_byte_offset = 0;
1390   real->segment_char_offset = 0;
1391   
1392   /* Find first segment in line */
1393   real->any_segment = real->line->segments;
1394   real->segment = gtk_text_line_byte_to_segment(real->line,
1395                                                  0, &offset);
1396
1397   g_assert(offset == 0);
1398
1399   /* Note that if we are on the first line, we snap to the start
1400      of the first line and return TRUE, so TRUE means the
1401      iterator changed, not that the line changed; this is maybe
1402      a bit weird. I'm not sure there's an obvious right thing
1403      to do though. */
1404
1405   check_invariants(iter);
1406   
1407   return TRUE;
1408 }
1409
1410 gboolean
1411 gtk_text_iter_forward_lines(GtkTextIter *iter, gint count)
1412 {
1413   if (count < 0)
1414     return gtk_text_iter_backward_lines(iter, 0 - count);
1415   else if (count == 0)
1416     return FALSE;
1417   else if (count == 1)
1418     {
1419       check_invariants(iter);
1420       return gtk_text_iter_forward_line(iter);
1421     }
1422   else
1423     {
1424       gint old_line;
1425       
1426       old_line = gtk_text_iter_get_line_number(iter);
1427       
1428       gtk_text_iter_set_line_number(iter, old_line + count);
1429
1430       check_invariants(iter);
1431       
1432       return (gtk_text_iter_get_line_number(iter) != old_line);
1433     }
1434 }
1435
1436 gboolean
1437 gtk_text_iter_backward_lines(GtkTextIter *iter, gint count)
1438 {
1439   if (count < 0)
1440     return gtk_text_iter_forward_lines(iter, 0 - count);
1441   else if (count == 0)
1442     return FALSE;
1443   else if (count == 1)
1444     {
1445       return gtk_text_iter_backward_line(iter);
1446     }
1447   else
1448     {
1449       gint old_line;
1450       
1451       old_line = gtk_text_iter_get_line_number(iter);
1452       
1453       gtk_text_iter_set_line_number(iter, MAX(old_line - count, 0));
1454
1455       return (gtk_text_iter_get_line_number(iter) != old_line);
1456     }
1457 }
1458
1459 static gboolean
1460 is_word_char(gunichar ch, gpointer user_data)
1461 {
1462   /* will likely need some i18n help FIXME */
1463   return isalpha(ch);
1464 }
1465
1466 static gboolean
1467 is_not_word_char(gunichar ch, gpointer user_data)
1468 {
1469   return !is_word_char(ch, user_data);
1470 }
1471
1472 static gboolean
1473 gtk_text_iter_is_in_word(const GtkTextIter *iter)
1474 {
1475   gint ch;
1476
1477   ch = gtk_text_iter_get_char(iter);
1478
1479   return is_word_char(ch, NULL);
1480 }
1481
1482 gboolean
1483 gtk_text_iter_forward_word_end(GtkTextIter      *iter)
1484 {  
1485   gboolean in_word;
1486   GtkTextIter start;
1487   
1488   g_return_val_if_fail(iter != NULL, FALSE);
1489
1490   start = *iter;
1491   
1492   in_word = gtk_text_iter_is_in_word(iter);
1493
1494   if (!in_word)
1495     {
1496       if (!gtk_text_iter_forward_find_char(iter, is_word_char, NULL))
1497         return !gtk_text_iter_equal(iter, &start);
1498       else
1499         in_word = TRUE;
1500     }
1501
1502   g_assert(in_word);
1503   
1504   gtk_text_iter_forward_find_char(iter, is_not_word_char, NULL);
1505
1506   return !gtk_text_iter_equal(iter, &start);
1507 }
1508
1509 gboolean
1510 gtk_text_iter_backward_word_start(GtkTextIter      *iter)
1511 {
1512   gboolean in_word;
1513   GtkTextIter start;
1514   
1515   g_return_val_if_fail(iter != NULL, FALSE);
1516
1517   start = *iter;
1518   
1519   in_word = gtk_text_iter_is_in_word(iter);
1520
1521   if (!in_word)
1522     {
1523       if (!gtk_text_iter_backward_find_char(iter, is_word_char, NULL))
1524         return !gtk_text_iter_equal(iter, &start);
1525       else
1526         in_word = TRUE;
1527     }
1528
1529   g_assert(in_word);
1530   
1531   gtk_text_iter_backward_find_char(iter, is_not_word_char, NULL);
1532   gtk_text_iter_forward_char(iter); /* point to first char of word,
1533                                         not first non-word char. */
1534   
1535   return !gtk_text_iter_equal(iter, &start);
1536 }
1537
1538 gboolean
1539 gtk_text_iter_forward_word_ends(GtkTextIter      *iter,
1540                                  gint               count)
1541 {
1542   g_return_val_if_fail(iter != NULL, FALSE);
1543   g_return_val_if_fail(count > 0, FALSE);
1544
1545   if (!gtk_text_iter_forward_word_end(iter))
1546     return FALSE;
1547   --count;
1548   
1549   while (count > 0)
1550     {
1551       if (!gtk_text_iter_forward_word_end(iter))
1552         break;
1553       --count;
1554     }
1555   return TRUE;
1556 }
1557
1558 gboolean
1559 gtk_text_iter_backward_word_starts(GtkTextIter      *iter,
1560                                     gint               count)
1561 {
1562   g_return_val_if_fail(iter != NULL, FALSE);
1563   g_return_val_if_fail(count > 0, FALSE);
1564
1565   if (!gtk_text_iter_backward_word_start(iter))
1566     return FALSE;
1567   --count;
1568   
1569   while (count > 0)
1570     {
1571       if (!gtk_text_iter_backward_word_start(iter))
1572         break;
1573       --count;
1574     }
1575   return TRUE;
1576 }
1577
1578 /* up/down lines maintain the char offset, while forward/backward lines
1579    always sets the char offset to 0. */
1580 gboolean
1581 gtk_text_iter_up_lines        (GtkTextIter *iter,
1582                                 gint count)
1583 {
1584   gint char_offset;
1585
1586   if (count < 0)
1587     return gtk_text_iter_down_lines(iter, 0 - count);
1588   
1589   char_offset = gtk_text_iter_get_line_char(iter);
1590
1591   if (!gtk_text_iter_backward_line(iter))
1592     return FALSE;
1593   --count;
1594   
1595   while (count > 0)
1596     {
1597       if (!gtk_text_iter_backward_line(iter))
1598         break;
1599       --count;
1600     }
1601
1602   gtk_text_iter_set_line_char(iter, char_offset);
1603   
1604   return TRUE;
1605 }
1606
1607 gboolean
1608 gtk_text_iter_down_lines        (GtkTextIter *iter,
1609                                   gint count)
1610 {
1611   gint char_offset;
1612
1613   if (count < 0)
1614     return gtk_text_iter_up_lines(iter, 0 - count);
1615   
1616   char_offset = gtk_text_iter_get_line_char(iter);
1617
1618   if (!gtk_text_iter_forward_line(iter))
1619     return FALSE;
1620   --count;
1621   
1622   while (count > 0)
1623     {
1624       if (!gtk_text_iter_forward_line(iter))
1625         break;
1626       --count;
1627     }
1628
1629   gtk_text_iter_set_line_char(iter, char_offset);
1630   
1631   return TRUE;
1632 }
1633
1634 void
1635 gtk_text_iter_set_line_char(GtkTextIter *iter,
1636                              gint char_on_line)
1637 {
1638   GtkTextRealIter *real;
1639   
1640   g_return_if_fail(iter != NULL);
1641   
1642   real = gtk_text_iter_make_surreal(iter);
1643
1644   if (real == NULL)
1645     return;
1646
1647   check_invariants(iter);
1648   
1649   iter_set_from_char_offset(real, real->line, char_on_line);
1650
1651   check_invariants(iter);
1652 }
1653
1654 void
1655 gtk_text_iter_set_line_number(GtkTextIter *iter, gint line_number)
1656 {
1657   GtkTextLine *line;
1658   gint real_line;
1659   GtkTextRealIter *real;
1660   
1661   g_return_if_fail(iter != NULL);
1662   
1663   real = gtk_text_iter_make_surreal(iter);
1664
1665   if (real == NULL)
1666     return;
1667
1668   check_invariants(iter);
1669   
1670   line = gtk_text_btree_get_line(real->tree, line_number, &real_line);
1671
1672   iter_set_from_char_offset(real, line, 0);
1673   
1674   /* We might as well cache this, since we know it. */
1675   real->cached_line_number = real_line;
1676
1677   check_invariants(iter);
1678 }
1679
1680 void
1681 gtk_text_iter_set_char_index(GtkTextIter *iter, gint char_index)
1682 {
1683   GtkTextLine *line;
1684   GtkTextRealIter *real;
1685   gint line_start;
1686   gint real_char_index;
1687   
1688   g_return_if_fail(iter != NULL);
1689   
1690   real = gtk_text_iter_make_surreal(iter);
1691
1692   if (real == NULL)
1693     return;
1694
1695   check_invariants(iter);
1696   
1697   if (real->cached_char_index >= 0 &&
1698       real->cached_char_index == char_index)
1699     return;
1700
1701   line = gtk_text_btree_get_line_at_char(real->tree,
1702                                           char_index,
1703                                           &line_start,
1704                                           &real_char_index);
1705
1706   iter_set_from_char_offset(real, line, real_char_index - line_start);
1707   
1708   /* Go ahead and cache this since we have it. */
1709   real->cached_char_index = real_char_index;
1710
1711   check_invariants(iter);
1712 }
1713
1714 void
1715 gtk_text_iter_forward_to_end  (GtkTextIter       *iter)
1716 {
1717   GtkTextBuffer *buffer;
1718   GtkTextRealIter *real;
1719
1720   g_return_if_fail(iter != NULL);
1721   
1722   real = gtk_text_iter_make_surreal(iter);
1723
1724   if (real == NULL)
1725     return;
1726   
1727   buffer = gtk_text_btree_get_buffer(real->tree);
1728
1729   gtk_text_buffer_get_last_iter(buffer, iter);
1730 }
1731
1732 gboolean
1733 gtk_text_iter_forward_to_newline(GtkTextIter *iter)
1734 {
1735   gint current_offset;
1736   gint new_offset;
1737   
1738   g_return_val_if_fail(iter != NULL, FALSE);
1739   
1740   current_offset = gtk_text_iter_get_line_char(iter);
1741   new_offset = gtk_text_iter_get_chars_in_line(iter) - 1;
1742
1743   if (current_offset < new_offset)
1744     {
1745       /* Move to end of this line. */
1746       gtk_text_iter_set_line_char(iter, new_offset);
1747       return TRUE;
1748     }
1749   else
1750     {
1751       /* Move to end of next line. */
1752       if (gtk_text_iter_forward_line(iter))
1753         {
1754           gtk_text_iter_forward_to_newline(iter);
1755           return TRUE;
1756         }
1757       else
1758         return FALSE;
1759     }
1760 }
1761
1762 gboolean
1763 gtk_text_iter_forward_find_tag_toggle (GtkTextIter *iter,
1764                                         GtkTextTag  *tag)
1765 {
1766   GtkTextLine *next_line;
1767   GtkTextLine *current_line;
1768   GtkTextRealIter *real;
1769
1770   g_return_val_if_fail(iter != NULL, FALSE);
1771   
1772   real = gtk_text_iter_make_real(iter);
1773
1774   if (real == NULL)
1775     return FALSE;
1776
1777   check_invariants(iter);
1778   
1779   current_line = real->line;
1780   next_line = gtk_text_line_next_could_contain_tag(current_line,
1781                                                     real->tree, tag);
1782   
1783   while (gtk_text_iter_forward_indexable_segment(iter))
1784     {
1785       /* If we went forward to a line that couldn't contain a toggle
1786          for the tag, then skip forward to a line that could contain
1787          it. This potentially skips huge hunks of the tree, so we
1788          aren't a purely linear search. */
1789       if (real->line != current_line)
1790         {
1791           if (next_line == NULL)
1792             {
1793               /* End of search. Set to end of buffer. */
1794               gtk_text_btree_get_last_iter(real->tree, iter);
1795               return FALSE;
1796             }
1797               
1798           if (real->line != next_line)
1799             iter_set_from_byte_offset(real, next_line, 0);
1800
1801           current_line = real->line;
1802           next_line = gtk_text_line_next_could_contain_tag(current_line,
1803                                                             real->tree,
1804                                                             tag);
1805         }
1806
1807       if (gtk_text_iter_toggles_tag(iter, tag))
1808         {
1809           /* If there's a toggle here, it isn't indexable so
1810              any_segment can't be the indexable segment. */
1811           g_assert(real->any_segment != real->segment);
1812           return TRUE;
1813         }
1814     }
1815
1816   /* Reached end of buffer */
1817   return FALSE;
1818 }
1819
1820 gboolean
1821 gtk_text_iter_backward_find_tag_toggle (GtkTextIter *iter,
1822                                          GtkTextTag  *tag)
1823 {
1824
1825   g_warning("FIXME");
1826 }
1827
1828 static gboolean
1829 matches_pred(GtkTextIter *iter,
1830              GtkTextViewCharPredicate pred,
1831              gpointer user_data)
1832 {
1833   gint ch;
1834
1835   ch = gtk_text_iter_get_char(iter);
1836
1837   return (*pred) (ch, user_data);
1838 }
1839
1840 gboolean
1841 gtk_text_iter_forward_find_char (GtkTextIter *iter,
1842                                   GtkTextViewCharPredicate pred,
1843                                   gpointer user_data)
1844 {
1845   g_return_val_if_fail(iter != NULL, FALSE);
1846   g_return_val_if_fail(pred != NULL, FALSE);
1847
1848   while (gtk_text_iter_forward_char(iter))
1849     {
1850       if (matches_pred(iter, pred, user_data))
1851         return TRUE;
1852     }
1853   
1854   return FALSE;
1855 }
1856
1857 gboolean
1858 gtk_text_iter_backward_find_char (GtkTextIter *iter,
1859                                    GtkTextViewCharPredicate pred,
1860                                    gpointer user_data)
1861 {
1862   g_return_val_if_fail(iter != NULL, FALSE);
1863   g_return_val_if_fail(pred != NULL, FALSE);
1864
1865   while (gtk_text_iter_backward_char(iter))
1866     {
1867       if (matches_pred(iter, pred, user_data))
1868         return TRUE;
1869     }
1870   
1871   return FALSE;
1872 }
1873
1874 /*
1875  * Comparisons
1876  */
1877
1878 gboolean
1879 gtk_text_iter_equal(const GtkTextIter *lhs, const GtkTextIter *rhs)
1880 {
1881   GtkTextRealIter *real_lhs;
1882   GtkTextRealIter *real_rhs;
1883
1884   real_lhs = (GtkTextRealIter*)lhs;
1885   real_rhs = (GtkTextRealIter*)rhs;
1886
1887   check_invariants(lhs);
1888   check_invariants(rhs);
1889   
1890   if (real_lhs->line != real_rhs->line)
1891     return FALSE;
1892   else if (real_lhs->line_byte_offset >= 0 &&
1893            real_rhs->line_byte_offset >= 0)
1894     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
1895   else
1896     {
1897       /* the ensure_char_offsets() calls do nothing if the char offsets
1898          are already up-to-date. */
1899       ensure_char_offsets(real_lhs);
1900       ensure_char_offsets(real_rhs);
1901       return real_lhs->line_char_offset == real_rhs->line_char_offset; 
1902     }
1903 }
1904
1905 gint
1906 gtk_text_iter_compare(const GtkTextIter *lhs, const GtkTextIter *rhs)
1907 {
1908   GtkTextRealIter *real_lhs;
1909   GtkTextRealIter *real_rhs;
1910
1911   real_lhs = gtk_text_iter_make_surreal(lhs);
1912   real_rhs = gtk_text_iter_make_surreal(rhs);
1913
1914   check_invariants(lhs);
1915   check_invariants(rhs);
1916   
1917   if (real_lhs == NULL ||
1918       real_rhs == NULL)
1919     return -1; /* why not */
1920
1921   if (real_lhs->line == real_rhs->line)
1922     {
1923       gint left_index, right_index;
1924
1925       if (real_lhs->line_byte_offset >= 0 &&
1926           real_rhs->line_byte_offset >= 0)
1927         {
1928           left_index = real_lhs->line_byte_offset;
1929           right_index = real_rhs->line_byte_offset;
1930         }
1931       else
1932         {
1933           /* the ensure_char_offsets() calls do nothing if
1934              the offsets are already up-to-date. */
1935           ensure_char_offsets(real_lhs);
1936           ensure_char_offsets(real_rhs);
1937           left_index = real_lhs->line_char_offset;
1938           right_index = real_rhs->line_char_offset;
1939         }
1940       
1941       if (left_index < right_index)
1942         return -1;
1943       else if (left_index > right_index)
1944         return 1;
1945       else
1946         return 0;
1947     }
1948   else
1949     {
1950       gint line1, line2;
1951       
1952       line1 = gtk_text_iter_get_line_number(lhs);
1953       line2 = gtk_text_iter_get_line_number(rhs);
1954       if (line1 < line2)
1955         return -1;
1956       else if (line1 > line2)
1957         return 1;
1958       else
1959         return 0;  
1960     }
1961 }
1962
1963 gboolean
1964 gtk_text_iter_in_region (const GtkTextIter *iter,
1965                           const GtkTextIter *start,
1966                           const GtkTextIter *end)
1967 {
1968   return gtk_text_iter_compare(iter, start) >= 0 &&
1969     gtk_text_iter_compare(iter, end) < 0;
1970 }
1971
1972 void
1973 gtk_text_iter_reorder         (GtkTextIter *first,
1974                                 GtkTextIter *second)
1975 {
1976   g_return_if_fail(first != NULL);
1977   g_return_if_fail(second != NULL);
1978
1979   if (gtk_text_iter_compare(first, second) > 0)
1980     {
1981       GtkTextIter tmp;
1982
1983       tmp = *first;
1984       *first = *second;
1985       *second = tmp;
1986     }
1987 }
1988
1989 /*
1990  * Init iterators from the BTree
1991  */
1992
1993 void
1994 gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
1995                                   GtkTextIter *iter,
1996                                   gint char_index)
1997 {
1998   GtkTextRealIter *real = (GtkTextRealIter*)iter;
1999   gint real_char_index;
2000   gint line_start;
2001   GtkTextLine *line;
2002   
2003   g_return_if_fail(iter != NULL);
2004   g_return_if_fail(tree != NULL);
2005
2006   line = gtk_text_btree_get_line_at_char(tree, char_index,
2007                                           &line_start, &real_char_index);
2008   
2009   iter_init_from_char_offset(iter, tree, line, real_char_index - line_start);
2010
2011  real->cached_char_index = real_char_index;
2012
2013  check_invariants(iter);
2014 }
2015
2016 void
2017 gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
2018                                        GtkTextIter *iter,
2019                                        gint line_number,
2020                                        gint char_on_line)
2021 {
2022   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2023   GtkTextLine *line;
2024   gint real_line;
2025   
2026   g_return_if_fail(iter != NULL);
2027   g_return_if_fail(tree != NULL);
2028   
2029   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2030
2031   iter_init_from_char_offset(iter, tree, line, char_on_line);
2032
2033   /* We might as well cache this, since we know it. */
2034   real->cached_line_number = real_line;
2035
2036   check_invariants(iter);
2037 }
2038
2039 void
2040 gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
2041                                       GtkTextIter    *iter,
2042                                       gint            line_number,
2043                                       gint            byte_index)
2044 {
2045   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2046   GtkTextLine *line;
2047   gint real_line;
2048   
2049   g_return_if_fail(iter != NULL);
2050   g_return_if_fail(tree != NULL);
2051   
2052   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2053
2054   iter_init_from_byte_offset(iter, tree, line, byte_index);
2055
2056   /* We might as well cache this, since we know it. */
2057   real->cached_line_number = real_line;
2058
2059   check_invariants(iter);
2060 }
2061
2062 void
2063 gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
2064                                        GtkTextIter    *iter,
2065                                        GtkTextLine    *line,
2066                                        gint             byte_offset)
2067 {
2068   g_return_if_fail(iter != NULL);
2069   g_return_if_fail(tree != NULL);
2070   g_return_if_fail(line != NULL);
2071
2072   iter_init_from_byte_offset(iter, tree, line, byte_offset);
2073
2074   check_invariants(iter);
2075 }
2076
2077 gboolean
2078 gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
2079                                           GtkTextIter    *iter,
2080                                           GtkTextTag     *tag)
2081 {
2082   GtkTextLine *line;
2083   
2084   g_return_val_if_fail(iter != NULL, FALSE);
2085   g_return_val_if_fail(tree != NULL, FALSE);
2086
2087   line = gtk_text_btree_first_could_contain_tag(tree, tag);
2088
2089   if (line == NULL)
2090     {
2091       /* Set iter to last in tree */
2092       gtk_text_btree_get_last_iter(tree, iter);
2093       check_invariants(iter);
2094       return FALSE;
2095     }
2096   else
2097     {
2098       iter_init_from_byte_offset(iter, tree, line, 0);
2099       gtk_text_iter_forward_find_tag_toggle(iter, tag);
2100       check_invariants(iter);
2101       return TRUE;
2102     }
2103 }
2104
2105 gboolean
2106 gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
2107                                           GtkTextIter    *iter,
2108                                           GtkTextTag     *tag)
2109 {
2110   GtkTextLine *line;
2111   
2112   g_return_val_if_fail(iter != NULL, FALSE);
2113   g_return_val_if_fail(tree != NULL, FALSE);
2114
2115   line = gtk_text_btree_last_could_contain_tag(tree, tag);
2116
2117   if (line == NULL)
2118     {
2119       /* Set iter to first in tree */
2120       gtk_text_btree_get_iter_at_line_char(tree, iter, 0, 0);
2121       check_invariants(iter);
2122       return FALSE;
2123     }
2124   else
2125     {
2126       iter_init_from_byte_offset(iter, tree, line, -1);
2127       gtk_text_iter_backward_find_tag_toggle(iter, tag);
2128       check_invariants(iter);
2129       return TRUE;
2130     }
2131 }
2132
2133 gboolean
2134 gtk_text_btree_get_iter_from_string (GtkTextBTree *tree,
2135                                       GtkTextIter *iter,
2136                                       const gchar *string)
2137 {
2138   g_return_val_if_fail(iter != NULL, FALSE);
2139   g_return_val_if_fail(tree != NULL, FALSE);
2140   
2141   g_warning("FIXME");
2142 }
2143
2144 gboolean
2145 gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
2146                                        GtkTextIter *iter,
2147                                        const gchar *mark_name)
2148 {
2149   GtkTextMark *mark;
2150   
2151   g_return_val_if_fail(iter != NULL, FALSE);
2152   g_return_val_if_fail(tree != NULL, FALSE);
2153   
2154   mark = gtk_text_btree_get_mark_by_name(tree, mark_name);
2155
2156   if (mark == NULL)
2157     return FALSE;
2158   else
2159     {
2160       gtk_text_btree_get_iter_at_mark(tree, iter, mark);
2161       check_invariants(iter);
2162       return TRUE;
2163     }
2164 }
2165
2166 void
2167 gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
2168                                   GtkTextIter *iter,
2169                                   GtkTextMark *mark)
2170 {
2171   GtkTextLineSegment *seg = (GtkTextLineSegment*) mark;
2172   
2173   g_return_if_fail(iter != NULL);
2174   g_return_if_fail(tree != NULL);
2175   g_return_if_fail(GTK_IS_TEXT_MARK (mark));
2176   
2177   iter_init_from_segment(iter, tree,
2178                          seg->body.mark.line, seg);
2179   g_assert(seg->body.mark.line == gtk_text_iter_get_line(iter));
2180   check_invariants(iter);
2181 }
2182
2183 void
2184 gtk_text_btree_get_last_iter         (GtkTextBTree   *tree,
2185                                        GtkTextIter    *iter)
2186 {
2187   g_return_if_fail(iter != NULL);
2188   g_return_if_fail(tree != NULL);
2189   
2190   gtk_text_btree_get_iter_at_char(tree,
2191                                    iter,
2192                                    gtk_text_btree_char_count(tree));
2193   check_invariants(iter);
2194 }
2195
2196 void
2197 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
2198 {
2199   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2200   
2201   g_return_if_fail(iter != NULL);
2202
2203   if (real->chars_changed_stamp != gtk_text_btree_get_chars_changed_stamp(real->tree))
2204     g_print(" %20s: <invalidated iterator>\n", desc);
2205   else
2206     {
2207       check_invariants(iter);
2208       g_print(" %20s: line %d / char %d / line char %d / line byte %d\n",
2209              desc,
2210              gtk_text_iter_get_line_number(iter),
2211              gtk_text_iter_get_char_index(iter),
2212              gtk_text_iter_get_line_char(iter),
2213              gtk_text_iter_get_line_byte(iter));
2214       check_invariants(iter);
2215     }
2216 }
2217
2218 void
2219 gtk_text_iter_check(const GtkTextIter *iter)
2220 {
2221   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
2222   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
2223   GtkTextLineSegment *byte_segment;
2224   GtkTextLineSegment *byte_any_segment;
2225   GtkTextLineSegment *char_segment;
2226   GtkTextLineSegment *char_any_segment;
2227   gboolean segments_updated;
2228   
2229   /* We are going to check our class invariants for the Iter class. */
2230
2231   if (real->chars_changed_stamp !=
2232       gtk_text_btree_get_chars_changed_stamp(real->tree))
2233     g_error("iterator check failed: invalid iterator");
2234   
2235   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
2236     g_error("iterator check failed: both char and byte offsets are invalid");
2237
2238   segments_updated = (real->segments_changed_stamp ==
2239                       gtk_text_btree_get_segments_changed_stamp(real->tree));
2240
2241 #if 0
2242   printf("checking iter, segments %s updated, byte %d char %d\n",
2243          segments_updated ? "are" : "aren't",
2244          real->line_byte_offset,
2245          real->line_char_offset);
2246 #endif
2247
2248   if (real->line_byte_offset == 97 &&
2249       real->line_char_offset == 95)
2250     G_BREAKPOINT();
2251   
2252   if (segments_updated)
2253     {
2254       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
2255         g_error("iterator check failed: both char and byte segment offsets are invalid");
2256       
2257       if (real->segment->char_count == 0)
2258         g_error("iterator check failed: segment is not indexable.");
2259
2260       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
2261         g_error("segment char offset is not properly up-to-date");
2262
2263       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
2264         g_error("segment byte offset is not properly up-to-date");
2265
2266       if (real->segment_byte_offset >= 0 &&
2267           real->segment_byte_offset >= real->segment->byte_count)
2268         g_error("segment byte offset is too large.");
2269
2270       if (real->segment_char_offset >= 0 &&
2271           real->segment_char_offset >= real->segment->char_count)
2272         g_error("segment char offset is too large.");
2273     }
2274   
2275   if (real->line_byte_offset >= 0)
2276     {
2277       gtk_text_line_byte_locate(real->line, real->line_byte_offset,
2278                                  &byte_segment, &byte_any_segment,
2279                                  &seg_byte_offset, &line_byte_offset);
2280
2281       if (line_byte_offset != real->line_byte_offset)
2282         g_error("wrong byte offset was stored in iterator");
2283       
2284       if (segments_updated)
2285         {
2286           if (real->segment != byte_segment)
2287             g_error("wrong segment was stored in iterator");
2288
2289           if (real->any_segment != byte_any_segment)
2290             g_error("wrong any_segment was stored in iterator");
2291           
2292           if (seg_byte_offset != real->segment_byte_offset)
2293             g_error("wrong segment byte offset was stored in iterator");
2294         }
2295     }
2296   
2297   if (real->line_char_offset >= 0)
2298     {
2299       gtk_text_line_char_locate(real->line, real->line_char_offset,
2300                                  &char_segment, &char_any_segment,
2301                                  &seg_char_offset, &line_char_offset);
2302
2303       if (line_char_offset != real->line_char_offset)
2304         g_error("wrong char offset was stored in iterator");
2305       
2306       if (segments_updated)
2307         {
2308           if (real->segment != char_segment)
2309             g_error("wrong segment was stored in iterator");
2310
2311           if (real->any_segment != char_any_segment)
2312             g_error("wrong any_segment was stored in iterator");
2313           
2314           if (seg_char_offset != real->segment_char_offset)
2315             g_error("wrong segment char offset was stored in iterator");
2316         }
2317     }
2318   
2319   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
2320     {
2321       if (byte_segment != char_segment)
2322         g_error("char and byte offsets did not point to the same segment");
2323
2324       if (byte_any_segment != char_any_segment)
2325         g_error("char and byte offsets did not point to the same any segment");
2326
2327       /* Make sure the segment offsets are equivalent, if it's a char
2328          segment. */
2329       if (char_segment->type == &gtk_text_char_type)
2330         {
2331           gint byte_offset = 0;
2332           gint char_offset = 0;
2333           while (char_offset < seg_char_offset)
2334             {
2335               const char * start = char_segment->body.chars + byte_offset;
2336               byte_offset += g_utf8_next_char (start) - start;
2337               char_offset += 1;
2338             }
2339
2340           if (byte_offset != seg_byte_offset)
2341             g_error("byte offset did not correspond to char offset");
2342
2343           char_offset =
2344             g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
2345
2346           if (char_offset != seg_char_offset)
2347             g_error("char offset did not correspond to byte offset");
2348         }
2349     }
2350
2351   if (real->cached_line_number >= 0)
2352     {
2353       gint should_be;
2354
2355       should_be = gtk_text_line_get_number(real->line);
2356       if (real->cached_line_number != should_be)
2357         g_error("wrong line number was cached");
2358     }
2359
2360   if (real->cached_char_index >= 0)
2361     {
2362       if (real->line_char_offset >= 0) /* only way we can check it
2363                                           efficiently, not a real
2364                                           invariant. */
2365         {
2366           gint char_index;
2367
2368           char_index = gtk_text_line_char_index(real->line);
2369           char_index += real->line_char_offset;
2370
2371           if (real->cached_char_index != char_index)
2372             g_error("wrong char index was cached");
2373         }
2374     }
2375 }
2376