]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
Remove all references to offscreen flag which was no longer used.
[~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 gint
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       GtkTextUniChar ch;
598
599       ensure_byte_offsets(real);
600       
601       gtk_text_utf_to_unichar(real->segment->body.chars +
602                                real->segment_byte_offset,
603                                &ch);
604
605       return ch;
606     }
607   else
608     {
609       /* Unicode "unknown character" 0xFFFD */
610       return gtk_text_unknown_char;
611     }
612 }
613
614 gchar*
615 gtk_text_iter_get_slice       (const GtkTextIter *start,
616                                 const GtkTextIter *end)
617 {
618   g_return_val_if_fail(start != NULL, NULL);
619   g_return_val_if_fail(end != NULL, NULL);
620
621   check_invariants(start);
622   check_invariants(end);
623   
624   return gtk_text_btree_get_text(start, end, TRUE, TRUE);
625 }
626
627 gchar*
628 gtk_text_iter_get_text       (const GtkTextIter *start,
629                                const GtkTextIter *end)
630 {
631   g_return_val_if_fail(start != NULL, NULL);
632   g_return_val_if_fail(end != NULL, NULL);
633
634   check_invariants(start);
635   check_invariants(end);
636   
637   return gtk_text_btree_get_text(start, end, TRUE, FALSE);
638 }
639
640 gchar*
641 gtk_text_iter_get_visible_slice (const GtkTextIter  *start,
642                                   const GtkTextIter  *end)
643 {
644   g_return_val_if_fail(start != NULL, NULL);
645   g_return_val_if_fail(end != NULL, NULL);
646
647   check_invariants(start);
648   check_invariants(end);
649   
650   return gtk_text_btree_get_text(start, end, FALSE, TRUE);
651 }
652
653 gchar*
654 gtk_text_iter_get_visible_text (const GtkTextIter  *start,
655                                  const GtkTextIter  *end)
656 {
657   g_return_val_if_fail(start != NULL, NULL);
658   g_return_val_if_fail(end != NULL, NULL);
659   
660   check_invariants(start);
661   check_invariants(end);
662   
663   return gtk_text_btree_get_text(start, end, FALSE, FALSE);
664 }
665
666 gboolean
667 gtk_text_iter_get_pixmap      (const GtkTextIter *iter,
668                                 GdkPixmap** pixmap,
669                                 GdkBitmap** mask)
670 {
671   GtkTextRealIter *real;
672
673   g_return_val_if_fail(iter != NULL, FALSE);
674   g_return_val_if_fail(pixmap != NULL, FALSE);
675   g_return_val_if_fail(mask != NULL, FALSE);
676
677   *pixmap = NULL;
678   *mask = NULL;
679   
680   real = gtk_text_iter_make_real(iter);
681
682   if (real == NULL)
683     return FALSE;
684   
685   check_invariants(iter);
686   
687   if (real->segment->type != &gtk_text_pixmap_type)
688     return FALSE;
689   else
690     {
691       *pixmap = real->segment->body.pixmap.pixmap;
692       *mask = real->segment->body.pixmap.pixmap;
693
694       return TRUE;
695     }
696 }
697
698 GSList*
699 gtk_text_iter_get_toggled_tags  (const GtkTextIter  *iter,
700                                   gboolean             toggled_on)
701 {
702   GtkTextRealIter *real;
703   GtkTextLineSegment *seg;
704   GSList *retval;
705   
706   g_return_val_if_fail(iter != NULL, NULL);
707   
708   real = gtk_text_iter_make_real(iter);
709
710   if (real == NULL)
711     return NULL;
712
713   check_invariants(iter);
714   
715   retval = NULL;
716   seg = real->any_segment;
717   while (seg != real->segment)
718     {
719       if (toggled_on)
720         {
721           if (seg->type == &gtk_text_toggle_on_type)
722             {
723               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
724             }
725         }
726       else
727         {
728           if (seg->type == &gtk_text_toggle_off_type)
729             {
730               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
731             }
732         }
733       
734       seg = seg->next;
735     }
736
737   /* The returned list isn't guaranteed to be in any special order,
738      and it isn't. */
739   return retval;
740 }
741
742 gboolean
743 gtk_text_iter_begins_tag    (const GtkTextIter  *iter,
744                               GtkTextTag         *tag)
745 {
746   GtkTextRealIter *real;
747   GtkTextLineSegment *seg;
748   
749   g_return_val_if_fail(iter != NULL, FALSE);
750   
751   real = gtk_text_iter_make_real(iter);
752
753   if (real == NULL)
754     return FALSE;
755
756   check_invariants(iter);
757   
758   seg = real->any_segment;
759   while (seg != real->segment)
760     {
761       if (seg->type == &gtk_text_toggle_on_type)
762         {
763           if (tag == NULL ||
764               seg->body.toggle.info->tag == tag)
765             return TRUE;
766         }
767       
768       seg = seg->next;
769     }
770
771   return FALSE;
772 }
773
774 gboolean
775 gtk_text_iter_ends_tag   (const GtkTextIter  *iter,
776                            GtkTextTag         *tag)
777 {
778   GtkTextRealIter *real;
779   GtkTextLineSegment *seg;
780   
781   g_return_val_if_fail(iter != NULL, FALSE);
782   
783   real = gtk_text_iter_make_real(iter);
784
785   if (real == NULL)
786     return FALSE;
787
788   check_invariants(iter);
789   
790   seg = real->any_segment;
791   while (seg != real->segment)
792     {
793       if (seg->type == &gtk_text_toggle_off_type)
794         {
795           if (tag == NULL ||
796               seg->body.toggle.info->tag == tag)
797             return TRUE;
798         }
799       
800       seg = seg->next;
801     }
802
803   return FALSE;
804 }
805
806 gboolean
807 gtk_text_iter_toggles_tag       (const GtkTextIter  *iter,
808                                   GtkTextTag         *tag)
809 {
810   GtkTextRealIter *real;
811   GtkTextLineSegment *seg;
812   
813   g_return_val_if_fail(iter != NULL, FALSE);
814   
815   real = gtk_text_iter_make_real(iter);
816
817   if (real == NULL)
818     return FALSE;
819
820   check_invariants(iter);
821   
822   seg = real->any_segment;
823   while (seg != real->segment)
824     {
825       if ( (seg->type == &gtk_text_toggle_off_type ||
826             seg->type == &gtk_text_toggle_on_type) &&
827            (tag == NULL ||
828             seg->body.toggle.info->tag == tag) )
829         return TRUE;
830       
831       seg = seg->next;
832     }
833
834   return FALSE;
835 }
836
837 gboolean
838 gtk_text_iter_has_tag           (const GtkTextIter   *iter,
839                                   GtkTextTag          *tag)
840 {
841   GtkTextRealIter *real;
842   
843   g_return_val_if_fail(iter != NULL, FALSE);
844   g_return_val_if_fail(GTK_IS_TEXT_TAG(tag), FALSE);
845   
846   real = gtk_text_iter_make_surreal(iter);
847
848   if (real == NULL)
849     return FALSE; 
850
851   check_invariants(iter);
852   
853   if (real->line_byte_offset >= 0)
854     {
855       return gtk_text_line_byte_has_tag(real->line, real->tree,
856                                          real->line_byte_offset, tag);
857     }
858   else
859     {
860       g_assert(real->line_char_offset >= 0);
861       return gtk_text_line_char_has_tag(real->line, real->tree,
862                                          real->line_char_offset, tag);
863     }
864 }
865
866 gboolean
867 gtk_text_iter_starts_line (const GtkTextIter   *iter)
868 {
869   GtkTextRealIter *real;
870   
871   g_return_val_if_fail(iter != NULL, FALSE);
872   
873   real = gtk_text_iter_make_surreal(iter);
874
875   if (real == NULL)
876     return FALSE;  
877
878   check_invariants(iter);
879   
880   if (real->line_byte_offset >= 0)
881     {
882       return (real->line_byte_offset == 0);
883     }
884   else
885     {
886       g_assert(real->line_char_offset >= 0);
887       return (real->line_char_offset == 0);
888     }
889 }
890
891 gboolean
892 gtk_text_iter_ends_line (const GtkTextIter   *iter)
893 {
894   g_return_val_if_fail(iter != NULL, FALSE);
895
896   check_invariants(iter);
897   
898   return gtk_text_iter_get_char(iter) == '\n';
899 }
900
901 gint
902 gtk_text_iter_get_chars_in_line (const GtkTextIter   *iter)
903 {
904   GtkTextRealIter *real;
905   gint count;
906   GtkTextLineSegment *seg;
907   
908   g_return_val_if_fail(iter != NULL, FALSE);
909   
910   real = gtk_text_iter_make_surreal(iter);
911
912   if (real == NULL)
913     return 0;  
914
915   check_invariants(iter);
916   
917   if (real->line_char_offset >= 0)
918     {
919       /* We can start at the segments we've already found. */
920       count = real->line_char_offset - real->segment_char_offset;
921       seg = gtk_text_iter_get_indexable_segment(iter);
922     }
923   else
924     {
925       /* count whole line. */
926       seg = real->line->segments;
927       count = 0;
928     }
929
930   
931   while (seg != NULL)
932     {
933       count += seg->char_count;
934       
935       seg = seg->next;
936     }
937
938   return count;
939 }
940
941 /*
942  * Increments/decrements
943  */
944
945 static gboolean
946 forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
947 {
948   GtkTextLine *new_line;
949   
950   new_line = gtk_text_line_next(real->line);
951
952   g_assert(new_line != real->line);
953   
954   if (new_line != NULL)
955     {
956       real->line = new_line;
957
958       real->line_byte_offset = 0;
959       real->line_char_offset = 0;
960       
961       real->segment_byte_offset = 0;
962       real->segment_char_offset = 0;
963       
964       /* Find first segments in new line */
965       real->any_segment = real->line->segments;
966       real->segment = real->any_segment;
967       while (real->segment->char_count == 0)
968         real->segment = real->segment->next;
969       
970       return TRUE;
971     }
972   else
973     {
974       /* There is no way to move forward; we were already
975          at the "end" index. (the end index is the last
976          line pointer, segment_byte_offset of 0) */
977
978       g_assert(real->line_char_offset == 0 ||
979                real->line_byte_offset == 0);
980       
981       /* The only indexable segment allowed on the bogus
982          line at the end is a single char segment containing
983          a newline. */
984       if (real->segments_changed_stamp ==
985           gtk_text_btree_get_segments_changed_stamp(real->tree))
986         {
987           g_assert(real->segment->type == &gtk_text_char_type);
988           g_assert(real->segment->char_count == 1);
989         }
990       /* We leave real->line as-is */
991       
992       return FALSE;
993     }
994 }
995
996 static gboolean
997 forward_char(GtkTextRealIter *real)
998 {
999   GtkTextIter *iter = (GtkTextIter*)real;
1000
1001   check_invariants((GtkTextIter*)real);
1002   
1003   ensure_char_offsets(real);
1004   
1005   if ( (real->segment_char_offset + 1) == real->segment->char_count)
1006     {
1007       /* Need to move to the next segment; if no next segment,
1008          need to move to next line. */
1009       return gtk_text_iter_forward_indexable_segment(iter);
1010     }
1011   else
1012     {
1013       /* Just moving within a segment. Keep byte count
1014          up-to-date, if it was already up-to-date. */
1015
1016       g_assert(real->segment->type == &gtk_text_char_type);
1017       
1018       if (real->line_byte_offset >= 0)
1019         {
1020           gint bytes;
1021           GtkTextUniChar ch;
1022
1023           bytes = gtk_text_utf_to_unichar(real->segment->body.chars +
1024                                            real->segment_byte_offset,
1025                                            &ch);
1026
1027           real->line_byte_offset += bytes;
1028           real->segment_byte_offset += bytes;
1029
1030           g_assert(real->segment_byte_offset < real->segment->byte_count);
1031         }
1032
1033       real->line_char_offset += 1;
1034       real->segment_char_offset += 1;
1035
1036       adjust_char_index(real, 1);
1037       
1038       g_assert(real->segment_char_offset < real->segment->char_count);
1039
1040       /* We moved into the middle of a segment, so the any_segment
1041          must now be the segment we're in the middle of. */
1042       real->any_segment = real->segment;
1043       
1044       check_invariants((GtkTextIter*)real);
1045       
1046       return TRUE;
1047     }
1048 }
1049
1050 gboolean
1051 gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
1052 {
1053   /* Need to move to the next segment; if no next segment,
1054      need to move to next line. */
1055   GtkTextLineSegment *seg;
1056   GtkTextLineSegment *any_seg;
1057   GtkTextRealIter *real;
1058   gint chars_skipped;
1059   gint bytes_skipped;
1060   
1061   g_return_val_if_fail(iter != NULL, FALSE);
1062   
1063   real = gtk_text_iter_make_real(iter);
1064
1065   if (real == NULL)
1066     return FALSE;
1067
1068   check_invariants(iter);
1069   
1070   if (real->line_char_offset >= 0)
1071     {
1072       chars_skipped = real->segment->char_count - real->segment_char_offset;
1073       g_assert(chars_skipped > 0);
1074     }
1075   else
1076     chars_skipped = 0;
1077
1078   if (real->line_byte_offset >= 0)
1079     {
1080       bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
1081       g_assert(bytes_skipped > 0);
1082     }
1083   else
1084     bytes_skipped = 0;
1085   
1086   /* Get first segment of any kind */
1087   any_seg = real->segment->next;
1088   /* skip non-indexable segments, if any */
1089   seg = any_seg;
1090   while (seg != NULL && seg->char_count == 0)
1091     seg = seg->next;
1092   
1093   if (seg != NULL)
1094     {
1095       real->any_segment = any_seg;
1096       real->segment = seg;
1097
1098       if (real->line_byte_offset >= 0)
1099         {
1100           g_assert(bytes_skipped > 0);
1101           real->segment_byte_offset = 0;
1102           real->line_byte_offset += bytes_skipped;
1103         }
1104
1105       if (real->line_char_offset >= 0)
1106         {
1107           g_assert(chars_skipped > 0);
1108           real->segment_char_offset = 0;
1109           real->line_char_offset += chars_skipped;
1110           adjust_char_index(real, chars_skipped);
1111         }
1112
1113       check_invariants(iter);
1114       
1115       return TRUE;
1116     }
1117   else
1118     {      
1119       /* End of the line */
1120       if (forward_line_leaving_caches_unmodified(real))
1121         {
1122           adjust_line_number(real, 1);
1123           if (real->line_char_offset >= 0)
1124             adjust_char_index(real, chars_skipped);
1125
1126           check_invariants(iter);
1127
1128           g_assert(real->line_byte_offset == 0);
1129           g_assert(real->line_char_offset == 0);
1130           g_assert(real->segment_byte_offset == 0);
1131           g_assert(real->segment_char_offset == 0);
1132           g_assert(gtk_text_iter_starts_line(iter));
1133
1134           check_invariants(iter);
1135           
1136           return TRUE;
1137         }
1138       else
1139         {
1140           /* End of buffer */
1141
1142           check_invariants(iter);
1143           
1144           return FALSE;
1145         }
1146     }
1147 }
1148
1149 gboolean
1150 gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
1151 {
1152   g_warning("FIXME");
1153
1154 }
1155
1156 gboolean
1157 gtk_text_iter_forward_char(GtkTextIter *iter)
1158 {
1159   GtkTextRealIter *real;
1160   
1161   g_return_val_if_fail(iter != NULL, FALSE);
1162   
1163   real = gtk_text_iter_make_real(iter);
1164
1165   if (real == NULL)
1166     return FALSE;
1167   else
1168     {
1169       check_invariants(iter);
1170       return forward_char(real);
1171     }
1172 }
1173
1174 gboolean
1175 gtk_text_iter_backward_char(GtkTextIter *iter)
1176 {
1177   g_return_val_if_fail(iter != NULL, FALSE);
1178
1179   check_invariants(iter);
1180   
1181   return gtk_text_iter_backward_chars(iter, 1);
1182 }
1183
1184 /*
1185   Definitely we should try to linear scan as often as possible for
1186   movement within a single line, because we can't use the BTree to
1187   speed within-line searches up; for movement between lines, we would
1188   like to avoid the linear scan probably.
1189   
1190   Instead of using this constant, it might be nice to cache the line
1191   length in the iterator and linear scan if motion is within a single
1192   line.
1193
1194   I guess you'd have to profile the various approaches.
1195 */
1196 #define MAX_LINEAR_SCAN 300
1197
1198 gboolean
1199 gtk_text_iter_forward_chars(GtkTextIter *iter, gint count)
1200 {
1201   GtkTextRealIter *real;
1202   
1203   g_return_val_if_fail(iter != NULL, FALSE);
1204   
1205   real = gtk_text_iter_make_real(iter);
1206   
1207   if (real == NULL)
1208     return FALSE;
1209   else if (count == 0)
1210     return FALSE;
1211   else if (count < 0)
1212     return gtk_text_iter_backward_chars(iter, 0 - count);
1213   else if (count < MAX_LINEAR_SCAN)
1214     {
1215       check_invariants(iter);
1216       
1217       while (count > 1)
1218         {
1219           if (!forward_char(real))
1220             return FALSE;
1221           --count;
1222         }
1223       
1224       return forward_char(real);
1225     }
1226   else
1227     {
1228       gint current_char_index;
1229       gint new_char_index;
1230
1231       check_invariants(iter);
1232       
1233       current_char_index = gtk_text_iter_get_char_index(iter);
1234
1235       if (current_char_index == gtk_text_btree_char_count(real->tree))
1236         return FALSE; /* can't move forward */
1237       
1238       new_char_index = current_char_index + count;
1239       gtk_text_iter_set_char_index(iter, new_char_index);
1240
1241       check_invariants(iter);
1242
1243       return TRUE;
1244     }
1245 }
1246
1247 gboolean
1248 gtk_text_iter_backward_chars(GtkTextIter *iter, gint count)
1249 {
1250   GtkTextRealIter *real;
1251
1252   g_return_val_if_fail(iter != NULL, FALSE);
1253   
1254   real = gtk_text_iter_make_real(iter);
1255   
1256   if (real == NULL)
1257     return FALSE;
1258   else if (count == 0)
1259     return FALSE;
1260   else if (count < 0)
1261     return gtk_text_iter_forward_chars(iter, 0 - count);
1262
1263   ensure_char_offsets(real);
1264   check_invariants(iter);
1265   
1266   if (count <= real->segment_char_offset)
1267     {
1268       /* Optimize the within-segment case */      
1269       g_assert(real->segment->char_count > 0);
1270       g_assert(real->segment->type == &gtk_text_char_type);
1271
1272       real->segment_char_offset -= count;
1273       g_assert(real->segment_char_offset >= 0);
1274
1275       if (real->line_byte_offset >= 0)
1276         {
1277           gint new_byte_offset;
1278           gint i;
1279
1280           new_byte_offset = 0;
1281           i = 0;
1282           while (i < real->segment_char_offset)
1283             {
1284               GtkTextUniChar ch;
1285               new_byte_offset +=
1286                 gtk_text_utf_to_unichar(real->segment->body.chars + new_byte_offset,
1287                                          &ch);
1288               ++i;
1289             }
1290       
1291           real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
1292           real->segment_byte_offset = new_byte_offset;
1293         }
1294       
1295       real->line_char_offset -= count;
1296
1297       adjust_char_index(real, 0 - count);
1298
1299       check_invariants(iter);
1300       
1301       return TRUE;
1302     }
1303   else
1304     {
1305       /* We need to go back into previous segments. For now,
1306          just keep this really simple. */
1307       gint current_char_index;
1308       gint new_char_index;
1309       
1310       current_char_index = gtk_text_iter_get_char_index(iter);
1311
1312       if (current_char_index == 0)
1313         return FALSE; /* can't move backward */
1314       
1315       new_char_index = current_char_index - count;
1316       if (new_char_index < 0)
1317         new_char_index = 0;
1318       gtk_text_iter_set_char_index(iter, new_char_index);
1319
1320       check_invariants(iter);
1321       
1322       return TRUE;
1323     }
1324 }
1325
1326 gboolean
1327 gtk_text_iter_forward_line(GtkTextIter *iter)
1328 {
1329   GtkTextRealIter *real;
1330   
1331   g_return_val_if_fail(iter != NULL, FALSE);
1332   
1333   real = gtk_text_iter_make_real(iter);
1334   
1335   if (real == NULL)
1336     return FALSE;
1337
1338   check_invariants(iter);
1339   
1340   if (forward_line_leaving_caches_unmodified(real))
1341     {
1342       invalidate_char_index(real);
1343       adjust_line_number(real, 1);
1344
1345       check_invariants(iter);
1346       
1347       return TRUE;
1348     }
1349   else
1350     {
1351       check_invariants(iter);
1352       return FALSE;
1353     }
1354 }
1355
1356 gboolean
1357 gtk_text_iter_backward_line(GtkTextIter *iter)
1358 {
1359   GtkTextLine *new_line;
1360   GtkTextRealIter *real;
1361   gboolean offset_will_change;
1362   gint offset;
1363   
1364   g_return_val_if_fail(iter != NULL, FALSE);
1365   
1366   real = gtk_text_iter_make_real(iter);
1367   
1368   if (real == NULL)
1369     return FALSE;
1370
1371   check_invariants(iter);
1372   
1373   new_line = gtk_text_line_previous(real->line);
1374
1375   offset_will_change = FALSE;
1376   if (real->line_char_offset > 0)
1377     offset_will_change = TRUE;
1378           
1379   if (new_line != NULL)
1380     {
1381       real->line = new_line;
1382       
1383       adjust_line_number(real, -1);
1384     }
1385   else
1386     {
1387       if (!offset_will_change)
1388         return FALSE;
1389     }
1390
1391   invalidate_char_index(real);
1392   
1393   real->line_byte_offset = 0;
1394   real->line_char_offset = 0;
1395
1396   real->segment_byte_offset = 0;
1397   real->segment_char_offset = 0;
1398   
1399   /* Find first segment in line */
1400   real->any_segment = real->line->segments;
1401   real->segment = gtk_text_line_byte_to_segment(real->line,
1402                                                  0, &offset);
1403
1404   g_assert(offset == 0);
1405
1406   /* Note that if we are on the first line, we snap to the start
1407      of the first line and return TRUE, so TRUE means the
1408      iterator changed, not that the line changed; this is maybe
1409      a bit weird. I'm not sure there's an obvious right thing
1410      to do though. */
1411
1412   check_invariants(iter);
1413   
1414   return TRUE;
1415 }
1416
1417 gboolean
1418 gtk_text_iter_forward_lines(GtkTextIter *iter, gint count)
1419 {
1420   if (count < 0)
1421     return gtk_text_iter_backward_lines(iter, 0 - count);
1422   else if (count == 0)
1423     return FALSE;
1424   else if (count == 1)
1425     {
1426       check_invariants(iter);
1427       return gtk_text_iter_forward_line(iter);
1428     }
1429   else
1430     {
1431       gint old_line;
1432       
1433       old_line = gtk_text_iter_get_line_number(iter);
1434       
1435       gtk_text_iter_set_line_number(iter, old_line + count);
1436
1437       check_invariants(iter);
1438       
1439       return (gtk_text_iter_get_line_number(iter) != old_line);
1440     }
1441 }
1442
1443 gboolean
1444 gtk_text_iter_backward_lines(GtkTextIter *iter, gint count)
1445 {
1446   if (count < 0)
1447     return gtk_text_iter_forward_lines(iter, 0 - count);
1448   else if (count == 0)
1449     return FALSE;
1450   else if (count == 1)
1451     {
1452       return gtk_text_iter_backward_line(iter);
1453     }
1454   else
1455     {
1456       gint old_line;
1457       
1458       old_line = gtk_text_iter_get_line_number(iter);
1459       
1460       gtk_text_iter_set_line_number(iter, MAX(old_line - count, 0));
1461
1462       return (gtk_text_iter_get_line_number(iter) != old_line);
1463     }
1464 }
1465
1466 static gboolean
1467 is_word_char(GtkTextUniChar ch, gpointer user_data)
1468 {
1469   /* will likely need some i18n help FIXME */
1470   return isalpha(ch);
1471 }
1472
1473 static gboolean
1474 is_not_word_char(GtkTextUniChar ch, gpointer user_data)
1475 {
1476   return !is_word_char(ch, user_data);
1477 }
1478
1479 static gboolean
1480 gtk_text_iter_is_in_word(const GtkTextIter *iter)
1481 {
1482   gint ch;
1483
1484   ch = gtk_text_iter_get_char(iter);
1485
1486   return is_word_char(ch, NULL);
1487 }
1488
1489 gboolean
1490 gtk_text_iter_forward_word_end(GtkTextIter      *iter)
1491 {  
1492   gboolean in_word;
1493   GtkTextIter start;
1494   
1495   g_return_val_if_fail(iter != NULL, FALSE);
1496
1497   start = *iter;
1498   
1499   in_word = gtk_text_iter_is_in_word(iter);
1500
1501   if (!in_word)
1502     {
1503       if (!gtk_text_iter_forward_find_char(iter, is_word_char, NULL))
1504         return !gtk_text_iter_equal(iter, &start);
1505       else
1506         in_word = TRUE;
1507     }
1508
1509   g_assert(in_word);
1510   
1511   gtk_text_iter_forward_find_char(iter, is_not_word_char, NULL);
1512
1513   return !gtk_text_iter_equal(iter, &start);
1514 }
1515
1516 gboolean
1517 gtk_text_iter_backward_word_start(GtkTextIter      *iter)
1518 {
1519   gboolean in_word;
1520   GtkTextIter start;
1521   
1522   g_return_val_if_fail(iter != NULL, FALSE);
1523
1524   start = *iter;
1525   
1526   in_word = gtk_text_iter_is_in_word(iter);
1527
1528   if (!in_word)
1529     {
1530       if (!gtk_text_iter_backward_find_char(iter, is_word_char, NULL))
1531         return !gtk_text_iter_equal(iter, &start);
1532       else
1533         in_word = TRUE;
1534     }
1535
1536   g_assert(in_word);
1537   
1538   gtk_text_iter_backward_find_char(iter, is_not_word_char, NULL);
1539   gtk_text_iter_forward_char(iter); /* point to first char of word,
1540                                         not first non-word char. */
1541   
1542   return !gtk_text_iter_equal(iter, &start);
1543 }
1544
1545 gboolean
1546 gtk_text_iter_forward_word_ends(GtkTextIter      *iter,
1547                                  gint               count)
1548 {
1549   g_return_val_if_fail(iter != NULL, FALSE);
1550   g_return_val_if_fail(count > 0, FALSE);
1551
1552   if (!gtk_text_iter_forward_word_end(iter))
1553     return FALSE;
1554   --count;
1555   
1556   while (count > 0)
1557     {
1558       if (!gtk_text_iter_forward_word_end(iter))
1559         break;
1560       --count;
1561     }
1562   return TRUE;
1563 }
1564
1565 gboolean
1566 gtk_text_iter_backward_word_starts(GtkTextIter      *iter,
1567                                     gint               count)
1568 {
1569   g_return_val_if_fail(iter != NULL, FALSE);
1570   g_return_val_if_fail(count > 0, FALSE);
1571
1572   if (!gtk_text_iter_backward_word_start(iter))
1573     return FALSE;
1574   --count;
1575   
1576   while (count > 0)
1577     {
1578       if (!gtk_text_iter_backward_word_start(iter))
1579         break;
1580       --count;
1581     }
1582   return TRUE;
1583 }
1584
1585 /* up/down lines maintain the char offset, while forward/backward lines
1586    always sets the char offset to 0. */
1587 gboolean
1588 gtk_text_iter_up_lines        (GtkTextIter *iter,
1589                                 gint count)
1590 {
1591   gint char_offset;
1592
1593   if (count < 0)
1594     return gtk_text_iter_down_lines(iter, 0 - count);
1595   
1596   char_offset = gtk_text_iter_get_line_char(iter);
1597
1598   if (!gtk_text_iter_backward_line(iter))
1599     return FALSE;
1600   --count;
1601   
1602   while (count > 0)
1603     {
1604       if (!gtk_text_iter_backward_line(iter))
1605         break;
1606       --count;
1607     }
1608
1609   gtk_text_iter_set_line_char(iter, char_offset);
1610   
1611   return TRUE;
1612 }
1613
1614 gboolean
1615 gtk_text_iter_down_lines        (GtkTextIter *iter,
1616                                   gint count)
1617 {
1618   gint char_offset;
1619
1620   if (count < 0)
1621     return gtk_text_iter_up_lines(iter, 0 - count);
1622   
1623   char_offset = gtk_text_iter_get_line_char(iter);
1624
1625   if (!gtk_text_iter_forward_line(iter))
1626     return FALSE;
1627   --count;
1628   
1629   while (count > 0)
1630     {
1631       if (!gtk_text_iter_forward_line(iter))
1632         break;
1633       --count;
1634     }
1635
1636   gtk_text_iter_set_line_char(iter, char_offset);
1637   
1638   return TRUE;
1639 }
1640
1641 void
1642 gtk_text_iter_set_line_char(GtkTextIter *iter,
1643                              gint char_on_line)
1644 {
1645   GtkTextRealIter *real;
1646   
1647   g_return_if_fail(iter != NULL);
1648   
1649   real = gtk_text_iter_make_surreal(iter);
1650
1651   if (real == NULL)
1652     return;
1653
1654   check_invariants(iter);
1655   
1656   iter_set_from_char_offset(real, real->line, char_on_line);
1657
1658   check_invariants(iter);
1659 }
1660
1661 void
1662 gtk_text_iter_set_line_number(GtkTextIter *iter, gint line_number)
1663 {
1664   GtkTextLine *line;
1665   gint real_line;
1666   GtkTextRealIter *real;
1667   
1668   g_return_if_fail(iter != NULL);
1669   
1670   real = gtk_text_iter_make_surreal(iter);
1671
1672   if (real == NULL)
1673     return;
1674
1675   check_invariants(iter);
1676   
1677   line = gtk_text_btree_get_line(real->tree, line_number, &real_line);
1678
1679   iter_set_from_char_offset(real, line, 0);
1680   
1681   /* We might as well cache this, since we know it. */
1682   real->cached_line_number = real_line;
1683
1684   check_invariants(iter);
1685 }
1686
1687 void
1688 gtk_text_iter_set_char_index(GtkTextIter *iter, gint char_index)
1689 {
1690   GtkTextLine *line;
1691   GtkTextRealIter *real;
1692   gint line_start;
1693   gint real_char_index;
1694   
1695   g_return_if_fail(iter != NULL);
1696   
1697   real = gtk_text_iter_make_surreal(iter);
1698
1699   if (real == NULL)
1700     return;
1701
1702   check_invariants(iter);
1703   
1704   if (real->cached_char_index >= 0 &&
1705       real->cached_char_index == char_index)
1706     return;
1707
1708   line = gtk_text_btree_get_line_at_char(real->tree,
1709                                           char_index,
1710                                           &line_start,
1711                                           &real_char_index);
1712
1713   iter_set_from_char_offset(real, line, real_char_index - line_start);
1714   
1715   /* Go ahead and cache this since we have it. */
1716   real->cached_char_index = real_char_index;
1717
1718   check_invariants(iter);
1719 }
1720
1721 void
1722 gtk_text_iter_forward_to_end  (GtkTextIter       *iter)
1723 {
1724   GtkTextBuffer *buffer;
1725   GtkTextRealIter *real;
1726
1727   g_return_if_fail(iter != NULL);
1728   
1729   real = gtk_text_iter_make_surreal(iter);
1730
1731   if (real == NULL)
1732     return;
1733   
1734   buffer = gtk_text_btree_get_buffer(real->tree);
1735
1736   gtk_text_buffer_get_last_iter(buffer, iter);
1737 }
1738
1739 gboolean
1740 gtk_text_iter_forward_to_newline(GtkTextIter *iter)
1741 {
1742   gint current_offset;
1743   gint new_offset;
1744   
1745   g_return_val_if_fail(iter != NULL, FALSE);
1746   
1747   current_offset = gtk_text_iter_get_line_char(iter);
1748   new_offset = gtk_text_iter_get_chars_in_line(iter) - 1;
1749
1750   if (current_offset < new_offset)
1751     {
1752       /* Move to end of this line. */
1753       gtk_text_iter_set_line_char(iter, new_offset);
1754       return TRUE;
1755     }
1756   else
1757     {
1758       /* Move to end of next line. */
1759       if (gtk_text_iter_forward_line(iter))
1760         {
1761           gtk_text_iter_forward_to_newline(iter);
1762           return TRUE;
1763         }
1764       else
1765         return FALSE;
1766     }
1767 }
1768
1769 gboolean
1770 gtk_text_iter_forward_find_tag_toggle (GtkTextIter *iter,
1771                                         GtkTextTag  *tag)
1772 {
1773   GtkTextLine *next_line;
1774   GtkTextLine *current_line;
1775   GtkTextRealIter *real;
1776
1777   g_return_val_if_fail(iter != NULL, FALSE);
1778   
1779   real = gtk_text_iter_make_real(iter);
1780
1781   if (real == NULL)
1782     return FALSE;
1783
1784   check_invariants(iter);
1785   
1786   current_line = real->line;
1787   next_line = gtk_text_line_next_could_contain_tag(current_line,
1788                                                     real->tree, tag);
1789   
1790   while (gtk_text_iter_forward_indexable_segment(iter))
1791     {
1792       /* If we went forward to a line that couldn't contain a toggle
1793          for the tag, then skip forward to a line that could contain
1794          it. This potentially skips huge hunks of the tree, so we
1795          aren't a purely linear search. */
1796       if (real->line != current_line)
1797         {
1798           if (next_line == NULL)
1799             {
1800               /* End of search. Set to end of buffer. */
1801               gtk_text_btree_get_last_iter(real->tree, iter);
1802               return FALSE;
1803             }
1804               
1805           if (real->line != next_line)
1806             iter_set_from_byte_offset(real, next_line, 0);
1807
1808           current_line = real->line;
1809           next_line = gtk_text_line_next_could_contain_tag(current_line,
1810                                                             real->tree,
1811                                                             tag);
1812         }
1813
1814       if (gtk_text_iter_toggles_tag(iter, tag))
1815         {
1816           /* If there's a toggle here, it isn't indexable so
1817              any_segment can't be the indexable segment. */
1818           g_assert(real->any_segment != real->segment);
1819           return TRUE;
1820         }
1821     }
1822
1823   /* Reached end of buffer */
1824   return FALSE;
1825 }
1826
1827 gboolean
1828 gtk_text_iter_backward_find_tag_toggle (GtkTextIter *iter,
1829                                          GtkTextTag  *tag)
1830 {
1831
1832   g_warning("FIXME");
1833 }
1834
1835 static gboolean
1836 matches_pred(GtkTextIter *iter,
1837              GtkTextViewCharPredicate pred,
1838              gpointer user_data)
1839 {
1840   gint ch;
1841
1842   ch = gtk_text_iter_get_char(iter);
1843
1844   return (*pred) (ch, user_data);
1845 }
1846
1847 gboolean
1848 gtk_text_iter_forward_find_char (GtkTextIter *iter,
1849                                   GtkTextViewCharPredicate pred,
1850                                   gpointer user_data)
1851 {
1852   g_return_val_if_fail(iter != NULL, FALSE);
1853   g_return_val_if_fail(pred != NULL, FALSE);
1854
1855   while (gtk_text_iter_forward_char(iter))
1856     {
1857       if (matches_pred(iter, pred, user_data))
1858         return TRUE;
1859     }
1860   
1861   return FALSE;
1862 }
1863
1864 gboolean
1865 gtk_text_iter_backward_find_char (GtkTextIter *iter,
1866                                    GtkTextViewCharPredicate pred,
1867                                    gpointer user_data)
1868 {
1869   g_return_val_if_fail(iter != NULL, FALSE);
1870   g_return_val_if_fail(pred != NULL, FALSE);
1871
1872   while (gtk_text_iter_backward_char(iter))
1873     {
1874       if (matches_pred(iter, pred, user_data))
1875         return TRUE;
1876     }
1877   
1878   return FALSE;
1879 }
1880
1881 /*
1882  * Comparisons
1883  */
1884
1885 gboolean
1886 gtk_text_iter_equal(const GtkTextIter *lhs, const GtkTextIter *rhs)
1887 {
1888   GtkTextRealIter *real_lhs;
1889   GtkTextRealIter *real_rhs;
1890
1891   real_lhs = (GtkTextRealIter*)lhs;
1892   real_rhs = (GtkTextRealIter*)rhs;
1893
1894   check_invariants(lhs);
1895   check_invariants(rhs);
1896   
1897   if (real_lhs->line != real_rhs->line)
1898     return FALSE;
1899   else if (real_lhs->line_byte_offset >= 0 &&
1900            real_rhs->line_byte_offset >= 0)
1901     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
1902   else
1903     {
1904       /* the ensure_char_offsets() calls do nothing if the char offsets
1905          are already up-to-date. */
1906       ensure_char_offsets(real_lhs);
1907       ensure_char_offsets(real_rhs);
1908       return real_lhs->line_char_offset == real_rhs->line_char_offset; 
1909     }
1910 }
1911
1912 gint
1913 gtk_text_iter_compare(const GtkTextIter *lhs, const GtkTextIter *rhs)
1914 {
1915   GtkTextRealIter *real_lhs;
1916   GtkTextRealIter *real_rhs;
1917
1918   real_lhs = gtk_text_iter_make_surreal(lhs);
1919   real_rhs = gtk_text_iter_make_surreal(rhs);
1920
1921   check_invariants(lhs);
1922   check_invariants(rhs);
1923   
1924   if (real_lhs == NULL ||
1925       real_rhs == NULL)
1926     return -1; /* why not */
1927
1928   if (real_lhs->line == real_rhs->line)
1929     {
1930       gint left_index, right_index;
1931
1932       if (real_lhs->line_byte_offset >= 0 &&
1933           real_rhs->line_byte_offset >= 0)
1934         {
1935           left_index = real_lhs->line_byte_offset;
1936           right_index = real_rhs->line_byte_offset;
1937         }
1938       else
1939         {
1940           /* the ensure_char_offsets() calls do nothing if
1941              the offsets are already up-to-date. */
1942           ensure_char_offsets(real_lhs);
1943           ensure_char_offsets(real_rhs);
1944           left_index = real_lhs->line_char_offset;
1945           right_index = real_rhs->line_char_offset;
1946         }
1947       
1948       if (left_index < right_index)
1949         return -1;
1950       else if (left_index > right_index)
1951         return 1;
1952       else
1953         return 0;
1954     }
1955   else
1956     {
1957       gint line1, line2;
1958       
1959       line1 = gtk_text_iter_get_line_number(lhs);
1960       line2 = gtk_text_iter_get_line_number(rhs);
1961       if (line1 < line2)
1962         return -1;
1963       else if (line1 > line2)
1964         return 1;
1965       else
1966         return 0;  
1967     }
1968 }
1969
1970 gboolean
1971 gtk_text_iter_in_region (const GtkTextIter *iter,
1972                           const GtkTextIter *start,
1973                           const GtkTextIter *end)
1974 {
1975   return gtk_text_iter_compare(iter, start) >= 0 &&
1976     gtk_text_iter_compare(iter, end) < 0;
1977 }
1978
1979 void
1980 gtk_text_iter_reorder         (GtkTextIter *first,
1981                                 GtkTextIter *second)
1982 {
1983   g_return_if_fail(first != NULL);
1984   g_return_if_fail(second != NULL);
1985
1986   if (gtk_text_iter_compare(first, second) > 0)
1987     {
1988       GtkTextIter tmp;
1989
1990       tmp = *first;
1991       *first = *second;
1992       *second = tmp;
1993     }
1994 }
1995
1996 /*
1997  * Init iterators from the BTree
1998  */
1999
2000 void
2001 gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
2002                                   GtkTextIter *iter,
2003                                   gint char_index)
2004 {
2005   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2006   gint real_char_index;
2007   gint line_start;
2008   GtkTextLine *line;
2009   
2010   g_return_if_fail(iter != NULL);
2011   g_return_if_fail(tree != NULL);
2012
2013   line = gtk_text_btree_get_line_at_char(tree, char_index,
2014                                           &line_start, &real_char_index);
2015   
2016   iter_init_from_char_offset(iter, tree, line, real_char_index - line_start);
2017
2018  real->cached_char_index = real_char_index;
2019
2020  check_invariants(iter);
2021 }
2022
2023 void
2024 gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
2025                                        GtkTextIter *iter,
2026                                        gint line_number,
2027                                        gint char_on_line)
2028 {
2029   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2030   GtkTextLine *line;
2031   gint real_line;
2032   
2033   g_return_if_fail(iter != NULL);
2034   g_return_if_fail(tree != NULL);
2035   
2036   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2037
2038   iter_init_from_char_offset(iter, tree, line, char_on_line);
2039
2040   /* We might as well cache this, since we know it. */
2041   real->cached_line_number = real_line;
2042
2043   check_invariants(iter);
2044 }
2045
2046 void
2047 gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
2048                                       GtkTextIter    *iter,
2049                                       gint            line_number,
2050                                       gint            byte_index)
2051 {
2052   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2053   GtkTextLine *line;
2054   gint real_line;
2055   
2056   g_return_if_fail(iter != NULL);
2057   g_return_if_fail(tree != NULL);
2058   
2059   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2060
2061   iter_init_from_byte_offset(iter, tree, line, byte_index);
2062
2063   /* We might as well cache this, since we know it. */
2064   real->cached_line_number = real_line;
2065
2066   check_invariants(iter);
2067 }
2068
2069 void
2070 gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
2071                                        GtkTextIter    *iter,
2072                                        GtkTextLine    *line,
2073                                        gint             byte_offset)
2074 {
2075   g_return_if_fail(iter != NULL);
2076   g_return_if_fail(tree != NULL);
2077   g_return_if_fail(line != NULL);
2078
2079   iter_init_from_byte_offset(iter, tree, line, byte_offset);
2080
2081   check_invariants(iter);
2082 }
2083
2084 gboolean
2085 gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
2086                                           GtkTextIter    *iter,
2087                                           GtkTextTag     *tag)
2088 {
2089   GtkTextLine *line;
2090   
2091   g_return_val_if_fail(iter != NULL, FALSE);
2092   g_return_val_if_fail(tree != NULL, FALSE);
2093
2094   line = gtk_text_btree_first_could_contain_tag(tree, tag);
2095
2096   if (line == NULL)
2097     {
2098       /* Set iter to last in tree */
2099       gtk_text_btree_get_last_iter(tree, iter);
2100       check_invariants(iter);
2101       return FALSE;
2102     }
2103   else
2104     {
2105       iter_init_from_byte_offset(iter, tree, line, 0);
2106       gtk_text_iter_forward_find_tag_toggle(iter, tag);
2107       check_invariants(iter);
2108       return TRUE;
2109     }
2110 }
2111
2112 gboolean
2113 gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
2114                                           GtkTextIter    *iter,
2115                                           GtkTextTag     *tag)
2116 {
2117   GtkTextLine *line;
2118   
2119   g_return_val_if_fail(iter != NULL, FALSE);
2120   g_return_val_if_fail(tree != NULL, FALSE);
2121
2122   line = gtk_text_btree_last_could_contain_tag(tree, tag);
2123
2124   if (line == NULL)
2125     {
2126       /* Set iter to first in tree */
2127       gtk_text_btree_get_iter_at_line_char(tree, iter, 0, 0);
2128       check_invariants(iter);
2129       return FALSE;
2130     }
2131   else
2132     {
2133       iter_init_from_byte_offset(iter, tree, line, -1);
2134       gtk_text_iter_backward_find_tag_toggle(iter, tag);
2135       check_invariants(iter);
2136       return TRUE;
2137     }
2138 }
2139
2140 gboolean
2141 gtk_text_btree_get_iter_from_string (GtkTextBTree *tree,
2142                                       GtkTextIter *iter,
2143                                       const gchar *string)
2144 {
2145   g_return_val_if_fail(iter != NULL, FALSE);
2146   g_return_val_if_fail(tree != NULL, FALSE);
2147   
2148   g_warning("FIXME");
2149 }
2150
2151 gboolean
2152 gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
2153                                        GtkTextIter *iter,
2154                                        const gchar *mark_name)
2155 {
2156   GtkTextLineSegment *mark;
2157   
2158   g_return_val_if_fail(iter != NULL, FALSE);
2159   g_return_val_if_fail(tree != NULL, FALSE);
2160   
2161   mark = gtk_text_btree_get_mark_by_name(tree, mark_name);
2162
2163   if (mark == NULL)
2164     return FALSE;
2165   else
2166     {
2167       gtk_text_btree_get_iter_at_mark(tree, iter, mark);
2168       check_invariants(iter);
2169       return TRUE;
2170     }
2171 }
2172
2173 void
2174 gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
2175                                   GtkTextIter *iter,
2176                                   GtkTextLineSegment *mark)
2177 {
2178   g_return_if_fail(iter != NULL);
2179   g_return_if_fail(tree != NULL);
2180   g_return_if_fail(mark->type == &gtk_text_left_mark_type ||
2181                    mark->type == &gtk_text_right_mark_type);
2182   
2183   iter_init_from_segment(iter, tree, mark->body.mark.line, mark);
2184   g_assert(mark->body.mark.line == gtk_text_iter_get_line(iter));
2185   check_invariants(iter);
2186 }
2187
2188 void
2189 gtk_text_btree_get_last_iter         (GtkTextBTree   *tree,
2190                                        GtkTextIter    *iter)
2191 {
2192   g_return_if_fail(iter != NULL);
2193   g_return_if_fail(tree != NULL);
2194   
2195   gtk_text_btree_get_iter_at_char(tree,
2196                                    iter,
2197                                    gtk_text_btree_char_count(tree));
2198   check_invariants(iter);
2199 }
2200
2201 void
2202 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
2203 {
2204   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2205   
2206   g_return_if_fail(iter != NULL);
2207
2208   if (real->chars_changed_stamp != gtk_text_btree_get_chars_changed_stamp(real->tree))
2209     g_print(" %20s: <invalidated iterator>\n", desc);
2210   else
2211     {
2212       check_invariants(iter);
2213       g_print(" %20s: line %d / char %d / line char %d / line byte %d\n",
2214              desc,
2215              gtk_text_iter_get_line_number(iter),
2216              gtk_text_iter_get_char_index(iter),
2217              gtk_text_iter_get_line_char(iter),
2218              gtk_text_iter_get_line_byte(iter));
2219       check_invariants(iter);
2220     }
2221 }
2222
2223 void
2224 gtk_text_iter_check(const GtkTextIter *iter)
2225 {
2226   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
2227   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
2228   GtkTextLineSegment *byte_segment;
2229   GtkTextLineSegment *byte_any_segment;
2230   GtkTextLineSegment *char_segment;
2231   GtkTextLineSegment *char_any_segment;
2232   gboolean segments_updated;
2233   
2234   /* We are going to check our class invariants for the Iter class. */
2235
2236   if (real->chars_changed_stamp !=
2237       gtk_text_btree_get_chars_changed_stamp(real->tree))
2238     g_error("iterator check failed: invalid iterator");
2239   
2240   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
2241     g_error("iterator check failed: both char and byte offsets are invalid");
2242
2243   segments_updated = (real->segments_changed_stamp ==
2244                       gtk_text_btree_get_segments_changed_stamp(real->tree));
2245
2246 #if 0
2247   printf("checking iter, segments %s updated, byte %d char %d\n",
2248          segments_updated ? "are" : "aren't",
2249          real->line_byte_offset,
2250          real->line_char_offset);
2251 #endif
2252
2253   if (real->line_byte_offset == 97 &&
2254       real->line_char_offset == 95)
2255     G_BREAKPOINT();
2256   
2257   if (segments_updated)
2258     {
2259       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
2260         g_error("iterator check failed: both char and byte segment offsets are invalid");
2261       
2262       if (real->segment->char_count == 0)
2263         g_error("iterator check failed: segment is not indexable.");
2264
2265       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
2266         g_error("segment char offset is not properly up-to-date");
2267
2268       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
2269         g_error("segment byte offset is not properly up-to-date");
2270
2271       if (real->segment_byte_offset >= 0 &&
2272           real->segment_byte_offset >= real->segment->byte_count)
2273         g_error("segment byte offset is too large.");
2274
2275       if (real->segment_char_offset >= 0 &&
2276           real->segment_char_offset >= real->segment->char_count)
2277         g_error("segment char offset is too large.");
2278     }
2279   
2280   if (real->line_byte_offset >= 0)
2281     {
2282       gtk_text_line_byte_locate(real->line, real->line_byte_offset,
2283                                  &byte_segment, &byte_any_segment,
2284                                  &seg_byte_offset, &line_byte_offset);
2285
2286       if (line_byte_offset != real->line_byte_offset)
2287         g_error("wrong byte offset was stored in iterator");
2288       
2289       if (segments_updated)
2290         {
2291           if (real->segment != byte_segment)
2292             g_error("wrong segment was stored in iterator");
2293
2294           if (real->any_segment != byte_any_segment)
2295             g_error("wrong any_segment was stored in iterator");
2296           
2297           if (seg_byte_offset != real->segment_byte_offset)
2298             g_error("wrong segment byte offset was stored in iterator");
2299         }
2300     }
2301   
2302   if (real->line_char_offset >= 0)
2303     {
2304       gtk_text_line_char_locate(real->line, real->line_char_offset,
2305                                  &char_segment, &char_any_segment,
2306                                  &seg_char_offset, &line_char_offset);
2307
2308       if (line_char_offset != real->line_char_offset)
2309         g_error("wrong char offset was stored in iterator");
2310       
2311       if (segments_updated)
2312         {
2313           if (real->segment != char_segment)
2314             g_error("wrong segment was stored in iterator");
2315
2316           if (real->any_segment != char_any_segment)
2317             g_error("wrong any_segment was stored in iterator");
2318           
2319           if (seg_char_offset != real->segment_char_offset)
2320             g_error("wrong segment char offset was stored in iterator");
2321         }
2322     }
2323   
2324   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
2325     {
2326       if (byte_segment != char_segment)
2327         g_error("char and byte offsets did not point to the same segment");
2328
2329       if (byte_any_segment != char_any_segment)
2330         g_error("char and byte offsets did not point to the same any segment");
2331
2332       /* Make sure the segment offsets are equivalent, if it's a char
2333          segment. */
2334       if (char_segment->type == &gtk_text_char_type)
2335         {
2336           gint byte_offset = 0;
2337           gint char_offset = 0;
2338           while (char_offset < seg_char_offset)
2339             {
2340               GtkTextUniChar ch;
2341               byte_offset +=
2342                 gtk_text_utf_to_unichar(char_segment->body.chars + byte_offset,
2343                                          &ch);
2344               char_offset += 1;
2345             }
2346
2347           if (byte_offset != seg_byte_offset)
2348             g_error("byte offset did not correspond to char offset");
2349
2350           char_offset =
2351             gtk_text_view_num_utf_chars(char_segment->body.chars,
2352                                     seg_byte_offset);
2353
2354           if (char_offset != seg_char_offset)
2355             g_error("char offset did not correspond to byte offset");
2356         }
2357     }
2358
2359   if (real->cached_line_number >= 0)
2360     {
2361       gint should_be;
2362
2363       should_be = gtk_text_line_get_number(real->line);
2364       if (real->cached_line_number != should_be)
2365         g_error("wrong line number was cached");
2366     }
2367
2368   if (real->cached_char_index >= 0)
2369     {
2370       if (real->line_char_offset >= 0) /* only way we can check it
2371                                           efficiently, not a real
2372                                           invariant. */
2373         {
2374           gint char_index;
2375
2376           char_index = gtk_text_line_char_index(real->line);
2377           char_index += real->line_char_offset;
2378
2379           if (real->cached_char_index != char_index)
2380             g_error("wrong char index was cached");
2381         }
2382     }
2383 }
2384