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