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
gtk_text_iter_spew(const GtkTextIter * iter,const char * desc)27 gtk_text_iter_spew (const GtkTextIter *iter, const char *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
check_get_set_text(GtkTextBuffer * buffer,const char * str)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 int
count_toggles_at_iter(GtkTextIter * iter,GtkTextTag * of_tag)94 count_toggles_at_iter (GtkTextIter *iter,
95                        GtkTextTag  *of_tag)
96 {
97   GSList *tags;
98   GSList *tmp;
99   int 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 = tmp->next;
115     }
116 
117   g_slist_free (tags);
118 
119   return count;
120 }
121 
122 static int
count_toggles_in_range_by_char(GtkTextBuffer * buffer,GtkTextTag * of_tag,const GtkTextIter * start,const GtkTextIter * end)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   int 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 int
count_toggles_in_buffer(GtkTextBuffer * buffer,GtkTextTag * of_tag)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
check_specific_tag_in_range(GtkTextBuffer * buffer,const char * tag_name,const GtkTextIter * start,const GtkTextIter * end)159 check_specific_tag_in_range (GtkTextBuffer     *buffer,
160                              const char        *tag_name,
161                              const GtkTextIter *start,
162                              const GtkTextIter *end)
163 {
164   GtkTextIter iter;
165   GtkTextTag *tag;
166   gboolean state;
167   int count;
168   int buffer_count;
169   int 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           int 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_starts_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",
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           int 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_starts_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
check_specific_tag(GtkTextBuffer * buffer,const char * tag_name)272 check_specific_tag (GtkTextBuffer *buffer,
273                     const char    *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
run_tests(GtkTextBuffer * buffer)286 run_tests (GtkTextBuffer *buffer)
287 {
288   GtkTextIter iter;
289   GtkTextIter start;
290   GtkTextIter end;
291   GtkTextIter mark;
292   int i, j;
293   int num_chars;
294   GtkTextMark *bar_mark;
295   GtkTextTag *tag;
296   GHashTable *tag_states;
297   int count;
298   int 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 = tmp->next;
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 = tmp->next;
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 = tmp->next;
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 = tmp->next;
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",
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
fill_buffer(GtkTextBuffer * buffer)633 fill_buffer (GtkTextBuffer *buffer)
634 {
635   GtkTextTag *tag;
636   GdkRGBA color;
637   GdkRGBA color2;
638   GtkTextIter iter;
639   GtkTextIter iter2;
640   GdkPixbuf *pixbuf;
641   GdkTexture *texture;
642   int i;
643 
644   color.red = 0.0;
645   color.green = 0.0;
646   color.blue = 1.0;
647   color.alpha = 1.0;
648 
649   color2.red = 1.0;
650   color2.green = 0.0;
651   color2.blue = 0.0;
652   color2.alpha = 1.0;
653 
654   gtk_text_buffer_create_tag (buffer, "fg_blue",
655                               "foreground_rgba", &color,
656                               "background_rgba", &color2,
657                               "font", "-*-courier-bold-r-*-*-30-*-*-*-*-*-*-*",
658                               NULL);
659 
660   color.red = 1.0;
661   color.green = 0.0;
662   color.blue = 0.0;
663 
664   gtk_text_buffer_create_tag (buffer, "fg_red",
665                               "rise", -4,
666                               "foreground_rgba", &color,
667                               NULL);
668 
669   color.red = 0.0;
670   color.green = 1.0;
671   color.blue = 0.0;
672 
673   gtk_text_buffer_create_tag (buffer, "bg_green",
674                               "background_rgba", &color,
675                               "font", "-*-courier-bold-r-*-*-10-*-*-*-*-*-*-*",
676                               NULL);
677 
678   pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
679   texture = gdk_texture_new_for_pixbuf (pixbuf);
680 
681   g_assert_nonnull (texture);
682 
683   for (i = 0; i < 10; i++)
684     {
685       char *str;
686 
687       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
688 
689       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE (texture));
690 
691       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 1);
692 
693       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE (texture));
694 
695       str = g_strdup_printf ("%d Hello World!\nwoo woo woo woo woo woo woo woo\n",
696                             i);
697 
698       gtk_text_buffer_insert (buffer, &iter, str, -1);
699 
700       g_free (str);
701 
702       gtk_text_buffer_insert (buffer, &iter,
703                               "(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"
704                               /* This is UTF8 stuff, Emacs doesn't
705                                  really know how to display it */
706                               "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",
707                               -1);
708 
709       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE(texture));
710       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE(texture));
711 
712       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 4);
713 
714       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE(texture));
715 
716       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 7);
717 
718       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE(texture));
719 
720       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 8);
721 
722       gtk_text_buffer_insert_paintable (buffer, &iter, GDK_PAINTABLE(texture));
723 
724       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 8);
725       iter2 = iter;
726       gtk_text_iter_forward_chars (&iter2, 10);
727 
728       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
729 
730       gtk_text_iter_forward_chars (&iter, 7);
731       gtk_text_iter_forward_chars (&iter2, 10);
732 
733       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
734 
735       gtk_text_iter_forward_chars (&iter, 12);
736       gtk_text_iter_forward_chars (&iter2, 10);
737 
738       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
739 
740       gtk_text_iter_forward_chars (&iter, 10);
741       gtk_text_iter_forward_chars (&iter2, 15);
742 
743       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
744       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
745 
746       gtk_text_iter_forward_chars (&iter, 20);
747       gtk_text_iter_forward_chars (&iter2, 20);
748 
749       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
750       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
751 
752       gtk_text_iter_backward_chars (&iter, 25);
753       gtk_text_iter_forward_chars (&iter2, 5);
754 
755       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
756       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
757 
758       gtk_text_iter_forward_chars (&iter, 15);
759       gtk_text_iter_backward_chars (&iter2, 10);
760 
761       gtk_text_buffer_remove_tag_by_name (buffer, "fg_red", &iter, &iter2);
762       gtk_text_buffer_remove_tag_by_name (buffer, "fg_blue", &iter, &iter2);
763     }
764 
765   /* Put in tags that are just at the beginning, and just near the end,
766    * and just near the middle.
767    */
768   tag = gtk_text_buffer_create_tag (buffer, "front_tag", NULL);
769   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
770   gtk_text_buffer_get_iter_at_offset (buffer, &iter2, 300);
771 
772   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
773 
774   tag = gtk_text_buffer_create_tag (buffer, "end_tag", NULL);
775   gtk_text_buffer_get_end_iter (buffer, &iter2);
776   gtk_text_iter_backward_chars (&iter2, 12);
777   iter = iter2;
778   gtk_text_iter_backward_chars (&iter, 157);
779 
780   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
781 
782   tag = gtk_text_buffer_create_tag (buffer, "center_tag", NULL);
783   gtk_text_buffer_get_iter_at_offset (buffer, &iter,
784                                       gtk_text_buffer_get_char_count (buffer)/2);
785   gtk_text_iter_backward_chars (&iter, 37);
786   iter2 = iter;
787   gtk_text_iter_forward_chars (&iter2, 57);
788 
789   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
790 
791   g_object_unref (pixbuf);
792   g_object_unref (texture);
793 }
794 
795 
796 /*
797  * Line separator tests (initially to avoid regression on bugzilla #57428)
798  */
799 
800 static void
test_line_separation(const char * str,gboolean expect_next_line,gboolean expect_end_iter,int expected_line_count,int expected_line_break,int expected_next_line_start)801 test_line_separation (const char* str,
802                       gboolean    expect_next_line,
803                       gboolean    expect_end_iter,
804                       int         expected_line_count,
805                       int         expected_line_break,
806                       int         expected_next_line_start)
807 {
808   GtkTextIter iter;
809   GtkTextBuffer* buffer;
810   gboolean on_next_line;
811   gboolean on_end_iter;
812   int new_pos;
813 
814   buffer = gtk_text_buffer_new (NULL);
815 
816   gtk_text_buffer_set_text (buffer, str, -1);
817   gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
818 
819   g_assert_true (gtk_text_iter_ends_line (&iter) || gtk_text_iter_is_end (&iter));
820 
821   g_assert_cmpint (gtk_text_buffer_get_line_count (buffer), ==, expected_line_count);
822 
823   on_next_line = gtk_text_iter_forward_line (&iter);
824 
825   g_assert_cmpint (expect_next_line, ==, on_next_line);
826 
827   on_end_iter = gtk_text_iter_is_end (&iter);
828 
829   g_assert_true (on_end_iter == expect_end_iter);
830 
831   new_pos = gtk_text_iter_get_offset (&iter);
832 
833   if (on_next_line)
834     g_assert_cmpint (expected_next_line_start, ==, new_pos);
835 
836   ++expected_line_break;
837   while (expected_line_break < expected_next_line_start)
838     {
839       gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
840 
841       g_assert_false (gtk_text_iter_ends_line (&iter));
842 
843       on_next_line = gtk_text_iter_forward_line (&iter);
844 
845       g_assert_cmpint (expect_next_line, ==, on_next_line);
846 
847       new_pos = gtk_text_iter_get_offset (&iter);
848 
849       if (on_next_line)
850         g_assert_cmpint (expected_next_line_start, ==, new_pos);
851 
852       ++expected_line_break;
853     }
854 
855   /* FIXME tests for backward line */
856 
857   g_object_unref (buffer);
858 }
859 
860 /* there are cases where \r and \n should not be treated like \r\n,
861  * originally bug #337022. */
862 static void
split_r_n_separators_test(void)863 split_r_n_separators_test (void)
864 {
865   GtkTextBuffer *buffer;
866   GtkTextIter iter;
867 
868   buffer = gtk_text_buffer_new (NULL);
869 
870   gtk_text_buffer_set_text (buffer, "foo\ra\nbar\n", -1);
871 
872   /* delete 'a' so that we have
873 
874      1 foo\r
875      2 \n
876      3 bar\n
877 
878    * and both \r and \n are line separators */
879 
880   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 5);
881   gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
882 
883   g_assert_true (gtk_text_iter_ends_line (&iter));
884 
885   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
886   g_assert_true (gtk_text_iter_ends_line (&iter));
887 
888   g_object_unref (buffer);
889 }
890 
891 static void
test_line_separator(void)892 test_line_separator (void)
893 {
894   char *str;
895   char buf[7] = { '\0', };
896 
897   /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
898    * Unicode 3.0; update this if that changes.
899    */
900 #define PARAGRAPH_SEPARATOR 0x2029
901 
902   test_line_separation ("line", FALSE, TRUE, 1, 4, 4);
903   test_line_separation ("line\r\n", FALSE, TRUE, 2, 4, 6);
904   test_line_separation ("line\r", FALSE, TRUE, 2, 4, 5);
905   test_line_separation ("line\n", FALSE, TRUE, 2, 4, 5);
906   test_line_separation ("line\rqw", TRUE, FALSE, 2, 4, 5);
907   test_line_separation ("line\nqw", TRUE, FALSE, 2, 4, 5);
908   test_line_separation ("line\r\nqw", TRUE, FALSE, 2, 4, 6);
909 
910   g_unichar_to_utf8 (PARAGRAPH_SEPARATOR, buf);
911 
912   str = g_strdup_printf ("line%s", buf);
913   test_line_separation (str, FALSE, TRUE, 2, 4, 5);
914   g_free (str);
915   str = g_strdup_printf ("line%sqw", buf);
916   test_line_separation (str, TRUE, FALSE, 2, 4, 5);
917   g_free (str);
918 
919   split_r_n_separators_test ();
920 }
921 
922 static void
test_backspace(void)923 test_backspace (void)
924 {
925   GtkTextBuffer *buffer;
926   GtkTextIter iter;
927   gboolean ret;
928 
929   buffer = gtk_text_buffer_new (NULL);
930 
931   gtk_text_buffer_set_text (buffer, "foo", -1);
932   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 2);
933   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
934   g_assert_true (ret);
935   g_assert_cmpint (1, ==, gtk_text_iter_get_offset (&iter));
936   g_assert_cmpint (2, ==, gtk_text_buffer_get_char_count (buffer));
937 
938   gtk_text_buffer_set_text (buffer, "foo", -1);
939   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
940   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
941   g_assert_true (!ret);
942   g_assert_cmpint (0, ==, gtk_text_iter_get_offset (&iter));
943   g_assert_cmpint (3, ==, gtk_text_buffer_get_char_count (buffer));
944 
945   /* test bug #544724 */
946   gtk_text_buffer_set_text (buffer, "foo\r\n\r\nbar", -1);
947   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 5);
948   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
949   g_assert_true (ret);
950   g_assert_cmpint (0, ==, gtk_text_iter_get_line (&iter));
951   g_assert_cmpint (8, ==, gtk_text_buffer_get_char_count (buffer));
952 
953   /* test empty last line */
954   gtk_text_buffer_set_text (buffer, "", -1);
955   gtk_text_buffer_get_end_iter (buffer, &iter);
956   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
957   g_assert_false (ret);
958   g_assert_cmpint (0, ==, gtk_text_iter_get_offset (&iter));
959   g_assert_cmpint (0, ==, gtk_text_buffer_get_char_count (buffer));
960 
961   gtk_text_buffer_set_text (buffer, "foo\n", -1);
962   gtk_text_buffer_get_end_iter (buffer, &iter);
963   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
964   g_assert_true (ret);
965   g_assert_cmpint (3, ==, gtk_text_iter_get_offset (&iter));
966   g_assert_cmpint (3, ==, gtk_text_buffer_get_char_count (buffer));
967 
968   gtk_text_buffer_set_text (buffer, "foo\r\n", -1);
969   gtk_text_buffer_get_end_iter (buffer, &iter);
970   ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
971   g_assert_true (ret);
972   g_assert_cmpint (3, ==, gtk_text_iter_get_offset (&iter));
973   g_assert_cmpint (3, ==, gtk_text_buffer_get_char_count (buffer));
974 
975   g_object_unref (buffer);
976 }
977 
978 static void
test_logical_motion(void)979 test_logical_motion (void)
980 {
981   char *str;
982   char buf1[7] = { '\0', };
983   char buf2[7] = { '\0', };
984   char buf3[7] = { '\0', };
985   int expected[30];
986   int expected_steps;
987   int i;
988   GtkTextBuffer *buffer;
989   GtkTextIter iter;
990 
991   buffer = gtk_text_buffer_new (NULL);
992 
993 #define LEADING_JAMO 0x1111
994 #define VOWEL_JAMO 0x1167
995 #define TRAILING_JAMO 0x11B9
996 
997   g_unichar_to_utf8 (LEADING_JAMO, buf1);
998   g_unichar_to_utf8 (VOWEL_JAMO, buf2);
999   g_unichar_to_utf8 (TRAILING_JAMO, buf3);
1000 
1001   /* Build the string "abc<leading><vowel><trailing>def\r\nxyz" */
1002   str = g_strconcat ("abc", buf1, buf2, buf3, "def\r\nxyz", NULL);
1003   gtk_text_buffer_set_text (buffer, str, -1);
1004   g_free (str);
1005 
1006   /* Check cursor positions */
1007   memset (expected, 0, sizeof (expected));
1008   expected[0] = 0;    /* before 'a' */
1009   expected[1] = 1;    /* before 'b' */
1010   expected[2] = 2;    /* before 'c' */
1011   expected[3] = 3;    /* before jamo */
1012   expected[4] = 6;    /* before 'd' */
1013   expected[5] = 7;    /* before 'e' */
1014   expected[6] = 8;    /* before 'f' */
1015   expected[7] = 9;    /* before '\r' */
1016   expected[8] = 11;   /* before 'x' */
1017   expected[9] = 12;   /* before 'y' */
1018   expected[10] = 13;  /* before 'z' */
1019   expected[11] = 14;  /* after 'z' (only matters going backward) */
1020   expected_steps = 11;
1021 
1022   gtk_text_buffer_get_start_iter (buffer, &iter);
1023   i = 0;
1024   do
1025     {
1026       int pos;
1027 
1028       pos = gtk_text_iter_get_offset (&iter);
1029 
1030       if (pos != expected[i])
1031         {
1032           g_error ("Cursor position %d, expected %d",
1033                    pos, expected[i]);
1034         }
1035 
1036       ++i;
1037     }
1038   while (gtk_text_iter_forward_cursor_position (&iter));
1039 
1040   if (!gtk_text_iter_is_end (&iter))
1041     g_error ("Expected to stop at the end iterator");
1042 
1043   if (!gtk_text_iter_is_cursor_position (&iter))
1044     g_error ("Should be a cursor position before the end iterator");
1045 
1046   if (i != expected_steps)
1047     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1048 
1049   i = expected_steps;
1050   do
1051     {
1052       int pos;
1053 
1054       pos = gtk_text_iter_get_offset (&iter);
1055 
1056       if (pos != expected[i])
1057         {
1058           g_error ("Moving backward, cursor position %d, expected %d",
1059                    pos, expected[i]);
1060         }
1061 
1062       /* g_print ("%d = %d\n", pos, expected[i]); */
1063 
1064       --i;
1065     }
1066   while (gtk_text_iter_backward_cursor_position (&iter));
1067 
1068   if (i != -1)
1069     g_error ("Expected %d steps, there were actually %d", expected_steps - i, i);
1070 
1071   if (!gtk_text_iter_is_start (&iter))
1072     g_error ("Expected to stop at the start iterator");
1073 
1074 
1075   /* Check sentence boundaries */
1076 
1077   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
1078 
1079   memset (expected, 0, sizeof (expected));
1080 
1081   expected[0] = 0;    /* before first Hi */
1082   expected[1] = 3;    /* After first . */
1083   expected[2] = 7;    /* After second . */
1084   expected[3] = 12;   /* After ! */
1085   expected[4] = 16;   /* After third . */
1086   expected[5] = 20;   /* After ? */
1087 
1088   expected_steps = 6;
1089 
1090   gtk_text_buffer_get_start_iter (buffer, &iter);
1091   i = 0;
1092   do
1093     {
1094       int pos;
1095 
1096       pos = gtk_text_iter_get_offset (&iter);
1097 
1098       if (pos != expected[i])
1099         {
1100           g_error ("Sentence position %d, expected %d",
1101                    pos, expected[i]);
1102         }
1103 
1104       if (i != 0 &&
1105           !gtk_text_iter_is_end (&iter) &&
1106           !gtk_text_iter_ends_sentence (&iter))
1107         g_error ("Iterator at %d should end a sentence", pos);
1108 
1109       ++i;
1110     }
1111   while (gtk_text_iter_forward_sentence_end (&iter));
1112 
1113   if (i != expected_steps)
1114     g_error ("Expected %d steps, there were actually %d", expected_steps, i);
1115 
1116   if (!gtk_text_iter_is_end (&iter))
1117     g_error ("Expected to stop at the end iterator");
1118 
1119   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
1120 
1121   memset (expected, 0, sizeof (expected));
1122 
1123   expected[0] = 24;
1124   expected[1] = 21;
1125   expected[2] = 17;
1126   expected[3] = 13;
1127   expected[4] = 9;
1128   expected[5] = 4;
1129   expected[6] = 0;
1130 
1131   expected_steps = 7;
1132 
1133   gtk_text_buffer_get_end_iter (buffer, &iter);
1134   i = 0;
1135   do
1136     {
1137       int pos;
1138 
1139       pos = gtk_text_iter_get_offset (&iter);
1140 
1141       if (pos != expected[i])
1142         {
1143           g_error ("Sentence position %d, expected %d",
1144                    pos, expected[i]);
1145         }
1146 
1147       if (pos != 0 &&
1148           !gtk_text_iter_is_end (&iter) &&
1149           !gtk_text_iter_starts_sentence (&iter))
1150         g_error ("Iterator at %d should start a sentence", pos);
1151 
1152       ++i;
1153     }
1154   while (gtk_text_iter_backward_sentence_start (&iter));
1155 
1156   if (i != expected_steps)
1157     g_error ("Expected %d steps, there were actually %d", expected_steps, i);
1158 
1159   if (gtk_text_iter_get_offset (&iter) != 0)
1160     g_error ("Expected to stop at the start iterator");
1161 
1162   g_object_unref (buffer);
1163 }
1164 
1165 static void
test_marks(void)1166 test_marks (void)
1167 {
1168   GtkTextBuffer *buf1, *buf2;
1169   GtkTextMark *mark;
1170   GtkTextIter iter;
1171 
1172   buf1 = gtk_text_buffer_new (NULL);
1173   buf2 = gtk_text_buffer_new (NULL);
1174 
1175   gtk_text_buffer_get_start_iter (buf1, &iter);
1176   mark = gtk_text_buffer_create_mark (buf1, "foo", &iter, TRUE);
1177   g_object_ref (mark);
1178   gtk_text_mark_set_visible (mark, TRUE);
1179   gtk_text_buffer_delete_mark (buf1, mark);
1180 
1181   g_assert_true (gtk_text_mark_get_visible (mark));
1182   g_assert_true (gtk_text_mark_get_left_gravity (mark));
1183   g_assert_cmpstr ("foo", ==, gtk_text_mark_get_name (mark));
1184   g_assert_null (gtk_text_mark_get_buffer (mark));
1185   g_assert_true (gtk_text_mark_get_deleted (mark));
1186   g_assert_null (gtk_text_buffer_get_mark (buf1, "foo"));
1187 
1188   gtk_text_buffer_get_start_iter (buf2, &iter);
1189   gtk_text_buffer_add_mark (buf2, mark, &iter);
1190   gtk_text_buffer_insert (buf2, &iter, "ewfwefwefwe", -1);
1191   gtk_text_buffer_get_iter_at_mark (buf2, &iter, mark);
1192 
1193   g_assert_true (gtk_text_mark_get_visible (mark));
1194   g_assert_true (gtk_text_iter_is_start (&iter));
1195   g_assert_true (gtk_text_mark_get_left_gravity (mark));
1196   g_assert_cmpstr ("foo", ==, gtk_text_mark_get_name (mark));
1197   g_assert_true (gtk_text_mark_get_buffer (mark) == buf2);
1198   g_assert_false (gtk_text_mark_get_deleted (mark));
1199   g_assert_true (gtk_text_buffer_get_mark (buf2, "foo") == mark);
1200 
1201   gtk_text_buffer_delete_mark (buf2, mark);
1202   gtk_text_mark_set_visible (mark, FALSE);
1203   g_object_unref (mark);
1204 
1205   mark = gtk_text_mark_new ("blah", TRUE);
1206   gtk_text_buffer_get_start_iter (buf1, &iter);
1207   gtk_text_mark_set_visible (mark, TRUE);
1208   gtk_text_buffer_add_mark (buf1, mark, &iter);
1209 
1210   g_assert_true (gtk_text_mark_get_visible (mark));
1211   g_assert_true (gtk_text_mark_get_buffer (mark) == buf1);
1212   g_assert_false (gtk_text_mark_get_deleted (mark));
1213   g_assert_true (gtk_text_buffer_get_mark (buf1, "blah") == mark);
1214   g_assert_cmpstr ("blah", ==, gtk_text_mark_get_name (mark));
1215 
1216   gtk_text_mark_set_visible (mark, FALSE);
1217   gtk_text_buffer_delete_mark (buf1, mark);
1218   g_assert_false (gtk_text_mark_get_visible (mark));
1219   g_assert_null (gtk_text_buffer_get_mark (buf1, "blah"));
1220   g_assert_null (gtk_text_mark_get_buffer (mark));
1221   g_assert_true (gtk_text_mark_get_deleted (mark));
1222 
1223   gtk_text_buffer_get_start_iter (buf2, &iter);
1224   gtk_text_buffer_add_mark (buf2, mark, &iter);
1225   g_assert_true (gtk_text_mark_get_buffer (mark) == buf2);
1226   g_assert_false (gtk_text_mark_get_deleted (mark));
1227   g_assert_true (gtk_text_buffer_get_mark (buf2, "blah") == mark);
1228   g_assert_cmpstr ("blah", ==, gtk_text_mark_get_name (mark));
1229 
1230   g_object_unref (mark);
1231   g_object_unref (buf1);
1232   g_object_unref (buf2);
1233 }
1234 
1235 static void
test_utf8(void)1236 test_utf8 (void)
1237 {
1238   gunichar ch;
1239 
1240   /* Check UTF8 unknown char thing */
1241   g_assert_cmpint (GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN, ==, 3);
1242   g_assert_cmpint (g_utf8_strlen (gtk_text_unknown_char_utf8_gtk_tests_only (), 3), ==, 1);
1243   ch = g_utf8_get_char (gtk_text_unknown_char_utf8_gtk_tests_only ());
1244   g_assert_true (ch == GTK_TEXT_UNKNOWN_CHAR);
1245 }
1246 
1247 static void
test_empty_buffer(void)1248 test_empty_buffer (void)
1249 {
1250   GtkTextBuffer *buffer;
1251   int n;
1252   GtkTextIter start;
1253 
1254   buffer = gtk_text_buffer_new (NULL);
1255 
1256   /* Check that buffer starts with one empty line and zero chars */
1257   n = gtk_text_buffer_get_line_count (buffer);
1258   if (n != 1)
1259     g_error ("%d lines, expected 1", n);
1260 
1261   n = gtk_text_buffer_get_char_count (buffer);
1262   if (n != 0)
1263     g_error ("%d chars, expected 0", n);
1264 
1265   /* empty first line contains 0 chars */
1266   gtk_text_buffer_get_start_iter (buffer, &start);
1267   n = gtk_text_iter_get_chars_in_line (&start);
1268   if (n != 0)
1269     g_error ("%d chars in first line, expected 0", n);
1270   n = gtk_text_iter_get_bytes_in_line (&start);
1271   if (n != 0)
1272     g_error ("%d bytes in first line, expected 0", n);
1273 
1274   /* Run gruesome alien test suite on buffer */
1275   run_tests (buffer);
1276 
1277   g_object_unref (buffer);
1278 }
1279 
1280 static void
test_get_set(void)1281 test_get_set(void)
1282 {
1283   GtkTextBuffer *buffer;
1284 
1285   buffer = gtk_text_buffer_new (NULL);
1286 
1287   check_get_set_text (buffer, "Hello");
1288   check_get_set_text (buffer, "Hello\n");
1289   check_get_set_text (buffer, "Hello\r\n");
1290   check_get_set_text (buffer, "Hello\r");
1291   check_get_set_text (buffer, "Hello\nBar\nFoo");
1292   check_get_set_text (buffer, "Hello\nBar\nFoo\n");
1293 
1294   g_object_unref (buffer);
1295 }
1296 
1297 static void
test_fill_empty(void)1298 test_fill_empty (void)
1299 {
1300   GtkTextBuffer *buffer;
1301   int n;
1302   GtkTextIter start, end;
1303 
1304   buffer = gtk_text_buffer_new (NULL);
1305 
1306   /* Put stuff in the buffer */
1307   fill_buffer (buffer);
1308 
1309   /* Subject stuff-bloated buffer to further torment */
1310   run_tests (buffer);
1311 
1312   /* Delete all stuff from the buffer */
1313   gtk_text_buffer_get_bounds (buffer, &start, &end);
1314   gtk_text_buffer_delete (buffer, &start, &end);
1315 
1316   /* Check buffer for emptiness (note that a single
1317      empty line always remains in the buffer) */
1318   n = gtk_text_buffer_get_line_count (buffer);
1319   if (n != 1)
1320     g_error ("%d lines, expected 1", n);
1321 
1322   n = gtk_text_buffer_get_char_count (buffer);
1323   if (n != 0)
1324     g_error ("%d chars, expected 0", n);
1325 
1326   run_tests (buffer);
1327 
1328   g_object_unref (buffer);
1329 }
1330 
1331 static void
test_tag(void)1332 test_tag (void)
1333 {
1334   GtkTextBuffer *buffer;
1335   GtkTextIter start, end;
1336 
1337   buffer = gtk_text_buffer_new (NULL);
1338 
1339   fill_buffer (buffer);
1340 
1341   gtk_text_buffer_set_text (buffer, "adcdef", -1);
1342   gtk_text_buffer_get_iter_at_offset (buffer, &start, 1);
1343   gtk_text_buffer_get_iter_at_offset (buffer, &end, 3);
1344   gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &start, &end);
1345 
1346   run_tests (buffer);
1347 
1348   g_object_unref (buffer);
1349 }
1350 
1351 static void
check_buffer_contents(GtkTextBuffer * buffer,const char * contents)1352 check_buffer_contents (GtkTextBuffer *buffer,
1353                        const char    *contents)
1354 {
1355   GtkTextIter start;
1356   GtkTextIter end;
1357   char *buffer_contents;
1358 
1359   gtk_text_buffer_get_start_iter (buffer, &start);
1360   gtk_text_buffer_get_end_iter (buffer, &end);
1361   buffer_contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1362   g_assert_cmpstr (buffer_contents, ==, contents);
1363   g_free (buffer_contents);
1364 }
1365 
1366 static void
wait_for_changed(GtkTextBuffer * buffer)1367 wait_for_changed (GtkTextBuffer *buffer)
1368 {
1369   GMainLoop *loop;
1370   gulong id;
1371 
1372   loop = g_main_loop_new (NULL, FALSE);
1373   id = g_signal_connect_swapped (buffer, "changed", G_CALLBACK (g_main_loop_quit), loop);
1374   g_main_loop_run (loop);
1375   g_signal_handler_disconnect (buffer, id);
1376   g_main_loop_unref (loop);
1377 }
1378 
1379 static void
test_clipboard(void)1380 test_clipboard (void)
1381 {
1382   GdkClipboard *clipboard;
1383   GtkTextBuffer *buffer;
1384   GtkTextIter start;
1385   GtkTextIter end;
1386   GtkTextTag *tag;
1387 
1388   clipboard = gdk_display_get_clipboard (gdk_display_get_default ());
1389 
1390   buffer = gtk_text_buffer_new (NULL);
1391   gtk_text_buffer_set_text (buffer, "abcdef", -1);
1392 
1393   /* Simple cut & paste */
1394   gtk_text_buffer_get_start_iter (buffer, &start);
1395   gtk_text_buffer_get_iter_at_offset (buffer, &end, 3);
1396   gtk_text_buffer_select_range (buffer, &start, &end);
1397 
1398   gtk_text_buffer_cut_clipboard (buffer, clipboard, TRUE);
1399   check_buffer_contents (buffer, "def");
1400 
1401   gtk_text_buffer_get_end_iter (buffer, &end);
1402   gtk_text_buffer_paste_clipboard (buffer, clipboard, &end, TRUE);
1403   wait_for_changed (buffer);
1404 
1405   check_buffer_contents (buffer, "defabc");
1406 
1407   /* Simple copy & paste */
1408   gtk_text_buffer_get_iter_at_offset (buffer, &start, 3);
1409   gtk_text_buffer_get_end_iter (buffer, &end);
1410   gtk_text_buffer_select_range (buffer, &start, &end);
1411   gtk_text_buffer_copy_clipboard (buffer, clipboard);
1412 
1413   gtk_text_buffer_get_start_iter (buffer, &start);
1414   gtk_text_buffer_paste_clipboard (buffer, clipboard, &start, TRUE);
1415   wait_for_changed (buffer);
1416 
1417   check_buffer_contents (buffer, "abcdefabc");
1418 
1419   /* Replace the selection when pasting */
1420   gtk_text_buffer_set_text (buffer, "abcdef", -1);
1421 
1422   gtk_text_buffer_get_start_iter (buffer, &start);
1423   gtk_text_buffer_get_iter_at_offset (buffer, &end, 3);
1424   gtk_text_buffer_select_range (buffer, &start, &end);
1425   gtk_text_buffer_copy_clipboard (buffer, clipboard);
1426 
1427   gtk_text_buffer_get_iter_at_offset (buffer, &start, 3);
1428   gtk_text_buffer_get_end_iter (buffer, &end);
1429   gtk_text_buffer_select_range (buffer, &start, &end);
1430   gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, TRUE);
1431   wait_for_changed (buffer);
1432 
1433   check_buffer_contents (buffer, "abcabc");
1434 
1435   /* Copy & paste text with tags.
1436    * See https://bugzilla.gnome.org/show_bug.cgi?id=339539
1437    */
1438   gtk_text_buffer_set_text (buffer, "abcdef", -1);
1439 
1440   tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
1441 
1442   gtk_text_buffer_get_start_iter (buffer, &start);
1443   gtk_text_buffer_get_iter_at_offset (buffer, &end, 4);
1444   gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
1445 
1446   gtk_text_buffer_get_iter_at_offset (buffer, &start, 3);
1447   gtk_text_buffer_get_end_iter (buffer, &end);
1448   gtk_text_buffer_select_range (buffer, &start, &end);
1449   gtk_text_buffer_copy_clipboard (buffer, clipboard);
1450   gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, TRUE);
1451   wait_for_changed (buffer);
1452 
1453   check_buffer_contents (buffer, "abcdef");
1454 
1455   gtk_text_buffer_get_iter_at_offset (buffer, &start, 3);
1456   g_assert_true (gtk_text_iter_forward_to_tag_toggle (&start, tag));
1457   g_assert_cmpint (4, ==, gtk_text_iter_get_offset (&start));
1458 
1459   g_object_unref (buffer);
1460 }
1461 
1462 static void
test_get_iter(void)1463 test_get_iter (void)
1464 {
1465   GtkTextBuffer *buffer;
1466   GtkTextIter iter;
1467   int offset;
1468 
1469   buffer = gtk_text_buffer_new (NULL);
1470 
1471   /* ß takes 2 bytes in UTF-8 */
1472   gtk_text_buffer_set_text (buffer, "ab\nßd\r\nef", -1);
1473 
1474   /* Test get_iter_at_line() */
1475   g_assert_true (gtk_text_buffer_get_iter_at_line (buffer, &iter, 0));
1476   g_assert_true (gtk_text_iter_is_start (&iter));
1477 
1478   g_assert_true (gtk_text_buffer_get_iter_at_line (buffer, &iter, 1));
1479   offset = gtk_text_iter_get_offset (&iter);
1480   g_assert_cmpint (offset, ==, 3);
1481 
1482   g_assert_true (gtk_text_buffer_get_iter_at_line (buffer, &iter, 2));
1483   offset = gtk_text_iter_get_offset (&iter);
1484   g_assert_cmpint (offset, ==, 7);
1485 
1486   g_assert_false (gtk_text_buffer_get_iter_at_line (buffer, &iter, 3));
1487   g_assert_true (gtk_text_iter_is_end (&iter));
1488 
1489   /* Test get_iter_at_line_offset() */
1490   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 0));
1491   g_assert_true (gtk_text_iter_is_start (&iter));
1492 
1493   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 1));
1494   offset = gtk_text_iter_get_offset (&iter);
1495   g_assert_cmpint (offset, ==, 1);
1496 
1497   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 2));
1498   offset = gtk_text_iter_get_offset (&iter);
1499   g_assert_cmpint (offset, ==, 2);
1500 
1501   g_assert_false (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 3));
1502   offset = gtk_text_iter_get_offset (&iter);
1503   g_assert_cmpint (offset, ==, 2);
1504 
1505   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 1));
1506   offset = gtk_text_iter_get_offset (&iter);
1507   g_assert_cmpint (offset, ==, 4);
1508 
1509   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 2, 1));
1510   offset = gtk_text_iter_get_offset (&iter);
1511   g_assert_cmpint (offset, ==, 8);
1512 
1513   g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 2, 2));
1514   g_assert_true (gtk_text_iter_is_end (&iter));
1515 
1516   g_assert_false (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 2, 3));
1517   g_assert_true (gtk_text_iter_is_end (&iter));
1518 
1519   g_assert_false (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 3, 1));
1520   g_assert_true (gtk_text_iter_is_end (&iter));
1521 
1522   /* Test get_iter_at_line_index() */
1523   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 0, 0));
1524   g_assert_true (gtk_text_iter_is_start (&iter));
1525 
1526   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 0, 1));
1527   offset = gtk_text_iter_get_offset (&iter);
1528   g_assert_cmpint (offset, ==, 1);
1529 
1530   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 0, 2));
1531   offset = gtk_text_iter_get_offset (&iter);
1532   g_assert_cmpint (offset, ==, 2);
1533 
1534   g_assert_false (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 0, 3));
1535   offset = gtk_text_iter_get_offset (&iter);
1536   g_assert_cmpint (offset, ==, 2);
1537 
1538   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 1, 0));
1539   offset = gtk_text_iter_get_offset (&iter);
1540   g_assert_cmpint (offset, ==, 3);
1541 
1542   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 1, 2));
1543   offset = gtk_text_iter_get_offset (&iter);
1544   g_assert_cmpint (offset, ==, 4);
1545 
1546   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 1, 3));
1547   offset = gtk_text_iter_get_offset (&iter);
1548   g_assert_cmpint (offset, ==, 5);
1549 
1550   g_assert_true (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 2, 2));
1551   g_assert_true (gtk_text_iter_is_end (&iter));
1552 
1553   g_assert_false (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 2, 3));
1554   g_assert_true (gtk_text_iter_is_end (&iter));
1555 
1556   g_assert_false (gtk_text_buffer_get_iter_at_line_index (buffer, &iter, 3, 1));
1557   g_assert_true (gtk_text_iter_is_end (&iter));
1558 
1559   /* Test get_iter_at_offset() */
1560   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
1561   g_assert_true (gtk_text_iter_is_start (&iter));
1562 
1563   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 1);
1564   offset = gtk_text_iter_get_offset (&iter);
1565   g_assert_cmpint (offset, ==, 1);
1566 
1567   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 8);
1568   offset = gtk_text_iter_get_offset (&iter);
1569   g_assert_cmpint (offset, ==, 8);
1570   g_assert_false (gtk_text_iter_is_end (&iter));
1571 
1572   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 9);
1573   g_assert_true (gtk_text_iter_is_end (&iter));
1574 
1575   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 100);
1576   g_assert_true (gtk_text_iter_is_end (&iter));
1577 
1578   gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
1579   g_assert_true (gtk_text_iter_is_end (&iter));
1580 
1581   g_object_unref (buffer);
1582 }
1583 
1584 /* Check that basic undo works */
1585 static void
test_undo0(void)1586 test_undo0 (void)
1587 {
1588   GtkTextBuffer *buffer;
1589   const char *text;
1590 
1591   buffer = gtk_text_buffer_new (NULL);
1592 
1593   g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1594   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1595 
1596   gtk_text_buffer_set_text (buffer, "text before", -1);
1597   check_buffer_contents (buffer, "text before");
1598   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1599 
1600   text = "The quick brown fox jumps over the lazy dog.";
1601   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1602   check_buffer_contents (buffer, "text before"
1603                                  "The quick brown fox jumps over the lazy dog.");
1604   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1605 
1606   text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1607   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1608   check_buffer_contents (buffer, "text before"
1609                                  "The quick brown fox jumps over the lazy dog."
1610                                  "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1611   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1612 
1613   gtk_text_buffer_undo (buffer);
1614 
1615   check_buffer_contents (buffer, "text before"
1616                                  "The quick brown fox jumps over the lazy dog.");
1617   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1618 
1619   gtk_text_buffer_undo (buffer);
1620 
1621   check_buffer_contents (buffer, "text before");
1622   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1623 
1624   g_object_unref (buffer);
1625 }
1626 
1627 /* Check that bundling user actions works with history */
1628 static void
test_undo1(void)1629 test_undo1 (void)
1630 {
1631   GtkTextBuffer *buffer;
1632   const char *text;
1633 
1634   buffer = gtk_text_buffer_new (NULL);
1635 
1636   g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1637   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1638 
1639   gtk_text_buffer_set_text (buffer, "text before", -1);
1640   check_buffer_contents (buffer, "text before");
1641   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1642 
1643   gtk_text_buffer_begin_user_action (buffer);
1644 
1645   text = "The quick brown fox jumps over the lazy dog.";
1646 
1647   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1648   check_buffer_contents (buffer, "text before"
1649                                  "The quick brown fox jumps over the lazy dog.");
1650 
1651   text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1652 
1653   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1654   check_buffer_contents (buffer, "text before"
1655                                  "The quick brown fox jumps over the lazy dog."
1656                                  "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1657 
1658   gtk_text_buffer_end_user_action (buffer);
1659   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1660 
1661   gtk_text_buffer_undo (buffer);
1662   check_buffer_contents (buffer, "text before");
1663 
1664   g_object_unref (buffer);
1665 }
1666 
1667 /* Check that irreversible actions work */
1668 static void
test_undo2(void)1669 test_undo2 (void)
1670 {
1671   GtkTextBuffer *buffer;
1672   const char *text;
1673 
1674   buffer = gtk_text_buffer_new (NULL);
1675 
1676   g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1677 
1678   gtk_text_buffer_set_text (buffer, "text before", -1);
1679   check_buffer_contents (buffer, "text before");
1680   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1681 
1682   gtk_text_buffer_begin_irreversible_action (buffer);
1683 
1684   text = "The quick brown fox jumps over the lazy dog.";
1685 
1686   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1687   check_buffer_contents (buffer, "text before"
1688                                  "The quick brown fox jumps over the lazy dog.");
1689 
1690   text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1691 
1692   gtk_text_buffer_insert_at_cursor (buffer, text, strlen (text));
1693   check_buffer_contents (buffer, "text before"
1694                                  "The quick brown fox jumps over the lazy dog."
1695                                  "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1696 
1697   gtk_text_buffer_end_irreversible_action (buffer);
1698   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1699 
1700   g_object_unref (buffer);
1701 }
1702 
1703 /* Simulate typing, check that words get batched togethe */
1704 static void
test_undo3(void)1705 test_undo3 (void)
1706 {
1707   GtkTextBuffer *buffer;
1708   const char *str;
1709   GtkTextIter iter;
1710 
1711   buffer = gtk_text_buffer_new (NULL);
1712 
1713   str = "abc def. 122";
1714   for (int i = 0; str[i]; i++)
1715     gtk_text_buffer_insert_interactive_at_cursor (buffer, &str[i], 1, TRUE);
1716 
1717   gtk_text_buffer_get_end_iter (buffer, &iter);
1718   gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE);
1719   gtk_text_buffer_insert_interactive_at_cursor (buffer, "3", 1, TRUE);
1720 
1721   check_buffer_contents (buffer, "abc def. 123");
1722   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1723 
1724   gtk_text_buffer_undo (buffer);
1725 
1726   check_buffer_contents (buffer, "abc def. 12");
1727   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1728 
1729   gtk_text_buffer_undo (buffer);
1730 
1731   check_buffer_contents (buffer, "abc def. 122");
1732   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1733 
1734   gtk_text_buffer_undo (buffer);
1735 
1736   check_buffer_contents (buffer, "abc def.");
1737   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1738 
1739   gtk_text_buffer_undo (buffer);
1740 
1741   check_buffer_contents (buffer, "abc");
1742   g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1743 
1744   gtk_text_buffer_undo (buffer);
1745 
1746   check_buffer_contents (buffer, "");
1747   g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1748 
1749   g_object_unref (buffer);
1750 }
1751 
1752 int
main(int argc,char ** argv)1753 main (int argc, char** argv)
1754 {
1755   /* First, we turn on btree debugging. */
1756   gtk_set_debug_flags (gtk_get_debug_flags () | GTK_DEBUG_TEXT);
1757 
1758   gtk_test_init (&argc, &argv);
1759 
1760   g_test_add_func ("/TextBuffer/UTF8 unknown char", test_utf8);
1761   g_test_add_func ("/TextBuffer/Line separator", test_line_separator);
1762   g_test_add_func ("/TextBuffer/Backspace", test_backspace);
1763   g_test_add_func ("/TextBuffer/Logical motion", test_logical_motion);
1764   g_test_add_func ("/TextBuffer/Marks", test_marks);
1765   g_test_add_func ("/TextBuffer/Empty buffer", test_empty_buffer);
1766   g_test_add_func ("/TextBuffer/Get and Set", test_get_set);
1767   g_test_add_func ("/TextBuffer/Fill and Empty", test_fill_empty);
1768   g_test_add_func ("/TextBuffer/Tag", test_tag);
1769   g_test_add_func ("/TextBuffer/Clipboard", test_clipboard);
1770   g_test_add_func ("/TextBuffer/Get iter", test_get_iter);
1771   g_test_add_func ("/TextBuffer/Undo 0", test_undo0);
1772   g_test_add_func ("/TextBuffer/Undo 1", test_undo1);
1773   g_test_add_func ("/TextBuffer/Undo 2", test_undo2);
1774   g_test_add_func ("/TextBuffer/Undo 3", test_undo3);
1775 
1776   return g_test_run();
1777 }
1778