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, ¤t, i);
315
316 if (!gtk_text_iter_equal (&iter, ¤t))
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, ¤t, i);
381
382 if (!gtk_text_iter_equal (&iter, ¤t))
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