]> Pileus Git - ~andy/gtk/blob - gtk/gtktextiter.c
Rename GtkTextStyleValues to GtkTextAttributes
[~andy/gtk] / gtk / gtktextiter.c
1 #include "gtktextiter.h"
2 #include "gtktextbtree.h"
3 #include "gtktextiterprivate.h"
4 #include "gtkdebug.h"
5 #include <string.h>
6 #include <ctype.h>
7
8 typedef struct _GtkTextRealIter GtkTextRealIter;
9
10 struct _GtkTextRealIter {
11   /* Always-valid information */
12   GtkTextBTree *tree;
13   GtkTextLine *line;
14   /* At least one of these is always valid;
15      if invalid, they are -1.
16
17      If the line byte offset is valid, so is the segment byte offset;
18      and ditto for char offsets. */
19   gint line_byte_offset;
20   gint line_char_offset;
21   /* These two are valid if >= 0 */
22   gint cached_char_index;
23   gint cached_line_number;
24   /* Stamps to detect the buffer changing under us */
25   gint chars_changed_stamp;
26   gint segments_changed_stamp;
27   /* Valid if the segments_changed_stamp is up-to-date */
28   GtkTextLineSegment *segment;     /* indexable segment we index */
29   GtkTextLineSegment *any_segment; /* first segment in our location,
30                                    maybe same as "segment" */
31   /* One of these will always be valid if segments_changed_stamp is
32      up-to-date. If invalid, they are -1.
33
34      If the line byte offset is valid, so is the segment byte offset;
35      and ditto for char offsets. */
36   gint segment_byte_offset;
37   gint segment_char_offset;
38   /* Pads are here for binary-compatible expansion space. */
39   gpointer pad1;
40   guint pad2;
41 };
42
43 /* These "set" functions should not assume any fields
44    other than the char stamp and the tree are valid.
45 */
46 static void
47 iter_set_common(GtkTextRealIter *iter,
48                 GtkTextLine *line)
49 {
50   /* Update segments stamp */
51   iter->segments_changed_stamp =
52     gtk_text_btree_get_segments_changed_stamp(iter->tree);
53       
54   iter->line = line;
55
56   iter->line_byte_offset = -1;
57   iter->line_char_offset = -1;
58   iter->segment_byte_offset = -1;
59   iter->segment_char_offset = -1;
60   iter->cached_char_index = -1;
61   iter->cached_line_number = -1;
62 }
63
64 static void
65 iter_set_from_byte_offset(GtkTextRealIter *iter,
66                           GtkTextLine *line,
67                           gint byte_offset)
68 {
69   iter_set_common(iter, line);
70
71   gtk_text_line_byte_locate(iter->line,
72                              byte_offset,
73                              &iter->segment,
74                              &iter->any_segment,
75                              &iter->segment_byte_offset,
76                              &iter->line_byte_offset);
77
78 }
79
80 static void
81 iter_set_from_char_offset(GtkTextRealIter *iter,
82                           GtkTextLine *line,
83                           gint char_offset)
84 {
85   iter_set_common(iter, line);
86
87   gtk_text_line_char_locate(iter->line,
88                              char_offset,
89                              &iter->segment,
90                              &iter->any_segment,
91                              &iter->segment_char_offset,
92                              &iter->line_char_offset);
93 }
94
95 static void
96 iter_set_from_segment(GtkTextRealIter *iter,
97                       GtkTextLine *line,
98                       GtkTextLineSegment *segment)
99 {
100   GtkTextLineSegment *seg;
101   gint byte_offset;
102
103   /* This could theoretically be optimized by computing all the iter
104      fields in this same loop, but I'm skipping it for now. */
105   byte_offset = 0;
106   seg = line->segments;
107   while (seg != segment)
108     {
109       byte_offset += seg->byte_count;
110       seg = seg->next;
111     }
112
113   iter_set_from_byte_offset(iter, line, byte_offset);
114 }
115
116 /* This function ensures that the segment-dependent information is
117    truly computed lazily; often we don't need to do the full make_real
118    work. This ensures the btree and line are valid, but doesn't
119    update the segments. */
120 static GtkTextRealIter*
121 gtk_text_iter_make_surreal(const GtkTextIter *_iter)
122 {
123   GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
124   
125   if (iter->chars_changed_stamp !=
126       gtk_text_btree_get_chars_changed_stamp(iter->tree))
127     {
128       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.");
129       return NULL;
130     }
131
132   /* We don't update the segments information since we are becoming
133      only surreal. However we do invalidate the segments information
134      if appropriate, to be sure we segfault if we try to use it and we
135      should have used make_real. */
136
137   if (iter->segments_changed_stamp !=
138       gtk_text_btree_get_segments_changed_stamp(iter->tree))
139     {
140       iter->segment = NULL;
141       iter->any_segment = NULL;
142       /* set to segfault-causing values. */
143       iter->segment_byte_offset = -10000;
144       iter->segment_char_offset = -10000;
145     }
146   
147   return iter;
148 }
149
150 static GtkTextRealIter*
151 gtk_text_iter_make_real(const GtkTextIter *_iter)
152 {
153   GtkTextRealIter *iter;
154   
155   iter = gtk_text_iter_make_surreal(_iter);
156   
157   if (iter->segments_changed_stamp !=
158       gtk_text_btree_get_segments_changed_stamp(iter->tree))
159     {
160       if (iter->line_byte_offset >= 0)
161         {
162           iter_set_from_byte_offset(iter,
163                                     iter->line,
164                                     iter->line_byte_offset);
165         }
166       else
167         {
168           g_assert(iter->line_char_offset >= 0);
169           
170           iter_set_from_char_offset(iter,
171                                     iter->line,
172                                     iter->line_char_offset);
173         }
174     }
175
176   g_assert(iter->segment != NULL);
177   g_assert(iter->any_segment != NULL);
178   g_assert(iter->segment->char_count > 0);
179   
180   return iter;
181 }
182
183 static GtkTextRealIter*
184 iter_init_common(GtkTextIter *_iter,
185                  GtkTextBTree *tree)
186 {
187   GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
188
189   g_return_val_if_fail(iter != NULL, NULL);
190   g_return_val_if_fail(tree != NULL, NULL);
191
192   iter->tree = tree;
193
194   iter->chars_changed_stamp =
195     gtk_text_btree_get_chars_changed_stamp(iter->tree);
196   
197   return iter;
198 }
199
200 static GtkTextRealIter*
201 iter_init_from_segment(GtkTextIter *iter,
202                        GtkTextBTree *tree,
203                        GtkTextLine *line,
204                        GtkTextLineSegment *segment)
205 {
206   GtkTextRealIter *real;
207   
208   g_return_val_if_fail(line != NULL, NULL);
209
210   real = iter_init_common(iter, tree);
211   
212   iter_set_from_segment(real, line, segment);
213   
214   return real;
215 }
216
217 static GtkTextRealIter*
218 iter_init_from_byte_offset(GtkTextIter *iter,
219                            GtkTextBTree *tree,
220                            GtkTextLine *line,
221                            gint line_byte_offset)
222 {
223   GtkTextRealIter *real;
224   
225   g_return_val_if_fail(line != NULL, NULL);
226
227   real = iter_init_common(iter, tree);
228   
229   iter_set_from_byte_offset(real, line, line_byte_offset);
230   
231   return real;
232 }
233
234 static GtkTextRealIter*
235 iter_init_from_char_offset(GtkTextIter *iter,
236                            GtkTextBTree *tree,
237                            GtkTextLine *line,
238                            gint line_char_offset)
239 {
240   GtkTextRealIter *real;
241   
242   g_return_val_if_fail(line != NULL, NULL);
243
244   real = iter_init_common(iter, tree);
245   
246   iter_set_from_char_offset(real, line, line_char_offset);
247   
248   return real;
249 }
250
251 static inline void
252 invalidate_segment(GtkTextRealIter *iter)
253 {
254   iter->segments_changed_stamp -= 1;
255 }
256
257 static inline void
258 invalidate_char_index(GtkTextRealIter *iter)
259 {
260   iter->cached_char_index = -1;
261 }
262
263 static inline void
264 invalidate_line_number(GtkTextRealIter *iter)
265 {
266   iter->cached_line_number = -1;
267 }
268
269 static inline void
270 adjust_char_index(GtkTextRealIter *iter, gint count)
271 {
272   if (iter->cached_char_index >= 0)
273     iter->cached_char_index += count;
274 }
275
276 static inline void
277 adjust_line_number(GtkTextRealIter *iter, gint count)
278 {
279   if (iter->cached_line_number >= 0)
280     iter->cached_line_number += count;
281 }
282
283 static inline void
284 adjust_char_offsets(GtkTextRealIter *iter, gint count)
285 {
286   if (iter->line_char_offset >= 0)
287     {
288       iter->line_char_offset += count;
289       g_assert(iter->segment_char_offset >= 0);
290       iter->segment_char_offset += count;
291     }
292 }
293
294 static inline void
295 adjust_byte_offsets(GtkTextRealIter *iter, gint count)
296 {
297   if (iter->line_byte_offset >= 0)
298     {
299       iter->line_byte_offset += count;
300       g_assert(iter->segment_byte_offset >= 0);
301       iter->segment_byte_offset += count;
302     }
303 }
304
305 static inline void
306 ensure_char_offsets(GtkTextRealIter *iter)
307 {
308   if (iter->line_char_offset < 0)
309     {
310       g_assert(iter->line_byte_offset >= 0);
311
312       gtk_text_line_byte_to_char_offsets(iter->line,
313                                          iter->line_byte_offset,
314                                          &iter->line_char_offset,
315                                          &iter->segment_char_offset);
316     }
317 }
318
319 static inline void
320 ensure_byte_offsets(GtkTextRealIter *iter)
321 {
322   if (iter->line_byte_offset < 0)
323     {
324       g_assert(iter->line_char_offset >= 0);
325
326       gtk_text_line_char_to_byte_offsets(iter->line,
327                                          iter->line_char_offset,
328                                          &iter->line_byte_offset,
329                                          &iter->segment_byte_offset);
330     }
331 }
332
333 #if 1
334 static void
335 check_invariants(const GtkTextIter *iter)
336 {
337   if (gtk_debug_flags & GTK_DEBUG_TEXT)
338     gtk_text_iter_check(iter);
339 }
340 #else
341 #define check_invariants(x)
342 #endif
343
344 /**
345  * gtk_text_iter_get_buffer:
346  * @iter: an iterator
347  * 
348  * Return the #GtkTextBuffer this iterator is associated with
349  * 
350  * Return value: the buffer
351  **/
352 GtkTextBuffer*
353 gtk_text_iter_get_buffer(const GtkTextIter *iter)
354 {
355   GtkTextRealIter *real;
356
357   g_return_val_if_fail(iter != NULL, NULL);
358   
359   real = gtk_text_iter_make_surreal(iter);
360
361   if (real == NULL)
362     return NULL;
363
364   check_invariants(iter);
365   
366   return gtk_text_btree_get_buffer(real->tree);
367 }
368
369 /**
370  * gtk_text_iter_copy:
371  * @iter: an iterator
372  * 
373  * Create a dynamically-allocated copy of an iterator. This function
374  * is not useful in applications, because iterators can be copied with a
375  * simple assignment (<literal>GtkTextIter i = j;</literal>). The
376  * function is used by language bindings.
377  * 
378  * Return value: a copy of the @iter, free with gtk_text_iter_free()
379  **/
380 GtkTextIter*
381 gtk_text_iter_copy(const GtkTextIter *iter)
382 {
383   GtkTextIter *new_iter;
384
385   g_return_val_if_fail(iter != NULL, NULL);
386   
387   new_iter = g_new(GtkTextIter, 1);
388
389   *new_iter = *iter;
390   
391   return new_iter;
392 }
393
394 /**
395  * gtk_text_iter_free:
396  * @iter: a dynamically-allocated iterator
397  *
398  * Free an iterator allocated on the heap. This function
399  * is intended for use in language bindings, and is not
400  * especially useful for applications, because iterators can
401  * simply be allocated on the stack.
402  * 
403  **/
404 void
405 gtk_text_iter_free(GtkTextIter *iter)
406 {
407   g_return_if_fail(iter != NULL);
408
409   g_free(iter);
410 }
411
412 GtkTextLineSegment*
413 gtk_text_iter_get_indexable_segment(const GtkTextIter *iter)
414 {
415   GtkTextRealIter *real;
416
417   g_return_val_if_fail(iter != NULL, 0);
418   
419   real = gtk_text_iter_make_real(iter);
420
421   if (real == NULL)
422     return NULL;
423
424   check_invariants(iter);
425   
426   g_assert(real->segment != NULL);
427   
428   return real->segment;
429 }
430
431 GtkTextLineSegment*
432 gtk_text_iter_get_any_segment(const GtkTextIter *iter)
433 {
434   GtkTextRealIter *real;
435
436   g_return_val_if_fail(iter != NULL, 0);
437   
438   real = gtk_text_iter_make_real(iter);
439
440   if (real == NULL)
441     return NULL;
442
443   check_invariants(iter);
444   
445   g_assert(real->any_segment != NULL);
446   
447   return real->any_segment;
448 }
449
450 gint
451 gtk_text_iter_get_segment_byte(const GtkTextIter *iter)
452 {
453   GtkTextRealIter *real;
454
455   g_return_val_if_fail(iter != NULL, 0);
456   
457   real = gtk_text_iter_make_real(iter);
458
459   if (real == NULL)
460     return 0;
461
462   ensure_byte_offsets(real);
463
464   check_invariants(iter);
465   
466   return real->segment_byte_offset;
467 }
468
469 gint
470 gtk_text_iter_get_segment_char(const GtkTextIter *iter)
471 {
472   GtkTextRealIter *real;
473
474   g_return_val_if_fail(iter != NULL, 0);
475   
476   real = gtk_text_iter_make_real(iter);
477
478   if (real == NULL)
479     return 0;
480
481   ensure_char_offsets(real);
482
483   check_invariants(iter);
484   
485   return real->segment_char_offset;
486 }
487
488 /* This function does not require a still-valid
489    iterator */
490 GtkTextLine*
491 gtk_text_iter_get_text_line(const GtkTextIter *iter)
492 {
493   const GtkTextRealIter *real;
494
495   g_return_val_if_fail(iter != NULL, 0);
496   
497   real = (const GtkTextRealIter*)iter;
498
499   return real->line;
500 }
501
502 /* This function does not require a still-valid
503    iterator */
504 GtkTextBTree*
505 gtk_text_iter_get_btree(const GtkTextIter *iter)
506 {
507   const GtkTextRealIter *real;
508
509   g_return_val_if_fail(iter != NULL, 0);
510   
511   real = (const GtkTextRealIter*)iter;
512
513   return real->tree;
514 }
515
516 /*
517  * Conversions
518  */
519
520 /**
521  * gtk_text_iter_get_offset:
522  * @iter: an iterator
523  * 
524  * Returns the character offset of an iterator.
525  * Each character in a #GtkTextBuffer has an offset,
526  * starting with 0 for the first character in the buffer.
527  * Use gtk_text_buffer_get_iter_at_offset() to convert an
528  * offset back into an iterator.
529  * 
530  * Return value: a character offset
531  **/
532 gint
533 gtk_text_iter_get_offset(const GtkTextIter *iter)
534 {
535   GtkTextRealIter *real;
536
537   g_return_val_if_fail(iter != NULL, 0);
538   
539   real = gtk_text_iter_make_surreal(iter);
540
541   if (real == NULL)
542     return 0;
543
544   if (real->cached_char_index < 0)
545     {
546       real->cached_char_index =
547         gtk_text_line_char_index(real->line);
548       ensure_char_offsets(real);
549       real->cached_char_index += real->line_char_offset;
550     }
551
552   check_invariants(iter);
553   
554   return real->cached_char_index;
555 }
556
557 /**
558  * gtk_text_iter_get_line:
559  * @iter: an iterator
560  * 
561  * Returns the line number containing the iterator. Lines in
562  * a #GtkTextBuffer are numbered beginning with 0 for the first
563  * line in the buffer.
564  * 
565  * Return value: a line number
566  **/
567 gint
568 gtk_text_iter_get_line(const GtkTextIter *iter)
569 {
570   GtkTextRealIter *real;
571
572   g_return_val_if_fail(iter != NULL, 0);
573   
574   real = gtk_text_iter_make_surreal(iter);
575
576   if (real == NULL)
577     return 0;
578
579   if (real->cached_line_number < 0)
580     real->cached_line_number =
581       gtk_text_line_get_number(real->line);
582
583   check_invariants(iter);
584   
585   return real->cached_line_number;
586 }
587
588 /**
589  * gtk_text_iter_get_line_offset:
590  * @iter: an iterator
591  * 
592  * Returns the character offset of the iterator,
593  * counting from the start of a newline-terminated line.
594  * The first character on the line has offset 0.
595  * 
596  * Return value: offset from start of line
597  **/
598 gint
599 gtk_text_iter_get_line_offset(const GtkTextIter *iter)
600 {
601
602   GtkTextRealIter *real;
603
604   g_return_val_if_fail(iter != NULL, 0);
605   
606   real = gtk_text_iter_make_surreal(iter);
607
608   if (real == NULL)
609     return 0;
610
611   ensure_char_offsets(real);
612
613   check_invariants(iter);
614   
615   return real->line_char_offset;
616 }
617
618 /**
619  * gtk_text_iter_get_line_index:
620  * @iter: an iterator
621  * 
622  * Returns the byte index of the iterator, counting
623  * from the start of a newline-terminated line.
624  * Remember that #GtkTextBuffer encodes text in
625  * UTF-8, and that characters can require a variable
626  * number of bytes to represent.
627  * 
628  * Return value: distance from start of line, in bytes
629  **/
630 gint
631 gtk_text_iter_get_line_index(const GtkTextIter *iter)
632 {
633   GtkTextRealIter *real;
634
635   g_return_val_if_fail(iter != NULL, 0);
636   
637   real = gtk_text_iter_make_surreal(iter);
638
639   if (real == NULL)
640     return 0;
641
642   ensure_byte_offsets(real);
643
644   check_invariants(iter);
645   
646   return real->line_byte_offset;
647 }
648
649 /*
650  * Dereferencing
651  */
652
653 /**
654  * gtk_text_iter_get_char:
655  * @iter: an iterator
656  * 
657  * Returns the Unicode character at this iterator.  (Equivalent to
658  * operator* on a C++ iterator.)  If the iterator points at a
659  * non-character element, such as an image embedded in the buffer, the
660  * Unicode "unknown" character 0xFFFD is returned.
661  * 
662  * Return value: a Unicode character
663  **/
664 gunichar
665 gtk_text_iter_get_char(const GtkTextIter *iter)
666 {
667   GtkTextRealIter *real;
668
669   g_return_val_if_fail(iter != NULL, 0);
670   
671   real = gtk_text_iter_make_real(iter);
672
673   if (real == NULL)
674     return 0;
675
676   check_invariants(iter);
677   
678   /* FIXME probably want to special-case the end iterator
679      and either have an error or return 0 */
680   
681   if (real->segment->type == &gtk_text_char_type)
682     {
683       ensure_byte_offsets(real);
684       
685       return g_utf8_get_char (real->segment->body.chars +
686                               real->segment_byte_offset);
687     }
688   else
689     {
690       /* Unicode "unknown character" 0xFFFD */
691       return gtk_text_unknown_char;
692     }
693 }
694
695 /**
696  * gtk_text_iter_get_slice:
697  * @start: iterator at start of a range
698  * @end: iterator at end of a range
699  * 
700  * Returns the text in the given range. A "slice" is an array of
701  * characters encoded in UTF-8 format, including the Unicode "unknown"
702  * character 0xFFFD for iterable non-character elements in the buffer,
703  * such as images.  Because images are encoded in the slice, byte and
704  * character offsets in the returned array will correspond to byte
705  * offsets in the text buffer.
706  * 
707  * Return value: slice of text from the buffer
708  **/
709 gchar*
710 gtk_text_iter_get_slice       (const GtkTextIter *start,
711                                const GtkTextIter *end)
712 {
713   g_return_val_if_fail(start != NULL, NULL);
714   g_return_val_if_fail(end != NULL, NULL);
715
716   check_invariants(start);
717   check_invariants(end);
718   
719   return gtk_text_btree_get_text(start, end, TRUE, TRUE);
720 }
721
722 /**
723  * gtk_text_iter_get_text:
724  * @start: iterator at start of a range
725  * @end: iterator at end of a range
726  * 
727  * Returns <emphasis>text</emphasis> in the given range.  If the range
728  * contains non-text elements such as images, the character and byte
729  * offsets in the returned string will not correspond to character and
730  * byte offsets in the buffer. If you want offsets to correspond, see
731  * gtk_text_iter_get_slice().
732  * 
733  * Return value: array of characters from the buffer
734  **/
735 gchar*
736 gtk_text_iter_get_text       (const GtkTextIter *start,
737                               const GtkTextIter *end)
738 {
739   g_return_val_if_fail(start != NULL, NULL);
740   g_return_val_if_fail(end != NULL, NULL);
741
742   check_invariants(start);
743   check_invariants(end);
744   
745   return gtk_text_btree_get_text(start, end, TRUE, FALSE);
746 }
747
748 /**
749  * gtk_text_iter_get_visible_slice:
750  * @start: iterator at start of range
751  * @end: iterator at end of range
752  * 
753  * Like gtk_text_iter_get_slice(), but invisible text is not included.
754  * Invisible text is usually invisible because a #GtkTextTag with the
755  * "invisible" attribute turned on has been applied to it.
756  * 
757  * Return value: slice of text from the buffer
758  **/
759 gchar*
760 gtk_text_iter_get_visible_slice (const GtkTextIter  *start,
761                                  const GtkTextIter  *end)
762 {
763   g_return_val_if_fail(start != NULL, NULL);
764   g_return_val_if_fail(end != NULL, NULL);
765
766   check_invariants(start);
767   check_invariants(end);
768   
769   return gtk_text_btree_get_text(start, end, FALSE, TRUE);
770 }
771
772 /**
773  * gtk_text_iter_get_visible_text:
774  * @start: iterator at start of range
775  * @end: iterator at end of range
776  * 
777  * Like gtk_text_iter_get_text(), but invisible text is not included.
778  * Invisible text is usually invisible because a #GtkTextTag with the
779  * "invisible" attribute turned on has been applied to it.
780  * 
781  * Return value: string containing visible text in the range
782  **/
783 gchar*
784 gtk_text_iter_get_visible_text (const GtkTextIter  *start,
785                                  const GtkTextIter  *end)
786 {
787   g_return_val_if_fail(start != NULL, NULL);
788   g_return_val_if_fail(end != NULL, NULL);
789   
790   check_invariants(start);
791   check_invariants(end);
792   
793   return gtk_text_btree_get_text(start, end, FALSE, FALSE);
794 }
795
796 /**
797  * gtk_text_iter_get_pixmap:
798  * @iter: an iterator
799  * @pixmap: return location for the pixmap
800  * @mask: return location for the mask
801  * 
802  * If the location pointed to by @iter contains a pixmap, the pixmap
803  * is placed in @pixmap, the mask is placed in @mask, and
804  * gtk_text_iter_get_pixmap() returns TRUE.  If @iter points at
805  * something else, FALSE will be returned and @pixmap/@mask will
806  * remain unchanged. The pixmap and mask do not have their reference
807  * count incremented. If the pixmap has no mask, NULL is returned for
808  * the mask.
809  * 
810  * Return value: whether the iterator pointed at a pixmap
811  **/
812 gboolean
813 gtk_text_iter_get_pixmap      (const GtkTextIter *iter,
814                                GdkPixmap** pixmap,
815                                GdkBitmap** mask)
816 {
817   GtkTextRealIter *real;
818
819   g_return_val_if_fail(iter != NULL, FALSE);
820   g_return_val_if_fail(pixmap != NULL, FALSE);
821   g_return_val_if_fail(mask != NULL, FALSE);
822
823   *pixmap = NULL;
824   *mask = NULL;
825   
826   real = gtk_text_iter_make_real(iter);
827
828   if (real == NULL)
829     return FALSE;
830   
831   check_invariants(iter);
832   
833   if (real->segment->type != &gtk_text_pixmap_type)
834     return FALSE;
835   else
836     {
837       if (pixmap)
838         *pixmap = real->segment->body.pixmap.pixmap;
839       if (mask)
840         *mask = real->segment->body.pixmap.pixmap;
841
842       return TRUE;
843     }
844 }
845
846 /**
847  * gtk_text_iter_get_marks:
848  * @iter: an iterator
849  * 
850  * Returns a list of all #GtkTextMark at this location. Because marks
851  * are not iterable (they don't take up any "space" in the buffer,
852  * they are just marks in between iterable locations), multiple marks
853  * can exist in the same place. The returned list is not in any
854  * meaningful order.
855  * 
856  * Return value: list of #GtkTextMark
857  **/
858 GSList*
859 gtk_text_iter_get_marks (const GtkTextIter *iter)
860 {
861   GtkTextRealIter *real;
862   GtkTextLineSegment *seg;
863   GSList *retval;
864   
865   g_return_val_if_fail(iter != NULL, NULL);
866   
867   real = gtk_text_iter_make_real(iter);
868
869   if (real == NULL)
870     return NULL;
871
872   check_invariants(iter);
873   
874   retval = NULL;
875   seg = real->any_segment;
876   while (seg != real->segment)
877     {
878       if (seg->type == &gtk_text_left_mark_type ||
879           seg->type == &gtk_text_right_mark_type)
880         retval = g_slist_prepend(retval, (GtkTextMark*)seg);
881       
882       seg = seg->next;
883     }
884
885   /* The returned list isn't guaranteed to be in any special order,
886      and it isn't. */
887   return retval;
888 }
889
890 /**
891  * gtk_text_iter_get_toggled_tags:
892  * @iter: an iterator
893  * @toggled_on: TRUE to get toggled-on tags
894  * 
895  * Returns a list of #GtkTextTag that are toggled on or off at this
896  * point.  (If @toggled_on is TRUE, the list contains tags that are
897  * toggled on.) If a tag is toggled on at @iter, then some non-empty
898  * range of characters following @iter has that tag applied to it.  If
899  * a tag is toggled off, then some non-empty range following @iter
900  * does <emphasis>not</emphasis> have the tag applied to it.
901  * 
902  * Return value: tags toggled at this point
903  **/
904 GSList*
905 gtk_text_iter_get_toggled_tags  (const GtkTextIter  *iter,
906                                  gboolean            toggled_on)
907 {
908   GtkTextRealIter *real;
909   GtkTextLineSegment *seg;
910   GSList *retval;
911   
912   g_return_val_if_fail(iter != NULL, NULL);
913   
914   real = gtk_text_iter_make_real(iter);
915
916   if (real == NULL)
917     return NULL;
918
919   check_invariants(iter);
920   
921   retval = NULL;
922   seg = real->any_segment;
923   while (seg != real->segment)
924     {
925       if (toggled_on)
926         {
927           if (seg->type == &gtk_text_toggle_on_type)
928             {
929               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
930             }
931         }
932       else
933         {
934           if (seg->type == &gtk_text_toggle_off_type)
935             {
936               retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
937             }
938         }
939       
940       seg = seg->next;
941     }
942
943   /* The returned list isn't guaranteed to be in any special order,
944      and it isn't. */
945   return retval;
946 }
947
948 /**
949  * gtk_text_iter_begins_tag:
950  * @iter: an iterator
951  * @tag: a #GtkTextTag, or NULL
952  * 
953  * Returns TRUE if @tag is toggled on at exactly this point. If @tag
954  * is NULL, returns TRUE if any tag is toggled on at this point. Note
955  * that the gtk_text_iter_begins_tag() returns TRUE if @iter is the
956  * <emphasis>start</emphasis> of the tagged range;
957  * gtk_text_iter_has_tag() tells you whether an iterator is
958  * <emphasis>within</emphasis> a tagged range.
959  * 
960  * Return value: whether @iter is the start of a range tagged with @tag
961  **/
962 gboolean
963 gtk_text_iter_begins_tag    (const GtkTextIter  *iter,
964                              GtkTextTag         *tag)
965 {
966   GtkTextRealIter *real;
967   GtkTextLineSegment *seg;
968   
969   g_return_val_if_fail(iter != NULL, FALSE);
970   
971   real = gtk_text_iter_make_real(iter);
972
973   if (real == NULL)
974     return FALSE;
975
976   check_invariants(iter);
977   
978   seg = real->any_segment;
979   while (seg != real->segment)
980     {
981       if (seg->type == &gtk_text_toggle_on_type)
982         {
983           if (tag == NULL ||
984               seg->body.toggle.info->tag == tag)
985             return TRUE;
986         }
987       
988       seg = seg->next;
989     }
990
991   return FALSE;
992 }
993
994 /**
995  * gtk_text_iter_ends_tag:
996  * @iter: an iterator
997  * @tag: a #GtkTextTag, or NULL
998  *
999  * Returns TRUE if @tag is toggled off at exactly this point. If @tag
1000  * is NULL, returns TRUE if any tag is toggled off at this point. Note
1001  * that the gtk_text_iter_ends_tag() returns TRUE if @iter is the
1002  * <emphasis>end</emphasis> of the tagged range;
1003  * gtk_text_iter_has_tag() tells you whether an iterator is
1004  * <emphasis>within</emphasis> a tagged range.
1005  * 
1006  * Return value: whether @iter is the end of a range tagged with @tag
1007  * 
1008  **/
1009 gboolean
1010 gtk_text_iter_ends_tag   (const GtkTextIter  *iter,
1011                            GtkTextTag         *tag)
1012 {
1013   GtkTextRealIter *real;
1014   GtkTextLineSegment *seg;
1015   
1016   g_return_val_if_fail(iter != NULL, FALSE);
1017   
1018   real = gtk_text_iter_make_real(iter);
1019
1020   if (real == NULL)
1021     return FALSE;
1022
1023   check_invariants(iter);
1024   
1025   seg = real->any_segment;
1026   while (seg != real->segment)
1027     {
1028       if (seg->type == &gtk_text_toggle_off_type)
1029         {
1030           if (tag == NULL ||
1031               seg->body.toggle.info->tag == tag)
1032             return TRUE;
1033         }
1034       
1035       seg = seg->next;
1036     }
1037
1038   return FALSE;
1039 }
1040
1041 /**
1042  * gtk_text_iter_toggles_tag:
1043  * @iter: an iterator
1044  * @tag: a #GtkTextTag, or NULL
1045  * 
1046  * This is equivalent to (gtk_text_iter_begins_tag() ||
1047  * gtk_text_iter_ends_tag()), i.e. it tells you whether a range with
1048  * @tag applied to it begins <emphasis>or</emphasis> ends at @iter.
1049  * 
1050  * Return value: whether @tag is toggled on or off at @iter
1051  **/
1052 gboolean
1053 gtk_text_iter_toggles_tag       (const GtkTextIter  *iter,
1054                                   GtkTextTag         *tag)
1055 {
1056   GtkTextRealIter *real;
1057   GtkTextLineSegment *seg;
1058   
1059   g_return_val_if_fail(iter != NULL, FALSE);
1060   
1061   real = gtk_text_iter_make_real(iter);
1062
1063   if (real == NULL)
1064     return FALSE;
1065
1066   check_invariants(iter);
1067   
1068   seg = real->any_segment;
1069   while (seg != real->segment)
1070     {
1071       if ( (seg->type == &gtk_text_toggle_off_type ||
1072             seg->type == &gtk_text_toggle_on_type) &&
1073            (tag == NULL ||
1074             seg->body.toggle.info->tag == tag) )
1075         return TRUE;
1076       
1077       seg = seg->next;
1078     }
1079
1080   return FALSE;
1081 }
1082
1083 /**
1084  * gtk_text_iter_has_tag:
1085  * @iter: an iterator
1086  * @tag: a #GtkTextTag
1087  * 
1088  * Returns TRUE if @iter is within a range tagged with @tag.
1089  * 
1090  * Return value: whether @iter is tagged with @tag
1091  **/
1092 gboolean
1093 gtk_text_iter_has_tag           (const GtkTextIter   *iter,
1094                                  GtkTextTag          *tag)
1095 {
1096   GtkTextRealIter *real;
1097   
1098   g_return_val_if_fail(iter != NULL, FALSE);
1099   g_return_val_if_fail(GTK_IS_TEXT_TAG(tag), FALSE);
1100   
1101   real = gtk_text_iter_make_surreal(iter);
1102
1103   if (real == NULL)
1104     return FALSE; 
1105
1106   check_invariants(iter);
1107   
1108   if (real->line_byte_offset >= 0)
1109     {
1110       return gtk_text_line_byte_has_tag(real->line, real->tree,
1111                                          real->line_byte_offset, tag);
1112     }
1113   else
1114     {
1115       g_assert(real->line_char_offset >= 0);
1116       return gtk_text_line_char_has_tag(real->line, real->tree,
1117                                          real->line_char_offset, tag);
1118     }
1119 }
1120
1121 /**
1122  * gtk_text_iter_editable:
1123  * @iter: an iterator
1124  * @default_setting: TRUE if text is editable by default
1125  * 
1126  * Returns whether @iter is within an editable region of text.
1127  * Non-editable text is "locked" and can't be changed by the user via
1128  * #GtkTextView. This function is simply a convenience wrapper around
1129  * gtk_text_iter_get_attributes(). If no tags applied to this text
1130  * affect editability, @default_setting will be returned.
1131  * 
1132  * Return value: whether @iter is inside an editable range
1133  **/
1134 gboolean
1135 gtk_text_iter_editable (const GtkTextIter *iter,
1136                         gboolean           default_setting)
1137 {
1138   GtkTextAttributes *values;
1139   gboolean retval;
1140   
1141   values = gtk_text_attributes_new ();
1142
1143   values->editable = default_setting;
1144
1145   gtk_text_iter_get_attributes (iter, values);
1146
1147   retval = values->editable;
1148   
1149   gtk_text_attributes_unref (values);
1150
1151   return retval;
1152 }
1153
1154 /**
1155  * gtk_text_iter_get_language:
1156  * @iter: an iterator
1157  * 
1158  * A convenience wrapper around gtk_text_iter_get_attributes(),
1159  * which returns the language in effect at @iter. If no tags affecting
1160  * language * apply to @iter, the return value is identical to that of
1161  * gtk_get_default_language().
1162  * 
1163  * Return value: language in effect at @iter
1164  **/
1165 static gchar*
1166 gtk_text_iter_get_language (const GtkTextIter *iter)
1167 {
1168   GtkTextAttributes *values;
1169   gchar *retval;
1170   
1171   values = gtk_text_attributes_new ();
1172
1173   gtk_text_iter_get_attributes (iter, values);
1174
1175   retval = g_strdup (values->language);
1176   
1177   gtk_text_attributes_unref (values);
1178
1179   return retval;
1180 }
1181
1182 /**
1183  * gtk_text_iter_starts_line:
1184  * @iter: an iterator
1185  * 
1186  * Returns TRUE if @iter begins a newline-terminated line,
1187  * i.e. gtk_text_iter_get_line_offset() would return 0.
1188  * However this function is potentially more efficient than
1189  * gtk_text_iter_get_line_offset() because it doesn't have to compute
1190  * the offset, it just has to see whether it's 0.
1191  * 
1192  * Return value: whether @iter begins a line
1193  **/
1194 gboolean
1195 gtk_text_iter_starts_line (const GtkTextIter   *iter)
1196 {
1197   GtkTextRealIter *real;
1198   
1199   g_return_val_if_fail(iter != NULL, FALSE);
1200   
1201   real = gtk_text_iter_make_surreal(iter);
1202
1203   if (real == NULL)
1204     return FALSE;  
1205
1206   check_invariants(iter);
1207   
1208   if (real->line_byte_offset >= 0)
1209     {
1210       return (real->line_byte_offset == 0);
1211     }
1212   else
1213     {
1214       g_assert(real->line_char_offset >= 0);
1215       return (real->line_char_offset == 0);
1216     }
1217 }
1218
1219 /**
1220  * gtk_text_iter_ends_line:
1221  * @iter: an iterator
1222  * 
1223  * Returns TRUE if @iter points to a newline character.
1224  * 
1225  * Return value: whether @iter is at the end of a line
1226  **/
1227 gboolean
1228 gtk_text_iter_ends_line (const GtkTextIter   *iter)
1229 {
1230   g_return_val_if_fail(iter != NULL, FALSE);
1231
1232   check_invariants(iter);
1233   
1234   return gtk_text_iter_get_char(iter) == '\n';
1235 }
1236
1237 /**
1238  * gtk_text_iter_is_last:
1239  * @iter: an iterator
1240  * 
1241  * Returns TRUE if @iter is the end iterator, i.e. one past the last
1242  * dereferenceable iterator in the buffer. gtk_text_iter_is_last() is
1243  * the most efficient way to check whether an iterator is the end
1244  * iterator.
1245  * 
1246  * Return value: whether @iter is the end iterator 
1247  **/
1248 gboolean
1249 gtk_text_iter_is_last (const GtkTextIter *iter)
1250 {
1251   GtkTextRealIter *real;
1252   
1253   g_return_val_if_fail(iter != NULL, FALSE);
1254   
1255   real = gtk_text_iter_make_surreal(iter);
1256
1257   if (real == NULL)
1258     return FALSE;  
1259
1260   check_invariants(iter);
1261   
1262   return gtk_text_line_is_last(real->line, real->tree);
1263 }
1264
1265 /**
1266  * gtk_text_iter_is_first:
1267  * @iter: an iterator
1268  * 
1269  * Returns TRUE if @iter is the first iterator in the buffer, that is
1270  * if @iter has a character offset of 0.
1271  * 
1272  * Return value: whether @iter is the first in the buffer
1273  **/
1274 gboolean
1275 gtk_text_iter_is_first (const GtkTextIter *iter)
1276 {
1277   return gtk_text_iter_get_offset (iter) == 0;
1278 }
1279
1280 /**
1281  * gtk_text_iter_get_chars_in_line:
1282  * @iter: an iterator
1283  * 
1284  * Returns the number of characters in the line containing @iter,
1285  * including the terminating newline.
1286  * 
1287  * Return value: number of characters in the line
1288  **/
1289 gint
1290 gtk_text_iter_get_chars_in_line (const GtkTextIter   *iter)
1291 {
1292   GtkTextRealIter *real;
1293   gint count;
1294   GtkTextLineSegment *seg;
1295   
1296   g_return_val_if_fail(iter != NULL, FALSE);
1297   
1298   real = gtk_text_iter_make_surreal(iter);
1299
1300   if (real == NULL)
1301     return 0;  
1302
1303   check_invariants(iter);
1304   
1305   if (real->line_char_offset >= 0)
1306     {
1307       /* We can start at the segments we've already found. */
1308       count = real->line_char_offset - real->segment_char_offset;
1309       seg = gtk_text_iter_get_indexable_segment(iter);
1310     }
1311   else
1312     {
1313       /* count whole line. */
1314       seg = real->line->segments;
1315       count = 0;
1316     }
1317
1318   
1319   while (seg != NULL)
1320     {
1321       count += seg->char_count;
1322       
1323       seg = seg->next;
1324     }
1325
1326   return count;
1327 }
1328
1329 /**
1330  * gtk_text_iter_get_attributes:
1331  * @iter: an iterator
1332  * @values: a #GtkTextAttributes to be filled in
1333  * 
1334  * Computes the effect of any tags applied to this spot in the
1335  * text. The @values parameter should be initialized to the default
1336  * settings you wish to use if no tags are in effect.
1337  * gtk_text_iter_get_attributes() will modify @values, applying the
1338  * effects of any tags present at @iter. If any tags affected @values,
1339  * the function returns TRUE.
1340  * 
1341  * Return value: TRUE if @values was modified
1342  **/
1343 gboolean
1344 gtk_text_iter_get_attributes (const GtkTextIter  *iter,
1345                                 GtkTextAttributes *values)
1346 {
1347   GtkTextTag** tags;
1348   gint tag_count = 0;
1349
1350   /* Get the tags at this spot */
1351   tags = gtk_text_btree_get_tags (iter, &tag_count);
1352
1353   /* No tags, use default style */
1354   if (tags == NULL || tag_count == 0)
1355     {
1356       if (tags)
1357         g_free (tags);
1358
1359       return FALSE;
1360     }
1361   
1362   /* Sort tags in ascending order of priority */
1363   gtk_text_tag_array_sort (tags, tag_count);
1364   
1365   gtk_text_attributes_fill_from_tags (values,
1366                                         tags,
1367                                         tag_count);
1368
1369   g_free (tags);
1370
1371   return TRUE;
1372 }
1373
1374 /*
1375  * Increments/decrements
1376  */
1377
1378 /* The return value of this indicates WHETHER WE MOVED.
1379  * The return value of public functions indicates
1380  * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
1381  */
1382 static gboolean
1383 forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
1384 {
1385   GtkTextLine *new_line;
1386   
1387   new_line = gtk_text_line_next(real->line);
1388
1389   g_assert(new_line != real->line);
1390   
1391   if (new_line != NULL)
1392     {      
1393       real->line = new_line;
1394
1395       real->line_byte_offset = 0;
1396       real->line_char_offset = 0;
1397       
1398       real->segment_byte_offset = 0;
1399       real->segment_char_offset = 0;
1400       
1401       /* Find first segments in new line */
1402       real->any_segment = real->line->segments;
1403       real->segment = real->any_segment;
1404       while (real->segment->char_count == 0)
1405         real->segment = real->segment->next;
1406
1407       return TRUE;
1408     }
1409   else
1410     {
1411       /* There is no way to move forward; we were already
1412          at the "end" index. (the end index is the last
1413          line pointer, segment_byte_offset of 0) */
1414
1415       g_assert(real->line_char_offset == 0 ||
1416                real->line_byte_offset == 0);
1417       
1418       /* The only indexable segment allowed on the bogus
1419          line at the end is a single char segment containing
1420          a newline. */
1421       if (real->segments_changed_stamp ==
1422           gtk_text_btree_get_segments_changed_stamp(real->tree))
1423         {
1424           g_assert(real->segment->type == &gtk_text_char_type);
1425           g_assert(real->segment->char_count == 1);
1426         }
1427       /* We leave real->line as-is */
1428       
1429       return FALSE;
1430     }
1431 }
1432
1433 static gboolean
1434 forward_char(GtkTextRealIter *real)
1435 {
1436   GtkTextIter *iter = (GtkTextIter*)real;
1437
1438   check_invariants((GtkTextIter*)real);
1439   
1440   ensure_char_offsets(real);
1441   
1442   if ( (real->segment_char_offset + 1) == real->segment->char_count)
1443     {
1444       /* Need to move to the next segment; if no next segment,
1445          need to move to next line. */
1446       return gtk_text_iter_forward_indexable_segment(iter);
1447     }
1448   else
1449     {
1450       /* Just moving within a segment. Keep byte count
1451          up-to-date, if it was already up-to-date. */
1452
1453       g_assert(real->segment->type == &gtk_text_char_type);
1454       
1455       if (real->line_byte_offset >= 0)
1456         {
1457           gint bytes;
1458           const char * start =
1459             real->segment->body.chars + real->segment_byte_offset;
1460           
1461           bytes = g_utf8_next_char (start) - start;
1462
1463           real->line_byte_offset += bytes;
1464           real->segment_byte_offset += bytes;
1465
1466           g_assert(real->segment_byte_offset < real->segment->byte_count);
1467         }
1468
1469       real->line_char_offset += 1;
1470       real->segment_char_offset += 1;
1471
1472       adjust_char_index(real, 1);
1473       
1474       g_assert(real->segment_char_offset < real->segment->char_count);
1475
1476       /* We moved into the middle of a segment, so the any_segment
1477          must now be the segment we're in the middle of. */
1478       real->any_segment = real->segment;
1479       
1480       check_invariants((GtkTextIter*)real);
1481
1482       /* FIXME we don't currently return FALSE if
1483        * we moved onto the end iterator
1484        */
1485       
1486       return TRUE;
1487     }
1488 }
1489
1490 gboolean
1491 gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
1492 {
1493   /* Need to move to the next segment; if no next segment,
1494      need to move to next line. */
1495   GtkTextLineSegment *seg;
1496   GtkTextLineSegment *any_seg;
1497   GtkTextRealIter *real;
1498   gint chars_skipped;
1499   gint bytes_skipped;
1500   
1501   g_return_val_if_fail(iter != NULL, FALSE);
1502   
1503   real = gtk_text_iter_make_real(iter);
1504
1505   if (real == NULL)
1506     return FALSE;
1507
1508   check_invariants(iter);
1509   
1510   if (real->line_char_offset >= 0)
1511     {
1512       chars_skipped = real->segment->char_count - real->segment_char_offset;
1513       g_assert(chars_skipped > 0);
1514     }
1515   else
1516     chars_skipped = 0;
1517
1518   if (real->line_byte_offset >= 0)
1519     {
1520       bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
1521       g_assert(bytes_skipped > 0);
1522     }
1523   else
1524     bytes_skipped = 0;
1525   
1526   /* Get first segment of any kind */
1527   any_seg = real->segment->next;
1528   /* skip non-indexable segments, if any */
1529   seg = any_seg;
1530   while (seg != NULL && seg->char_count == 0)
1531     seg = seg->next;
1532   
1533   if (seg != NULL)
1534     {
1535       real->any_segment = any_seg;
1536       real->segment = seg;
1537
1538       if (real->line_byte_offset >= 0)
1539         {
1540           g_assert(bytes_skipped > 0);
1541           real->segment_byte_offset = 0;
1542           real->line_byte_offset += bytes_skipped;
1543         }
1544
1545       if (real->line_char_offset >= 0)
1546         {
1547           g_assert(chars_skipped > 0);
1548           real->segment_char_offset = 0;
1549           real->line_char_offset += chars_skipped;
1550           adjust_char_index(real, chars_skipped);
1551         }
1552
1553       check_invariants(iter);
1554       
1555       return TRUE;
1556     }
1557   else
1558     {      
1559       /* End of the line */
1560       if (forward_line_leaving_caches_unmodified(real))
1561         {
1562           adjust_line_number(real, 1);
1563           if (real->line_char_offset >= 0)
1564             adjust_char_index(real, chars_skipped);
1565
1566           check_invariants(iter);
1567
1568           g_assert(real->line_byte_offset == 0);
1569           g_assert(real->line_char_offset == 0);
1570           g_assert(real->segment_byte_offset == 0);
1571           g_assert(real->segment_char_offset == 0);
1572           g_assert(gtk_text_iter_starts_line(iter));
1573
1574           check_invariants(iter);
1575
1576           if (gtk_text_iter_is_last (iter))
1577             return FALSE;
1578           else
1579             return TRUE;
1580         }
1581       else
1582         {
1583           /* End of buffer */
1584
1585           check_invariants(iter);
1586           
1587           return FALSE;
1588         }
1589     }
1590 }
1591
1592 gboolean
1593 gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
1594 {
1595   g_warning("FIXME");
1596
1597
1598   return FALSE;
1599 }
1600
1601 /**
1602  * gtk_text_iter_next_char:
1603  * @iter: an iterator
1604  * 
1605  * Moves @iter forward by one character offset. Note that images
1606  * embedded in the buffer occupy 1 character slot, so
1607  * gtk_text_iter_next_char() may actually move onto an image instead
1608  * of a character, if you have images in your buffer.  If @iter is the
1609  * end iterator or one character before it, @iter will now point at
1610  * the end iterator, and gtk_text_iter_next_char() returns FALSE for
1611  * convenience when writing loops.
1612  * 
1613  * Return value: whether the new position is the end iterator
1614  **/
1615 gboolean
1616 gtk_text_iter_next_char(GtkTextIter *iter)
1617 {
1618   GtkTextRealIter *real;
1619   
1620   g_return_val_if_fail(iter != NULL, FALSE);
1621   
1622   real = gtk_text_iter_make_real(iter);
1623
1624   if (real == NULL)
1625     return FALSE;
1626   else
1627     {
1628       check_invariants(iter);
1629       return forward_char(real);
1630     }
1631 }
1632
1633 /**
1634  * gtk_text_iter_prev_char:
1635  * @iter: an iterator
1636  * 
1637  * Moves backward by one character offset. Returns TRUE if movement
1638  * was possible; if @iter was the first in the buffer (character
1639  * offset 0), gtk_text_iter_prev_char() returns FALSE for convenience when
1640  * writing loops.
1641  * 
1642  * Return value: whether movement was possible
1643  **/
1644 gboolean
1645 gtk_text_iter_prev_char(GtkTextIter *iter)
1646 {
1647   g_return_val_if_fail(iter != NULL, FALSE);
1648
1649   check_invariants(iter);
1650   
1651   return gtk_text_iter_backward_chars(iter, 1);
1652 }
1653
1654 /*
1655   Definitely we should try to linear scan as often as possible for
1656   movement within a single line, because we can't use the BTree to
1657   speed within-line searches up; for movement between lines, we would
1658   like to avoid the linear scan probably.
1659   
1660   Instead of using this constant, it might be nice to cache the line
1661   length in the iterator and linear scan if motion is within a single
1662   line.
1663
1664   I guess you'd have to profile the various approaches.
1665 */
1666 #define MAX_LINEAR_SCAN 300
1667
1668
1669 /**
1670  * gtk_text_iter_forward_chars:
1671  * @iter: an iterator
1672  * @count: number of characters to move, may be negative
1673  * 
1674  * Moves @count characters if possible (if @count would move past the
1675  * start or end of the buffer, moves to the start or end of the
1676  * buffer). If @count is positive, the return value indicates whether
1677  * @iter was moved to a dereferenceable location (FALSE is returned if
1678  * @iter was moved to the non-dereferenceable end iterator). If @count
1679  * is negative, the return value indicates whether @iter was already
1680  * at character offset 0. If @count is 0, the function does nothing
1681  * and returns FALSE.
1682  * 
1683  * Return value: whether @iter moved or is dereferenceable
1684  **/
1685 gboolean
1686 gtk_text_iter_forward_chars(GtkTextIter *iter, gint count)
1687 {
1688   GtkTextRealIter *real;
1689   
1690   g_return_val_if_fail(iter != NULL, FALSE);
1691   
1692   real = gtk_text_iter_make_real(iter);
1693   
1694   if (real == NULL)
1695     return FALSE;
1696   else if (count == 0)
1697     return FALSE;
1698   else if (count < 0)
1699     return gtk_text_iter_backward_chars(iter, 0 - count);
1700   else if (count < MAX_LINEAR_SCAN)
1701     {
1702       check_invariants(iter);
1703       
1704       while (count > 1)
1705         {
1706           if (!forward_char(real))
1707             return FALSE;
1708           --count;
1709         }
1710       
1711       return forward_char(real);
1712     }
1713   else
1714     {
1715       gint current_char_index;
1716       gint new_char_index;
1717
1718       check_invariants(iter);
1719       
1720       current_char_index = gtk_text_iter_get_offset(iter);
1721
1722       if (current_char_index == gtk_text_btree_char_count(real->tree))
1723         return FALSE; /* can't move forward */
1724       
1725       new_char_index = current_char_index + count;
1726       gtk_text_iter_set_offset(iter, new_char_index);
1727
1728       check_invariants(iter);
1729
1730       /* Return FALSE if we're on the non-dereferenceable end
1731        * iterator.
1732        */
1733       if (gtk_text_iter_is_last (iter))
1734         return FALSE;
1735       else
1736         return TRUE;
1737     }
1738 }
1739
1740 /**
1741  * gtk_text_iter_backward_chars:
1742  * @iter: an iterator
1743  * @count: number of characters to move
1744  *
1745  * Moves @count characters backward, if possible (if @count would move
1746  * past the start or end of the buffer, moves to the start or end of
1747  * the buffer). If @count is negative, the return value indicates
1748  * whether @iter was moved to a dereferenceable location (FALSE is
1749  * returned if @iter was moved to the non-dereferenceable end
1750  * iterator). If @count is positive, the return value indicates
1751  * whether @iter was already at character offset 0. If @count is 0,
1752  * the function does nothing and returns FALSE.
1753  * 
1754  * Return value: whether @iter moved or is dereferenceable
1755  *  
1756  **/
1757 gboolean
1758 gtk_text_iter_backward_chars(GtkTextIter *iter, gint count)
1759 {
1760   GtkTextRealIter *real;
1761
1762   g_return_val_if_fail(iter != NULL, FALSE);
1763   
1764   real = gtk_text_iter_make_real(iter);
1765   
1766   if (real == NULL)
1767     return FALSE;
1768   else if (count == 0)
1769     return FALSE;
1770   else if (count < 0)
1771     return gtk_text_iter_forward_chars(iter, 0 - count);
1772
1773   ensure_char_offsets(real);
1774   check_invariants(iter);
1775   
1776   if (count <= real->segment_char_offset)
1777     {
1778       /* Optimize the within-segment case */      
1779       g_assert(real->segment->char_count > 0);
1780       g_assert(real->segment->type == &gtk_text_char_type);
1781
1782       real->segment_char_offset -= count;
1783       g_assert(real->segment_char_offset >= 0);
1784
1785       if (real->line_byte_offset >= 0)
1786         {
1787           gint new_byte_offset;
1788           gint i;
1789
1790           new_byte_offset = 0;
1791           i = 0;
1792           while (i < real->segment_char_offset)
1793             {
1794               const char * start = real->segment->body.chars + new_byte_offset;
1795               new_byte_offset += g_utf8_next_char (start) - start;
1796
1797               ++i;
1798             }
1799       
1800           real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
1801           real->segment_byte_offset = new_byte_offset;
1802         }
1803       
1804       real->line_char_offset -= count;
1805
1806       adjust_char_index(real, 0 - count);
1807
1808       check_invariants(iter);
1809       
1810       return TRUE;
1811     }
1812   else
1813     {
1814       /* We need to go back into previous segments. For now,
1815          just keep this really simple. */
1816       gint current_char_index;
1817       gint new_char_index;
1818       
1819       current_char_index = gtk_text_iter_get_offset(iter);
1820
1821       if (current_char_index == 0)
1822         return FALSE; /* can't move backward */
1823       
1824       new_char_index = current_char_index - count;
1825       if (new_char_index < 0)
1826         new_char_index = 0;
1827       gtk_text_iter_set_offset(iter, new_char_index);
1828
1829       check_invariants(iter);
1830       
1831       return TRUE;
1832     }
1833 }
1834
1835 /**
1836  * gtk_text_iter_forward_line:
1837  * @iter: an iterator
1838  * 
1839  * Moves @iter to the start of the next line. Returns TRUE if there
1840  * was a next line to move to, and FALSE if @iter was simply moved to
1841  * the end of the buffer and is now not dereferenceable.
1842  * 
1843  * Return value: whether @iter can be dereferenced
1844  **/
1845 gboolean
1846 gtk_text_iter_forward_line(GtkTextIter *iter)
1847 {
1848   GtkTextRealIter *real;
1849   
1850   g_return_val_if_fail(iter != NULL, FALSE);
1851   
1852   real = gtk_text_iter_make_real(iter);
1853   
1854   if (real == NULL)
1855     return FALSE;
1856
1857   check_invariants(iter);
1858   
1859   if (forward_line_leaving_caches_unmodified(real))
1860     {
1861       invalidate_char_index(real);
1862       adjust_line_number(real, 1);
1863
1864       check_invariants(iter);
1865
1866       if (gtk_text_iter_is_last (iter))
1867         return FALSE;
1868       else
1869         return TRUE;
1870     }
1871   else
1872     {
1873       check_invariants(iter);
1874       return FALSE;
1875     }
1876 }
1877
1878 /**
1879  * gtk_text_iter_backward_line:
1880  * @iter: an iterator
1881  * 
1882  * Moves @iter to the start of the previous line. Returns TRUE if
1883  * @iter could be moved; i.e. if @iter was at character offset 0, this
1884  * function returns FALSE. Therefore if @iter was already on line 0,
1885  * but not at the start of the line, @iter is snapped to the start of
1886  * the line and the function returns TRUE.
1887  * 
1888  * Return value: whether @iter moved
1889  **/
1890 gboolean
1891 gtk_text_iter_backward_line(GtkTextIter *iter)
1892 {
1893   GtkTextLine *new_line;
1894   GtkTextRealIter *real;
1895   gboolean offset_will_change;
1896   gint offset;
1897   
1898   g_return_val_if_fail(iter != NULL, FALSE);
1899   
1900   real = gtk_text_iter_make_real(iter);
1901   
1902   if (real == NULL)
1903     return FALSE;
1904
1905   check_invariants(iter);
1906   
1907   new_line = gtk_text_line_previous(real->line);
1908
1909   offset_will_change = FALSE;
1910   if (real->line_char_offset > 0)
1911     offset_will_change = TRUE;
1912           
1913   if (new_line != NULL)
1914     {
1915       real->line = new_line;
1916       
1917       adjust_line_number(real, -1);
1918     }
1919   else
1920     {
1921       if (!offset_will_change)
1922         return FALSE;
1923     }
1924
1925   invalidate_char_index(real);
1926   
1927   real->line_byte_offset = 0;
1928   real->line_char_offset = 0;
1929
1930   real->segment_byte_offset = 0;
1931   real->segment_char_offset = 0;
1932   
1933   /* Find first segment in line */
1934   real->any_segment = real->line->segments;
1935   real->segment = gtk_text_line_byte_to_segment(real->line,
1936                                                  0, &offset);
1937
1938   g_assert(offset == 0);
1939
1940   /* Note that if we are on the first line, we snap to the start
1941      of the first line and return TRUE, so TRUE means the
1942      iterator changed, not that the line changed; this is maybe
1943      a bit weird. I'm not sure there's an obvious right thing
1944      to do though. */
1945
1946   check_invariants(iter);
1947   
1948   return TRUE;
1949 }
1950
1951 gboolean
1952 gtk_text_iter_forward_lines(GtkTextIter *iter, gint count)
1953 {
1954   if (count < 0)
1955     return gtk_text_iter_backward_lines(iter, 0 - count);
1956   else if (count == 0)
1957     return FALSE;
1958   else if (count == 1)
1959     {
1960       check_invariants(iter);
1961       return gtk_text_iter_forward_line(iter);
1962     }
1963   else
1964     {
1965       gint old_line;
1966       
1967       old_line = gtk_text_iter_get_line(iter);
1968       
1969       gtk_text_iter_set_line(iter, old_line + count);
1970
1971       check_invariants(iter);
1972
1973       /* FIXME this needs to return FALSE if we moved onto the
1974        * end iterator.
1975        */
1976       return (gtk_text_iter_get_line(iter) != old_line);
1977     }
1978 }
1979
1980 gboolean
1981 gtk_text_iter_backward_lines(GtkTextIter *iter, gint count)
1982 {
1983   if (count < 0)
1984     return gtk_text_iter_forward_lines(iter, 0 - count);
1985   else if (count == 0)
1986     return FALSE;
1987   else if (count == 1)
1988     {
1989       return gtk_text_iter_backward_line(iter);
1990     }
1991   else
1992     {
1993       gint old_line;
1994       
1995       old_line = gtk_text_iter_get_line(iter);
1996       
1997       gtk_text_iter_set_line(iter, MAX(old_line - count, 0));
1998
1999       return (gtk_text_iter_get_line(iter) != old_line);
2000     }
2001 }
2002
2003 typedef gboolean (* FindLogAttrFunc) (PangoLogAttr *attrs,
2004                                       gint          offset,
2005                                       gint          min_offset,
2006                                       gint          len,
2007                                       gint         *found_offset);     
2008
2009 static gboolean
2010 find_word_end_func (PangoLogAttr *attrs,
2011                     gint          offset,
2012                     gint          min_offset,
2013                     gint          len,
2014                     gint         *found_offset)
2015 {
2016   ++offset; /* We always go to the NEXT word end */
2017   
2018   /* Find start of next word */
2019   while (offset < min_offset + len &&
2020          !attrs[offset].is_word_stop)
2021     ++offset;
2022   
2023   /* Find end */
2024   while (offset < min_offset + len &&
2025          !attrs[offset].is_white)
2026     ++offset;
2027   
2028   *found_offset = offset;
2029
2030   return offset < min_offset + len;
2031 }
2032
2033 static gboolean
2034 find_word_start_func (PangoLogAttr *attrs,
2035                       gint          offset,
2036                       gint          min_offset,
2037                       gint          len,
2038                       gint         *found_offset)
2039 {
2040   --offset; /* We always go to the NEXT word start */
2041   
2042   /* Find end of prev word */
2043   while (offset >= min_offset &&
2044          attrs[offset].is_white)
2045     --offset;
2046   
2047   /* Find start */
2048   while (offset >= min_offset &&
2049          !attrs[offset].is_word_stop)
2050     --offset;
2051   
2052   *found_offset = offset;
2053
2054   return offset >= min_offset;
2055 }
2056
2057 /* FIXME this function is very, very gratuitously slow */
2058 static gboolean
2059 find_by_log_attrs (GtkTextIter *iter,
2060                    FindLogAttrFunc func,
2061                    gboolean forward)
2062 {
2063   GtkTextIter orig;
2064   GtkTextIter start;
2065   GtkTextIter end;
2066   gchar *paragraph;
2067   gint char_len, byte_len;
2068   PangoLogAttr *attrs;
2069   int offset;
2070   gboolean found = FALSE;
2071   
2072   g_return_val_if_fail(iter != NULL, FALSE);
2073
2074   orig = *iter;
2075   start = *iter;
2076   end = *iter;
2077
2078   gtk_text_iter_set_line_offset (&start, 0);
2079   gtk_text_iter_forward_to_newline (&end);
2080   
2081   paragraph = gtk_text_iter_get_text (&start, &end);
2082   char_len = g_utf8_strlen (paragraph, -1);
2083   byte_len = strlen (paragraph);
2084
2085   offset = gtk_text_iter_get_line_offset (iter);
2086   
2087   if (char_len > 0 && offset < char_len)
2088     {
2089       gchar *lang;
2090       
2091       attrs = g_new (PangoLogAttr, char_len);
2092       
2093       lang = gtk_text_iter_get_language (iter);
2094
2095       pango_get_log_attrs (paragraph, byte_len, -1,
2096                            lang,
2097                            attrs);
2098
2099       g_free (lang);
2100
2101       found = (* func) (attrs, offset, 0, char_len, &offset);
2102       
2103       g_free (attrs);
2104     }
2105   
2106   g_free (paragraph);
2107   
2108   if (!found)
2109     {
2110       if (forward)
2111         {
2112           if (gtk_text_iter_forward_line (iter))
2113             return find_by_log_attrs (iter, func, forward);
2114           else
2115             return FALSE;
2116         }
2117       else
2118         {
2119           if (gtk_text_iter_backward_line (iter))
2120             return find_by_log_attrs (iter, func, forward);
2121           else
2122             return FALSE;
2123         }
2124     }
2125   else
2126     {     
2127       gtk_text_iter_set_line_offset (iter, offset);
2128       
2129       return
2130         !gtk_text_iter_equal(iter, &orig) &&
2131         !gtk_text_iter_is_last (iter);
2132     }
2133 }
2134
2135 gboolean
2136 gtk_text_iter_forward_word_end(GtkTextIter *iter)
2137 {
2138   return find_by_log_attrs (iter, find_word_end_func, TRUE);
2139 }
2140
2141 gboolean
2142 gtk_text_iter_backward_word_start(GtkTextIter      *iter)
2143 {
2144   return find_by_log_attrs (iter, find_word_start_func, FALSE);
2145 }
2146
2147 /* FIXME a loop around a truly slow function means
2148  * a truly spectacularly slow function.
2149  */
2150 gboolean
2151 gtk_text_iter_forward_word_ends(GtkTextIter      *iter,
2152                                  gint               count)
2153 {
2154   g_return_val_if_fail(iter != NULL, FALSE);
2155   g_return_val_if_fail(count > 0, FALSE);
2156
2157   if (!gtk_text_iter_forward_word_end(iter))
2158     return FALSE;
2159   --count;
2160   
2161   while (count > 0)
2162     {
2163       if (!gtk_text_iter_forward_word_end(iter))
2164         break;
2165       --count;
2166     }
2167   return TRUE;
2168 }
2169
2170 gboolean
2171 gtk_text_iter_backward_word_starts(GtkTextIter      *iter,
2172                                     gint               count)
2173 {
2174   g_return_val_if_fail(iter != NULL, FALSE);
2175   g_return_val_if_fail(count > 0, FALSE);
2176
2177   if (!gtk_text_iter_backward_word_start(iter))
2178     return FALSE;
2179   --count;
2180   
2181   while (count > 0)
2182     {
2183       if (!gtk_text_iter_backward_word_start(iter))
2184         break;
2185       --count;
2186     }
2187   return TRUE;
2188 }
2189
2190 void
2191 gtk_text_iter_set_line_offset(GtkTextIter *iter,
2192                              gint char_on_line)
2193 {
2194   GtkTextRealIter *real;
2195   
2196   g_return_if_fail(iter != NULL);
2197   
2198   real = gtk_text_iter_make_surreal(iter);
2199
2200   if (real == NULL)
2201     return;
2202
2203   check_invariants(iter);
2204   
2205   iter_set_from_char_offset(real, real->line, char_on_line);
2206
2207   check_invariants(iter);
2208 }
2209
2210 void
2211 gtk_text_iter_set_line(GtkTextIter *iter, gint line_number)
2212 {
2213   GtkTextLine *line;
2214   gint real_line;
2215   GtkTextRealIter *real;
2216   
2217   g_return_if_fail(iter != NULL);
2218   
2219   real = gtk_text_iter_make_surreal(iter);
2220
2221   if (real == NULL)
2222     return;
2223
2224   check_invariants(iter);
2225   
2226   line = gtk_text_btree_get_line(real->tree, line_number, &real_line);
2227
2228   iter_set_from_char_offset(real, line, 0);
2229   
2230   /* We might as well cache this, since we know it. */
2231   real->cached_line_number = real_line;
2232
2233   check_invariants(iter);
2234 }
2235
2236 void
2237 gtk_text_iter_set_offset(GtkTextIter *iter, gint char_index)
2238 {
2239   GtkTextLine *line;
2240   GtkTextRealIter *real;
2241   gint line_start;
2242   gint real_char_index;
2243   
2244   g_return_if_fail(iter != NULL);
2245   
2246   real = gtk_text_iter_make_surreal(iter);
2247
2248   if (real == NULL)
2249     return;
2250
2251   check_invariants(iter);
2252   
2253   if (real->cached_char_index >= 0 &&
2254       real->cached_char_index == char_index)
2255     return;
2256
2257   line = gtk_text_btree_get_line_at_char(real->tree,
2258                                           char_index,
2259                                           &line_start,
2260                                           &real_char_index);
2261
2262   iter_set_from_char_offset(real, line, real_char_index - line_start);
2263   
2264   /* Go ahead and cache this since we have it. */
2265   real->cached_char_index = real_char_index;
2266
2267   check_invariants(iter);
2268 }
2269
2270 void
2271 gtk_text_iter_forward_to_end  (GtkTextIter       *iter)
2272 {
2273   GtkTextBuffer *buffer;
2274   GtkTextRealIter *real;
2275
2276   g_return_if_fail(iter != NULL);
2277   
2278   real = gtk_text_iter_make_surreal(iter);
2279
2280   if (real == NULL)
2281     return;
2282   
2283   buffer = gtk_text_btree_get_buffer(real->tree);
2284
2285   gtk_text_buffer_get_last_iter(buffer, iter);
2286 }
2287
2288 gboolean
2289 gtk_text_iter_forward_to_newline(GtkTextIter *iter)
2290 {
2291   gint current_offset;
2292   gint new_offset;
2293   
2294   g_return_val_if_fail(iter != NULL, FALSE);
2295   
2296   current_offset = gtk_text_iter_get_line_offset(iter);
2297   new_offset = gtk_text_iter_get_chars_in_line(iter) - 1;
2298
2299   if (current_offset < new_offset)
2300     {
2301       /* Move to end of this line. */
2302       gtk_text_iter_set_line_offset(iter, new_offset);
2303       return TRUE;
2304     }
2305   else
2306     {
2307       /* Move to end of next line. */
2308       if (gtk_text_iter_forward_line(iter))
2309         {
2310           gtk_text_iter_forward_to_newline(iter);
2311           return TRUE;
2312         }
2313       else
2314         return FALSE;
2315     }
2316 }
2317
2318 gboolean
2319 gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
2320                                      GtkTextTag  *tag)
2321 {
2322   GtkTextLine *next_line;
2323   GtkTextLine *current_line;
2324   GtkTextRealIter *real;
2325
2326   g_return_val_if_fail(iter != NULL, FALSE);
2327   
2328   real = gtk_text_iter_make_real(iter);
2329
2330   if (real == NULL)
2331     return FALSE;
2332
2333   check_invariants(iter);
2334   
2335   current_line = real->line;
2336   next_line = gtk_text_line_next_could_contain_tag(current_line,
2337                                                     real->tree, tag);
2338   
2339   while (gtk_text_iter_forward_indexable_segment(iter))
2340     {
2341       /* If we went forward to a line that couldn't contain a toggle
2342          for the tag, then skip forward to a line that could contain
2343          it. This potentially skips huge hunks of the tree, so we
2344          aren't a purely linear search. */
2345       if (real->line != current_line)
2346         {
2347           if (next_line == NULL)
2348             {
2349               /* End of search. Set to end of buffer. */
2350               gtk_text_btree_get_last_iter(real->tree, iter);
2351               return FALSE;
2352             }
2353               
2354           if (real->line != next_line)
2355             iter_set_from_byte_offset(real, next_line, 0);
2356
2357           current_line = real->line;
2358           next_line = gtk_text_line_next_could_contain_tag(current_line,
2359                                                             real->tree,
2360                                                             tag);
2361         }
2362
2363       if (gtk_text_iter_toggles_tag(iter, tag))
2364         {
2365           /* If there's a toggle here, it isn't indexable so
2366              any_segment can't be the indexable segment. */
2367           g_assert(real->any_segment != real->segment);
2368           return TRUE;
2369         }
2370     }
2371
2372   /* Check end iterator for tags */
2373   if (gtk_text_iter_toggles_tag(iter, tag))
2374     {
2375       /* If there's a toggle here, it isn't indexable so
2376          any_segment can't be the indexable segment. */
2377       g_assert(real->any_segment != real->segment);
2378       return TRUE;
2379     }
2380   
2381   /* Reached end of buffer */
2382   return FALSE;
2383 }
2384
2385 gboolean
2386 gtk_text_iter_backward_to_tag_toggle (GtkTextIter *iter,
2387                                       GtkTextTag  *tag)
2388 {
2389
2390   g_warning("FIXME");
2391 }
2392
2393 static gboolean
2394 matches_pred(GtkTextIter *iter,
2395              GtkTextCharPredicate pred,
2396              gpointer user_data)
2397 {
2398   gint ch;
2399
2400   ch = gtk_text_iter_get_char(iter);
2401
2402   return (*pred) (ch, user_data);
2403 }
2404
2405 gboolean
2406 gtk_text_iter_forward_find_char (GtkTextIter *iter,
2407                                   GtkTextCharPredicate pred,
2408                                   gpointer user_data)
2409 {
2410   g_return_val_if_fail(iter != NULL, FALSE);
2411   g_return_val_if_fail(pred != NULL, FALSE);
2412
2413   while (gtk_text_iter_next_char(iter))
2414     {
2415       if (matches_pred(iter, pred, user_data))
2416         return TRUE;
2417     }
2418   
2419   return FALSE;
2420 }
2421
2422 gboolean
2423 gtk_text_iter_backward_find_char (GtkTextIter *iter,
2424                                    GtkTextCharPredicate pred,
2425                                    gpointer user_data)
2426 {
2427   g_return_val_if_fail(iter != NULL, FALSE);
2428   g_return_val_if_fail(pred != NULL, FALSE);
2429
2430   while (gtk_text_iter_prev_char(iter))
2431     {
2432       if (matches_pred(iter, pred, user_data))
2433         return TRUE;
2434     }
2435   
2436   return FALSE;
2437 }
2438
2439 static gboolean
2440 lines_match (const GtkTextIter *start,
2441              const gchar **lines,
2442              gboolean visible_only,
2443              gboolean slice,
2444              GtkTextIter *match_start)
2445 {
2446   GtkTextIter next;
2447   gchar *line_text;
2448   const gchar *found;
2449   gint offset;
2450   
2451   if (*lines == NULL || **lines == '\0')
2452     return TRUE;
2453   
2454   next = *start;
2455   gtk_text_iter_forward_line (&next);
2456
2457   gtk_text_iter_spew (start, "start");
2458   gtk_text_iter_spew (&next, "next");
2459   
2460   /* No more text in buffer, but *lines is nonempty */
2461   if (gtk_text_iter_equal (start, &next))
2462     {
2463       return FALSE;
2464     }
2465
2466   if (slice)
2467     {
2468       if (visible_only)
2469         line_text = gtk_text_iter_get_visible_slice (start, &next);
2470       else
2471         line_text = gtk_text_iter_get_slice (start, &next);
2472     }
2473   else
2474     {
2475       /* FIXME */
2476       g_warning ("Searching for non-slice text is currently broken (you must include 'unknown char' for pixmaps in order to match them)");
2477       if (visible_only)
2478         line_text = gtk_text_iter_get_visible_text (start, &next);
2479       else
2480         line_text = gtk_text_iter_get_text (start, &next);
2481     }
2482   
2483   if (match_start) /* if this is the first line we're matching */
2484     found = strstr (line_text, *lines);
2485   else
2486     {
2487       /* If it's not the first line, we have to match from the
2488        * start of the line.
2489        */
2490       if (strncmp (line_text, *lines, strlen (*lines)) == 0)
2491         found = line_text;
2492       else
2493         found = NULL;
2494     }
2495
2496   if (found == NULL)
2497     {
2498       g_free (line_text);
2499       return FALSE;
2500     }
2501   
2502   /* Get offset to start of search string */
2503   offset = g_utf8_strlen (line_text, found - line_text);
2504   
2505   next = *start;
2506   
2507   /* If match start needs to be returned, set it to the
2508    * start of the search string.
2509    */
2510   if (match_start)
2511     {
2512       *match_start = next;
2513       gtk_text_iter_forward_chars (match_start, offset);
2514     }
2515
2516   /* Go to end of search string */
2517   offset += g_utf8_strlen (*lines, -1);
2518
2519   gtk_text_iter_forward_chars (&next, offset);
2520   
2521   g_free (line_text);
2522
2523   ++lines;
2524
2525   /* pass NULL for match_start, since we don't need to find the
2526    * start again.
2527    */
2528   return lines_match (&next, lines, visible_only, slice, NULL);
2529 }
2530
2531 /* strsplit() that retains the delimiter as part of the string. */
2532 static gchar **
2533 strbreakup (const char *string,
2534             const char *delimiter,
2535             gint        max_tokens)
2536 {
2537   GSList *string_list = NULL, *slist;
2538   gchar **str_array, *s;
2539   guint i, n = 1;
2540
2541   g_return_val_if_fail (string != NULL, NULL);
2542   g_return_val_if_fail (delimiter != NULL, NULL);
2543
2544   if (max_tokens < 1)
2545     max_tokens = G_MAXINT;
2546
2547   s = strstr (string, delimiter);
2548   if (s)
2549     {
2550       guint delimiter_len = strlen (delimiter);
2551
2552       do
2553         {
2554           guint len;
2555           gchar *new_string;
2556
2557           len = s - string + delimiter_len;
2558           new_string = g_new (gchar, len + 1);
2559           strncpy (new_string, string, len);
2560           new_string[len] = 0;
2561           string_list = g_slist_prepend (string_list, new_string);
2562           n++;
2563           string = s + delimiter_len;
2564           s = strstr (string, delimiter);
2565         }
2566       while (--max_tokens && s);
2567     }
2568   if (*string)
2569     {
2570       n++;
2571       string_list = g_slist_prepend (string_list, g_strdup (string));
2572     }
2573
2574   str_array = g_new (gchar*, n);
2575
2576   i = n - 1;
2577
2578   str_array[i--] = NULL;
2579   for (slist = string_list; slist; slist = slist->next)
2580     str_array[i--] = slist->data;
2581
2582   g_slist_free (string_list);
2583
2584   return str_array;
2585 }
2586
2587 gboolean
2588 gtk_text_iter_forward_search (GtkTextIter *iter,
2589                               const char  *str,
2590                               gboolean visible_only,
2591                               gboolean slice)
2592 {
2593   gchar **lines = NULL;
2594   GtkTextIter match;
2595   gboolean retval = FALSE;
2596   GtkTextIter search;
2597   
2598   g_return_val_if_fail (iter != NULL, FALSE);
2599   g_return_val_if_fail (str != NULL, FALSE);
2600
2601   if (*str == '\0')
2602     return TRUE; /* we found the empty string */
2603   
2604   /* locate all lines */
2605
2606   lines = strbreakup (str, "\n", -1);
2607   
2608   search = *iter;
2609
2610   do
2611     {      
2612       /* This loop has an inefficient worst-case, where
2613        * gtk_text_iter_get_text() is called repeatedly on
2614        * a single line.
2615        */
2616       if (lines_match (&search, (const gchar**)lines, visible_only, slice, &match))
2617         {
2618           retval = TRUE;
2619           
2620           *iter = match;
2621           
2622           break;
2623         }
2624     }
2625   while (gtk_text_iter_forward_line (&search));
2626   
2627   g_strfreev ((gchar**)lines);
2628
2629   return retval;
2630 }
2631
2632 gboolean
2633 gtk_text_iter_backward_search (GtkTextIter *iter,
2634                                const char  *str,
2635                                gboolean visible_only,
2636                                gboolean slice)
2637 {
2638   g_return_val_if_fail (iter != NULL, FALSE);
2639   g_return_val_if_fail (str != NULL, FALSE);
2640
2641
2642
2643 }
2644
2645 /*
2646  * Comparisons
2647  */
2648
2649 gboolean
2650 gtk_text_iter_equal(const GtkTextIter *lhs, const GtkTextIter *rhs)
2651 {
2652   GtkTextRealIter *real_lhs;
2653   GtkTextRealIter *real_rhs;
2654
2655   real_lhs = (GtkTextRealIter*)lhs;
2656   real_rhs = (GtkTextRealIter*)rhs;
2657
2658   check_invariants(lhs);
2659   check_invariants(rhs);
2660   
2661   if (real_lhs->line != real_rhs->line)
2662     return FALSE;
2663   else if (real_lhs->line_byte_offset >= 0 &&
2664            real_rhs->line_byte_offset >= 0)
2665     return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
2666   else
2667     {
2668       /* the ensure_char_offsets() calls do nothing if the char offsets
2669          are already up-to-date. */
2670       ensure_char_offsets(real_lhs);
2671       ensure_char_offsets(real_rhs);
2672       return real_lhs->line_char_offset == real_rhs->line_char_offset; 
2673     }
2674 }
2675
2676 gint
2677 gtk_text_iter_compare(const GtkTextIter *lhs, const GtkTextIter *rhs)
2678 {
2679   GtkTextRealIter *real_lhs;
2680   GtkTextRealIter *real_rhs;
2681
2682   real_lhs = gtk_text_iter_make_surreal(lhs);
2683   real_rhs = gtk_text_iter_make_surreal(rhs);
2684
2685   check_invariants(lhs);
2686   check_invariants(rhs);
2687   
2688   if (real_lhs == NULL ||
2689       real_rhs == NULL)
2690     return -1; /* why not */
2691
2692   if (real_lhs->line == real_rhs->line)
2693     {
2694       gint left_index, right_index;
2695
2696       if (real_lhs->line_byte_offset >= 0 &&
2697           real_rhs->line_byte_offset >= 0)
2698         {
2699           left_index = real_lhs->line_byte_offset;
2700           right_index = real_rhs->line_byte_offset;
2701         }
2702       else
2703         {
2704           /* the ensure_char_offsets() calls do nothing if
2705              the offsets are already up-to-date. */
2706           ensure_char_offsets(real_lhs);
2707           ensure_char_offsets(real_rhs);
2708           left_index = real_lhs->line_char_offset;
2709           right_index = real_rhs->line_char_offset;
2710         }
2711       
2712       if (left_index < right_index)
2713         return -1;
2714       else if (left_index > right_index)
2715         return 1;
2716       else
2717         return 0;
2718     }
2719   else
2720     {
2721       gint line1, line2;
2722       
2723       line1 = gtk_text_iter_get_line(lhs);
2724       line2 = gtk_text_iter_get_line(rhs);
2725       if (line1 < line2)
2726         return -1;
2727       else if (line1 > line2)
2728         return 1;
2729       else
2730         return 0;  
2731     }
2732 }
2733
2734 gboolean
2735 gtk_text_iter_in_region (const GtkTextIter *iter,
2736                           const GtkTextIter *start,
2737                           const GtkTextIter *end)
2738 {
2739   return gtk_text_iter_compare(iter, start) >= 0 &&
2740     gtk_text_iter_compare(iter, end) < 0;
2741 }
2742
2743 void
2744 gtk_text_iter_reorder         (GtkTextIter *first,
2745                                 GtkTextIter *second)
2746 {
2747   g_return_if_fail(first != NULL);
2748   g_return_if_fail(second != NULL);
2749
2750   if (gtk_text_iter_compare(first, second) > 0)
2751     {
2752       GtkTextIter tmp;
2753
2754       tmp = *first;
2755       *first = *second;
2756       *second = tmp;
2757     }
2758 }
2759
2760 /*
2761  * Init iterators from the BTree
2762  */
2763
2764 void
2765 gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
2766                                   GtkTextIter *iter,
2767                                   gint char_index)
2768 {
2769   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2770   gint real_char_index;
2771   gint line_start;
2772   GtkTextLine *line;
2773   
2774   g_return_if_fail(iter != NULL);
2775   g_return_if_fail(tree != NULL);
2776
2777   line = gtk_text_btree_get_line_at_char(tree, char_index,
2778                                           &line_start, &real_char_index);
2779   
2780   iter_init_from_char_offset(iter, tree, line, real_char_index - line_start);
2781
2782  real->cached_char_index = real_char_index;
2783
2784  check_invariants(iter);
2785 }
2786
2787 void
2788 gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
2789                                        GtkTextIter *iter,
2790                                        gint line_number,
2791                                        gint char_on_line)
2792 {
2793   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2794   GtkTextLine *line;
2795   gint real_line;
2796   
2797   g_return_if_fail(iter != NULL);
2798   g_return_if_fail(tree != NULL);
2799   
2800   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2801
2802   iter_init_from_char_offset(iter, tree, line, char_on_line);
2803
2804   /* We might as well cache this, since we know it. */
2805   real->cached_line_number = real_line;
2806
2807   check_invariants(iter);
2808 }
2809
2810 void
2811 gtk_text_btree_get_iter_at_line_byte (GtkTextBTree   *tree,
2812                                       GtkTextIter    *iter,
2813                                       gint            line_number,
2814                                       gint            byte_index)
2815 {
2816   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2817   GtkTextLine *line;
2818   gint real_line;
2819   
2820   g_return_if_fail(iter != NULL);
2821   g_return_if_fail(tree != NULL);
2822   
2823   line = gtk_text_btree_get_line(tree, line_number, &real_line);
2824
2825   iter_init_from_byte_offset(iter, tree, line, byte_index);
2826
2827   /* We might as well cache this, since we know it. */
2828   real->cached_line_number = real_line;
2829
2830   check_invariants(iter);
2831 }
2832
2833 void
2834 gtk_text_btree_get_iter_at_line      (GtkTextBTree   *tree,
2835                                        GtkTextIter    *iter,
2836                                        GtkTextLine    *line,
2837                                        gint             byte_offset)
2838 {
2839   g_return_if_fail(iter != NULL);
2840   g_return_if_fail(tree != NULL);
2841   g_return_if_fail(line != NULL);
2842
2843   iter_init_from_byte_offset(iter, tree, line, byte_offset);
2844
2845   check_invariants(iter);
2846 }
2847
2848 gboolean
2849 gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree   *tree,
2850                                           GtkTextIter    *iter,
2851                                           GtkTextTag     *tag)
2852 {
2853   GtkTextLine *line;
2854   
2855   g_return_val_if_fail(iter != NULL, FALSE);
2856   g_return_val_if_fail(tree != NULL, FALSE);
2857
2858   line = gtk_text_btree_first_could_contain_tag(tree, tag);
2859
2860   if (line == NULL)
2861     {
2862       /* Set iter to last in tree */
2863       gtk_text_btree_get_last_iter(tree, iter);
2864       check_invariants(iter);
2865       return FALSE;
2866     }
2867   else
2868     {
2869       iter_init_from_byte_offset(iter, tree, line, 0);
2870       gtk_text_iter_forward_to_tag_toggle(iter, tag);
2871       check_invariants(iter);
2872       return TRUE;
2873     }
2874 }
2875
2876 gboolean
2877 gtk_text_btree_get_iter_at_last_toggle  (GtkTextBTree   *tree,
2878                                           GtkTextIter    *iter,
2879                                           GtkTextTag     *tag)
2880 {
2881   GtkTextLine *line;
2882   
2883   g_return_val_if_fail(iter != NULL, FALSE);
2884   g_return_val_if_fail(tree != NULL, FALSE);
2885
2886   line = gtk_text_btree_last_could_contain_tag(tree, tag);
2887
2888   if (line == NULL)
2889     {
2890       /* Set iter to first in tree */
2891       gtk_text_btree_get_iter_at_line_char(tree, iter, 0, 0);
2892       check_invariants(iter);
2893       return FALSE;
2894     }
2895   else
2896     {
2897       iter_init_from_byte_offset(iter, tree, line, -1);
2898       gtk_text_iter_backward_to_tag_toggle(iter, tag);
2899       check_invariants(iter);
2900       return TRUE;
2901     }
2902 }
2903
2904 gboolean
2905 gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
2906                                        GtkTextIter *iter,
2907                                        const gchar *mark_name)
2908 {
2909   GtkTextMark *mark;
2910   
2911   g_return_val_if_fail(iter != NULL, FALSE);
2912   g_return_val_if_fail(tree != NULL, FALSE);
2913   
2914   mark = gtk_text_btree_get_mark_by_name(tree, mark_name);
2915
2916   if (mark == NULL)
2917     return FALSE;
2918   else
2919     {
2920       gtk_text_btree_get_iter_at_mark(tree, iter, mark);
2921       check_invariants(iter);
2922       return TRUE;
2923     }
2924 }
2925
2926 void
2927 gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
2928                                   GtkTextIter *iter,
2929                                   GtkTextMark *mark)
2930 {
2931   GtkTextLineSegment *seg = (GtkTextLineSegment*) mark;
2932   
2933   g_return_if_fail(iter != NULL);
2934   g_return_if_fail(tree != NULL);
2935   g_return_if_fail(GTK_IS_TEXT_MARK (mark));
2936   
2937   iter_init_from_segment(iter, tree,
2938                          seg->body.mark.line, seg);
2939   g_assert(seg->body.mark.line == gtk_text_iter_get_text_line(iter));
2940   check_invariants(iter);
2941 }
2942
2943 void
2944 gtk_text_btree_get_last_iter         (GtkTextBTree   *tree,
2945                                        GtkTextIter    *iter)
2946 {
2947   g_return_if_fail(iter != NULL);
2948   g_return_if_fail(tree != NULL);
2949   
2950   gtk_text_btree_get_iter_at_char(tree,
2951                                    iter,
2952                                    gtk_text_btree_char_count(tree));
2953   check_invariants(iter);
2954 }
2955
2956 void
2957 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
2958 {
2959   GtkTextRealIter *real = (GtkTextRealIter*)iter;
2960   
2961   g_return_if_fail(iter != NULL);
2962
2963   if (real->chars_changed_stamp != gtk_text_btree_get_chars_changed_stamp(real->tree))
2964     g_print(" %20s: <invalidated iterator>\n", desc);
2965   else
2966     {
2967       check_invariants(iter);
2968       g_print(" %20s: line %d / char %d / line char %d / line byte %d\n",
2969              desc,
2970              gtk_text_iter_get_line(iter),
2971              gtk_text_iter_get_offset(iter),
2972              gtk_text_iter_get_line_offset(iter),
2973              gtk_text_iter_get_line_index(iter));
2974       check_invariants(iter);
2975     }
2976 }
2977
2978 void
2979 gtk_text_iter_check(const GtkTextIter *iter)
2980 {
2981   const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
2982   gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
2983   GtkTextLineSegment *byte_segment;
2984   GtkTextLineSegment *byte_any_segment;
2985   GtkTextLineSegment *char_segment;
2986   GtkTextLineSegment *char_any_segment;
2987   gboolean segments_updated;
2988   
2989   /* We are going to check our class invariants for the Iter class. */
2990
2991   if (real->chars_changed_stamp !=
2992       gtk_text_btree_get_chars_changed_stamp(real->tree))
2993     g_error("iterator check failed: invalid iterator");
2994   
2995   if (real->line_char_offset < 0 && real->line_byte_offset < 0)
2996     g_error("iterator check failed: both char and byte offsets are invalid");
2997
2998   segments_updated = (real->segments_changed_stamp ==
2999                       gtk_text_btree_get_segments_changed_stamp(real->tree));
3000
3001 #if 0
3002   printf("checking iter, segments %s updated, byte %d char %d\n",
3003          segments_updated ? "are" : "aren't",
3004          real->line_byte_offset,
3005          real->line_char_offset);
3006 #endif
3007
3008   if (real->line_byte_offset == 97 &&
3009       real->line_char_offset == 95)
3010     G_BREAKPOINT();
3011   
3012   if (segments_updated)
3013     {
3014       if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
3015         g_error("iterator check failed: both char and byte segment offsets are invalid");
3016       
3017       if (real->segment->char_count == 0)
3018         g_error("iterator check failed: segment is not indexable.");
3019
3020       if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
3021         g_error("segment char offset is not properly up-to-date");
3022
3023       if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
3024         g_error("segment byte offset is not properly up-to-date");
3025
3026       if (real->segment_byte_offset >= 0 &&
3027           real->segment_byte_offset >= real->segment->byte_count)
3028         g_error("segment byte offset is too large.");
3029
3030       if (real->segment_char_offset >= 0 &&
3031           real->segment_char_offset >= real->segment->char_count)
3032         g_error("segment char offset is too large.");
3033     }
3034   
3035   if (real->line_byte_offset >= 0)
3036     {
3037       gtk_text_line_byte_locate(real->line, real->line_byte_offset,
3038                                  &byte_segment, &byte_any_segment,
3039                                  &seg_byte_offset, &line_byte_offset);
3040
3041       if (line_byte_offset != real->line_byte_offset)
3042         g_error("wrong byte offset was stored in iterator");
3043       
3044       if (segments_updated)
3045         {
3046           if (real->segment != byte_segment)
3047             g_error("wrong segment was stored in iterator");
3048
3049           if (real->any_segment != byte_any_segment)
3050             g_error("wrong any_segment was stored in iterator");
3051           
3052           if (seg_byte_offset != real->segment_byte_offset)
3053             g_error("wrong segment byte offset was stored in iterator");
3054         }
3055     }
3056   
3057   if (real->line_char_offset >= 0)
3058     {
3059       gtk_text_line_char_locate(real->line, real->line_char_offset,
3060                                  &char_segment, &char_any_segment,
3061                                  &seg_char_offset, &line_char_offset);
3062
3063       if (line_char_offset != real->line_char_offset)
3064         g_error("wrong char offset was stored in iterator");
3065       
3066       if (segments_updated)
3067         {
3068           if (real->segment != char_segment)
3069             g_error("wrong segment was stored in iterator");
3070
3071           if (real->any_segment != char_any_segment)
3072             g_error("wrong any_segment was stored in iterator");
3073           
3074           if (seg_char_offset != real->segment_char_offset)
3075             g_error("wrong segment char offset was stored in iterator");
3076         }
3077     }
3078   
3079   if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
3080     {
3081       if (byte_segment != char_segment)
3082         g_error("char and byte offsets did not point to the same segment");
3083
3084       if (byte_any_segment != char_any_segment)
3085         g_error("char and byte offsets did not point to the same any segment");
3086
3087       /* Make sure the segment offsets are equivalent, if it's a char
3088          segment. */
3089       if (char_segment->type == &gtk_text_char_type)
3090         {
3091           gint byte_offset = 0;
3092           gint char_offset = 0;
3093           while (char_offset < seg_char_offset)
3094             {
3095               const char * start = char_segment->body.chars + byte_offset;
3096               byte_offset += g_utf8_next_char (start) - start;
3097               char_offset += 1;
3098             }
3099
3100           if (byte_offset != seg_byte_offset)
3101             g_error("byte offset did not correspond to char offset");
3102
3103           char_offset =
3104             g_utf8_strlen (char_segment->body.chars, seg_byte_offset);
3105
3106           if (char_offset != seg_char_offset)
3107             g_error("char offset did not correspond to byte offset");
3108         }
3109     }
3110
3111   if (real->cached_line_number >= 0)
3112     {
3113       gint should_be;
3114
3115       should_be = gtk_text_line_get_number(real->line);
3116       if (real->cached_line_number != should_be)
3117         g_error("wrong line number was cached");
3118     }
3119
3120   if (real->cached_char_index >= 0)
3121     {
3122       if (real->line_char_offset >= 0) /* only way we can check it
3123                                           efficiently, not a real
3124                                           invariant. */
3125         {
3126           gint char_index;
3127
3128           char_index = gtk_text_line_char_index(real->line);
3129           char_index += real->line_char_offset;
3130
3131           if (real->cached_char_index != char_index)
3132             g_error("wrong char index was cached");
3133         }
3134     }
3135 }
3136