push 8742e928a3d078c1d2cecb5ceee0ffde3118cbb7
[wine/hacks.git] / dlls / riched32 / tests / editor.c
blob1d07b8d81da7657cceb88a055878d451340e65f5
1 /*
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
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <time.h>
34 #include <wine/test.h>
36 static HMODULE hmoduleRichEdit;
38 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
39 HWND hwnd;
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());
44 return hwnd;
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";
70 char buf[1024] = {0};
71 LRESULT result;
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
77 a \r\n pair.
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. This causes
87 TestItem2 to fail its test.
90 #define TEST_SETTEXT(a, b, nlines, is_todo, is_todo2) \
91 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
92 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
93 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
94 ok (result == lstrlen(buf), \
95 "WM_GETTEXT returned %ld instead of expected %u\n", \
96 result, lstrlen(buf)); \
97 result = strcmp(b, buf); \
98 if (is_todo) todo_wine { \
99 ok(result == 0, \
100 "WM_SETTEXT round trip: strcmp = %ld\n", result); \
101 } else { \
102 ok(result == 0, \
103 "WM_SETTEXT round trip: strcmp = %ld\n", result); \
105 result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0); \
106 if (is_todo2) todo_wine { \
107 ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
108 } else { \
109 ok(result == nlines, "EM_GETLINECOUNT returned %ld, expected %d\n", result, nlines); \
112 TEST_SETTEXT(TestItem1, TestItem1, 1, 0, 0)
113 TEST_SETTEXT(TestItem2, TestItem2, 1, 0, 0)
114 TEST_SETTEXT(TestItem3, TestItem3, 2, 0, 0)
115 TEST_SETTEXT(TestItem4, TestItem4, 3, 0, 0)
116 TEST_SETTEXT(TestItem5, TestItem5, 2, 0, 0)
117 TEST_SETTEXT(TestItem6, TestItem6, 3, 0, 0)
118 TEST_SETTEXT(TestItem7, TestItem7, 4, 0, 0)
119 TEST_SETTEXT(TestItem8, TestItem8, 2, 0, 0)
120 TEST_SETTEXT(TestItem9, TestItem9, 3, 0, 0)
121 TEST_SETTEXT(TestItem10, TestItem10, 3, 0, 0)
122 TEST_SETTEXT(TestItem11, TestItem11, 1, 0, 0)
123 TEST_SETTEXT(TestItem12, TestItem12, 2, 0, 0)
124 TEST_SETTEXT(TestItem13, TestItem13, 3, 0, 0)
125 TEST_SETTEXT(TestItem14, TestItem14, 2, 0, 0)
126 TEST_SETTEXT(TestItem15, TestItem15, 3, 0, 0)
127 TEST_SETTEXT(TestItem16, TestItem16, 4, 0, 0)
129 #undef TEST_SETTEXT
130 DestroyWindow(hwndRichEdit);
133 static void test_WM_GETTEXTLENGTH(void)
135 HWND hwndRichEdit = new_richedit(NULL);
136 static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
137 static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
138 int result;
140 /* Test for WM_GETTEXTLENGTH */
141 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
142 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
143 ok(result == lstrlen(text3),
144 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
145 result, lstrlen(text3));
147 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
148 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
149 ok(result == lstrlen(text4),
150 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
151 result, lstrlen(text4));
153 DestroyWindow(hwndRichEdit);
156 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
157 LPBYTE pbBuff,
158 LONG cb,
159 LONG *pcb)
161 const char** str = (const char**)dwCookie;
162 int size = strlen(*str);
163 *pcb = cb;
164 if (*pcb > size) {
165 *pcb = size;
167 if (*pcb > 0) {
168 memcpy(pbBuff, *str, *pcb);
169 *str += *pcb;
171 return 0;
175 static void test_EM_STREAMIN(void)
177 HWND hwndRichEdit = new_richedit(NULL);
178 LRESULT result;
179 EDITSTREAM es;
180 char buffer[1024] = {0};
182 const char * streamText0 = "{\\rtf1 TestSomeText}";
183 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
184 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
186 const char * streamText1 =
187 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
188 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
189 "}\r\n";
191 /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
192 const char * streamText2 =
193 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
194 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
195 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
196 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
197 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
198 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
199 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
201 const char * streamText3 = "RichEdit1";
203 /* Minimal test without \par at the end */
204 es.dwCookie = (DWORD_PTR)&streamText0;
205 es.dwError = 0;
206 es.pfnCallback = test_EM_STREAMIN_esCallback;
207 SendMessage(hwndRichEdit, EM_STREAMIN,
208 (WPARAM)(SF_RTF), (LPARAM)&es);
210 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
211 ok (result == 12,
212 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
213 result = strcmp (buffer,"TestSomeText");
214 ok (result == 0,
215 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
216 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
218 /* Native richedit 2.0 ignores last \par */
219 es.dwCookie = (DWORD_PTR)&streamText0a;
220 es.dwError = 0;
221 es.pfnCallback = test_EM_STREAMIN_esCallback;
222 SendMessage(hwndRichEdit, EM_STREAMIN,
223 (WPARAM)(SF_RTF), (LPARAM)&es);
225 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
226 ok (result == 12,
227 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
228 result = strcmp (buffer,"TestSomeText");
229 ok (result == 0,
230 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
231 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
233 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
234 es.dwCookie = (DWORD_PTR)&streamText0b;
235 es.dwError = 0;
236 es.pfnCallback = test_EM_STREAMIN_esCallback;
237 SendMessage(hwndRichEdit, EM_STREAMIN,
238 (WPARAM)(SF_RTF), (LPARAM)&es);
240 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
241 ok (result == 14,
242 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
243 result = strcmp (buffer,"TestSomeText\r\n");
244 ok (result == 0,
245 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
246 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
248 es.dwCookie = (DWORD_PTR)&streamText1;
249 es.dwError = 0;
250 es.pfnCallback = test_EM_STREAMIN_esCallback;
251 SendMessage(hwndRichEdit, EM_STREAMIN,
252 (WPARAM)(SF_RTF), (LPARAM)&es);
254 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
255 ok (result == 12,
256 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
257 result = strcmp (buffer,"TestSomeText");
258 ok (result == 0,
259 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
260 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
263 es.dwCookie = (DWORD_PTR)&streamText2;
264 es.dwError = 0;
265 SendMessage(hwndRichEdit, EM_STREAMIN,
266 (WPARAM)(SF_RTF), (LPARAM)&es);
268 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
269 todo_wine {
270 ok (result == 9,
271 "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
273 result = strcmp (buffer,"RichEdit1");
274 todo_wine {
275 ok (result == 0,
276 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
278 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
280 es.dwCookie = (DWORD_PTR)&streamText3;
281 es.dwError = 0;
282 SendMessage(hwndRichEdit, EM_STREAMIN,
283 (WPARAM)(SF_RTF), (LPARAM)&es);
285 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
286 ok (result == 0,
287 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
288 ok (strlen(buffer) == 0,
289 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
290 ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
292 DestroyWindow(hwndRichEdit);
295 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
296 LPBYTE pbBuff,
297 LONG cb,
298 LONG *pcb)
300 char** str = (char**)dwCookie;
301 *pcb = cb;
302 if (*pcb > 0) {
303 memcpy(*str, pbBuff, *pcb);
304 *str += *pcb;
306 return 0;
309 static void test_EM_STREAMOUT(void)
311 HWND hwndRichEdit = new_richedit(NULL);
312 int r;
313 EDITSTREAM es;
314 char buf[1024] = {0};
315 char * p;
317 const char * TestItem1 = "TestSomeText";
318 const char * TestItem2 = "TestSomeText\r";
319 const char * TestItem3 = "TestSomeText\r\n";
321 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
322 p = buf;
323 es.dwCookie = (DWORD_PTR)&p;
324 es.dwError = 0;
325 es.pfnCallback = test_WM_SETTEXT_esCallback;
326 memset(buf, 0, sizeof(buf));
327 SendMessage(hwndRichEdit, EM_STREAMOUT,
328 (WPARAM)(SF_TEXT), (LPARAM)&es);
329 r = strlen(buf);
330 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
331 ok(strcmp(buf, TestItem1) == 0,
332 "streamed text different, got %s\n", buf);
334 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
335 p = buf;
336 es.dwCookie = (DWORD_PTR)&p;
337 es.dwError = 0;
338 es.pfnCallback = test_WM_SETTEXT_esCallback;
339 memset(buf, 0, sizeof(buf));
340 SendMessage(hwndRichEdit, EM_STREAMOUT,
341 (WPARAM)(SF_TEXT), (LPARAM)&es);
342 r = strlen(buf);
344 ok(r == 13, "streamed text length is %d, expecting 13\n", r);
345 ok(strcmp(buf, TestItem2) == 0,
346 "streamed text different, got %s\n", buf);
348 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
349 p = buf;
350 es.dwCookie = (DWORD_PTR)&p;
351 es.dwError = 0;
352 es.pfnCallback = test_WM_SETTEXT_esCallback;
353 memset(buf, 0, sizeof(buf));
354 SendMessage(hwndRichEdit, EM_STREAMOUT,
355 (WPARAM)(SF_TEXT), (LPARAM)&es);
356 r = strlen(buf);
357 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
358 ok(strcmp(buf, TestItem3) == 0,
359 "streamed text different, got %s\n", buf);
361 DestroyWindow(hwndRichEdit);
364 static const struct getline_s {
365 int line;
366 size_t buffer_len;
367 const char *text;
368 int wine_todo;
369 } gl[] = {
370 {0, 10, "foo bar\r\n", 0},
371 {1, 10, "\n", 0},
372 {2, 10, "bar\n", 0},
373 {3, 10, "\r\n", 0},
375 /* Buffer smaller than line length */
376 {0, 2, "foo bar\r", 0},
377 {0, 1, "foo bar\r", 0},
378 {0, 0, "foo bar\r", 0}
381 static void test_EM_GETLINE(void)
383 int i;
384 HWND hwndRichEdit = new_richedit(NULL);
385 static const int nBuf = 1024;
386 char dest[1024], origdest[1024];
387 const char text[] = "foo bar\r\n"
388 "\n"
389 "bar\n";
391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
393 memset(origdest, 0xBB, nBuf);
394 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
396 int nCopied;
397 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
398 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1);
399 memset(dest, 0xBB, nBuf);
400 *(WORD *) dest = gl[i].buffer_len;
402 /* EM_GETLINE appends a "\r\0" to the end of the line
403 * nCopied counts up to and including the '\r' */
404 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
405 if (gl[i].wine_todo) todo_wine {
406 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
407 expected_nCopied);
408 } else
409 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
410 expected_nCopied);
411 /* two special cases since a parameter is passed via dest */
412 if (gl[i].buffer_len == 0)
413 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
414 "buffer_len=0\n");
415 else if (gl[i].buffer_len == 1)
416 ok(dest[0] == gl[i].text[0] && !dest[1] &&
417 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
418 else
420 if (gl[i].wine_todo) todo_wine {
421 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
422 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
423 ok(!strncmp(dest + expected_bytes_written, origdest
424 + expected_bytes_written, nBuf - expected_bytes_written),
425 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
427 else
429 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
430 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
431 ok(!strncmp(dest + expected_bytes_written, origdest
432 + expected_bytes_written, nBuf - expected_bytes_written),
433 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
438 DestroyWindow(hwndRichEdit);
441 static void test_EM_LINELENGTH(void)
443 HWND hwndRichEdit = new_richedit(NULL);
444 const char * text =
445 "richedit1\r"
446 "richedit1\n"
447 "richedit1\r\n"
448 "richedit1\r\r\r\r\r\n";
449 int offset_test[10][2] = {
450 {0, 9},
451 {5, 9},
452 {10, 9},
453 {15, 9},
454 {20, 9},
455 {25, 9},
456 {30, 9},
457 {35, 9},
458 {40, 9}, /* <----- in the middle of the \r run, but run not counted */
459 {45, 0},
461 int i;
462 LRESULT result;
464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
466 for (i = 0; i < 10; i++) {
467 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
468 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
469 offset_test[i][0], result, offset_test[i][1]);
472 DestroyWindow(hwndRichEdit);
475 static void test_EM_GETTEXTRANGE(void)
477 HWND hwndRichEdit = new_richedit(NULL);
478 const char * text1 = "foo bar\r\nfoo bar";
479 const char * text2 = "foo bar\rfoo bar";
480 const char * expect1 = "bar\r\nfoo";
481 const char * expect2 = "bar\rfoo";
482 char buffer[1024] = {0};
483 LRESULT result;
484 TEXTRANGEA textRange;
486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
488 textRange.lpstrText = buffer;
489 textRange.chrg.cpMin = 4;
490 textRange.chrg.cpMax = 12;
491 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
492 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
493 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
495 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
497 textRange.lpstrText = buffer;
498 textRange.chrg.cpMin = 4;
499 textRange.chrg.cpMax = 11;
500 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
501 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
503 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
506 DestroyWindow(hwndRichEdit);
509 static void test_EM_GETSELTEXT(void)
511 HWND hwndRichEdit = new_richedit(NULL);
512 const char * text1 = "foo bar\r\nfoo bar";
513 const char * text2 = "foo bar\rfoo bar";
514 const char * expect1 = "bar\r\nfoo";
515 const char * expect2 = "bar\rfoo";
516 char buffer[1024] = {0};
517 LRESULT result;
519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
521 SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
522 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
523 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
524 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
526 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
528 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
529 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
530 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
532 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
535 DestroyWindow(hwndRichEdit);
538 static const char haystack[] = "WINEWine wineWine wine WineWine";
539 /* ^0 ^10 ^20 ^30 */
541 struct find_s {
542 int start;
543 int end;
544 const char *needle;
545 int flags;
546 int expected_loc;
547 int _todo_wine;
551 struct find_s find_tests[] = {
552 /* Find in empty text */
553 {0, -1, "foo", FR_DOWN, -1, 0},
554 {0, -1, "foo", 0, -1, 0},
555 {0, -1, "", FR_DOWN, -1, 0},
556 {20, 5, "foo", FR_DOWN, -1, 0},
557 {5, 20, "foo", FR_DOWN, -1, 0}
560 struct find_s find_tests2[] = {
561 /* No-result find */
562 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
563 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
565 /* Subsequent finds */
566 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
567 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
568 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
569 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
571 /* Find backwards */
572 {19, 20, "Wine", FR_MATCHCASE, -1, 0},
573 {10, 20, "Wine", FR_MATCHCASE, 13, 0},
574 {20, 10, "Wine", FR_MATCHCASE, -1, 0},
576 /* Case-insensitive */
577 {1, 31, "wInE", FR_DOWN, 4, 0},
578 {1, 31, "Wine", FR_DOWN, 4, 0},
580 /* High-to-low ranges */
581 {20, 5, "Wine", FR_DOWN, -1, 0},
582 {2, 1, "Wine", FR_DOWN, -1, 0},
583 {30, 29, "Wine", FR_DOWN, -1, 0},
584 {20, 5, "Wine", 0, /*13*/ -1, 0},
586 /* Find nothing */
587 {5, 10, "", FR_DOWN, -1, 0},
588 {10, 5, "", FR_DOWN, -1, 0},
589 {0, -1, "", FR_DOWN, -1, 0},
590 {10, 5, "", 0, -1, 0},
592 /* Whole-word search */
593 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
594 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
595 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
596 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
597 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
598 {11, -1, "winewine", FR_WHOLEWORD, 23, 0},
599 {31, -1, "winewine", FR_WHOLEWORD, -1, 0},
601 /* Bad ranges */
602 {5, 200, "XXX", FR_DOWN, -1, 0},
603 {-20, 20, "Wine", FR_DOWN, -1, 0},
604 {-20, 20, "Wine", FR_DOWN, -1, 0},
605 {-15, -20, "Wine", FR_DOWN, -1, 0},
606 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
608 /* Check the case noted in bug 4479 where matches at end aren't recognized */
609 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
610 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
611 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
612 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
613 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
615 /* The backwards case of bug 4479; bounds look right
616 * Fails because backward find is wrong */
617 {19, 20, "WINE", FR_MATCHCASE, -1, 0},
618 {0, 20, "WINE", FR_MATCHCASE, 0, 0},
620 {0, -1, "wineWine wine", FR_DOWN, 0, 0},
621 {0, -1, "wineWine wine", 0, 0, 0},
622 {0, -1, "INEW", 0, 1, 0},
623 {0, 31, "INEW", 0, 1, 0},
624 {4, -1, "INEW", 0, 10, 0},
627 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
628 int findloc;
629 FINDTEXT ft;
630 memset(&ft, 0, sizeof(ft));
631 ft.chrg.cpMin = f->start;
632 ft.chrg.cpMax = f->end;
633 ft.lpstrText = f->needle;
634 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
635 ok(findloc == f->expected_loc,
636 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
637 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
640 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
641 int id) {
642 int findloc;
643 FINDTEXTEX ft;
644 int expected_end_loc;
646 memset(&ft, 0, sizeof(ft));
647 ft.chrg.cpMin = f->start;
648 ft.chrg.cpMax = f->end;
649 ft.lpstrText = f->needle;
650 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
651 ok(findloc == f->expected_loc,
652 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
653 name, id, f->needle, f->start, f->end, f->flags, findloc);
654 ok(ft.chrgText.cpMin == f->expected_loc,
655 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
656 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
657 expected_end_loc = ((f->expected_loc == -1) ? -1
658 : f->expected_loc + strlen(f->needle));
659 ok(ft.chrgText.cpMax == expected_end_loc,
660 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
661 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
664 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
665 int num_tests)
667 int i;
669 for (i = 0; i < num_tests; i++) {
670 if (find[i]._todo_wine) {
671 todo_wine {
672 check_EM_FINDTEXT(hwnd, name, &find[i], i);
673 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
675 } else {
676 check_EM_FINDTEXT(hwnd, name, &find[i], i);
677 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
682 static void test_EM_FINDTEXT(void)
684 HWND hwndRichEdit = new_richedit(NULL);
686 /* Empty rich edit control */
687 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
688 sizeof(find_tests)/sizeof(struct find_s));
690 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
692 /* Haystack text */
693 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
694 sizeof(find_tests2)/sizeof(struct find_s));
696 DestroyWindow(hwndRichEdit);
699 static void test_EM_POSFROMCHAR(void)
701 HWND hwndRichEdit = new_richedit(NULL);
702 int i;
703 POINTL pl;
704 LRESULT result;
705 unsigned int height = 0;
706 int xpos = 0;
707 static const char text[] = "aa\n"
708 "this is a long line of text that should be longer than the "
709 "control's width\n"
710 "cc\n"
711 "dd\n"
712 "ee\n"
713 "ff\n"
714 "gg\n"
715 "hh\n";
717 /* Fill the control to lines to ensure that most of them are offscreen */
718 for (i = 0; i < 50; i++)
720 /* Do not modify the string; it is exactly 16 characters long. */
721 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
722 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
726 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
727 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
728 Richedit 3.0 accepts either of the above API conventions.
731 /* Testing Richedit 1.0 API format */
733 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
734 Since all lines are identical and drawn with the same font,
735 they should have the same height... right?
737 for (i = 0; i < 50; i++)
739 /* All the lines are 16 characters long */
740 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
741 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
742 if (i == 0)
744 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
745 todo_wine {
746 ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
748 xpos = pl.x;
750 else if (i == 1)
752 ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
753 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
754 height = pl.y;
756 else
758 ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
759 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
763 /* Testing position at end of text */
764 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
765 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
766 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
767 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
769 /* Testing position way past end of text */
770 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
771 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
772 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
773 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
776 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
777 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
778 for (i = 0; i < 50; i++)
780 /* All the lines are 16 characters long */
781 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
782 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
783 ok(pl.y == (i - 1) * height,
784 "EM_POSFROMCHAR reports y=%d, expected %d\n",
785 pl.y, (i - 1) * height);
786 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
789 /* Testing position at end of text */
790 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
791 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
792 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
793 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
795 /* Testing position way past end of text */
796 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
797 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
798 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
799 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
801 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
802 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
803 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
805 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
806 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
807 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
808 todo_wine {
809 ok(pl.x == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
811 xpos = pl.x;
813 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
814 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
815 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
816 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
817 todo_wine {
818 /* Fails on builtin because horizontal scrollbar is not being shown */
819 ok(pl.x < xpos, "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n", pl.x, xpos);
821 DestroyWindow(hwndRichEdit);
825 START_TEST( editor )
827 MSG msg;
828 time_t end;
830 /* Must explicitly LoadLibrary(). The test has no references to functions in
831 * RICHED32.DLL, so the linker doesn't actually link to it. */
832 hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
833 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
835 test_WM_SETTEXT();
836 test_EM_GETTEXTRANGE();
837 test_EM_GETSELTEXT();
838 test_WM_GETTEXTLENGTH();
839 test_EM_STREAMIN();
840 test_EM_STREAMOUT();
841 test_EM_GETLINE();
842 test_EM_LINELENGTH();
843 test_EM_FINDTEXT();
844 test_EM_POSFROMCHAR();
846 /* Set the environment variable WINETEST_RICHED32 to keep windows
847 * responsive and open for 30 seconds. This is useful for debugging.
849 * The message pump uses PeekMessage() to empty the queue and then sleeps for
850 * 50ms before retrying the queue. */
851 end = time(NULL) + 30;
852 if (getenv( "WINETEST_RICHED32" )) {
853 while (time(NULL) < end) {
854 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
855 TranslateMessage(&msg);
856 DispatchMessage(&msg);
857 } else {
858 Sleep(50);
863 OleFlushClipboard();
864 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());