1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include <gtk/gtk.h>
23 
24 static void
test_empty_search(void)25 test_empty_search (void)
26 {
27   GtkTextBuffer *buffer;
28   GtkTextIter it, s, e;
29   gboolean res;
30 
31   buffer = gtk_text_buffer_new (NULL);
32   gtk_text_buffer_set_text (buffer, "This is some foo text", -1);
33 
34   /* search from start forward */
35   gtk_text_buffer_get_start_iter (buffer, &it);
36   res = gtk_text_iter_forward_search (&it, "", 0, &s, &e, NULL);
37   g_assert_true (res);
38   g_assert_cmpint (gtk_text_iter_get_offset (&s), ==, gtk_text_iter_get_offset (&e));
39   g_assert_cmpint (gtk_text_iter_get_offset (&s), ==, 1);
40 
41   /* search from end backward */
42   gtk_text_buffer_get_end_iter (buffer, &it);
43   res = gtk_text_iter_backward_search (&it, "", 0, &s, &e, NULL);
44   g_assert_true (res);
45   g_assert_cmpint (gtk_text_iter_get_offset (&s), ==, gtk_text_iter_get_offset (&e));
46   g_assert_cmpint (gtk_text_iter_get_offset (&s), ==, 20);
47 }
48 
49 static void
check_found_forward(const char * haystack,const char * needle,GtkTextSearchFlags flags,int expected_start,int expected_end,const char * expected_string)50 check_found_forward (const char *haystack,
51                      const char *needle,
52                      GtkTextSearchFlags flags,
53                      int expected_start,
54                      int expected_end,
55                      const char *expected_string)
56 {
57   GtkTextBuffer *buffer;
58   GtkTextIter i, s, e;
59   gboolean res;
60   char *text;
61 
62   buffer = gtk_text_buffer_new (NULL);
63 
64   gtk_text_buffer_set_text (buffer, haystack, -1);
65 
66   /* TODO: add test with limit before, after and in the middle
67      of expected start and end */
68 
69   /* search from start forward */
70   gtk_text_buffer_get_start_iter (buffer, &i);
71   res = gtk_text_iter_forward_search (&i, needle, flags, &s, &e, NULL);
72   g_assert_true (res);
73   g_assert_cmpint (expected_start, ==, gtk_text_iter_get_offset (&s));
74   g_assert_cmpint (expected_end, ==, gtk_text_iter_get_offset (&e));
75   text = gtk_text_iter_get_text (&s, &e);
76   g_assert_cmpstr (expected_string, ==, text);
77   g_free (text);
78 
79   g_object_unref (buffer);
80 }
81 
82 static void
check_found_backward(const char * haystack,const char * needle,GtkTextSearchFlags flags,int expected_start,int expected_end,const char * expected_string)83 check_found_backward (const char *haystack,
84                       const char *needle,
85                       GtkTextSearchFlags flags,
86                       int expected_start,
87                       int expected_end,
88                       const char *expected_string)
89 {
90   GtkTextBuffer *buffer;
91   GtkTextIter i, s, e;
92   gboolean res;
93   char *text;
94 
95   buffer = gtk_text_buffer_new (NULL);
96 
97   gtk_text_buffer_set_text (buffer, haystack, -1);
98 
99   /* search from end backward */
100   gtk_text_buffer_get_end_iter (buffer, &i);
101   res = gtk_text_iter_backward_search (&i, needle, flags, &s, &e, NULL);
102   g_assert_true (res);
103   g_assert_cmpint (expected_start, ==, gtk_text_iter_get_offset (&s));
104   g_assert_cmpint (expected_end, ==, gtk_text_iter_get_offset (&e));
105   text = gtk_text_iter_get_text (&s, &e);
106   g_assert_cmpstr (expected_string, ==, text);
107   g_free (text);
108 
109   g_object_unref (buffer);
110 }
111 
112 static void
check_not_found(const char * haystack,const char * needle,GtkTextSearchFlags flags)113 check_not_found (const char *haystack,
114                  const char *needle,
115                  GtkTextSearchFlags flags)
116 {
117   GtkTextBuffer *buffer;
118   GtkTextIter i, s, e;
119   gboolean res;
120 
121   buffer = gtk_text_buffer_new (NULL);
122 
123   gtk_text_buffer_set_text (buffer, haystack, -1);
124 
125   /* search from start forward */
126   gtk_text_buffer_get_start_iter (buffer, &i);
127   res = gtk_text_iter_forward_search (&i, needle, flags, &s, &e, NULL);
128   g_assert_false (res);
129 
130   /* search from end backward */
131   gtk_text_buffer_get_end_iter (buffer, &i);
132   res = gtk_text_iter_backward_search (&i, needle, flags, &s, &e, NULL);
133   g_assert_false (res);
134 
135   g_object_unref (buffer);
136 }
137 
138 static void
test_search_full_buffer(void)139 test_search_full_buffer (void)
140 {
141   check_found_forward ("foo", "foo", 0, 0, 3, "foo");
142   check_found_backward ("foo", "foo", 0, 0, 3, "foo");
143   check_found_forward ("foo", "foo", GTK_TEXT_SEARCH_CASE_INSENSITIVE, 0, 3, "foo");
144   check_found_backward ("foo", "foo", GTK_TEXT_SEARCH_CASE_INSENSITIVE, 0, 3, "foo");
145   check_found_forward ("foo", "Foo", GTK_TEXT_SEARCH_CASE_INSENSITIVE, 0, 3, "foo");
146   check_found_backward ("foo", "Foo", GTK_TEXT_SEARCH_CASE_INSENSITIVE, 0, 3, "foo");
147 }
148 
149 static void
test_search(void)150 test_search (void)
151 {
152   /* simple match */
153   check_found_forward ("This is some foo text", "foo", 0, 13, 16, "foo");
154   check_found_backward ("This is some foo text", "foo", 0, 13, 16, "foo");
155   check_not_found ("This is some foo text", "Foo", 0);
156 
157   /* different matches for forward and backward */
158   check_found_forward ("This is some foo foo text", "foo", 0, 13, 16, "foo");
159   check_found_backward ("This is some foo foo text", "foo", 0, 17, 20, "foo");
160 
161   /* new lines in the haystack */
162   check_found_forward ("This is some\nfoo text", "foo", 0, 13, 16, "foo");
163   check_found_backward ("This is some\nfoo text", "foo", 0, 13, 16, "foo");
164   check_found_forward ("This is some foo\nfoo text", "foo", 0, 13, 16, "foo");
165   check_found_backward ("This is some foo\nfoo text", "foo", 0, 17, 20, "foo");
166   check_not_found ("This is some\nfoo text", "Foo", 0);
167 
168   /* end of buffer */
169   check_found_forward ("This is some\ntext foo", "foo", 0, 18, 21, "foo");
170   check_found_backward ("This is some\ntext foo", "foo", 0, 18, 21, "foo");
171   check_not_found ("This is some\ntext foo", "Foo", 0);
172 
173   /* multiple lines in the needle */
174   check_found_forward ("This is some foo\nfoo text", "foo\nfoo", 0, 13, 20, "foo\nfoo");
175   check_found_backward ("This is some foo\nfoo text", "foo\nfoo", 0, 13, 20, "foo\nfoo");
176   check_not_found ("This is some foo\nfoo text", "Foo\nfoo", 0);
177 
178   /* check also that different composition of utf8 characters
179      (e.g. accented letters) match */
180 
181   check_found_forward ("This is some \303\200 text", "\303\200", 0, 13, 14, "\303\200");
182   check_found_forward ("This is some \303\200 text", "some \303\200", 0, 8, 14, "some \303\200");
183   check_found_forward ("This is some \303\200 text", "\303\200 text", 0, 13, 19, "\303\200 text");
184   check_found_forward ("This is some \303\200 text", "some \303\200 text", 0, 8, 19, "some \303\200 text");
185   check_found_backward ("This is some \303\240 text", "\303\240", 0, 13, 14, "\303\240");
186   check_found_backward ("This is some \303\240 text", "some \303\240", 0, 8, 14, "some \303\240");
187   check_found_backward ("This is some \303\240 text", "\303\240 text", 0, 13, 19, "\303\240 text");
188   check_found_backward ("This is some \303\240 text", "some \303\240 text", 0, 8, 19, "some \303\240 text");
189 
190   /* multi-byte characters outside the needle */
191   check_found_forward ("\303\200 aa", "aa", 0, 2, 4, "aa");
192   check_found_forward ("aa \303\200", "aa", 0, 0, 2, "aa");
193   check_found_backward ("\303\200 aa", "aa", 0, 2, 4, "aa");
194   check_found_backward ("aa \303\200", "aa", 0, 0, 2, "aa");
195 }
196 
197 static void
test_search_caseless(void)198 test_search_caseless (void)
199 {
200   GtkTextSearchFlags flags;
201 
202   flags = GTK_TEXT_SEARCH_CASE_INSENSITIVE;
203 
204   /* simple match */
205   check_found_forward ("This is some foo text", "foo", flags, 13, 16, "foo");
206   check_found_forward ("This is some foo text", "Foo", flags, 13, 16, "foo");
207   check_found_forward ("This is some Foo text", "foo", flags, 13, 16, "Foo");
208   check_found_backward ("This is some foo text", "foo", flags, 13, 16, "foo");
209   check_found_backward ("This is some foo text", "Foo", flags, 13, 16, "foo");
210   check_found_backward ("This is some Foo text", "foo", flags, 13, 16, "Foo");
211 
212   /* check also that different composition of utf8 characters
213      (e.g. accented letters) match */
214 
215   /* different matches for forward and backward */
216   check_found_forward ("This is some foo foo text", "foo", flags, 13, 16, "foo");
217   check_found_forward ("This is some foo foo text", "Foo", flags, 13, 16, "foo");
218   check_found_forward ("This is some Foo foo text", "foo", flags, 13, 16, "Foo");
219   check_found_forward ("This is some \303\200 \303\240 text", "\303\240", flags, 13, 14, "\303\200");
220   check_found_forward ("This is some \303\200 \303\240 text", "\303\200", flags, 13, 14, "\303\200");
221   check_found_forward ("This is some \303\200 \303\240 text", "a\314\200", flags, 13, 14, "\303\200");
222   check_found_backward ("This is some foo foo text", "foo", flags, 17, 20, "foo");
223   check_found_backward ("This is some foo foo text", "Foo", flags, 17, 20, "foo");
224   check_found_backward ("This is some foo Foo text", "foo", flags, 17, 20, "Foo");
225   check_found_backward ("This is some \303\200 \303\240 text", "\303\240", flags, 15, 16, "\303\240");
226   check_found_backward ("This is some \303\200 \303\240 text", "\303\200", flags, 15, 16, "\303\240");
227   check_found_backward ("This is some \303\200 \303\240 text", "a\314\200", flags, 15, 16, "\303\240");
228 
229   /* new lines in the haystack */
230   check_found_forward ("This is some\nfoo text", "foo", flags, 13, 16, "foo");
231   check_found_forward ("This is some\nfoo text", "Foo", flags, 13, 16, "foo");
232   check_found_forward ("This is some\nFoo text", "foo", flags, 13, 16, "Foo");
233   check_found_forward ("This is some\n\303\200 text", "\303\240", flags, 13, 14, "\303\200");
234   check_found_forward ("This is some\n\303\200 text", "a\314\200", flags, 13, 14, "\303\200");
235   check_found_backward ("This is some\nfoo text", "foo", flags, 13, 16, "foo");
236   check_found_backward ("This is some\nfoo text", "Foo", flags, 13, 16, "foo");
237   check_found_backward ("This is some\nFoo text", "foo", flags, 13, 16, "Foo");
238   check_found_backward ("This is some\n\303\200 text", "\303\240", flags, 13, 14, "\303\200");
239   check_found_backward ("This is some\n\303\200 text", "a\314\200", flags, 13, 14, "\303\200");
240   check_found_forward ("This is some foo\nfoo text", "foo", flags, 13, 16, "foo");
241   check_found_forward ("This is some foo\nfoo text", "Foo", flags, 13, 16, "foo");
242   check_found_forward ("This is some Foo\nfoo text", "foo", flags, 13, 16, "Foo");
243   check_found_forward ("This is some \303\200\n\303\200 text", "\303\240", flags, 13, 14, "\303\200");
244   check_found_forward ("This is some \303\200\n\303\200 text", "a\314\200", flags, 13, 14, "\303\200");
245   check_found_backward ("This is some foo\nfoo text", "foo", flags, 17, 20, "foo");
246   check_found_backward ("This is some foo\nfoo text", "Foo", flags, 17, 20, "foo");
247   check_found_backward ("This is some foo\nFoo text", "foo", flags, 17, 20, "Foo");
248   check_found_backward ("This is some \303\200\n\303\200 text", "\303\240", flags, 15, 16, "\303\200");
249   check_found_backward ("This is some \303\200\n\303\200 text", "a\314\200", flags, 15, 16, "\303\200");
250 
251   /* end of buffer */
252   check_found_forward ("This is some\ntext foo", "foo", flags, 18, 21, "foo");
253   check_found_forward ("This is some\ntext foo", "Foo", flags, 18, 21, "foo");
254   check_found_forward ("This is some\ntext Foo", "foo", flags, 18, 21, "Foo");
255   check_found_forward ("This is some\ntext \303\200", "\303\240", flags, 18, 19, "\303\200");
256   check_found_forward ("This is some\ntext \303\200", "a\314\200", flags, 18, 19, "\303\200");
257   check_found_backward ("This is some\ntext foo", "foo", flags, 18, 21, "foo");
258   check_found_backward ("This is some\ntext foo", "Foo", flags, 18, 21, "foo");
259   check_found_backward ("This is some\ntext Foo", "foo", flags, 18, 21, "Foo");
260   check_found_backward ("This is some\ntext \303\200", "\303\240", flags, 18, 19, "\303\200");
261   check_found_backward ("This is some\ntext \303\200", "a\314\200", flags, 18, 19, "\303\200");
262 
263   /* multiple lines in the needle */
264   check_found_forward ("This is some foo\nfoo text", "foo\nfoo", flags, 13, 20, "foo\nfoo");
265   check_found_forward ("This is some foo\nfoo text", "Foo\nFoo", flags, 13, 20, "foo\nfoo");
266   check_found_forward ("This is some Foo\nFoo text", "foo\nfoo", flags, 13, 20, "Foo\nFoo");
267   check_found_forward ("This is some \303\200\n\303\200 text", "\303\240\n\303\240", flags, 13, 16, "\303\200\n\303\200");
268   check_found_forward ("This is some \303\200\n\303\200 text", "a\314\200\na\314\200", flags, 13, 16, "\303\200\n\303\200");
269   check_found_backward ("This is some foo\nfoo text", "foo\nfoo", flags, 13, 20, "foo\nfoo");
270   check_found_backward ("This is some foo\nfoo text", "Foo\nFoo", flags, 13, 20, "foo\nfoo");
271   check_found_backward ("This is some Foo\nFoo text", "foo\nfoo", flags, 13, 20, "Foo\nFoo");
272   check_found_backward ("This is some \303\200\n\303\200 text", "\303\240\n\303\240", flags, 13, 16, "\303\200\n\303\200");
273   check_found_backward ("This is some \303\200\n\303\200 text", "a\314\200\na\314\200", flags, 13, 16, "\303\200\n\303\200");
274 
275   /* multi-byte characters outside the needle */
276   check_found_forward ("\303\200 aa", "aa", flags, 2, 4, "aa");
277   check_found_forward ("aa \303\200", "aa", flags, 0, 2, "aa");
278   check_found_backward ("\303\200 aa", "aa", flags, 2, 4, "aa");
279   check_found_backward ("aa \303\200", "aa", flags, 0, 2, "aa");
280 }
281 
282 static void
test_forward_to_tag_toggle(void)283 test_forward_to_tag_toggle (void)
284 {
285   GtkTextBuffer *buffer;
286   GtkTextTag *bold_tag;
287   GtkTextTag *editable_tag;
288   GtkTextIter iter;
289   int offset;
290   gboolean ret;
291 
292   buffer = gtk_text_buffer_new (NULL);
293 
294   bold_tag = gtk_text_buffer_create_tag (buffer, "bold",
295                                          "weight", PANGO_WEIGHT_BOLD,
296                                          NULL);
297 
298   editable_tag = gtk_text_buffer_create_tag (buffer, "not-editable",
299                                              "editable", FALSE,
300                                              NULL);
301 
302   gtk_text_buffer_get_start_iter (buffer, &iter);
303 
304   gtk_text_buffer_insert (buffer, &iter, "a", -1);
305   gtk_text_buffer_insert_with_tags (buffer, &iter, "b", -1, bold_tag, NULL);
306   gtk_text_buffer_insert_with_tags (buffer, &iter, "c", -1, editable_tag, NULL);
307 
308   /* Go to the first "on" toggle */
309   gtk_text_buffer_get_start_iter (buffer, &iter);
310   ret = gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
311   g_assert_true (ret);
312   offset = gtk_text_iter_get_offset (&iter);
313   g_assert_cmpint (offset, ==, 1);
314 
315   /* Go to the last "off" toggle for the bold tag */
316   ret = gtk_text_iter_forward_to_tag_toggle (&iter, bold_tag);
317   g_assert_true (ret);
318   offset = gtk_text_iter_get_offset (&iter);
319   g_assert_cmpint (offset, ==, 2);
320 
321   ret = gtk_text_iter_forward_to_tag_toggle (&iter, bold_tag);
322   g_assert_false (ret);
323 
324   /* Go to the first "on" toggle for the editable tag */
325   gtk_text_buffer_get_start_iter (buffer, &iter);
326   ret = gtk_text_iter_forward_to_tag_toggle (&iter, editable_tag);
327   g_assert_true (ret);
328   offset = gtk_text_iter_get_offset (&iter);
329   g_assert_cmpint (offset, ==, 2);
330 
331   /* Test with the end iter */
332   gtk_text_buffer_get_end_iter (buffer, &iter);
333   ret = gtk_text_iter_forward_to_tag_toggle (&iter, editable_tag);
334   g_assert_false (ret);
335 
336   g_object_unref (buffer);
337 }
338 
339 static void
check_forward_line_end(const char * buffer_text,int initial_offset,int result_offset,gboolean ret)340 check_forward_line_end (const char *buffer_text,
341                         int          initial_offset,
342                         int          result_offset,
343                         gboolean     ret)
344 {
345   GtkTextBuffer *buffer;
346   GtkTextIter iter;
347 
348   buffer = gtk_text_buffer_new (NULL);
349   gtk_text_buffer_set_text (buffer, buffer_text, -1);
350 
351   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
352 
353   g_assert_cmpint (ret, ==, gtk_text_iter_forward_to_line_end (&iter));
354   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
355 
356   g_object_unref (buffer);
357 }
358 
359 static void
test_forward_to_line_end(void)360 test_forward_to_line_end (void)
361 {
362   check_forward_line_end("a", 0, 1, FALSE);
363   check_forward_line_end("a\n", 0, 1, TRUE);
364   check_forward_line_end("a\r\n", 0, 1, TRUE);
365   check_forward_line_end("a\na\n", 1, 3, TRUE);
366   check_forward_line_end("a\na\n\n", 1, 3, TRUE);
367   check_forward_line_end("a\r\na\n", 1, 4, TRUE);
368   check_forward_line_end("a\r\na\r\n\r\n", 1, 4, TRUE);
369 }
370 
371 static void
check_word_boundaries(const char * buffer_text,int offset,gboolean starts_word,gboolean ends_word,gboolean inside_word)372 check_word_boundaries (const char *buffer_text,
373                        int          offset,
374                        gboolean     starts_word,
375                        gboolean     ends_word,
376                        gboolean     inside_word)
377 {
378   GtkTextBuffer *buffer;
379   GtkTextIter iter;
380 
381   buffer = gtk_text_buffer_new (NULL);
382   gtk_text_buffer_set_text (buffer, buffer_text, -1);
383 
384   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
385 
386   g_assert_cmpint (starts_word, ==, gtk_text_iter_starts_word (&iter));
387   g_assert_cmpint (ends_word, ==, gtk_text_iter_ends_word (&iter));
388   g_assert_cmpint (inside_word, ==, gtk_text_iter_inside_word (&iter));
389 
390   g_object_unref (buffer);
391 }
392 
393 static void
check_forward_word_end(const char * buffer_text,int initial_offset,int result_offset,gboolean ret)394 check_forward_word_end (const char *buffer_text,
395                         int          initial_offset,
396                         int          result_offset,
397                         gboolean     ret)
398 {
399   GtkTextBuffer *buffer;
400   GtkTextIter iter;
401 
402   buffer = gtk_text_buffer_new (NULL);
403   gtk_text_buffer_set_text (buffer, buffer_text, -1);
404 
405   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
406 
407   g_assert_cmpint (ret, ==, gtk_text_iter_forward_word_end (&iter));
408   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
409 
410   g_object_unref (buffer);
411 }
412 
413 static void
check_backward_word_start(const char * buffer_text,int initial_offset,int result_offset,gboolean ret)414 check_backward_word_start (const char *buffer_text,
415                            int          initial_offset,
416                            int          result_offset,
417                            gboolean     ret)
418 {
419   GtkTextBuffer *buffer;
420   GtkTextIter iter;
421 
422   buffer = gtk_text_buffer_new (NULL);
423   gtk_text_buffer_set_text (buffer, buffer_text, -1);
424 
425   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
426 
427   g_assert_cmpint (ret, ==, gtk_text_iter_backward_word_start (&iter));
428   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
429 
430   g_object_unref (buffer);
431 }
432 
433 static void
test_word_boundaries(void)434 test_word_boundaries (void)
435 {
436   /* Test with trivial content. The word boundaries are anyway determined by
437    * Pango and can change in the future for corner cases.
438    */
439 
440   check_word_boundaries ("ab ", 0, TRUE, FALSE, TRUE);
441   check_word_boundaries ("ab ", 1, FALSE, FALSE, TRUE);
442   check_word_boundaries ("ab ", 2, FALSE, TRUE, FALSE);
443   check_word_boundaries ("ab ", 3, FALSE, FALSE, FALSE);
444   check_word_boundaries ("", 0, FALSE, FALSE, FALSE);
445 
446   check_forward_word_end ("ab ", 0, 2, TRUE);
447   check_forward_word_end ("ab ", 1, 2, TRUE);
448   check_forward_word_end ("ab ", 2, 2, FALSE);
449   check_forward_word_end ("ab ", 3, 3, FALSE);
450   check_forward_word_end ("ab", 0, 2, FALSE);
451   check_forward_word_end ("ab\n", 2, 2, FALSE);
452 
453   check_backward_word_start (" ab", 3, 1, TRUE);
454   check_backward_word_start (" ab", 2, 1, TRUE);
455   check_backward_word_start (" ab", 1, 1, FALSE);
456   check_backward_word_start (" ab", 0, 0, FALSE);
457   check_backward_word_start ("ab", 2, 0, TRUE);
458 }
459 
460 static void
check_forward_visible_word_end(GtkTextBuffer * buffer,int initial_offset,int result_offset,gboolean ret)461 check_forward_visible_word_end (GtkTextBuffer *buffer,
462                                 int            initial_offset,
463                                 int            result_offset,
464                                 gboolean       ret)
465 {
466   GtkTextIter iter;
467 
468   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
469 
470   g_assert_cmpint (ret, ==, gtk_text_iter_forward_visible_word_end (&iter));
471   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
472 }
473 
474 static void
check_backward_visible_word_start(GtkTextBuffer * buffer,int initial_offset,int result_offset,gboolean ret)475 check_backward_visible_word_start (GtkTextBuffer *buffer,
476                                    int            initial_offset,
477                                    int            result_offset,
478                                    gboolean       ret)
479 {
480   GtkTextIter iter;
481 
482   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
483 
484   g_assert_cmpint (ret, ==, gtk_text_iter_backward_visible_word_start (&iter));
485   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
486 }
487 
488 static void
test_visible_word_boundaries(void)489 test_visible_word_boundaries (void)
490 {
491   /* Test with trivial content. The word boundaries are anyway determined by
492    * Pango and can change in the future for corner cases.
493    */
494 
495   GtkTextBuffer *buffer;
496   GtkTextTag *invisible_tag;
497   GtkTextIter iter;
498 
499   buffer = gtk_text_buffer_new (NULL);
500 
501   invisible_tag = gtk_text_buffer_create_tag (buffer, NULL,
502                                               "invisible", TRUE,
503                                               NULL);
504 
505   /* Buffer contents: " a b c " with " b " invisible */
506   gtk_text_buffer_get_start_iter (buffer, &iter);
507   gtk_text_buffer_insert (buffer, &iter, " a", -1);
508   gtk_text_buffer_insert_with_tags (buffer, &iter, " b ", -1, invisible_tag, NULL);
509   gtk_text_buffer_insert (buffer, &iter, "c ", -1);
510 
511   check_forward_visible_word_end (buffer, 0, 6, TRUE);
512   check_forward_visible_word_end (buffer, 1, 6, TRUE);
513   check_forward_visible_word_end (buffer, 2, 6, TRUE);
514   check_forward_visible_word_end (buffer, 3, 6, TRUE);
515   check_forward_visible_word_end (buffer, 4, 6, TRUE);
516   check_forward_visible_word_end (buffer, 5, 6, TRUE);
517   check_forward_visible_word_end (buffer, 6, 6, FALSE);
518   check_forward_visible_word_end (buffer, 7, 7, FALSE);
519 
520   check_backward_visible_word_start (buffer, 7, 5, TRUE); /* FIXME result_offset should be 1 */
521   check_backward_visible_word_start (buffer, 6, 5, TRUE); /* FIXME result_offset should be 1 */
522   check_backward_visible_word_start (buffer, 5, 1, TRUE);
523   check_backward_visible_word_start (buffer, 4, 1, TRUE);
524   check_backward_visible_word_start (buffer, 3, 1, TRUE);
525   check_backward_visible_word_start (buffer, 2, 1, TRUE);
526   check_backward_visible_word_start (buffer, 1, 1, FALSE);
527   check_backward_visible_word_start (buffer, 0, 0, FALSE);
528 
529   gtk_text_buffer_set_text (buffer, "ab", -1);
530   check_forward_visible_word_end (buffer, 0, 2, FALSE);
531 
532   /* Buffer contents: "b c " with "b" invisible */
533   gtk_text_buffer_set_text (buffer, "", -1);
534   gtk_text_buffer_get_start_iter (buffer, &iter);
535   gtk_text_buffer_insert_with_tags (buffer, &iter, "b", -1, invisible_tag, NULL);
536   gtk_text_buffer_insert (buffer, &iter, " c ", -1);
537 
538   check_forward_visible_word_end (buffer, 0, 1, TRUE); /* FIXME result_offset should be 3 */
539 
540   g_object_unref (buffer);
541 }
542 
543 static void
check_is_cursor_position(const char * buffer_text,int offset,gboolean ret)544 check_is_cursor_position (const char *buffer_text,
545                           int          offset,
546                           gboolean     ret)
547 {
548   GtkTextBuffer *buffer;
549   GtkTextIter iter;
550 
551   buffer = gtk_text_buffer_new (NULL);
552   gtk_text_buffer_set_text (buffer, buffer_text, -1);
553 
554   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
555   g_assert_cmpint (ret, ==, gtk_text_iter_is_cursor_position (&iter));
556 
557   g_object_unref (buffer);
558 }
559 
560 static void
check_cursor_position(const char * buffer_text,gboolean forward,int initial_offset,int result_offset,gboolean ret)561 check_cursor_position (const char *buffer_text,
562                        gboolean     forward,
563                        int          initial_offset,
564                        int          result_offset,
565                        gboolean     ret)
566 {
567   GtkTextBuffer *buffer;
568   GtkTextIter iter;
569 
570   buffer = gtk_text_buffer_new (NULL);
571   gtk_text_buffer_set_text (buffer, buffer_text, -1);
572 
573   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
574 
575   if (forward)
576     g_assert_cmpint (ret, ==, gtk_text_iter_forward_cursor_position (&iter));
577   else
578     g_assert_cmpint (ret, ==, gtk_text_iter_backward_cursor_position (&iter));
579 
580   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
581 
582   g_object_unref (buffer);
583 }
584 
585 static void
test_cursor_positions(void)586 test_cursor_positions (void)
587 {
588   check_is_cursor_position ("a\r\n", 0, TRUE);
589   check_is_cursor_position ("a\r\n", 1, TRUE);
590   check_is_cursor_position ("a\r\n", 2, FALSE);
591   check_is_cursor_position ("a\r\n", 3, TRUE);
592   check_is_cursor_position ("", 0, TRUE);
593 
594   /* forward */
595   check_cursor_position ("a\r\nb", TRUE, 0, 1, TRUE);
596   check_cursor_position ("a\r\nb", TRUE, 1, 3, TRUE);
597   check_cursor_position ("a\r\nb", TRUE, 2, 3, TRUE);
598   check_cursor_position ("a\r\nb", TRUE, 3, 4, FALSE);
599   check_cursor_position ("a\r\nb", TRUE, 4, 4, FALSE);
600   check_cursor_position ("a\n", TRUE, 1, 2, FALSE);
601 
602   /* backward */
603   check_cursor_position ("a\r\nb", FALSE, 4, 3, TRUE);
604   check_cursor_position ("a\r\nb", FALSE, 3, 1, TRUE);
605   check_cursor_position ("a\r\nb", FALSE, 2, 1, TRUE);
606   check_cursor_position ("a\r\nb", FALSE, 1, 0, TRUE);
607   check_cursor_position ("a\r\nb", FALSE, 0, 0, FALSE);
608 }
609 
610 static void
check_visible_cursor_position(GtkTextBuffer * buffer,gboolean forward,int initial_offset,int result_offset,gboolean ret)611 check_visible_cursor_position (GtkTextBuffer *buffer,
612                                gboolean       forward,
613                                int            initial_offset,
614                                int            result_offset,
615                                gboolean       ret)
616 {
617   GtkTextIter iter;
618 
619   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
620 
621   if (forward)
622     g_assert_cmpint (ret, ==, gtk_text_iter_forward_visible_cursor_position (&iter));
623   else
624     g_assert_cmpint (ret, ==, gtk_text_iter_backward_visible_cursor_position (&iter));
625 
626   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
627 }
628 
629 static void
test_visible_cursor_positions(void)630 test_visible_cursor_positions (void)
631 {
632   GtkTextBuffer *buffer;
633   GtkTextTag *invisible_tag;
634   GtkTextIter iter;
635 
636   buffer = gtk_text_buffer_new (NULL);
637 
638   invisible_tag = gtk_text_buffer_create_tag (buffer, NULL,
639                                               "invisible", TRUE,
640                                               NULL);
641 
642   /* Buffer contents: "abcd" with 'bc' invisible */
643   gtk_text_buffer_get_start_iter (buffer, &iter);
644   gtk_text_buffer_insert (buffer, &iter, "a", -1);
645   gtk_text_buffer_insert_with_tags (buffer, &iter, "bc", -1, invisible_tag, NULL);
646   gtk_text_buffer_insert (buffer, &iter, "d", -1);
647 
648   /* forward */
649   check_visible_cursor_position (buffer, TRUE, 0, 3, TRUE);
650   check_visible_cursor_position (buffer, TRUE, 1, 3, TRUE);
651   check_visible_cursor_position (buffer, TRUE, 2, 3, TRUE);
652   check_visible_cursor_position (buffer, TRUE, 3, 4, FALSE);
653   check_visible_cursor_position (buffer, TRUE, 4, 4, FALSE);
654 
655   /* backward */
656   check_visible_cursor_position (buffer, FALSE, 4, 3, TRUE);
657   check_visible_cursor_position (buffer, FALSE, 3, 0, TRUE);
658   check_visible_cursor_position (buffer, FALSE, 2, 0, TRUE);
659   check_visible_cursor_position (buffer, FALSE, 1, 0, TRUE);
660   check_visible_cursor_position (buffer, FALSE, 0, 0, FALSE);
661 
662   g_object_unref (buffer);
663 }
664 
665 static void
check_sentence_boundaries(const char * buffer_text,int offset,gboolean starts_sentence,gboolean ends_sentence,gboolean inside_sentence)666 check_sentence_boundaries (const char *buffer_text,
667                            int          offset,
668                            gboolean     starts_sentence,
669                            gboolean     ends_sentence,
670                            gboolean     inside_sentence)
671 {
672   GtkTextBuffer *buffer;
673   GtkTextIter iter;
674 
675   buffer = gtk_text_buffer_new (NULL);
676   gtk_text_buffer_set_text (buffer, buffer_text, -1);
677 
678   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
679 
680   g_assert_cmpint (starts_sentence, ==, gtk_text_iter_starts_sentence (&iter));
681   g_assert_cmpint (ends_sentence, ==, gtk_text_iter_ends_sentence (&iter));
682   g_assert_cmpint (inside_sentence, ==, gtk_text_iter_inside_sentence (&iter));
683 
684   g_object_unref (buffer);
685 }
686 
687 static void
check_forward_sentence_end(const char * buffer_text,int initial_offset,int result_offset,gboolean ret)688 check_forward_sentence_end (const char *buffer_text,
689                             int          initial_offset,
690                             int          result_offset,
691                             gboolean     ret)
692 {
693   GtkTextBuffer *buffer;
694   GtkTextIter iter;
695 
696   buffer = gtk_text_buffer_new (NULL);
697   gtk_text_buffer_set_text (buffer, buffer_text, -1);
698 
699   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
700 
701   g_assert_cmpint (ret, ==, gtk_text_iter_forward_sentence_end (&iter));
702   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
703 
704   g_object_unref (buffer);
705 }
706 
707 static void
check_backward_sentence_start(const char * buffer_text,int initial_offset,int result_offset,gboolean ret)708 check_backward_sentence_start (const char *buffer_text,
709                                int          initial_offset,
710                                int          result_offset,
711                                gboolean     ret)
712 {
713   GtkTextBuffer *buffer;
714   GtkTextIter iter;
715 
716   buffer = gtk_text_buffer_new (NULL);
717   gtk_text_buffer_set_text (buffer, buffer_text, -1);
718 
719   gtk_text_buffer_get_iter_at_offset (buffer, &iter, initial_offset);
720 
721   g_assert_cmpint (ret, ==, gtk_text_iter_backward_sentence_start (&iter));
722   g_assert_cmpint (result_offset, ==, gtk_text_iter_get_offset (&iter));
723 
724   g_object_unref (buffer);
725 }
726 
727 static void
test_sentence_boundaries(void)728 test_sentence_boundaries (void)
729 {
730   check_sentence_boundaries ("Hi. ", 0, TRUE, FALSE, TRUE);
731   check_sentence_boundaries ("Hi. ", 1, FALSE, FALSE, TRUE);
732   check_sentence_boundaries ("Hi. ", 2, FALSE, FALSE, TRUE);
733   check_sentence_boundaries ("Hi. ", 3, FALSE, TRUE, FALSE);
734   check_sentence_boundaries ("Hi. ", 4, FALSE, FALSE, FALSE);
735   check_sentence_boundaries ("", 0, FALSE, FALSE, FALSE);
736 
737   check_forward_sentence_end ("Hi. ", 0, 3, TRUE);
738   check_forward_sentence_end ("Hi. ", 1, 3, TRUE);
739   check_forward_sentence_end ("Hi. ", 2, 3, TRUE);
740   check_forward_sentence_end ("Hi. ", 3, 3, FALSE);
741   check_forward_sentence_end ("Hi. ", 4, 4, FALSE);
742   check_forward_sentence_end ("Hi.", 0, 3, FALSE);
743   check_forward_sentence_end ("Hi.\n", 3, 3, FALSE);
744 
745   check_backward_sentence_start (" Hi.", 4, 1, TRUE);
746   check_backward_sentence_start (" Hi.", 3, 1, TRUE);
747   check_backward_sentence_start (" Hi.", 2, 1, TRUE);
748   check_backward_sentence_start (" Hi.", 1, 1, FALSE);
749   check_backward_sentence_start (" Hi.", 0, 0, FALSE);
750 }
751 
752 static void
test_backward_line(void)753 test_backward_line (void)
754 {
755   GtkTextBuffer *buffer;
756   GtkTextIter iter, start, end;
757   gboolean ret;
758   int offset;
759 
760   buffer = gtk_text_buffer_new (NULL);
761   gtk_text_buffer_get_start_iter (buffer, &iter);
762   gtk_text_buffer_insert (buffer, &iter, "Hi line 1\nHi line 2", -1);
763 
764   /* Go to middle of first line */
765   gtk_text_iter_backward_line (&iter);
766   gtk_text_iter_set_line_offset (&iter, 4);
767 
768   /* Now insert some chars with gtk_text_buffer_insert_range() */
769   gtk_text_buffer_get_end_iter (buffer, &end);
770   start = end;
771   gtk_text_iter_backward_cursor_positions (&start, 5);
772   gtk_text_buffer_insert_range (buffer, &iter, &start, &end);
773 
774   /* Check we are still at the first line */
775   g_assert_cmpint (gtk_text_iter_get_line (&iter), ==, 0);
776 
777   /* Now a call to gtk_text_iter_backward_line() should return TRUE
778      and move &iter to start of the line, or return FALSE if &iter
779      was already at start of the line, so in both cases &iter should
780      be at the start of the line, so check that */
781   ret = gtk_text_iter_backward_line (&iter);
782   g_assert_true (ret);
783   offset = gtk_text_iter_get_line_offset (&iter);
784   g_assert_cmpint (offset, ==, 0);
785 
786   g_object_unref (buffer);
787 }
788 
789 int
main(int argc,char ** argv)790 main (int argc, char** argv)
791 {
792   gtk_test_init (&argc, &argv);
793 
794   g_test_add_func ("/TextIter/Search Empty", test_empty_search);
795   g_test_add_func ("/TextIter/Search Full Buffer", test_search_full_buffer);
796   g_test_add_func ("/TextIter/Search", test_search);
797   g_test_add_func ("/TextIter/Search Caseless", test_search_caseless);
798   g_test_add_func ("/TextIter/Forward To Tag Toggle", test_forward_to_tag_toggle);
799   g_test_add_func ("/TextIter/Forward To Line End", test_forward_to_line_end);
800   g_test_add_func ("/TextIter/Word Boundaries", test_word_boundaries);
801   g_test_add_func ("/TextIter/Visible Word Boundaries", test_visible_word_boundaries);
802   g_test_add_func ("/TextIter/Cursor Positions", test_cursor_positions);
803   g_test_add_func ("/TextIter/Visible Cursor Positions", test_visible_cursor_positions);
804   g_test_add_func ("/TextIter/Sentence Boundaries", test_sentence_boundaries);
805   g_test_add_func ("/TextIter/Backward line", test_backward_line);
806 
807   return g_test_run();
808 }
809