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