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
26static void
27gtk_text_iter_spew (const GtkTextIter *iter, const char *desc)
28{
29 g_print (format: " %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
37static void
38check_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, text: str, len: -1);
46 if (gtk_text_buffer_get_char_count (buffer) != g_utf8_strlen (p: str, max: -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: &start, end: &end);
51 text = gtk_text_buffer_get_text (buffer, start: &start, end: &end, TRUE);
52 if (strcmp (s1: text, s2: str) != 0)
53 g_error ("Got '%s' as buffer contents", text);
54 g_free (mem: 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: &iter);
62 }
63 while (gtk_text_iter_forward_line (iter: &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: &iter);
75 }
76 while (gtk_text_iter_forward_line (iter: &iter));
77
78 if (n != strlen (s: 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, text: "", len: -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
93static int
94count_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 (list1: tags,
104 list2: 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 (list: tags);
118
119 return count;
120}
121
122static int
123count_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: &iter, of_tag);
135 if (!gtk_text_iter_forward_char (iter: &iter))
136 {
137 /* end iterator */
138 count += count_toggles_at_iter (iter: &iter, of_tag);
139 break;
140 }
141 }
142 while (gtk_text_iter_compare (lhs: &iter, rhs: end) <= 0);
143
144 return count;
145}
146
147static int
148count_toggles_in_buffer (GtkTextBuffer *buffer,
149 GtkTextTag *of_tag)
150{
151 GtkTextIter start, end;
152
153 gtk_text_buffer_get_bounds (buffer, start: &start, end: &end);
154
155 return count_toggles_in_range_by_char (buffer, of_tag, start: &start, end: &end);
156}
157
158static void
159check_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 (lhs: start, rhs: end) > 0)
172 {
173 g_print (format: " (inverted range for checking tags, skipping)\n");
174 return;
175 }
176
177 tag = gtk_text_tag_table_lookup (table: gtk_text_buffer_get_tag_table (buffer),
178 name: tag_name);
179
180 buffer_count = count_toggles_in_range_by_char (buffer, of_tag: 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: &iter, tag) ||
188 gtk_text_iter_forward_to_tag_toggle (iter: &iter, tag))
189 {
190 do
191 {
192 int this_offset;
193
194 ++count;
195
196 this_offset = gtk_text_iter_get_offset (iter: &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: &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: &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: &iter, tag) &&
219 gtk_text_iter_compare (lhs: &iter, rhs: 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: &iter);
231 if (gtk_text_iter_toggles_tag (iter: &iter, tag) ||
232 gtk_text_iter_backward_to_tag_toggle (iter: &iter, tag))
233 {
234 do
235 {
236 int this_offset;
237
238 ++count;
239
240 this_offset = gtk_text_iter_get_offset (iter: &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: &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: &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: &iter, tag) &&
263 gtk_text_iter_compare (lhs: &iter, rhs: 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
271static void
272check_specific_tag (GtkTextBuffer *buffer,
273 const char *tag_name)
274{
275 GtkTextIter start, end;
276
277 gtk_text_buffer_get_bounds (buffer, start: &start, end: &end);
278 check_specific_tag_in_range (buffer, tag_name, start: &start, end: &end);
279 gtk_text_iter_forward_chars (iter: &start, count: 2);
280 gtk_text_iter_backward_chars (iter: &end, count: 2);
281 if (gtk_text_iter_compare (lhs: &start, rhs: &end) < 0)
282 check_specific_tag_in_range (buffer, tag_name, start: &start, end: &end);
283}
284
285static void
286run_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: &start, end: &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, mark_name: "bar", where: &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, iter: &current, char_offset: i);
315
316 if (!gtk_text_iter_equal (lhs: &iter, rhs: &current))
317 {
318 g_error ("get_char_index didn't return current iter");
319 }
320
321 j = gtk_text_iter_get_offset (iter: &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, iter: &mark, mark: bar_mark);
330
331 if (!gtk_text_iter_equal (lhs: &iter, rhs: &mark))
332 {
333 gtk_text_iter_spew (iter: &iter, desc: "iter");
334 gtk_text_iter_spew (iter: &mark, desc: "mark");
335 g_error ("Mark not moved to the right place.");
336 }
337
338 foo_mark = gtk_text_buffer_create_mark (buffer, mark_name: "foo", where: &iter, FALSE);
339 gtk_text_buffer_get_iter_at_mark (buffer, iter: &mark, mark: foo_mark);
340 gtk_text_buffer_delete_mark (buffer, mark: foo_mark);
341
342 if (!gtk_text_iter_equal (lhs: &iter, rhs: &mark))
343 {
344 gtk_text_iter_spew (iter: &iter, desc: "iter");
345 gtk_text_iter_spew (iter: &mark, desc: "mark");
346 g_error ("Mark not created in the right place.");
347 }
348
349 if (gtk_text_iter_is_end (iter: &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: &iter);
354
355 gtk_text_buffer_move_mark (buffer, mark: bar_mark, where: &iter);
356
357 ++i;
358 }
359
360 if (!gtk_text_iter_equal (lhs: &iter, rhs: &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: &iter, char_offset: -1);
367
368 gtk_text_buffer_move_mark (buffer, mark: bar_mark, where: &iter);
369
370 i = num_chars;
371
372 if (!gtk_text_iter_equal (lhs: &iter, rhs: &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, iter: &current, char_offset: i);
381
382 if (!gtk_text_iter_equal (lhs: &iter, rhs: &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: &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, iter: &mark, mark: bar_mark);
395
396 if (!gtk_text_iter_equal (lhs: &iter, rhs: &mark))
397 {
398 gtk_text_iter_spew (iter: &iter, desc: "iter");
399 gtk_text_iter_spew (iter: &mark, desc: "mark");
400 g_error ("Mark not moved to the right place.");
401 }
402
403 foo_mark = gtk_text_buffer_create_mark (buffer, mark_name: "foo", where: &iter, FALSE);
404 gtk_text_buffer_get_iter_at_mark (buffer, iter: &mark, mark: foo_mark);
405 gtk_text_buffer_delete_mark (buffer, mark: foo_mark);
406
407 if (!gtk_text_iter_equal (lhs: &iter, rhs: &mark))
408 {
409 gtk_text_iter_spew (iter: &iter, desc: "iter");
410 gtk_text_iter_spew (iter: &mark, desc: "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: &iter))
417 g_error ("iterators ran out before char indexes");
418
419 gtk_text_buffer_move_mark (buffer, mark: bar_mark, where: &iter);
420 }
421 else
422 {
423 if (gtk_text_iter_backward_char (iter: &iter))
424 g_error ("went backward from 0?");
425 }
426
427 --i;
428 }
429
430 if (!gtk_text_iter_equal (lhs: &iter, rhs: &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: &iter, line_number: 0);
439 while (gtk_text_iter_forward_line (iter: &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: &iter, char_offset: 0);
456 if (gtk_text_iter_toggles_tag (iter: &iter, NULL) ||
457 gtk_text_iter_forward_to_tag_toggle (iter: &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: &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 (hash_table: tag_states, key: tag))
479 g_error ("Tag %p is already on, and was toggled on?", tag);
480
481 g_hash_table_insert (hash_table: tag_states, key: tag, GINT_TO_POINTER (TRUE));
482
483 tmp = tmp->next;
484 }
485
486 g_slist_free (list: tags);
487
488 /* get toggled-off tags */
489 tags = gtk_text_iter_get_toggled_tags (iter: &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 (hash_table: tag_states, key: tag))
502 g_error ("Tag %p is already off, and was toggled off?", tag);
503
504 g_hash_table_remove (hash_table: tag_states, key: tag);
505
506 tmp = tmp->next;
507 }
508
509 g_slist_free (list: 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: &iter, NULL));
516 }
517
518 g_hash_table_destroy (hash_table: 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: &iter);
532 if (gtk_text_iter_toggles_tag (iter: &iter, NULL) ||
533 gtk_text_iter_backward_to_tag_toggle (iter: &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: &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 (hash_table: tag_states, key: tag))
555 g_error ("Tag %p has two off-toggles in a row?", tag);
556
557 g_hash_table_insert (hash_table: tag_states, key: tag, GINT_TO_POINTER (TRUE));
558
559 tmp = tmp->next;
560 }
561
562 g_slist_free (list: tags);
563
564 /* get toggled-on tags */
565 tags = gtk_text_iter_get_toggled_tags (iter: &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 (hash_table: tag_states, key: tag))
578 g_error ("Tag %p was toggled on, but saw no off-toggle?", tag);
579
580 g_hash_table_remove (hash_table: tag_states, key: tag);
581
582 tmp = tmp->next;
583 }
584
585 g_slist_free (list: 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: &iter, NULL));
591 }
592
593 g_hash_table_destroy (hash_table: 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, tag_name: "fg_red");
600 check_specific_tag (buffer, tag_name: "bg_green");
601 check_specific_tag (buffer, tag_name: "front_tag");
602 check_specific_tag (buffer, tag_name: "center_tag");
603 check_specific_tag (buffer, tag_name: "end_tag");
604}
605
606
607static 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
632static void
633fill_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, tag_name: "fg_blue",
655 first_property_name: "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, tag_name: "fg_red",
665 first_property_name: "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, tag_name: "bg_green",
674 first_property_name: "background_rgba", &color,
675 "font", "-*-courier-bold-r-*-*-10-*-*-*-*-*-*-*",
676 NULL);
677
678 pixbuf = gdk_pixbuf_new_from_xpm_data (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: &iter, char_offset: 0);
688
689 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE (ptr: texture));
690
691 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 1);
692
693 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE (ptr: texture));
694
695 str = g_strdup_printf (format: "%d Hello World!\nwoo woo woo woo woo woo woo woo\n",
696 i);
697
698 gtk_text_buffer_insert (buffer, iter: &iter, text: str, len: -1);
699
700 g_free (mem: str);
701
702 gtk_text_buffer_insert (buffer, iter: &iter,
703 text: "(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 len: -1);
708
709 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE(ptr: texture));
710 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE(ptr: texture));
711
712 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 4);
713
714 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE(ptr: texture));
715
716 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 7);
717
718 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE(ptr: texture));
719
720 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 8);
721
722 gtk_text_buffer_insert_paintable (buffer, iter: &iter, paintable: GDK_PAINTABLE(ptr: texture));
723
724 gtk_text_buffer_get_iter_at_line_offset (buffer, iter: &iter, line_number: 0, char_offset: 8);
725 iter2 = iter;
726 gtk_text_iter_forward_chars (iter: &iter2, count: 10);
727
728 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_blue", start: &iter, end: &iter2);
729
730 gtk_text_iter_forward_chars (iter: &iter, count: 7);
731 gtk_text_iter_forward_chars (iter: &iter2, count: 10);
732
733 gtk_text_buffer_apply_tag_by_name (buffer, name: "bg_green", start: &iter, end: &iter2);
734
735 gtk_text_iter_forward_chars (iter: &iter, count: 12);
736 gtk_text_iter_forward_chars (iter: &iter2, count: 10);
737
738 gtk_text_buffer_apply_tag_by_name (buffer, name: "bg_green", start: &iter, end: &iter2);
739
740 gtk_text_iter_forward_chars (iter: &iter, count: 10);
741 gtk_text_iter_forward_chars (iter: &iter2, count: 15);
742
743 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_red", start: &iter, end: &iter2);
744 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_blue", start: &iter, end: &iter2);
745
746 gtk_text_iter_forward_chars (iter: &iter, count: 20);
747 gtk_text_iter_forward_chars (iter: &iter2, count: 20);
748
749 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_red", start: &iter, end: &iter2);
750 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_blue", start: &iter, end: &iter2);
751
752 gtk_text_iter_backward_chars (iter: &iter, count: 25);
753 gtk_text_iter_forward_chars (iter: &iter2, count: 5);
754
755 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_red", start: &iter, end: &iter2);
756 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_blue", start: &iter, end: &iter2);
757
758 gtk_text_iter_forward_chars (iter: &iter, count: 15);
759 gtk_text_iter_backward_chars (iter: &iter2, count: 10);
760
761 gtk_text_buffer_remove_tag_by_name (buffer, name: "fg_red", start: &iter, end: &iter2);
762 gtk_text_buffer_remove_tag_by_name (buffer, name: "fg_blue", start: &iter, end: &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, tag_name: "front_tag", NULL);
769 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 3);
770 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter2, char_offset: 300);
771
772 gtk_text_buffer_apply_tag (buffer, tag, start: &iter, end: &iter2);
773
774 tag = gtk_text_buffer_create_tag (buffer, tag_name: "end_tag", NULL);
775 gtk_text_buffer_get_end_iter (buffer, iter: &iter2);
776 gtk_text_iter_backward_chars (iter: &iter2, count: 12);
777 iter = iter2;
778 gtk_text_iter_backward_chars (iter: &iter, count: 157);
779
780 gtk_text_buffer_apply_tag (buffer, tag, start: &iter, end: &iter2);
781
782 tag = gtk_text_buffer_create_tag (buffer, tag_name: "center_tag", NULL);
783 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter,
784 char_offset: gtk_text_buffer_get_char_count (buffer)/2);
785 gtk_text_iter_backward_chars (iter: &iter, count: 37);
786 iter2 = iter;
787 gtk_text_iter_forward_chars (iter: &iter2, count: 57);
788
789 gtk_text_buffer_apply_tag (buffer, tag, start: &iter, end: &iter2);
790
791 g_object_unref (object: pixbuf);
792 g_object_unref (object: texture);
793}
794
795
796/*
797 * Line separator tests (initially to avoid regression on bugzilla #57428)
798 */
799
800static void
801test_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, text: str, len: -1);
817 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 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: &iter);
824
825 g_assert_cmpint (expect_next_line, ==, on_next_line);
826
827 on_end_iter = gtk_text_iter_is_end (iter: &iter);
828
829 g_assert_true (on_end_iter == expect_end_iter);
830
831 new_pos = gtk_text_iter_get_offset (iter: &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: &iter, char_offset: 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: &iter);
844
845 g_assert_cmpint (expect_next_line, ==, on_next_line);
846
847 new_pos = gtk_text_iter_get_offset (iter: &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 (object: buffer);
858}
859
860/* there are cases where \r and \n should not be treated like \r\n,
861 * originally bug #337022. */
862static void
863split_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, text: "foo\ra\nbar\n", len: -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: &iter, char_offset: 5);
881 gtk_text_buffer_backspace (buffer, iter: &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: &iter, char_offset: 3);
886 g_assert_true (gtk_text_iter_ends_line (&iter));
887
888 g_object_unref (object: buffer);
889}
890
891static void
892test_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 (str: "line", FALSE, TRUE, expected_line_count: 1, expected_line_break: 4, expected_next_line_start: 4);
903 test_line_separation (str: "line\r\n", FALSE, TRUE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 6);
904 test_line_separation (str: "line\r", FALSE, TRUE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
905 test_line_separation (str: "line\n", FALSE, TRUE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
906 test_line_separation (str: "line\rqw", TRUE, FALSE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
907 test_line_separation (str: "line\nqw", TRUE, FALSE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
908 test_line_separation (str: "line\r\nqw", TRUE, FALSE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 6);
909
910 g_unichar_to_utf8 (PARAGRAPH_SEPARATOR, outbuf: buf);
911
912 str = g_strdup_printf (format: "line%s", buf);
913 test_line_separation (str, FALSE, TRUE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
914 g_free (mem: str);
915 str = g_strdup_printf (format: "line%sqw", buf);
916 test_line_separation (str, TRUE, FALSE, expected_line_count: 2, expected_line_break: 4, expected_next_line_start: 5);
917 g_free (mem: str);
918
919 split_r_n_separators_test ();
920}
921
922static void
923test_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, text: "foo", len: -1);
932 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 2);
933 ret = gtk_text_buffer_backspace (buffer, iter: &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, text: "foo", len: -1);
939 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 0);
940 ret = gtk_text_buffer_backspace (buffer, iter: &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, text: "foo\r\n\r\nbar", len: -1);
947 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 5);
948 ret = gtk_text_buffer_backspace (buffer, iter: &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, text: "", len: -1);
955 gtk_text_buffer_get_end_iter (buffer, iter: &iter);
956 ret = gtk_text_buffer_backspace (buffer, iter: &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, text: "foo\n", len: -1);
962 gtk_text_buffer_get_end_iter (buffer, iter: &iter);
963 ret = gtk_text_buffer_backspace (buffer, iter: &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, text: "foo\r\n", len: -1);
969 gtk_text_buffer_get_end_iter (buffer, iter: &iter);
970 ret = gtk_text_buffer_backspace (buffer, iter: &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 (object: buffer);
976}
977
978static void
979test_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, outbuf: buf1);
998 g_unichar_to_utf8 (VOWEL_JAMO, outbuf: buf2);
999 g_unichar_to_utf8 (TRAILING_JAMO, outbuf: buf3);
1000
1001 /* Build the string "abc<leading><vowel><trailing>def\r\nxyz" */
1002 str = g_strconcat (string1: "abc", buf1, buf2, buf3, "def\r\nxyz", NULL);
1003 gtk_text_buffer_set_text (buffer, text: str, len: -1);
1004 g_free (mem: str);
1005
1006 /* Check cursor positions */
1007 memset (s: expected, c: 0, n: 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: &iter);
1023 i = 0;
1024 do
1025 {
1026 int pos;
1027
1028 pos = gtk_text_iter_get_offset (iter: &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: &iter));
1039
1040 if (!gtk_text_iter_is_end (iter: &iter))
1041 g_error ("Expected to stop at the end iterator");
1042
1043 if (!gtk_text_iter_is_cursor_position (iter: &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: &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: &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: &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, text: "Hi.\nHi. \nHi! Hi. Hi? Hi.", len: -1);
1078
1079 memset (s: expected, c: 0, n: 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: &iter);
1091 i = 0;
1092 do
1093 {
1094 int pos;
1095
1096 pos = gtk_text_iter_get_offset (iter: &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: &iter) &&
1106 !gtk_text_iter_ends_sentence (iter: &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: &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: &iter))
1117 g_error ("Expected to stop at the end iterator");
1118
1119 gtk_text_buffer_set_text (buffer, text: "Hi.\nHi. \nHi! Hi. Hi? Hi.", len: -1);
1120
1121 memset (s: expected, c: 0, n: 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: &iter);
1134 i = 0;
1135 do
1136 {
1137 int pos;
1138
1139 pos = gtk_text_iter_get_offset (iter: &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: &iter) &&
1149 !gtk_text_iter_starts_sentence (iter: &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: &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: &iter) != 0)
1160 g_error ("Expected to stop at the start iterator");
1161
1162 g_object_unref (object: buffer);
1163}
1164
1165static void
1166test_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 (buffer: buf1, iter: &iter);
1176 mark = gtk_text_buffer_create_mark (buffer: buf1, mark_name: "foo", where: &iter, TRUE);
1177 g_object_ref (mark);
1178 gtk_text_mark_set_visible (mark, TRUE);
1179 gtk_text_buffer_delete_mark (buffer: 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 (buffer: buf2, iter: &iter);
1189 gtk_text_buffer_add_mark (buffer: buf2, mark, where: &iter);
1190 gtk_text_buffer_insert (buffer: buf2, iter: &iter, text: "ewfwefwefwe", len: -1);
1191 gtk_text_buffer_get_iter_at_mark (buffer: buf2, iter: &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 (buffer: buf2, mark);
1202 gtk_text_mark_set_visible (mark, FALSE);
1203 g_object_unref (object: mark);
1204
1205 mark = gtk_text_mark_new (name: "blah", TRUE);
1206 gtk_text_buffer_get_start_iter (buffer: buf1, iter: &iter);
1207 gtk_text_mark_set_visible (mark, TRUE);
1208 gtk_text_buffer_add_mark (buffer: buf1, mark, where: &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 (buffer: 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 (buffer: buf2, iter: &iter);
1224 gtk_text_buffer_add_mark (buffer: buf2, mark, where: &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 (object: mark);
1231 g_object_unref (object: buf1);
1232 g_object_unref (object: buf2);
1233}
1234
1235static void
1236test_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 (p: gtk_text_unknown_char_utf8_gtk_tests_only ());
1244 g_assert_true (ch == GTK_TEXT_UNKNOWN_CHAR);
1245}
1246
1247static void
1248test_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, iter: &start);
1267 n = gtk_text_iter_get_chars_in_line (iter: &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 (iter: &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 (object: buffer);
1278}
1279
1280static void
1281test_get_set(void)
1282{
1283 GtkTextBuffer *buffer;
1284
1285 buffer = gtk_text_buffer_new (NULL);
1286
1287 check_get_set_text (buffer, str: "Hello");
1288 check_get_set_text (buffer, str: "Hello\n");
1289 check_get_set_text (buffer, str: "Hello\r\n");
1290 check_get_set_text (buffer, str: "Hello\r");
1291 check_get_set_text (buffer, str: "Hello\nBar\nFoo");
1292 check_get_set_text (buffer, str: "Hello\nBar\nFoo\n");
1293
1294 g_object_unref (object: buffer);
1295}
1296
1297static void
1298test_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: &start, end: &end);
1314 gtk_text_buffer_delete (buffer, start: &start, end: &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 (object: buffer);
1329}
1330
1331static void
1332test_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, text: "adcdef", len: -1);
1342 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: 1);
1343 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end, char_offset: 3);
1344 gtk_text_buffer_apply_tag_by_name (buffer, name: "fg_blue", start: &start, end: &end);
1345
1346 run_tests (buffer);
1347
1348 g_object_unref (object: buffer);
1349}
1350
1351static void
1352check_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, iter: &start);
1360 gtk_text_buffer_get_end_iter (buffer, iter: &end);
1361 buffer_contents = gtk_text_buffer_get_text (buffer, start: &start, end: &end, FALSE);
1362 g_assert_cmpstr (buffer_contents, ==, contents);
1363 g_free (mem: buffer_contents);
1364}
1365
1366static void
1367wait_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 (instance: buffer, handler_id: id);
1376 g_main_loop_unref (loop);
1377}
1378
1379static void
1380test_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 (display: gdk_display_get_default ());
1389
1390 buffer = gtk_text_buffer_new (NULL);
1391 gtk_text_buffer_set_text (buffer, text: "abcdef", len: -1);
1392
1393 /* Simple cut & paste */
1394 gtk_text_buffer_get_start_iter (buffer, iter: &start);
1395 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end, char_offset: 3);
1396 gtk_text_buffer_select_range (buffer, ins: &start, bound: &end);
1397
1398 gtk_text_buffer_cut_clipboard (buffer, clipboard, TRUE);
1399 check_buffer_contents (buffer, contents: "def");
1400
1401 gtk_text_buffer_get_end_iter (buffer, iter: &end);
1402 gtk_text_buffer_paste_clipboard (buffer, clipboard, override_location: &end, TRUE);
1403 wait_for_changed (buffer);
1404
1405 check_buffer_contents (buffer, contents: "defabc");
1406
1407 /* Simple copy & paste */
1408 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: 3);
1409 gtk_text_buffer_get_end_iter (buffer, iter: &end);
1410 gtk_text_buffer_select_range (buffer, ins: &start, bound: &end);
1411 gtk_text_buffer_copy_clipboard (buffer, clipboard);
1412
1413 gtk_text_buffer_get_start_iter (buffer, iter: &start);
1414 gtk_text_buffer_paste_clipboard (buffer, clipboard, override_location: &start, TRUE);
1415 wait_for_changed (buffer);
1416
1417 check_buffer_contents (buffer, contents: "abcdefabc");
1418
1419 /* Replace the selection when pasting */
1420 gtk_text_buffer_set_text (buffer, text: "abcdef", len: -1);
1421
1422 gtk_text_buffer_get_start_iter (buffer, iter: &start);
1423 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end, char_offset: 3);
1424 gtk_text_buffer_select_range (buffer, ins: &start, bound: &end);
1425 gtk_text_buffer_copy_clipboard (buffer, clipboard);
1426
1427 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: 3);
1428 gtk_text_buffer_get_end_iter (buffer, iter: &end);
1429 gtk_text_buffer_select_range (buffer, ins: &start, bound: &end);
1430 gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, TRUE);
1431 wait_for_changed (buffer);
1432
1433 check_buffer_contents (buffer, contents: "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, text: "abcdef", len: -1);
1439
1440 tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
1441
1442 gtk_text_buffer_get_start_iter (buffer, iter: &start);
1443 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end, char_offset: 4);
1444 gtk_text_buffer_apply_tag (buffer, tag, start: &start, end: &end);
1445
1446 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: 3);
1447 gtk_text_buffer_get_end_iter (buffer, iter: &end);
1448 gtk_text_buffer_select_range (buffer, ins: &start, bound: &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, contents: "abcdef");
1454
1455 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: 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 (object: buffer);
1460}
1461
1462static void
1463test_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, text: "ab\nßd\r\nef", len: -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: &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: &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: &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: &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: &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: &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: &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: &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: &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: &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: &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: &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: &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: &iter, char_offset: 0);
1561 g_assert_true (gtk_text_iter_is_start (&iter));
1562
1563 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 1);
1564 offset = gtk_text_iter_get_offset (iter: &iter);
1565 g_assert_cmpint (offset, ==, 1);
1566
1567 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 8);
1568 offset = gtk_text_iter_get_offset (iter: &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: &iter, char_offset: 9);
1573 g_assert_true (gtk_text_iter_is_end (&iter));
1574
1575 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: 100);
1576 g_assert_true (gtk_text_iter_is_end (&iter));
1577
1578 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: -1);
1579 g_assert_true (gtk_text_iter_is_end (&iter));
1580
1581 g_object_unref (object: buffer);
1582}
1583
1584static void
1585test_iter_with_anchor (void)
1586{
1587 GtkTextView *text_view;
1588 GtkTextBuffer *buffer;
1589 GtkTextIter iter;
1590 GtkTextChildAnchor *anchor;
1591 GtkWidget *child;
1592
1593 text_view = GTK_TEXT_VIEW (gtk_text_view_new ());
1594 buffer = gtk_text_view_get_buffer (text_view);
1595
1596 gtk_text_buffer_set_text (buffer, text: "ab\ncd\r\nef", len: -1);
1597 g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 1));
1598 anchor = gtk_text_buffer_create_child_anchor (buffer, iter: &iter);
1599 child = gtk_label_new (str: "text");
1600 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1601 g_object_unref (object: child);
1602
1603 gtk_text_buffer_get_iter_at_child_anchor (buffer, iter: &iter, anchor);
1604 g_assert_cmpint (gtk_text_iter_get_char (&iter), ==, GTK_TEXT_UNKNOWN_CHAR);
1605
1606 g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 1));
1607 /* ß takes 2 bytes in UTF-8 */
1608 anchor = gtk_text_child_anchor_new_with_replacement (character: "ß");
1609 gtk_text_buffer_insert_child_anchor (buffer, iter: &iter, anchor);
1610 child = gtk_label_new (str: "text");
1611 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1612
1613 gtk_text_buffer_get_iter_at_child_anchor (buffer, iter: &iter, anchor);
1614 g_assert_cmpint (gtk_text_iter_get_char (&iter), ==, 223);
1615
1616 g_object_unref (object: child);
1617 g_object_ref_sink (text_view);
1618}
1619
1620static void
1621test_get_text_with_anchor (void)
1622{
1623 GtkTextView *text_view;
1624 GtkTextBuffer *buffer;
1625 GtkTextIter iter, start, end;
1626 GtkTextChildAnchor *anchor;
1627 GtkWidget *child;
1628
1629 text_view = GTK_TEXT_VIEW (gtk_text_view_new ());
1630 buffer = gtk_text_view_get_buffer (text_view);
1631
1632 gtk_text_buffer_set_text (buffer, text: "ab\ncd\r\nef", len: -1);
1633 g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 1));
1634 anchor = gtk_text_buffer_create_child_anchor (buffer, iter: &iter);
1635 child = gtk_label_new (str: "text");
1636 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1637 g_object_unref (object: child);
1638
1639 g_assert_true (gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 1));
1640 /* ß takes 2 bytes in UTF-8 */
1641 anchor = gtk_text_child_anchor_new_with_replacement (character: "ß");
1642 gtk_text_buffer_insert_child_anchor (buffer, iter: &iter, anchor);
1643 child = gtk_label_new (str: "text");
1644 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1645
1646 gtk_text_buffer_get_bounds (buffer, start: &start, end: &end);
1647 g_assert_cmpstr (gtk_text_buffer_get_text (buffer, &start, &end, FALSE), ==, "ab\ncßd\r\nef");
1648
1649 g_object_unref (object: child);
1650 g_object_ref_sink (text_view);
1651}
1652
1653/* Check that basic undo works */
1654static void
1655test_undo0 (void)
1656{
1657 GtkTextBuffer *buffer;
1658 const char *text;
1659
1660 buffer = gtk_text_buffer_new (NULL);
1661
1662 g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1663 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1664
1665 gtk_text_buffer_set_text (buffer, text: "text before", len: -1);
1666 check_buffer_contents (buffer, contents: "text before");
1667 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1668
1669 text = "The quick brown fox jumps over the lazy dog.";
1670 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1671 check_buffer_contents (buffer, contents: "text before"
1672 "The quick brown fox jumps over the lazy dog.");
1673 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1674
1675 text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1676 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1677 check_buffer_contents (buffer, contents: "text before"
1678 "The quick brown fox jumps over the lazy dog."
1679 "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1680 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1681
1682 gtk_text_buffer_undo (buffer);
1683
1684 check_buffer_contents (buffer, contents: "text before"
1685 "The quick brown fox jumps over the lazy dog.");
1686 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1687
1688 gtk_text_buffer_undo (buffer);
1689
1690 check_buffer_contents (buffer, contents: "text before");
1691 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1692
1693 g_object_unref (object: buffer);
1694}
1695
1696/* Check that bundling user actions works with history */
1697static void
1698test_undo1 (void)
1699{
1700 GtkTextBuffer *buffer;
1701 const char *text;
1702
1703 buffer = gtk_text_buffer_new (NULL);
1704
1705 g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1706 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1707
1708 gtk_text_buffer_set_text (buffer, text: "text before", len: -1);
1709 check_buffer_contents (buffer, contents: "text before");
1710 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1711
1712 gtk_text_buffer_begin_user_action (buffer);
1713
1714 text = "The quick brown fox jumps over the lazy dog.";
1715
1716 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1717 check_buffer_contents (buffer, contents: "text before"
1718 "The quick brown fox jumps over the lazy dog.");
1719
1720 text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1721
1722 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1723 check_buffer_contents (buffer, contents: "text before"
1724 "The quick brown fox jumps over the lazy dog."
1725 "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1726
1727 gtk_text_buffer_end_user_action (buffer);
1728 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1729
1730 gtk_text_buffer_undo (buffer);
1731 check_buffer_contents (buffer, contents: "text before");
1732
1733 g_object_unref (object: buffer);
1734}
1735
1736/* Check that irreversible actions work */
1737static void
1738test_undo2 (void)
1739{
1740 GtkTextBuffer *buffer;
1741 const char *text;
1742
1743 buffer = gtk_text_buffer_new (NULL);
1744
1745 g_assert_true (gtk_text_buffer_get_enable_undo (buffer));
1746
1747 gtk_text_buffer_set_text (buffer, text: "text before", len: -1);
1748 check_buffer_contents (buffer, contents: "text before");
1749 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1750
1751 gtk_text_buffer_begin_irreversible_action (buffer);
1752
1753 text = "The quick brown fox jumps over the lazy dog.";
1754
1755 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1756 check_buffer_contents (buffer, contents: "text before"
1757 "The quick brown fox jumps over the lazy dog.");
1758
1759 text = "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)";
1760
1761 gtk_text_buffer_insert_at_cursor (buffer, text, len: strlen (s: text));
1762 check_buffer_contents (buffer, contents: "text before"
1763 "The quick brown fox jumps over the lazy dog."
1764 "Θέλει αρετή και τόλμη η ελευθερία. (Ανδρέας Κάλβος)");
1765
1766 gtk_text_buffer_end_irreversible_action (buffer);
1767 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1768
1769 g_object_unref (object: buffer);
1770}
1771
1772/* Simulate typing, check that words get batched togethe */
1773static void
1774test_undo3 (void)
1775{
1776 GtkTextBuffer *buffer;
1777 const char *str;
1778 GtkTextIter iter;
1779
1780 buffer = gtk_text_buffer_new (NULL);
1781
1782 str = "abc def. 122";
1783 for (int i = 0; str[i]; i++)
1784 gtk_text_buffer_insert_interactive_at_cursor (buffer, text: &str[i], len: 1, TRUE);
1785
1786 gtk_text_buffer_get_end_iter (buffer, iter: &iter);
1787 gtk_text_buffer_backspace (buffer, iter: &iter, TRUE, TRUE);
1788 gtk_text_buffer_insert_interactive_at_cursor (buffer, text: "3", len: 1, TRUE);
1789
1790 check_buffer_contents (buffer, contents: "abc def. 123");
1791 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1792
1793 gtk_text_buffer_undo (buffer);
1794
1795 check_buffer_contents (buffer, contents: "abc def. 12");
1796 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1797
1798 gtk_text_buffer_undo (buffer);
1799
1800 check_buffer_contents (buffer, contents: "abc def. 122");
1801 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1802
1803 gtk_text_buffer_undo (buffer);
1804
1805 check_buffer_contents (buffer, contents: "abc def.");
1806 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1807
1808 gtk_text_buffer_undo (buffer);
1809
1810 check_buffer_contents (buffer, contents: "abc");
1811 g_assert_true (gtk_text_buffer_get_can_undo (buffer));
1812
1813 gtk_text_buffer_undo (buffer);
1814
1815 check_buffer_contents (buffer, contents: "");
1816 g_assert_false (gtk_text_buffer_get_can_undo (buffer));
1817
1818 g_object_unref (object: buffer);
1819}
1820
1821int
1822main (int argc, char** argv)
1823{
1824 /* First, we turn on btree debugging. */
1825 gtk_set_debug_flags (flags: gtk_get_debug_flags () | GTK_DEBUG_TEXT);
1826
1827 gtk_test_init (argcp: &argc, argvp: &argv);
1828
1829 g_test_add_func (testpath: "/TextBuffer/UTF8 unknown char", test_func: test_utf8);
1830 g_test_add_func (testpath: "/TextBuffer/Line separator", test_func: test_line_separator);
1831 g_test_add_func (testpath: "/TextBuffer/Backspace", test_func: test_backspace);
1832 g_test_add_func (testpath: "/TextBuffer/Logical motion", test_func: test_logical_motion);
1833 g_test_add_func (testpath: "/TextBuffer/Marks", test_func: test_marks);
1834 g_test_add_func (testpath: "/TextBuffer/Empty buffer", test_func: test_empty_buffer);
1835 g_test_add_func (testpath: "/TextBuffer/Get and Set", test_func: test_get_set);
1836 g_test_add_func (testpath: "/TextBuffer/Fill and Empty", test_func: test_fill_empty);
1837 g_test_add_func (testpath: "/TextBuffer/Tag", test_func: test_tag);
1838 g_test_add_func (testpath: "/TextBuffer/Clipboard", test_func: test_clipboard);
1839 g_test_add_func (testpath: "/TextBuffer/Get iter", test_func: test_get_iter);
1840 g_test_add_func (testpath: "/TextBuffer/Iter with anchor", test_func: test_iter_with_anchor);
1841 g_test_add_func (testpath: "/TextBuffer/Get text with anchor", test_func: test_get_text_with_anchor);
1842 g_test_add_func (testpath: "/TextBuffer/Undo 0", test_func: test_undo0);
1843 g_test_add_func (testpath: "/TextBuffer/Undo 1", test_func: test_undo1);
1844 g_test_add_func (testpath: "/TextBuffer/Undo 2", test_func: test_undo2);
1845 g_test_add_func (testpath: "/TextBuffer/Undo 3", test_func: test_undo3);
1846
1847 return g_test_run();
1848}
1849

source code of gtk/testsuite/gtk/textbuffer.c