]> Pileus Git - ~andy/gtk/blob - gtk/tests/textbuffer.c
appchooserdialog: remove redundant checks
[~andy/gtk] / gtk / tests / textbuffer.c
1 /* testtextbuffer.c -- Simplistic test suite
2  * Copyright (C) 2000 Red Hat, Inc
3  * Author: Havoc Pennington
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20 #include <stdio.h>
21 #include <string.h>
22
23 #include <gtk/gtk.h>
24 #include "gtk/gtktexttypes.h" /* Private header, for UNKNOWN_CHAR */
25
26 static void
27 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
28 {
29   g_print (" %20s: line %d / char %d / line char %d / line byte %d\n",
30            desc,
31            gtk_text_iter_get_line (iter),
32            gtk_text_iter_get_offset (iter),
33            gtk_text_iter_get_line_offset (iter),
34            gtk_text_iter_get_line_index (iter));
35 }
36
37 static void
38 check_get_set_text (GtkTextBuffer *buffer,
39                     const char    *str)
40 {
41   GtkTextIter start, end, iter;
42   char *text;
43   int n;
44   
45   gtk_text_buffer_set_text (buffer, str, -1);
46   if (gtk_text_buffer_get_char_count (buffer) != g_utf8_strlen (str, -1))
47     g_error ("Wrong number of chars (%d not %d)",
48              gtk_text_buffer_get_char_count (buffer),
49              (int) g_utf8_strlen (str, -1));
50   gtk_text_buffer_get_bounds (buffer, &start, &end);
51   text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
52   if (strcmp (text, str) != 0)
53     g_error ("Got '%s' as buffer contents", text);
54   g_free (text);
55
56   /* line char counts */
57   iter = start;
58   n = 0;
59   do
60     {
61       n += gtk_text_iter_get_chars_in_line (&iter);
62     }
63   while (gtk_text_iter_forward_line (&iter));
64
65   if (n != gtk_text_buffer_get_char_count (buffer))
66     g_error ("Sum of chars in lines is %d but buffer char count is %d",
67              n, gtk_text_buffer_get_char_count (buffer));
68
69   /* line byte counts */
70   iter = start;
71   n = 0;
72   do
73     {
74       n += gtk_text_iter_get_bytes_in_line (&iter);
75     }
76   while (gtk_text_iter_forward_line (&iter));
77
78   if (n != strlen (str))
79     g_error ("Sum of chars in lines is %d but buffer byte count is %d",
80              n, (int) strlen (str));
81   
82   gtk_text_buffer_set_text (buffer, "", -1);
83
84   n = gtk_text_buffer_get_line_count (buffer);
85   if (n != 1)
86     g_error ("%d lines, expected 1", n);
87
88   n = gtk_text_buffer_get_char_count (buffer);
89   if (n != 0)
90     g_error ("%d chars, expected 0", n);
91 }
92
93 static gint
94 count_toggles_at_iter (GtkTextIter *iter,
95                        GtkTextTag  *of_tag)
96 {
97   GSList *tags;
98   GSList *tmp;
99   gint count = 0;
100   
101   /* get toggle-ons and toggle-offs */
102   tags = gtk_text_iter_get_toggled_tags (iter, TRUE);
103   tags = g_slist_concat (tags,
104                          gtk_text_iter_get_toggled_tags (iter, FALSE));
105   
106   tmp = tags;
107   while (tmp != NULL)
108     {
109       if (of_tag == NULL)
110         ++count;
111       else if (of_tag == tmp->data)
112         ++count;
113       
114       tmp = g_slist_next (tmp);
115     }
116   
117   g_slist_free (tags);
118
119   return count;
120 }
121
122 static gint
123 count_toggles_in_range_by_char (GtkTextBuffer     *buffer,
124                                 GtkTextTag        *of_tag,
125                                 const GtkTextIter *start,
126                                 const GtkTextIter *end)
127 {
128   GtkTextIter iter;
129   gint count = 0;
130   
131   iter = *start;
132   do
133     {
134       count += count_toggles_at_iter (&iter, of_tag);
135       if (!gtk_text_iter_forward_char (&iter))
136         {
137           /* end iterator */
138           count += count_toggles_at_iter (&iter, of_tag);
139           break;
140         }
141     }
142   while (gtk_text_iter_compare (&iter, end) <= 0);
143   
144   return count;
145 }
146
147 static gint
148 count_toggles_in_buffer (GtkTextBuffer *buffer,
149                          GtkTextTag    *of_tag)
150 {
151   GtkTextIter start, end;
152
153   gtk_text_buffer_get_bounds (buffer, &start, &end);
154
155   return count_toggles_in_range_by_char (buffer, of_tag, &start, &end);
156 }
157
158 static void
159 check_specific_tag_in_range (GtkTextBuffer     *buffer,
160                              const gchar       *tag_name,
161                              const GtkTextIter *start,
162                              const GtkTextIter *end)
163 {
164   GtkTextIter iter;
165   GtkTextTag *tag;
166   gboolean state;
167   gint count;
168   gint buffer_count;
169   gint last_offset;
170
171   if (gtk_text_iter_compare (start, end) > 0)
172     {
173       g_print ("  (inverted range for checking tags, skipping)\n");
174       return;
175     }
176   
177   tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer),
178                                    tag_name);
179
180   buffer_count = count_toggles_in_range_by_char (buffer, tag, start, end);
181   
182   state = FALSE;
183   count = 0;
184
185   last_offset = -1;
186   iter = *start;
187   if (gtk_text_iter_toggles_tag (&iter, tag) ||
188       gtk_text_iter_forward_to_tag_toggle (&iter, tag))
189     {
190       do
191         {
192           gint this_offset;
193           
194           ++count;
195
196           this_offset = gtk_text_iter_get_offset (&iter);
197
198           if (this_offset <= last_offset)
199             g_error ("forward_to_tag_toggle moved in wrong direction");
200
201           last_offset = this_offset;
202           
203           if (gtk_text_iter_begins_tag (&iter, tag))
204             {
205               if (state)
206                 g_error ("Tag %p is already on, and was toggled on?", tag);
207               state = TRUE;
208             }          
209           else if (gtk_text_iter_ends_tag (&iter, tag))
210             {
211               if (!state)
212                 g_error ("Tag %p toggled off, but wasn't toggled on?", tag);
213               state = FALSE;
214             }
215           else
216             g_error ("forward_to_tag_toggle went to a location without a toggle");
217         }
218       while (gtk_text_iter_forward_to_tag_toggle (&iter, tag) &&
219              gtk_text_iter_compare (&iter, end) <= 0);
220     }
221
222   if (count != buffer_count)
223     g_error ("Counted %d tags iterating by char, %d iterating forward by tag toggle\n",
224              buffer_count, count);
225   
226   state = FALSE;
227   count = 0;
228   
229   iter = *end;
230   last_offset = gtk_text_iter_get_offset (&iter);
231   if (gtk_text_iter_toggles_tag (&iter, tag) ||
232       gtk_text_iter_backward_to_tag_toggle (&iter, tag))
233     {
234       do
235         {
236           gint this_offset;
237           
238           ++count;
239
240           this_offset = gtk_text_iter_get_offset (&iter);
241           
242           if (this_offset >= last_offset)
243             g_error ("backward_to_tag_toggle moved in wrong direction");
244           
245           last_offset = this_offset;
246
247           if (gtk_text_iter_begins_tag (&iter, tag))
248             {
249               if (!state)
250                 g_error ("Tag %p wasn't on when we got to the on toggle going backward?", tag);
251               state = FALSE;
252             }
253           else if (gtk_text_iter_ends_tag (&iter, tag))
254             {
255               if (state)
256                 g_error ("Tag %p off toggle, but we were already inside a tag?", tag);
257               state = TRUE;
258             }
259           else
260             g_error ("backward_to_tag_toggle went to a location without a toggle");
261         }
262       while (gtk_text_iter_backward_to_tag_toggle (&iter, tag) &&
263              gtk_text_iter_compare (&iter, start) >= 0);
264     }
265
266   if (count != buffer_count)
267     g_error ("Counted %d tags iterating by char, %d iterating backward by tag toggle\n",
268              buffer_count, count);
269 }
270
271 static void
272 check_specific_tag (GtkTextBuffer *buffer,
273                     const gchar   *tag_name)
274 {
275   GtkTextIter start, end;
276
277   gtk_text_buffer_get_bounds (buffer, &start, &end);
278   check_specific_tag_in_range (buffer, tag_name, &start, &end);
279   gtk_text_iter_forward_chars (&start, 2);
280   gtk_text_iter_backward_chars (&end, 2);
281   if (gtk_text_iter_compare (&start, &end) < 0)
282     check_specific_tag_in_range (buffer, tag_name, &start, &end);
283 }
284
285 static void
286 run_tests (GtkTextBuffer *buffer)
287 {
288   GtkTextIter iter;
289   GtkTextIter start;
290   GtkTextIter end;
291   GtkTextIter mark;
292   gint i, j;
293   gint num_chars;
294   GtkTextMark *bar_mark;
295   GtkTextTag *tag;
296   GHashTable *tag_states;
297   gint count;
298   gint buffer_count;
299   
300   gtk_text_buffer_get_bounds (buffer, &start, &end);
301
302   /* Check that walking the tree via chars and via iterators produces
303    * the same number of indexable locations.
304    */
305   num_chars = gtk_text_buffer_get_char_count (buffer);
306   iter = start;
307   bar_mark = gtk_text_buffer_create_mark (buffer, "bar", &iter, FALSE);
308   i = 0;
309   while (i < num_chars)
310     {
311       GtkTextIter current;
312       GtkTextMark *foo_mark;
313
314       gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
315
316       if (!gtk_text_iter_equal (&iter, &current))
317         {
318           g_error ("get_char_index didn't return current iter");
319         }
320
321       j = gtk_text_iter_get_offset (&iter);
322
323       if (i != j)
324         {
325           g_error ("iter converted to %d not %d", j, i);
326         }
327
328       /* get/set mark */
329       gtk_text_buffer_get_iter_at_mark (buffer, &mark, bar_mark);
330
331       if (!gtk_text_iter_equal (&iter, &mark))
332         {
333           gtk_text_iter_spew (&iter, "iter");
334           gtk_text_iter_spew (&mark, "mark");
335           g_error ("Mark not moved to the right place.");
336         }
337
338       foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
339       gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
340       gtk_text_buffer_delete_mark (buffer, foo_mark);
341
342       if (!gtk_text_iter_equal (&iter, &mark))
343         {
344           gtk_text_iter_spew (&iter, "iter");
345           gtk_text_iter_spew (&mark, "mark");
346           g_error ("Mark not created in the right place.");
347         }
348
349       if (gtk_text_iter_is_end (&iter))
350         g_error ("iterators ran out before chars (offset %d of %d)",
351                  i, num_chars);
352
353       gtk_text_iter_forward_char (&iter);
354
355       gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
356
357       ++i;
358     }
359
360   if (!gtk_text_iter_equal (&iter, &end))
361     g_error ("Iterating over all chars didn't end with the end iter");
362
363   /* Do the tree-walk backward
364    */
365   num_chars = gtk_text_buffer_get_char_count (buffer);
366   gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
367
368   gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
369
370   i = num_chars;
371
372   if (!gtk_text_iter_equal (&iter, &end))
373     g_error ("iter at char -1 is not equal to the end iterator");
374
375   while (i >= 0)
376     {
377       GtkTextIter current;
378       GtkTextMark *foo_mark;
379
380       gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
381
382       if (!gtk_text_iter_equal (&iter, &current))
383         {
384           g_error ("get_char_index didn't return current iter while going backward");
385         }
386       j = gtk_text_iter_get_offset (&iter);
387
388       if (i != j)
389         {
390           g_error ("going backward, iter converted to %d not %d", j, i);
391         }
392
393       /* get/set mark */
394       gtk_text_buffer_get_iter_at_mark (buffer, &mark, bar_mark);
395
396       if (!gtk_text_iter_equal (&iter, &mark))
397         {
398           gtk_text_iter_spew (&iter, "iter");
399           gtk_text_iter_spew (&mark, "mark");
400           g_error ("Mark not moved to the right place.");
401         }
402
403       foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
404       gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
405       gtk_text_buffer_delete_mark (buffer, foo_mark);
406
407       if (!gtk_text_iter_equal (&iter, &mark))
408         {
409           gtk_text_iter_spew (&iter, "iter");
410           gtk_text_iter_spew (&mark, "mark");
411           g_error ("Mark not created in the right place.");
412         }
413
414       if (i > 0)
415         {
416           if (!gtk_text_iter_backward_char (&iter))
417             g_error ("iterators ran out before char indexes");
418
419           gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
420         }
421       else
422         {
423           if (gtk_text_iter_backward_char (&iter))
424             g_error ("went backward from 0?");
425         }
426
427       --i;
428     }
429
430   if (!gtk_text_iter_equal (&iter, &start))
431     g_error ("Iterating backward over all chars didn't end with the start iter");
432
433   /*
434    * Check that get_line_count returns the same number of lines
435    * as walking the tree by line
436    */
437   i = 1; /* include current (first) line */
438   gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
439   while (gtk_text_iter_forward_line (&iter))
440     ++i;
441
442   if (i != gtk_text_buffer_get_line_count (buffer))
443     g_error ("Counted %d lines, buffer has %d", i,
444              gtk_text_buffer_get_line_count (buffer));
445
446   /*
447    * Check that moving over tag toggles thinks about working.
448    */
449
450   buffer_count = count_toggles_in_buffer (buffer, NULL);
451   
452   tag_states = g_hash_table_new (NULL, NULL);
453   count = 0;
454   
455   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
456   if (gtk_text_iter_toggles_tag (&iter, NULL) ||
457       gtk_text_iter_forward_to_tag_toggle (&iter, NULL))
458     {
459       do
460         {
461           GSList *tags;
462           GSList *tmp;
463           gboolean found_some = FALSE;
464           
465           /* get toggled-on tags */
466           tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
467
468           if (tags)
469             found_some = TRUE;
470           
471           tmp = tags;
472           while (tmp != NULL)
473             {
474               ++count;
475               
476               tag = tmp->data;
477               
478               if (g_hash_table_lookup (tag_states, tag))
479                 g_error ("Tag %p is already on, and was toggled on?", tag);
480
481               g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
482           
483               tmp = g_slist_next (tmp);
484             }
485
486           g_slist_free (tags);
487       
488           /* get toggled-off tags */
489           tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
490
491           if (tags)
492             found_some = TRUE;
493           
494           tmp = tags;
495           while (tmp != NULL)
496             {
497               ++count;
498               
499               tag = tmp->data;
500
501               if (!g_hash_table_lookup (tag_states, tag))
502                 g_error ("Tag %p is already off, and was toggled off?", tag);
503
504               g_hash_table_remove (tag_states, tag);
505           
506               tmp = g_slist_next (tmp);
507             }
508
509           g_slist_free (tags);
510
511           if (!found_some)
512             g_error ("No tags found going forward to tag toggle.");
513
514         }
515       while (gtk_text_iter_forward_to_tag_toggle (&iter, NULL));
516     }
517   
518   g_hash_table_destroy (tag_states);
519
520   if (count != buffer_count)
521     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
522              buffer_count, count);
523   
524   /* Go backward; here TRUE in the hash means we saw
525    * an off toggle last.
526    */
527   
528   tag_states = g_hash_table_new (NULL, NULL);
529   count = 0;
530   
531   gtk_text_buffer_get_end_iter (buffer, &iter);
532   if (gtk_text_iter_toggles_tag (&iter, NULL) ||
533       gtk_text_iter_backward_to_tag_toggle (&iter, NULL))
534     {
535       do
536         {
537           GSList *tags;
538           GSList *tmp;
539           gboolean found_some = FALSE;
540           
541           /* get toggled-off tags */
542           tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
543
544           if (tags)
545             found_some = TRUE;
546           
547           tmp = tags;
548           while (tmp != NULL)
549             {
550               ++count;
551               
552               tag = tmp->data;
553
554               if (g_hash_table_lookup (tag_states, tag))
555                 g_error ("Tag %p has two off-toggles in a row?", tag);
556           
557               g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
558           
559               tmp = g_slist_next (tmp);
560             }
561
562           g_slist_free (tags);
563       
564           /* get toggled-on tags */
565           tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
566
567           if (tags)
568             found_some = TRUE;
569           
570           tmp = tags;
571           while (tmp != NULL)
572             {
573               ++count;
574               
575               tag = tmp->data;
576
577               if (!g_hash_table_lookup (tag_states, tag))
578                 g_error ("Tag %p was toggled on, but saw no off-toggle?", tag);
579
580               g_hash_table_remove (tag_states, tag);
581           
582               tmp = g_slist_next (tmp);
583             }
584
585           g_slist_free (tags);
586
587           if (!found_some)
588             g_error ("No tags found going backward to tag toggle.");
589         }
590       while (gtk_text_iter_backward_to_tag_toggle (&iter, NULL));
591     }
592   
593   g_hash_table_destroy (tag_states);
594
595   if (count != buffer_count)
596     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
597              buffer_count, count);
598
599   check_specific_tag (buffer, "fg_red");
600   check_specific_tag (buffer, "bg_green");
601   check_specific_tag (buffer, "front_tag");
602   check_specific_tag (buffer, "center_tag");
603   check_specific_tag (buffer, "end_tag");
604 }
605
606
607 static const char  *book_closed_xpm[] = {
608 "16 16 6 1",
609 "       c None s None",
610 ".      c black",
611 "X      c red",
612 "o      c yellow",
613 "O      c #808080",
614 "#      c white",
615 "                ",
616 "       ..       ",
617 "     ..XX.      ",
618 "   ..XXXXX.     ",
619 " ..XXXXXXXX.    ",
620 ".ooXXXXXXXXX.   ",
621 "..ooXXXXXXXXX.  ",
622 ".X.ooXXXXXXXXX. ",
623 ".XX.ooXXXXXX..  ",
624 " .XX.ooXXX..#O  ",
625 "  .XX.oo..##OO. ",
626 "   .XX..##OO..  ",
627 "    .X.#OO..    ",
628 "     ..O..      ",
629 "      ..        ",
630 "                "};
631
632 static void
633 fill_buffer (GtkTextBuffer *buffer)
634 {
635   GtkTextTag *tag;
636   GdkColor color, color2;
637   GtkTextIter iter;
638   GtkTextIter iter2;
639   GdkPixbuf *pixbuf;
640   int i;
641
642   color.red = color.green = 0;
643   color.blue = 0xffff;
644   color2.red = 0xfff;
645   color2.blue = 0x0;
646   color2.green = 0;
647   
648   gtk_text_buffer_create_tag (buffer, "fg_blue",
649                               "foreground_gdk", &color,
650                               "background_gdk", &color2,
651                               "font", "-*-courier-bold-r-*-*-30-*-*-*-*-*-*-*",
652                               NULL);
653
654   color.blue = color.green = 0;
655   color.red = 0xffff;
656   
657   gtk_text_buffer_create_tag (buffer, "fg_red",
658                               "rise", -4,
659                               "foreground_gdk", &color,
660                               NULL);
661
662   color.blue = color.red = 0;
663   color.green = 0xffff;
664   
665   gtk_text_buffer_create_tag (buffer, "bg_green",
666                               "background_gdk", &color,
667                               "font", "-*-courier-bold-r-*-*-10-*-*-*-*-*-*-*",
668                               NULL);
669
670   pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
671
672   g_assert (pixbuf != NULL);
673
674   i = 0;
675   while (i < 10)
676     {
677       gchar *str;
678
679       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
680
681       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
682
683       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 1);
684
685       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
686
687       str = g_strdup_printf ("%d Hello World!\nwoo woo woo woo woo woo woo woo\n",
688                             i);
689
690       gtk_text_buffer_insert (buffer, &iter, str, -1);
691
692       g_free (str);
693
694       gtk_text_buffer_insert (buffer, &iter,
695                               "(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line\n\n"
696                               /* This is UTF8 stuff, Emacs doesn't
697                                  really know how to display it */
698                               "Spanish (Espa\303\261ol) \302\241Hola! / French (Fran\303\247ais) Bonjour, Salut / German (Deutsch S\303\274d) Gr\303\274\303\237 Gott (testing Latin-1 chars encoded in UTF8)\nThai (we can't display this, just making sure we don't crash)  (\340\270\240\340\270\262\340\270\251\340\270\262\340\271\204\340\270\227\340\270\242)  \340\270\252\340\270\247\340\270\261\340\270\252\340\270\224\340\270\265\340\270\204\340\270\243\340\270\261\340\270\232, \340\270\252\340\270\247\340\270\261\340\270\252\340\270\224\340\270\265\340\270\204\340\271\210\340\270\260\n",
699                               -1);
700
701       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
702       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
703
704       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 4);
705
706       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
707
708       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 7);
709
710       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
711
712       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 8);
713
714       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
715
716       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 8);
717       iter2 = iter;
718       gtk_text_iter_forward_chars (&iter2, 10);
719
720       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
721
722       gtk_text_iter_forward_chars (&iter, 7);
723       gtk_text_iter_forward_chars (&iter2, 10);
724
725       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
726
727       gtk_text_iter_forward_chars (&iter, 12);
728       gtk_text_iter_forward_chars (&iter2, 10);
729
730       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
731
732       gtk_text_iter_forward_chars (&iter, 10);
733       gtk_text_iter_forward_chars (&iter2, 15);
734
735       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
736       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
737
738       gtk_text_iter_forward_chars (&iter, 20);
739       gtk_text_iter_forward_chars (&iter2, 20);
740
741       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
742       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
743
744       gtk_text_iter_backward_chars (&iter, 25);
745       gtk_text_iter_forward_chars (&iter2, 5);
746
747       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
748       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
749
750       gtk_text_iter_forward_chars (&iter, 15);
751       gtk_text_iter_backward_chars (&iter2, 10);
752
753       gtk_text_buffer_remove_tag_by_name (buffer, "fg_red", &iter, &iter2);
754       gtk_text_buffer_remove_tag_by_name (buffer, "fg_blue", &iter, &iter2);
755
756       ++i;
757     }
758
759   /* Put in tags that are just at the beginning, and just near the end,
760    * and just near the middle.
761    */
762   tag = gtk_text_buffer_create_tag (buffer, "front_tag", NULL);
763   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
764   gtk_text_buffer_get_iter_at_offset (buffer, &iter2, 300);
765
766   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);  
767   
768   tag = gtk_text_buffer_create_tag (buffer, "end_tag", NULL);
769   gtk_text_buffer_get_end_iter (buffer, &iter2);
770   gtk_text_iter_backward_chars (&iter2, 12);
771   iter = iter2;
772   gtk_text_iter_backward_chars (&iter, 157);
773
774   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
775   
776   tag = gtk_text_buffer_create_tag (buffer, "center_tag", NULL);
777   gtk_text_buffer_get_iter_at_offset (buffer, &iter,
778                                       gtk_text_buffer_get_char_count (buffer)/2);
779   gtk_text_iter_backward_chars (&iter, 37);
780   iter2 = iter;
781   gtk_text_iter_forward_chars (&iter2, 57);
782
783   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);  
784
785   g_object_unref (pixbuf);
786 }
787
788
789 /*
790  * Line separator tests (initially to avoid regression on bugzilla #57428)
791  */
792
793 static void
794 test_line_separation (const char* str,
795                       gboolean    expect_next_line,
796                       gboolean    expect_end_iter,
797                       int         expected_line_count,
798                       int         expected_line_break,
799                       int         expected_next_line_start)
800 {
801   GtkTextIter iter;
802   GtkTextBuffer* buffer;
803   gboolean on_next_line;
804   gboolean on_end_iter;
805   gint new_pos;
806
807   buffer = gtk_text_buffer_new (NULL);
808
809   gtk_text_buffer_set_text (buffer, str, -1);
810   gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
811
812   g_assert (gtk_text_iter_ends_line (&iter) || gtk_text_iter_is_end (&iter));
813
814   g_assert (gtk_text_buffer_get_line_count (buffer) == expected_line_count);
815   
816   on_next_line = gtk_text_iter_forward_line (&iter);
817
818   g_assert (expect_next_line == on_next_line);
819
820   on_end_iter = gtk_text_iter_is_end (&iter);
821
822   g_assert (on_end_iter == expect_end_iter);
823   
824   new_pos = gtk_text_iter_get_offset (&iter);
825     
826   if (on_next_line)
827     g_assert (expected_next_line_start == new_pos);
828
829   ++expected_line_break;
830   while (expected_line_break < expected_next_line_start)
831     {
832       gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
833
834       g_assert (!gtk_text_iter_ends_line (&iter));
835
836       on_next_line = gtk_text_iter_forward_line (&iter);
837         
838       g_assert (expect_next_line == on_next_line);
839         
840       new_pos = gtk_text_iter_get_offset (&iter);
841         
842       if (on_next_line)
843         g_assert (expected_next_line_start == new_pos);
844         
845       ++expected_line_break;
846     }
847
848   /* FIXME tests for backward line */
849   
850   g_object_unref (buffer);
851 }
852
853 /* there are cases where \r and \n should not be treated like \r\n,
854  * originally bug #337022. */
855 static void
856 split_r_n_separators_test (void)
857 {
858   GtkTextBuffer *buffer;
859   GtkTextIter iter;
860
861   buffer = gtk_text_buffer_new (NULL);
862
863   gtk_text_buffer_set_text (buffer, "foo\ra\nbar\n", -1);
864
865   /* delete 'a' so that we have
866
867      1 foo\r
868      2 \n
869      3 bar\n
870
871    * and both \r and \n are line separators */
872
873   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 5);
874   gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
875
876   g_assert (gtk_text_iter_ends_line (&iter));
877
878   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
879   g_assert (gtk_text_iter_ends_line (&iter));
880
881   g_object_unref (buffer);
882 }
883
884 static void
885 test_line_separator (void)
886 {
887   char *str;
888   char buf[7] = { '\0', };
889
890   /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
891    * Unicode 3.0; update this if that changes.
892    */
893 #define PARAGRAPH_SEPARATOR 0x2029
894   
895   test_line_separation ("line", FALSE, TRUE, 1, 4, 4);
896   test_line_separation ("line\r\n", FALSE, TRUE, 2, 4, 6);
897   test_line_separation ("line\r", FALSE, TRUE, 2, 4, 5);
898   test_line_separation ("line\n", FALSE, TRUE, 2, 4, 5);
899   test_line_separation ("line\rqw", TRUE, FALSE, 2, 4, 5);
900   test_line_separation ("line\nqw", TRUE, FALSE, 2, 4, 5);
901   test_line_separation ("line\r\nqw", TRUE, FALSE, 2, 4, 6);
902   
903   g_unichar_to_utf8 (PARAGRAPH_SEPARATOR, buf);
904   
905   str = g_strdup_printf ("line%s", buf);
906   test_line_separation (str, FALSE, TRUE, 2, 4, 5);
907   g_free (str);
908   str = g_strdup_printf ("line%sqw", buf);
909   test_line_separation (str, TRUE, FALSE, 2, 4, 5);
910   g_free (str);
911
912   split_r_n_separators_test ();
913 }
914
915 static void
916 test_backspace (void)
917 {
918   GtkTextBuffer *buffer;
919   GtkTextIter iter;
920   gboolean ret;
921
922   buffer = gtk_text_buffer_new (NULL);
923
924   gtk_text_buffer_set_text (buffer, "foo", -1);
925   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 2);
926   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
927   g_assert (ret);
928   g_assert_cmpint (1, ==, gtk_text_iter_get_offset (&iter));
929   g_assert_cmpint (2, ==, gtk_text_buffer_get_char_count (buffer));
930
931   gtk_text_buffer_set_text (buffer, "foo", -1);
932   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
933   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
934   g_assert (!ret);
935   g_assert_cmpint (0, ==, gtk_text_iter_get_offset (&iter));
936   g_assert_cmpint (3, ==, gtk_text_buffer_get_char_count (buffer));
937
938   /* test bug #544724 */
939   gtk_text_buffer_set_text (buffer, "foo\r\n\r\nbar", -1);
940   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 5);
941   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
942   g_assert (ret);
943   g_assert_cmpint (0, ==, gtk_text_iter_get_line (&iter));
944   g_assert_cmpint (8, ==, gtk_text_buffer_get_char_count (buffer));
945
946   g_object_unref (buffer);
947 }
948
949 static void
950 test_logical_motion (void)
951 {
952   char *str;
953   char buf1[7] = { '\0', };
954   char buf2[7] = { '\0', };
955   char buf3[7] = { '\0', };
956   int expected[30];
957   int expected_steps;
958   int i;
959   GtkTextBuffer *buffer;
960   GtkTextIter iter;
961   
962   buffer = gtk_text_buffer_new (NULL);
963   
964 #define LEADING_JAMO 0x1111
965 #define VOWEL_JAMO 0x1167
966 #define TRAILING_JAMO 0x11B9
967   
968   g_unichar_to_utf8 (LEADING_JAMO, buf1);
969   g_unichar_to_utf8 (VOWEL_JAMO, buf2);
970   g_unichar_to_utf8 (TRAILING_JAMO, buf3);
971
972   /* Build the string "abc<leading><vowel><trailing>def\r\nxyz" */
973   str = g_strconcat ("abc", buf1, buf2, buf3, "def\r\nxyz", NULL);
974   gtk_text_buffer_set_text (buffer, str, -1);
975   g_free (str);
976   
977   /* Check cursor positions */
978   memset (expected, 0, sizeof (expected));
979   expected[0] = 0;    /* before 'a' */
980   expected[1] = 1;    /* before 'b' */
981   expected[2] = 2;    /* before 'c' */
982   expected[3] = 3;    /* before jamo */
983   expected[4] = 6;    /* before 'd' */
984   expected[5] = 7;    /* before 'e' */
985   expected[6] = 8;    /* before 'f' */
986   expected[7] = 9;    /* before '\r' */
987   expected[8] = 11;   /* before 'x' */
988   expected[9] = 12;   /* before 'y' */
989   expected[10] = 13;  /* before 'z' */
990   expected[11] = 14;  /* after 'z' (only matters going backward) */
991   expected_steps = 11;
992   
993   gtk_text_buffer_get_start_iter (buffer, &iter);
994   i = 0;
995   do
996     {
997       int pos;
998
999       pos = gtk_text_iter_get_offset (&iter);
1000       
1001       if (pos != expected[i])
1002         {
1003           g_error ("Cursor position %d, expected %d",
1004                    pos, expected[i]);
1005         }
1006
1007       ++i;      
1008     }
1009   while (gtk_text_iter_forward_cursor_position (&iter));
1010
1011   if (!gtk_text_iter_is_end (&iter))
1012     g_error ("Expected to stop at the end iterator\n");
1013
1014   if (!gtk_text_iter_is_cursor_position (&iter))
1015     g_error ("Should be a cursor position before the end iterator");
1016   
1017   if (i != expected_steps)
1018     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1019
1020   i = expected_steps;
1021   do
1022     {
1023       int pos;
1024
1025       pos = gtk_text_iter_get_offset (&iter);
1026       
1027       if (pos != expected[i])
1028         {
1029           g_error ("Moving backward, cursor position %d, expected %d",
1030                    pos, expected[i]);
1031         }
1032
1033       /* g_print ("%d = %d\n", pos, expected[i]); */
1034       
1035       --i;
1036     }
1037   while (gtk_text_iter_backward_cursor_position (&iter));
1038
1039   if (i != -1)
1040     g_error ("Expected %d steps, there were actually %d\n", expected_steps - i, i);
1041
1042   if (!gtk_text_iter_is_start (&iter))
1043     g_error ("Expected to stop at the start iterator\n");
1044
1045
1046   /* Check sentence boundaries */
1047   
1048   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
1049
1050   memset (expected, 0, sizeof (expected));
1051
1052   expected[0] = 0;    /* before first Hi */
1053   expected[1] = 3;    /* After first . */
1054   expected[2] = 7;    /* After second . */
1055   expected[3] = 12;   /* After ! */
1056   expected[4] = 16;   /* After third . */
1057   expected[5] = 20;   /* After ? */
1058   
1059   expected_steps = 6;
1060   
1061   gtk_text_buffer_get_start_iter (buffer, &iter);
1062   i = 0;
1063   do
1064     {
1065       int pos;
1066
1067       pos = gtk_text_iter_get_offset (&iter);
1068
1069       if (pos != expected[i])
1070         {
1071           g_error ("Sentence position %d, expected %d",
1072                    pos, expected[i]);
1073         }
1074
1075       if (i != 0 &&
1076           !gtk_text_iter_is_end (&iter) &&
1077           !gtk_text_iter_ends_sentence (&iter))
1078         g_error ("Iterator at %d should end a sentence", pos);
1079       
1080       ++i;
1081     }
1082   while (gtk_text_iter_forward_sentence_end (&iter));
1083
1084   if (i != expected_steps)
1085     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1086
1087   if (!gtk_text_iter_is_end (&iter))
1088     g_error ("Expected to stop at the end iterator\n");
1089   
1090   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
1091
1092   memset (expected, 0, sizeof (expected));
1093
1094   expected[0] = 24;
1095   expected[1] = 21;
1096   expected[2] = 17;
1097   expected[3] = 13;
1098   expected[4] = 9;
1099   expected[5] = 4;
1100   expected[6] = 0;
1101   
1102   expected_steps = 7;
1103   
1104   gtk_text_buffer_get_end_iter (buffer, &iter);
1105   i = 0;
1106   do
1107     {
1108       int pos;
1109
1110       pos = gtk_text_iter_get_offset (&iter);
1111
1112       if (pos != expected[i])
1113         {
1114           g_error ("Sentence position %d, expected %d",
1115                    pos, expected[i]);
1116         }
1117
1118       if (pos != 0 &&
1119           !gtk_text_iter_is_end (&iter) &&
1120           !gtk_text_iter_starts_sentence (&iter))
1121         g_error ("Iterator at %d should start a sentence", pos);
1122       
1123       ++i;
1124     }
1125   while (gtk_text_iter_backward_sentence_start (&iter));
1126
1127   if (i != expected_steps)
1128     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1129
1130   if (gtk_text_iter_get_offset (&iter) != 0)
1131     g_error ("Expected to stop at the start iterator\n");
1132   
1133   g_object_unref (buffer);
1134 }
1135
1136 static void
1137 test_marks (void)
1138 {
1139   GtkTextBuffer *buf1, *buf2;
1140   GtkTextMark *mark;
1141   GtkTextIter iter;
1142
1143   buf1 = gtk_text_buffer_new (NULL);
1144   buf2 = gtk_text_buffer_new (NULL);
1145
1146   gtk_text_buffer_get_start_iter (buf1, &iter);
1147   mark = gtk_text_buffer_create_mark (buf1, "foo", &iter, TRUE);
1148   g_object_ref (mark);
1149   gtk_text_mark_set_visible (mark, TRUE);
1150   gtk_text_buffer_delete_mark (buf1, mark);
1151
1152   g_assert (gtk_text_mark_get_visible (mark));
1153   g_assert (gtk_text_mark_get_left_gravity (mark));
1154   g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark)));
1155   g_assert (gtk_text_mark_get_buffer (mark) == NULL);
1156   g_assert (gtk_text_mark_get_deleted (mark));
1157   g_assert (gtk_text_buffer_get_mark (buf1, "foo") == NULL);
1158
1159   gtk_text_buffer_get_start_iter (buf2, &iter);
1160   gtk_text_buffer_add_mark (buf2, mark, &iter);
1161   gtk_text_buffer_insert (buf2, &iter, "ewfwefwefwe", -1);
1162   gtk_text_buffer_get_iter_at_mark (buf2, &iter, mark);
1163
1164   g_assert (gtk_text_mark_get_visible (mark));
1165   g_assert (gtk_text_iter_is_start (&iter));
1166   g_assert (gtk_text_mark_get_left_gravity (mark));
1167   g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark)));
1168   g_assert (gtk_text_mark_get_buffer (mark) == buf2);
1169   g_assert (!gtk_text_mark_get_deleted (mark));
1170   g_assert (gtk_text_buffer_get_mark (buf2, "foo") == mark);
1171
1172   gtk_text_buffer_delete_mark (buf2, mark);
1173   gtk_text_mark_set_visible (mark, FALSE);
1174   g_object_unref (mark);
1175
1176   mark = gtk_text_mark_new ("blah", TRUE);
1177   gtk_text_buffer_get_start_iter (buf1, &iter);
1178   gtk_text_mark_set_visible (mark, TRUE);
1179   gtk_text_buffer_add_mark (buf1, mark, &iter);
1180
1181   g_assert (gtk_text_mark_get_visible (mark));
1182   g_assert (gtk_text_mark_get_buffer (mark) == buf1);
1183   g_assert (!gtk_text_mark_get_deleted (mark));
1184   g_assert (gtk_text_buffer_get_mark (buf1, "blah") == mark);
1185   g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark)));
1186
1187   gtk_text_mark_set_visible (mark, FALSE);
1188   gtk_text_buffer_delete_mark (buf1, mark);
1189   g_assert (!gtk_text_mark_get_visible (mark));
1190   g_assert (gtk_text_buffer_get_mark (buf1, "blah") == NULL);
1191   g_assert (gtk_text_mark_get_buffer (mark) == NULL);
1192   g_assert (gtk_text_mark_get_deleted (mark));
1193
1194   gtk_text_buffer_get_start_iter (buf2, &iter);
1195   gtk_text_buffer_add_mark (buf2, mark, &iter);
1196   g_assert (gtk_text_mark_get_buffer (mark) == buf2);
1197   g_assert (!gtk_text_mark_get_deleted (mark));
1198   g_assert (gtk_text_buffer_get_mark (buf2, "blah") == mark);
1199   g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark)));
1200
1201   g_object_unref (mark);
1202   g_object_unref (buf1);
1203   g_object_unref (buf2);
1204 }
1205
1206 static void
1207 test_utf8 (void)
1208 {
1209   gunichar ch;
1210
1211   /* Check UTF8 unknown char thing */
1212   g_assert (GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN == 3);
1213   g_assert (g_utf8_strlen (gtk_text_unknown_char_utf8_gtk_tests_only (), 3) == 1);
1214   ch = g_utf8_get_char (gtk_text_unknown_char_utf8_gtk_tests_only ());
1215   g_assert (ch == GTK_TEXT_UNKNOWN_CHAR);
1216 }
1217
1218 static void
1219 test_empty_buffer (void)
1220 {
1221   GtkTextBuffer *buffer;
1222   int n;
1223   GtkTextIter start;
1224
1225   buffer = gtk_text_buffer_new (NULL);
1226
1227   /* Check that buffer starts with one empty line and zero chars */
1228   n = gtk_text_buffer_get_line_count (buffer);
1229   if (n != 1)
1230     g_error ("%d lines, expected 1", n);
1231
1232   n = gtk_text_buffer_get_char_count (buffer);
1233   if (n != 0)
1234     g_error ("%d chars, expected 0", n);
1235
1236   /* empty first line contains 0 chars */
1237   gtk_text_buffer_get_start_iter (buffer, &start);
1238   n = gtk_text_iter_get_chars_in_line (&start);
1239   if (n != 0)
1240     g_error ("%d chars in first line, expected 0", n);
1241   n = gtk_text_iter_get_bytes_in_line (&start);
1242   if (n != 0)
1243     g_error ("%d bytes in first line, expected 0", n);
1244   
1245   /* Run gruesome alien test suite on buffer */
1246   run_tests (buffer);
1247
1248   g_object_unref (buffer);
1249 }
1250
1251 static void
1252 test_get_set(void)
1253 {
1254   GtkTextBuffer *buffer;
1255
1256   buffer = gtk_text_buffer_new (NULL);
1257
1258   check_get_set_text (buffer, "Hello");
1259   check_get_set_text (buffer, "Hello\n");
1260   check_get_set_text (buffer, "Hello\r\n");
1261   check_get_set_text (buffer, "Hello\r");
1262   check_get_set_text (buffer, "Hello\nBar\nFoo");
1263   check_get_set_text (buffer, "Hello\nBar\nFoo\n");
1264
1265   g_object_unref (buffer);
1266 }
1267
1268 static void
1269 test_fill_empty (void)
1270 {
1271   GtkTextBuffer *buffer;
1272   int n;
1273   GtkTextIter start, end;
1274   
1275   buffer = gtk_text_buffer_new (NULL);
1276
1277   /* Put stuff in the buffer */
1278   fill_buffer (buffer);
1279
1280   /* Subject stuff-bloated buffer to further torment */
1281   run_tests (buffer);
1282
1283   /* Delete all stuff from the buffer */
1284   gtk_text_buffer_get_bounds (buffer, &start, &end);
1285   gtk_text_buffer_delete (buffer, &start, &end);
1286
1287   /* Check buffer for emptiness (note that a single
1288      empty line always remains in the buffer) */
1289   n = gtk_text_buffer_get_line_count (buffer);
1290   if (n != 1)
1291     g_error ("%d lines, expected 1", n);
1292
1293   n = gtk_text_buffer_get_char_count (buffer);
1294   if (n != 0)
1295     g_error ("%d chars, expected 0", n);
1296
1297   run_tests (buffer);
1298
1299   g_object_unref (buffer);
1300 }
1301
1302 static void
1303 test_tag (void)
1304 {
1305   GtkTextBuffer *buffer;
1306   GtkTextIter start, end;
1307   
1308   buffer = gtk_text_buffer_new (NULL);
1309
1310   fill_buffer (buffer);
1311
1312   gtk_text_buffer_set_text (buffer, "adcdef", -1);
1313   gtk_text_buffer_get_iter_at_offset (buffer, &start, 1);
1314   gtk_text_buffer_get_iter_at_offset (buffer, &end, 3);
1315   gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &start, &end);
1316   
1317   run_tests (buffer);
1318   
1319   g_object_unref (buffer);
1320 }
1321
1322 int
1323 main (int argc, char** argv)
1324 {
1325   /* First, we turn on btree debugging. */
1326   gtk_set_debug_flags (gtk_get_debug_flags () | GTK_DEBUG_TEXT);
1327
1328   gtk_test_init (&argc, &argv);
1329
1330   g_test_add_func ("/TextBuffer/UTF8 unknown char", test_utf8);
1331   g_test_add_func ("/TextBuffer/Line separator", test_line_separator);
1332   g_test_add_func ("/TextBuffer/Backspace", test_backspace);
1333   g_test_add_func ("/TextBuffer/Logical motion", test_logical_motion);
1334   g_test_add_func ("/TextBuffer/Marks", test_marks);
1335   g_test_add_func ("/TextBuffer/Empty buffer", test_empty_buffer);
1336   g_test_add_func ("/TextBuffer/Get and Set", test_get_set);
1337   g_test_add_func ("/TextBuffer/Fill and Empty", test_fill_empty);
1338   g_test_add_func ("/TextBuffer/Tag", test_tag);
1339   
1340   return g_test_run();
1341 }