2 * Unit test suite for rich edit control 1.0
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 * Copyright 2007 Alex VillacĂs Lasso
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include <wine/test.h>
36 static HMODULE hmoduleRichEdit;
38 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
40 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
41 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
42 hmoduleRichEdit, NULL);
43 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
47 static HWND new_richedit(HWND parent) {
48 return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
51 static void test_WM_SETTEXT()
53 HWND hwndRichEdit = new_richedit(NULL);
54 const char * TestItem1 = "TestSomeText";
55 const char * TestItem2 = "TestSomeText\r";
56 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
57 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
58 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
59 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
60 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
61 const char * TestItem8 = "TestSomeText\r\n";
62 const char * TestItem9 = "TestSomeText\r\nSomeMoreText\r\n";
63 const char * TestItem10 = "TestSomeText\r\n\r\nTestSomeText";
64 const char * TestItem11 = "TestSomeText TestSomeText";
65 const char * TestItem12 = "TestSomeText \r\nTestSomeText";
66 const char * TestItem13 = "TestSomeText\r\n \r\nTestSomeText";
67 const char * TestItem14 = "TestSomeText\n";
68 const char * TestItem15 = "TestSomeText\r\r\r";
69 const char * TestItem16 = "TestSomeText\r\r\rSomeMoreText";
73 /* This test attempts to show that WM_SETTEXT on a riched32 control does not
74 attempt to modify the text that is pasted into the control, and should
75 return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
76 Currently, builtin riched32 mangles solitary \r or \n when not part of
79 For riched32, the rules for breaking lines seem to be the following:
80 - \r\n is one line break. This is the normal case.
81 - \r{0,N}\n is one line break. In particular, \n by itself is a line break.
82 - \n{1,N} are that many line breaks.
83 - \r with text or other characters (except \n) past it, is a line break. That
84 is, a run of \r{N} without a terminating \n is considered N line breaks
85 - \r at the end of the text is NOT a line break. This differs from riched20,
86 where \r at the end of the text is a proper line break.
89 #define TEST_SETTEXT(a, b, nlines) \
90 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
91 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
92 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
93 ok (result == lstrlen(buf), \
94 "WM_GETTEXT returned %ld instead of expected %u\n", \
95 result, lstrlen(buf)); \
96 result = strcmp(b, buf); \
98 "WM_SETTEXT round trip: strcmp = %ld\n", result); \
99 result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); \
100 ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines);
102 TEST_SETTEXT(TestItem1, TestItem1, 1)
103 TEST_SETTEXT(TestItem2, TestItem2, 1)
104 TEST_SETTEXT(TestItem3, TestItem3, 2)
105 TEST_SETTEXT(TestItem4, TestItem4, 3)
106 TEST_SETTEXT(TestItem5, TestItem5, 2)
107 TEST_SETTEXT(TestItem6, TestItem6, 3)
108 TEST_SETTEXT(TestItem7, TestItem7, 4)
109 TEST_SETTEXT(TestItem8, TestItem8, 2)
110 TEST_SETTEXT(TestItem9, TestItem9, 3)
111 TEST_SETTEXT(TestItem10, TestItem10, 3)
112 TEST_SETTEXT(TestItem11, TestItem11, 1)
113 TEST_SETTEXT(TestItem12, TestItem12, 2)
114 TEST_SETTEXT(TestItem13, TestItem13, 3)
115 TEST_SETTEXT(TestItem14, TestItem14, 2)
116 TEST_SETTEXT(TestItem15, TestItem15, 3)
117 TEST_SETTEXT(TestItem16, TestItem16, 4)
120 DestroyWindow(hwndRichEdit);
123 static void test_WM_GETTEXTLENGTH(void)
125 HWND hwndRichEdit = new_richedit(NULL);
126 static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
127 static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
130 /* Test for WM_GETTEXTLENGTH */
131 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
132 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
133 ok(result == lstrlen(text3),
134 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
135 result, lstrlen(text3));
137 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
138 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
139 ok(result == lstrlen(text4),
140 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
141 result, lstrlen(text4));
143 DestroyWindow(hwndRichEdit);
146 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
151 const char** str = (const char**)dwCookie;
152 int size = strlen(*str);
158 memcpy(pbBuff, *str, *pcb);
165 static void test_EM_STREAMIN(void)
167 HWND hwndRichEdit = new_richedit(NULL);
170 char buffer[1024] = {0};
172 const char * streamText0 = "{\\rtf1 TestSomeText}";
173 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
174 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
176 const char * streamText1 =
177 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
178 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
181 /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
182 const char * streamText2 =
183 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
184 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
185 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
186 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
187 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
188 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
189 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
191 const char * streamText3 = "RichEdit1";
193 /* Minimal test without \par at the end */
194 es.dwCookie = (DWORD_PTR)&streamText0;
196 es.pfnCallback = test_EM_STREAMIN_esCallback;
197 SendMessage(hwndRichEdit, EM_STREAMIN,
198 (WPARAM)(SF_RTF), (LPARAM)&es);
200 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
202 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
203 result = strcmp (buffer,"TestSomeText");
205 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
206 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
208 /* Native richedit 2.0 ignores last \par */
209 es.dwCookie = (DWORD_PTR)&streamText0a;
211 es.pfnCallback = test_EM_STREAMIN_esCallback;
212 SendMessage(hwndRichEdit, EM_STREAMIN,
213 (WPARAM)(SF_RTF), (LPARAM)&es);
215 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
217 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
218 result = strcmp (buffer,"TestSomeText");
220 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
221 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
223 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
224 es.dwCookie = (DWORD_PTR)&streamText0b;
226 es.pfnCallback = test_EM_STREAMIN_esCallback;
227 SendMessage(hwndRichEdit, EM_STREAMIN,
228 (WPARAM)(SF_RTF), (LPARAM)&es);
230 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
232 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
233 result = strcmp (buffer,"TestSomeText\r\n");
235 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
236 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
238 es.dwCookie = (DWORD_PTR)&streamText1;
240 es.pfnCallback = test_EM_STREAMIN_esCallback;
241 SendMessage(hwndRichEdit, EM_STREAMIN,
242 (WPARAM)(SF_RTF), (LPARAM)&es);
244 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
246 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
247 result = strcmp (buffer,"TestSomeText");
249 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
250 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
253 es.dwCookie = (DWORD_PTR)&streamText2;
255 SendMessage(hwndRichEdit, EM_STREAMIN,
256 (WPARAM)(SF_RTF), (LPARAM)&es);
258 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
261 "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
263 result = strcmp (buffer,"RichEdit1");
266 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
268 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
270 es.dwCookie = (DWORD_PTR)&streamText3;
272 SendMessage(hwndRichEdit, EM_STREAMIN,
273 (WPARAM)(SF_RTF), (LPARAM)&es);
275 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
277 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
278 ok (strlen(buffer) == 0,
279 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
280 ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
282 DestroyWindow(hwndRichEdit);
285 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
290 char** str = (char**)dwCookie;
293 memcpy(*str, pbBuff, *pcb);
299 static void test_EM_STREAMOUT(void)
301 HWND hwndRichEdit = new_richedit(NULL);
304 char buf[1024] = {0};
307 const char * TestItem1 = "TestSomeText";
308 const char * TestItem2 = "TestSomeText\r";
309 const char * TestItem3 = "TestSomeText\r\n";
311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
313 es.dwCookie = (DWORD_PTR)&p;
315 es.pfnCallback = test_WM_SETTEXT_esCallback;
316 memset(buf, 0, sizeof(buf));
317 SendMessage(hwndRichEdit, EM_STREAMOUT,
318 (WPARAM)(SF_TEXT), (LPARAM)&es);
320 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
321 ok(strcmp(buf, TestItem1) == 0,
322 "streamed text different, got %s\n", buf);
324 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
326 es.dwCookie = (DWORD_PTR)&p;
328 es.pfnCallback = test_WM_SETTEXT_esCallback;
329 memset(buf, 0, sizeof(buf));
330 SendMessage(hwndRichEdit, EM_STREAMOUT,
331 (WPARAM)(SF_TEXT), (LPARAM)&es);
334 ok(r == 13, "streamed text length is %d, expecting 13\n", r);
335 ok(strcmp(buf, TestItem2) == 0,
336 "streamed text different, got %s\n", buf);
338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
340 es.dwCookie = (DWORD_PTR)&p;
342 es.pfnCallback = test_WM_SETTEXT_esCallback;
343 memset(buf, 0, sizeof(buf));
344 SendMessage(hwndRichEdit, EM_STREAMOUT,
345 (WPARAM)(SF_TEXT), (LPARAM)&es);
347 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
348 ok(strcmp(buf, TestItem3) == 0,
349 "streamed text different, got %s\n", buf);
351 DestroyWindow(hwndRichEdit);
354 static const struct getline_s {
359 {0, 10, "foo bar\r\n"},
364 /* Buffer smaller than line length */
370 static void test_EM_GETLINE(void)
373 HWND hwndRichEdit = new_richedit(NULL);
374 static const int nBuf = 1024;
375 char dest[1024], origdest[1024];
376 const char text[] = "foo bar\r\n"
380 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
382 memset(origdest, 0xBB, nBuf);
383 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
386 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
387 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
388 memset(dest, 0xBB, nBuf);
389 *(WORD *) dest = gl[i].buffer_len;
391 /* EM_GETLINE appends a "\r\0" to the end of the line
392 * nCopied counts up to and including the '\r' */
393 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
394 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
396 /* two special cases since a parameter is passed via dest */
397 if (gl[i].buffer_len == 0)
398 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
400 else if (gl[i].buffer_len == 1)
401 ok(dest[0] == gl[i].text[0] && !dest[1] &&
402 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
405 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
406 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
407 ok(!strncmp(dest + expected_bytes_written, origdest
408 + expected_bytes_written, nBuf - expected_bytes_written),
409 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
413 DestroyWindow(hwndRichEdit);
416 static void test_EM_LINELENGTH(void)
418 HWND hwndRichEdit = new_richedit(NULL);
423 "richedit1\r\r\r\r\r\n";
424 int offset_test[10][2] = {
433 {40, 9}, /* <----- in the middle of the \r run, but run not counted */
439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
441 for (i = 0; i < 10; i++) {
442 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
443 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
444 offset_test[i][0], result, offset_test[i][1]);
447 DestroyWindow(hwndRichEdit);
450 static void test_EM_GETTEXTRANGE(void)
452 HWND hwndRichEdit = new_richedit(NULL);
453 const char * text1 = "foo bar\r\nfoo bar";
454 const char * text2 = "foo bar\rfoo bar";
455 const char * expect1 = "bar\r\nfoo";
456 const char * expect2 = "bar\rfoo";
457 char buffer[1024] = {0};
459 TEXTRANGEA textRange;
461 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
463 textRange.lpstrText = buffer;
464 textRange.chrg.cpMin = 4;
465 textRange.chrg.cpMax = 12;
466 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
467 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
468 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
470 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
472 textRange.lpstrText = buffer;
473 textRange.chrg.cpMin = 4;
474 textRange.chrg.cpMax = 11;
475 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
476 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
478 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
481 DestroyWindow(hwndRichEdit);
484 static void test_EM_GETSELTEXT(void)
486 HWND hwndRichEdit = new_richedit(NULL);
487 const char * text1 = "foo bar\r\nfoo bar";
488 const char * text2 = "foo bar\rfoo bar";
489 const char * expect1 = "bar\r\nfoo";
490 const char * expect2 = "bar\rfoo";
491 char buffer[1024] = {0};
494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
496 SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
497 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
498 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
499 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
501 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
503 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
504 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
505 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
507 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
510 DestroyWindow(hwndRichEdit);
513 static const char haystack[] = "WINEWine wineWine wine WineWine";
525 struct find_s find_tests[] = {
526 /* Find in empty text */
527 {0, -1, "foo", FR_DOWN, -1},
528 {0, -1, "foo", 0, -1},
529 {0, -1, "", FR_DOWN, -1},
530 {20, 5, "foo", FR_DOWN, -1},
531 {5, 20, "foo", FR_DOWN, -1}
534 struct find_s find_tests2[] = {
536 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
537 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
539 /* Subsequent finds */
540 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
541 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
542 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
543 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
546 {19, 20, "Wine", FR_MATCHCASE, -1},
547 {10, 20, "Wine", FR_MATCHCASE, 13},
548 {20, 10, "Wine", FR_MATCHCASE, -1},
550 /* Case-insensitive */
551 {1, 31, "wInE", FR_DOWN, 4},
552 {1, 31, "Wine", FR_DOWN, 4},
554 /* High-to-low ranges */
555 {20, 5, "Wine", FR_DOWN, -1},
556 {2, 1, "Wine", FR_DOWN, -1},
557 {30, 29, "Wine", FR_DOWN, -1},
558 {20, 5, "Wine", 0, /*13*/ -1},
561 {5, 10, "", FR_DOWN, -1},
562 {10, 5, "", FR_DOWN, -1},
563 {0, -1, "", FR_DOWN, -1},
566 /* Whole-word search */
567 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
568 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
569 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
570 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
571 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
572 {11, -1, "winewine", FR_WHOLEWORD, 23},
573 {31, -1, "winewine", FR_WHOLEWORD, -1},
576 {5, 200, "XXX", FR_DOWN, -1},
577 {-20, 20, "Wine", FR_DOWN, -1},
578 {-20, 20, "Wine", FR_DOWN, -1},
579 {-15, -20, "Wine", FR_DOWN, -1},
580 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
582 /* Check the case noted in bug 4479 where matches at end aren't recognized */
583 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
584 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
585 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
586 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
587 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
589 /* The backwards case of bug 4479; bounds look right
590 * Fails because backward find is wrong */
591 {19, 20, "WINE", FR_MATCHCASE, -1},
592 {0, 20, "WINE", FR_MATCHCASE, 0},
594 {0, -1, "wineWine wine", FR_DOWN, 0},
595 {0, -1, "wineWine wine", 0, 0},
596 {0, -1, "INEW", 0, 1},
597 {0, 31, "INEW", 0, 1},
598 {4, -1, "INEW", 0, 10},
601 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
604 memset(&ft, 0, sizeof(ft));
605 ft.chrg.cpMin = f->start;
606 ft.chrg.cpMax = f->end;
607 ft.lpstrText = f->needle;
608 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
609 ok(findloc == f->expected_loc,
610 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
611 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
614 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
618 int expected_end_loc;
620 memset(&ft, 0, sizeof(ft));
621 ft.chrg.cpMin = f->start;
622 ft.chrg.cpMax = f->end;
623 ft.lpstrText = f->needle;
624 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
625 ok(findloc == f->expected_loc,
626 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
627 name, id, f->needle, f->start, f->end, f->flags, findloc);
628 ok(ft.chrgText.cpMin == f->expected_loc,
629 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
630 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
631 expected_end_loc = ((f->expected_loc == -1) ? -1
632 : f->expected_loc + strlen(f->needle));
633 ok(ft.chrgText.cpMax == expected_end_loc,
634 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
635 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
638 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
643 for (i = 0; i < num_tests; i++) {
644 check_EM_FINDTEXT(hwnd, name, &find[i], i);
645 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
649 static void test_EM_FINDTEXT(void)
651 HWND hwndRichEdit = new_richedit(NULL);
653 /* Empty rich edit control */
654 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
655 sizeof(find_tests)/sizeof(struct find_s));
657 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
660 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
661 sizeof(find_tests2)/sizeof(struct find_s));
663 DestroyWindow(hwndRichEdit);
666 static void test_EM_POSFROMCHAR(void)
668 HWND hwndRichEdit = new_richedit(NULL);
672 unsigned int height = 0;
674 static const char text[] = "aa\n"
675 "this is a long line of text that should be longer than the "
684 /* Fill the control to lines to ensure that most of them are offscreen */
685 for (i = 0; i < 50; i++)
687 /* Do not modify the string; it is exactly 16 characters long. */
688 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
689 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
693 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
694 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
695 Richedit 3.0 accepts either of the above API conventions.
698 /* Testing Richedit 1.0 API format */
700 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
701 Since all lines are identical and drawn with the same font,
702 they should have the same height... right?
704 for (i = 0; i < 50; i++)
706 /* All the lines are 16 characters long */
707 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
708 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
711 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
712 ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
717 ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
718 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
723 ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
724 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
728 /* Testing position at end of text */
729 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
730 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
731 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
732 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
734 /* Testing position way past end of text */
735 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
736 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
737 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
738 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
741 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
742 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
743 for (i = 0; i < 50; i++)
745 /* All the lines are 16 characters long */
746 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
747 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
748 ok(pl.y == (i - 1) * height,
749 "EM_POSFROMCHAR reports y=%d, expected %d\n",
750 pl.y, (i - 1) * height);
751 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
754 /* Testing position at end of text */
755 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
756 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
757 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
758 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
760 /* Testing position way past end of text */
761 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
762 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
763 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
764 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
766 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
767 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
768 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
770 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
771 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
772 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
773 ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
776 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
777 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
778 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
779 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
781 /* Fails on builtin because horizontal scrollbar is not being shown */
782 ok(pl.x < xpos, "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", pl.x, xpos);
784 DestroyWindow(hwndRichEdit);
787 static void test_word_wrap(void)
790 POINTL point = {0, 60}; /* This point must be below the first line */
791 const char *text = "Must be long enough to test line wrapping";
792 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
795 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
796 * when specified on window creation and set later. */
797 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
798 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
799 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
800 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
801 ok(res, "WM_SETTEXT failed.\n");
802 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
803 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
804 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
805 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
807 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
808 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
809 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
812 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
813 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
814 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
816 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
817 ok(res, "WM_SETTEXT failed.\n");
818 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
819 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
820 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
821 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
823 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
824 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
825 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
828 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
829 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
830 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
831 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
832 ok(res, "WM_SETTEXT failed.\n");
833 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
834 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
836 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
837 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
838 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
841 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
842 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
843 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
844 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
845 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
846 ok(res, "WM_SETTEXT failed.\n");
847 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
848 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
850 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
851 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
852 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
854 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
855 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
856 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
857 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
858 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
860 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
861 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
862 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
863 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
866 /* Test to see if wrapping happens with redraw disabled. */
867 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
868 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
869 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
870 ok(IsWindowVisible(hwnd), "Window should be visible.\n");
871 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
872 /* redraw is disabled by making the window invisible. */
873 ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
874 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
875 ok(res, "EM_REPLACESEL failed.\n");
876 MoveWindow(hwnd, 0, 0, 100, 80, TRUE);
877 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
878 /* Wrapping didn't happen while redraw was disabled. */
879 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
880 todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
881 /* There isn't even a rewrap from resizing the window. */
882 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
883 todo_wine ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
884 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
885 ok(res, "EM_REPLACESEL failed.\n");
886 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
887 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
898 /* Must explicitly LoadLibrary(). The test has no references to functions in
899 * RICHED32.DLL, so the linker doesn't actually link to it. */
900 hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
901 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
904 test_EM_GETTEXTRANGE();
905 test_EM_GETSELTEXT();
906 test_WM_GETTEXTLENGTH();
910 test_EM_LINELENGTH();
912 test_EM_POSFROMCHAR();
915 /* Set the environment variable WINETEST_RICHED32 to keep windows
916 * responsive and open for 30 seconds. This is useful for debugging.
918 * The message pump uses PeekMessage() to empty the queue and then sleeps for
919 * 50ms before retrying the queue. */
920 end = time(NULL) + 30;
921 if (getenv( "WINETEST_RICHED32" )) {
922 while (time(NULL) < end) {
923 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
924 TranslateMessage(&msg);
925 DispatchMessage(&msg);
933 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());