Add AppDefaults app selection to control panel
[wine/gsoc-2012-control.git] / dlls / riched32 / tests / editor.c
blob5a031e3c61203710550a6223a7d4b1dc01c39009
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 <stdio.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, 500, 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 BOOL is_rtl(void) {
52 LOCALESIGNATURE sig;
54 return (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
55 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
56 (sig.lsUsb[3] & 0x08000000) != 0);
59 static void test_WM_SETTEXT(void)
61 static const struct {
62 const char *itemtext;
63 DWORD lines;
64 DWORD lines_rtl;
65 DWORD lines_broken;
66 } testitems[] = {
67 { "TestSomeText", 1, 1},
68 { "TestSomeText\r", 1, 1},
69 { "TestSomeText\rSomeMoreText\r", 2, 1, 1}, /* NT4 and below */
70 { "TestSomeText\n\nTestSomeText", 3, 3},
71 { "TestSomeText\r\r\nTestSomeText", 2, 2},
72 { "TestSomeText\r\r\n\rTestSomeText", 3, 2, 2}, /* NT4 and below */
73 { "TestSomeText\r\n\r\r\n\rTestSomeText", 4, 3, 3}, /* NT4 and below */
74 { "TestSomeText\r\n", 2, 2},
75 { "TestSomeText\r\nSomeMoreText\r\n", 3, 3},
76 { "TestSomeText\r\n\r\nTestSomeText", 3, 3},
77 { "TestSomeText TestSomeText", 1, 1},
78 { "TestSomeText \r\nTestSomeText", 2, 2},
79 { "TestSomeText\r\n \r\nTestSomeText", 3, 3},
80 { "TestSomeText\n", 2, 2},
81 { "TestSomeText\r\r\r", 3, 1, 1}, /* NT4 and below */
82 { "TestSomeText\r\r\rSomeMoreText", 4, 1, 1} /* NT4 and below */
84 HWND hwndRichEdit = new_richedit(NULL);
85 int i;
86 BOOL rtl = is_rtl();
88 /* This test attempts to show that WM_SETTEXT on a riched32 control does not
89 * attempt to modify the text that is pasted into the control, and should
90 * return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
92 * For riched32, the rules for breaking lines seem to be the following:
93 * - \r\n is one line break. This is the normal case.
94 * - \r{0,2}\n is one line break. In particular, \n by itself is a line break.
95 * - \r{0,N-1}\r\r\n is N line breaks.
96 * - \n{1,N} are that many line breaks.
97 * - \r with text or other characters (except \n) past it, is a line break. That
98 * is, a run of \r{N} without a terminating \n is considered N line breaks
99 * - \r at the end of the text is NOT a line break. This differs from riched20,
100 * where \r at the end of the text is a proper line break.
101 * However, on RTL language versions, \r is simply skipped and never used
102 * for line breaking (only \n adds a line break)
105 for (i = 0; i < sizeof(testitems)/sizeof(testitems[0]); i++) {
107 char buf[1024] = {0};
108 LRESULT result;
110 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testitems[i].itemtext);
111 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
112 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
113 ok (result == lstrlen(buf),
114 "[%d] WM_GETTEXT returned %ld instead of expected %u\n",
115 i, result, lstrlen(buf));
116 result = strcmp(testitems[i].itemtext, buf);
117 ok (result == 0,
118 "[%d] WM_SETTEXT round trip: strcmp = %ld\n", i, result);
119 result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
120 ok (result == (rtl ? testitems[i].lines_rtl : testitems[i].lines) ||
121 broken(testitems[i].lines_broken && result == testitems[i].lines_broken),
122 "[%d] EM_GETLINECOUNT returned %ld, expected %d\n", i, result, testitems[i].lines);
125 DestroyWindow(hwndRichEdit);
128 static void test_WM_GETTEXTLENGTH(void)
130 HWND hwndRichEdit = new_richedit(NULL);
131 static const char text3[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
132 static const char text4[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
133 int result;
135 /* Test for WM_GETTEXTLENGTH */
136 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text3);
137 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
138 ok(result == lstrlen(text3),
139 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
140 result, lstrlen(text3));
142 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text4);
143 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
144 ok(result == lstrlen(text4),
145 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
146 result, lstrlen(text4));
148 DestroyWindow(hwndRichEdit);
151 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
152 LPBYTE pbBuff,
153 LONG cb,
154 LONG *pcb)
156 const char** str = (const char**)dwCookie;
157 int size = strlen(*str);
158 *pcb = cb;
159 if (*pcb > size) {
160 *pcb = size;
162 if (*pcb > 0) {
163 memcpy(pbBuff, *str, *pcb);
164 *str += *pcb;
166 return 0;
170 static void test_EM_STREAMIN(void)
172 HWND hwndRichEdit = new_richedit(NULL);
173 LRESULT result;
174 EDITSTREAM es;
175 char buffer[1024] = {0};
177 const char * streamText0 = "{\\rtf1 TestSomeText}";
178 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
179 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
181 const char * streamText1 =
182 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
183 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
184 "}\r\n";
186 /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
187 const char * streamText2 =
188 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
189 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
190 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
191 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
192 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
193 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
194 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
196 const char * streamText3 = "RichEdit1";
198 /* Minimal test without \par at the end */
199 es.dwCookie = (DWORD_PTR)&streamText0;
200 es.dwError = 0;
201 es.pfnCallback = test_EM_STREAMIN_esCallback;
202 SendMessage(hwndRichEdit, EM_STREAMIN,
203 (WPARAM)(SF_RTF), (LPARAM)&es);
205 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
206 ok (result == 12,
207 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
208 result = strcmp (buffer,"TestSomeText");
209 ok (result == 0,
210 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
211 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
213 /* Native richedit 2.0 ignores last \par */
214 es.dwCookie = (DWORD_PTR)&streamText0a;
215 es.dwError = 0;
216 es.pfnCallback = test_EM_STREAMIN_esCallback;
217 SendMessage(hwndRichEdit, EM_STREAMIN,
218 (WPARAM)(SF_RTF), (LPARAM)&es);
220 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
221 ok (result == 12,
222 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
223 result = strcmp (buffer,"TestSomeText");
224 ok (result == 0,
225 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
226 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
228 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
229 es.dwCookie = (DWORD_PTR)&streamText0b;
230 es.dwError = 0;
231 es.pfnCallback = test_EM_STREAMIN_esCallback;
232 SendMessage(hwndRichEdit, EM_STREAMIN,
233 (WPARAM)(SF_RTF), (LPARAM)&es);
235 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
236 ok (result == 14,
237 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
238 result = strcmp (buffer,"TestSomeText\r\n");
239 ok (result == 0,
240 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
241 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
243 es.dwCookie = (DWORD_PTR)&streamText1;
244 es.dwError = 0;
245 es.pfnCallback = test_EM_STREAMIN_esCallback;
246 SendMessage(hwndRichEdit, EM_STREAMIN,
247 (WPARAM)(SF_RTF), (LPARAM)&es);
249 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
250 ok (result == 12,
251 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
252 result = strcmp (buffer,"TestSomeText");
253 ok (result == 0,
254 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
255 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
258 es.dwCookie = (DWORD_PTR)&streamText2;
259 es.dwError = 0;
260 SendMessage(hwndRichEdit, EM_STREAMIN,
261 (WPARAM)(SF_RTF), (LPARAM)&es);
263 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
264 todo_wine {
265 ok (result == 9,
266 "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
268 result = strcmp (buffer,"RichEdit1");
269 todo_wine {
270 ok (result == 0,
271 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
273 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
275 es.dwCookie = (DWORD_PTR)&streamText3;
276 es.dwError = 0;
277 SendMessage(hwndRichEdit, EM_STREAMIN,
278 (WPARAM)(SF_RTF), (LPARAM)&es);
280 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
281 ok (result == 0,
282 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
283 ok (strlen(buffer) == 0,
284 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
285 ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
287 DestroyWindow(hwndRichEdit);
290 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
291 LPBYTE pbBuff,
292 LONG cb,
293 LONG *pcb)
295 char** str = (char**)dwCookie;
296 *pcb = cb;
297 if (*pcb > 0) {
298 memcpy(*str, pbBuff, *pcb);
299 *str += *pcb;
301 return 0;
304 static void test_EM_STREAMOUT(void)
306 HWND hwndRichEdit = new_richedit(NULL);
307 int r;
308 EDITSTREAM es;
309 char buf[1024] = {0};
310 char * p;
312 const char * TestItem1 = "TestSomeText";
313 const char * TestItem2 = "TestSomeText\r";
314 const char * TestItem3 = "TestSomeText\r\n";
316 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
317 p = buf;
318 es.dwCookie = (DWORD_PTR)&p;
319 es.dwError = 0;
320 es.pfnCallback = test_WM_SETTEXT_esCallback;
321 memset(buf, 0, sizeof(buf));
322 SendMessage(hwndRichEdit, EM_STREAMOUT,
323 (WPARAM)(SF_TEXT), (LPARAM)&es);
324 r = strlen(buf);
325 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
326 ok(strcmp(buf, TestItem1) == 0,
327 "streamed text different, got %s\n", buf);
329 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
330 p = buf;
331 es.dwCookie = (DWORD_PTR)&p;
332 es.dwError = 0;
333 es.pfnCallback = test_WM_SETTEXT_esCallback;
334 memset(buf, 0, sizeof(buf));
335 SendMessage(hwndRichEdit, EM_STREAMOUT,
336 (WPARAM)(SF_TEXT), (LPARAM)&es);
337 r = strlen(buf);
339 ok(r == 13, "streamed text length is %d, expecting 13\n", r);
340 ok(strcmp(buf, TestItem2) == 0,
341 "streamed text different, got %s\n", buf);
343 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
344 p = buf;
345 es.dwCookie = (DWORD_PTR)&p;
346 es.dwError = 0;
347 es.pfnCallback = test_WM_SETTEXT_esCallback;
348 memset(buf, 0, sizeof(buf));
349 SendMessage(hwndRichEdit, EM_STREAMOUT,
350 (WPARAM)(SF_TEXT), (LPARAM)&es);
351 r = strlen(buf);
352 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
353 ok(strcmp(buf, TestItem3) == 0,
354 "streamed text different, got %s\n", buf);
356 DestroyWindow(hwndRichEdit);
359 static const struct getline_s {
360 int line;
361 size_t buffer_len;
362 const char *text;
363 const char *broken_text;
364 } gl[] = {
365 {0, 10, "foo bar\r\n", "foo bar\r\n"},
366 {1, 10, "\r", "\r\r\r\n"},
367 {2, 10, "\r\r\n", "bar\n"},
368 {3, 10, "bar\n", "\r\n"},
369 {4, 10, "\r\n"},
371 /* Buffer smaller than line length */
372 {0, 2, "foo bar\r"},
373 {0, 1, "foo bar\r"},
374 {0, 0, "foo bar\r"}
377 static void test_EM_GETLINE(void)
379 int i;
380 HWND hwndRichEdit = new_richedit(NULL);
381 static const int nBuf = 1024;
382 char dest[1024], origdest[1024];
383 LRESULT linecount;
384 const char text[] = "foo bar\r\n"
385 "\r"
386 "\r\r\n"
387 "bar\n";
388 BOOL broken_os = FALSE;
389 BOOL rtl = is_rtl();
391 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
392 linecount = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
393 if (linecount == 4)
395 broken_os = TRUE;
396 win_skip("Win9x, WinME and NT4 handle '\\r only' differently\n");
399 memset(origdest, 0xBB, nBuf);
400 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
402 int nCopied, expected_nCopied, expected_bytes_written;
403 char gl_text[1024];
405 if (gl[i].line >= linecount)
406 continue; /* Win9x, WinME and NT4 */
408 if (broken_os && gl[i].broken_text)
409 /* Win9x, WinME and NT4 */
410 strcpy(gl_text, gl[i].broken_text);
411 else
412 strcpy(gl_text, gl[i].text);
414 expected_nCopied = min(gl[i].buffer_len, strlen(gl_text));
415 /* Cater for the fact that Win9x, WinME and NT4 don't append the '\0' */
416 expected_bytes_written = min(gl[i].buffer_len, strlen(gl_text) + (broken_os ? 0 : 1));
418 memset(dest, 0xBB, nBuf);
419 *(WORD *) dest = gl[i].buffer_len;
421 /* EM_GETLINE appends a "\r\0" to the end of the line
422 * nCopied counts up to and including the '\r' */
423 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
424 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
425 expected_nCopied);
426 /* two special cases since a parameter is passed via dest */
427 if (gl[i].buffer_len == 0)
428 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
429 "buffer_len=0\n");
430 else if (gl[i].buffer_len == 1)
431 ok(dest[0] == gl_text[0] && !dest[1] &&
432 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
433 else
435 ok(!strncmp(dest, gl_text, expected_bytes_written),
436 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
437 if (! rtl || expected_bytes_written == gl[i].buffer_len)
438 ok(!strncmp(dest + expected_bytes_written, origdest
439 + expected_bytes_written, nBuf - expected_bytes_written),
440 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
441 else
442 ok(dest[expected_bytes_written] == 0 &&
443 !strncmp(dest + expected_bytes_written + 1, origdest
444 + expected_bytes_written + 1, nBuf - (expected_bytes_written + 1)),
445 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
449 DestroyWindow(hwndRichEdit);
452 static void test_EM_LINELENGTH(void)
454 HWND hwndRichEdit = new_richedit(NULL);
455 const char * text =
456 "richedit1\r"
457 "richedit1\n"
458 "richedit1\r\n"
459 "short\r"
460 "richedit1\r"
461 "\r"
462 "\r"
463 "\r\r\n";
464 int offset_test[16][2] = {
465 {0, 9}, /* Line 1: |richedit1\r */
466 {5, 9}, /* Line 1: riche|dit1\r */
467 {10, 9}, /* Line 2: |richedit1\n */
468 {15, 9}, /* Line 2: riche|dit1\n */
469 {20, 9}, /* Line 3: |richedit1\r\n */
470 {25, 9}, /* Line 3: riche|dit1\r\n */
471 {30, 9}, /* Line 3: richedit1\r|\n */
472 {31, 5}, /* Line 4: |short\r */
473 {42, 9}, /* Line 5: riche|dit1\r */
474 {46, 9}, /* Line 5: richedit1|\r */
475 {47, 0}, /* Line 6: |\r */
476 {48, 0}, /* Line 7: |\r */
477 {49, 0}, /* Line 8: |\r\r\n */
478 {50, 0}, /* Line 8: \r|\r\n */
479 {51, 0}, /* Line 8: \r\r|\n */
480 {52, 0}, /* Line 9: \r\r\n| */
482 int i;
483 LRESULT result;
485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
487 result = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
488 if (result == 4) {
489 win_skip("Win9x, WinME and NT4 don't handle '\\r only' correctly\n");
490 return;
492 ok(result == 9, "Incorrect line count of %ld\n", result);
494 for (i = 0; i < sizeof(offset_test)/sizeof(offset_test[0]); i++) {
495 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
496 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
497 offset_test[i][0], result, offset_test[i][1]);
500 DestroyWindow(hwndRichEdit);
503 static void test_EM_GETTEXTRANGE(void)
505 HWND hwndRichEdit = new_richedit(NULL);
506 const char * text1 = "foo bar\r\nfoo bar";
507 const char * text3 = "foo bar\rfoo bar";
508 const char * expect1 = "bar\r\nfoo";
509 const char * expect2 = "\nfoo";
510 const char * expect3 = "bar\rfoo";
511 char buffer[1024] = {0};
512 LRESULT result;
513 TEXTRANGEA textRange;
515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
517 textRange.lpstrText = buffer;
518 textRange.chrg.cpMin = 4;
519 textRange.chrg.cpMax = 12;
520 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
521 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
522 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
524 textRange.lpstrText = buffer;
525 textRange.chrg.cpMin = 8;
526 textRange.chrg.cpMax = 12;
527 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
528 ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
529 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
533 textRange.lpstrText = buffer;
534 textRange.chrg.cpMin = 4;
535 textRange.chrg.cpMax = 11;
536 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
537 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
539 ok(!strcmp(expect3, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
542 DestroyWindow(hwndRichEdit);
545 static void test_EM_GETSELTEXT(void)
547 HWND hwndRichEdit = new_richedit(NULL);
548 const char * text1 = "foo bar\r\nfoo bar";
549 const char * text2 = "foo bar\rfoo bar";
550 const char * expect1 = "bar\r\nfoo";
551 const char * expect2 = "bar\rfoo";
552 char buffer[1024] = {0};
553 LRESULT result;
555 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
557 SendMessage(hwndRichEdit, EM_SETSEL, 4, 12);
558 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
559 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
560 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
564 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
565 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
566 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
568 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
571 DestroyWindow(hwndRichEdit);
574 static const char haystack[] = "WINEWine wineWine wine WineWine";
575 /* ^0 ^10 ^20 ^30 */
577 static const char haystack2[] = "first\r\r\nsecond";
579 struct find_s {
580 int start;
581 int end;
582 const char *needle;
583 int flags;
584 int expected_loc;
588 static struct find_s find_tests[] = {
589 /* Find in empty text */
590 {0, -1, "foo", FR_DOWN, -1},
591 {0, -1, "foo", 0, -1},
592 {0, -1, "", FR_DOWN, -1},
593 {20, 5, "foo", FR_DOWN, -1},
594 {5, 20, "foo", FR_DOWN, -1}
597 static struct find_s find_tests2[] = {
598 /* No-result find */
599 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
600 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
602 /* Subsequent finds */
603 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
604 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
605 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
606 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
608 /* Find backwards */
609 {19, 20, "Wine", FR_MATCHCASE, -1},
610 {10, 20, "Wine", FR_MATCHCASE, 13},
611 {20, 10, "Wine", FR_MATCHCASE, -1},
613 /* Case-insensitive */
614 {1, 31, "wInE", FR_DOWN, 4},
615 {1, 31, "Wine", FR_DOWN, 4},
617 /* High-to-low ranges */
618 {20, 5, "Wine", FR_DOWN, -1},
619 {2, 1, "Wine", FR_DOWN, -1},
620 {30, 29, "Wine", FR_DOWN, -1},
621 {20, 5, "Wine", 0, /*13*/ -1},
623 /* Find nothing */
624 {5, 10, "", FR_DOWN, -1},
625 {10, 5, "", FR_DOWN, -1},
626 {0, -1, "", FR_DOWN, -1},
627 {10, 5, "", 0, -1},
629 /* Whole-word search */
630 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
631 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
632 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
633 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
634 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
635 {11, -1, "winewine", FR_WHOLEWORD, 23},
636 {31, -1, "winewine", FR_WHOLEWORD, -1},
638 /* Bad ranges */
639 {5, 200, "XXX", FR_DOWN, -1},
640 {-20, 20, "Wine", FR_DOWN, -1},
641 {-20, 20, "Wine", FR_DOWN, -1},
642 {-15, -20, "Wine", FR_DOWN, -1},
643 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
645 /* Check the case noted in bug 4479 where matches at end aren't recognized */
646 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
647 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
648 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
649 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
650 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
652 /* The backwards case of bug 4479; bounds look right
653 * Fails because backward find is wrong */
654 {19, 20, "WINE", FR_MATCHCASE, -1},
655 {0, 20, "WINE", FR_MATCHCASE, 0},
657 {0, -1, "wineWine wine", FR_DOWN, 0},
658 {0, -1, "wineWine wine", 0, 0},
659 {0, -1, "INEW", 0, 1},
660 {0, 31, "INEW", 0, 1},
661 {4, -1, "INEW", 0, 10},
664 static struct find_s find_tests3[] = {
665 /* Searching for end of line characters */
666 {0, -1, "t\r\r\ns", FR_DOWN | FR_MATCHCASE, 4},
667 {6, -1, "\r\n", FR_DOWN | FR_MATCHCASE, 6},
668 {7, -1, "\n", FR_DOWN | FR_MATCHCASE, 7},
671 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
672 int findloc;
673 FINDTEXT ft;
674 memset(&ft, 0, sizeof(ft));
675 ft.chrg.cpMin = f->start;
676 ft.chrg.cpMax = f->end;
677 ft.lpstrText = f->needle;
678 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
679 ok(findloc == f->expected_loc,
680 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
681 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
684 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
685 int id) {
686 int findloc;
687 FINDTEXTEX ft;
688 int expected_end_loc;
690 memset(&ft, 0, sizeof(ft));
691 ft.chrg.cpMin = f->start;
692 ft.chrg.cpMax = f->end;
693 ft.lpstrText = f->needle;
694 ft.chrgText.cpMax = 0xdeadbeef;
695 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
696 ok(findloc == f->expected_loc,
697 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
698 name, id, f->needle, f->start, f->end, f->flags, findloc);
699 ok(ft.chrgText.cpMin == f->expected_loc,
700 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
701 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
702 expected_end_loc = ((f->expected_loc == -1) ? -1
703 : f->expected_loc + strlen(f->needle));
704 ok(ft.chrgText.cpMax == expected_end_loc ||
705 broken(ft.chrgText.cpMin == -1 && ft.chrgText.cpMax == 0xdeadbeef), /* Win9x, WinME and NT4 */
706 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
707 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
710 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
711 int num_tests)
713 int i;
715 for (i = 0; i < num_tests; i++) {
716 check_EM_FINDTEXT(hwnd, name, &find[i], i);
717 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
721 static void test_EM_FINDTEXT(void)
723 HWND hwndRichEdit = new_richedit(NULL);
725 /* Empty rich edit control */
726 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
727 sizeof(find_tests)/sizeof(struct find_s));
729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
731 /* Haystack text */
732 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
733 sizeof(find_tests2)/sizeof(struct find_s));
735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack2);
737 /* Haystack text 2 (with EOL characters) */
738 run_tests_EM_FINDTEXT(hwndRichEdit, "3", find_tests3,
739 sizeof(find_tests3)/sizeof(struct find_s));
741 DestroyWindow(hwndRichEdit);
744 static void test_EM_POSFROMCHAR(void)
746 HWND hwndRichEdit = new_richedit(NULL);
747 int i;
748 POINTL pl;
749 LRESULT result;
750 unsigned int height = 0;
751 int xpos = 0;
752 int xpos_rtl_adjusted = 0;
753 static const char text[] = "aa\n"
754 "this is a long line of text that should be longer than the "
755 "control's width\n"
756 "cc\n"
757 "dd\n"
758 "ee\n"
759 "ff\n"
760 "gg\n"
761 "hh\n";
763 /* Fill the control to lines to ensure that most of them are offscreen */
764 for (i = 0; i < 50; i++)
766 /* Do not modify the string; it is exactly 16 characters long. */
767 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
768 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
772 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
773 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
774 Richedit 3.0 accepts either of the above API conventions.
777 /* Testing Richedit 1.0 API format */
779 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
780 Since all lines are identical and drawn with the same font,
781 they should have the same height... right?
783 for (i = 0; i < 50; i++)
785 /* All the lines are 16 characters long */
786 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
787 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
788 if (i == 0)
790 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
791 ok(pl.x == 1 ||
792 broken(pl.x == 0), /* Win9x, WinME and NT4 */
793 "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
794 xpos = pl.x;
795 xpos_rtl_adjusted = xpos + (is_rtl() ? 7 : 0);
797 else if (i == 1)
799 ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
800 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
801 height = pl.y;
803 else
805 ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
806 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
810 /* Testing position at end of text */
811 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
812 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
813 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
814 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
816 /* Testing position way past end of text */
817 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
818 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
819 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
821 ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
824 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
825 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
826 for (i = 0; i < 50; i++)
828 /* All the lines are 16 characters long */
829 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
830 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
831 ok(pl.y == (i - 1) * height,
832 "EM_POSFROMCHAR reports y=%d, expected %d\n",
833 pl.y, (i - 1) * height);
834 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
837 /* Testing position at end of text */
838 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
839 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
840 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
841 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
843 /* Testing position way past end of text */
844 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
845 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
846 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
847 ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
849 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
851 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
853 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
854 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
855 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
856 ok(pl.x == 1 ||
857 broken(pl.x == 0), /* Win9x, WinME and NT4 */
858 "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
859 xpos = pl.x;
861 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
862 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
863 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
864 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
865 todo_wine {
866 /* Fails on builtin because horizontal scrollbar is not being shown */
867 ok(pl.x < xpos ||
868 broken(pl.x == xpos), /* Win9x, WinME and NT4 */
869 "EM_POSFROMCHAR reports x=%d, expected value less than %d\n", pl.x, xpos);
871 DestroyWindow(hwndRichEdit);
874 static void test_word_wrap(void)
876 HWND hwnd;
877 POINTL point = {0, 60}; /* This point must be below the first line */
878 const char *text = "Must be long enough to test line wrapping";
879 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
880 int res, pos, lines, prevlines, reflines[3];
882 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
883 * when specified on window creation and set later. */
884 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
885 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
886 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
887 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
888 ok(res, "WM_SETTEXT failed.\n");
889 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
890 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
891 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
892 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
894 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
895 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
896 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
897 DestroyWindow(hwnd);
899 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
900 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
901 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
903 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
904 ok(res, "WM_SETTEXT failed.\n");
905 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
906 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
907 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
908 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
910 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
911 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
912 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
913 DestroyWindow(hwnd);
915 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
916 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
917 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
918 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
919 ok(res, "WM_SETTEXT failed.\n");
920 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
921 ok(!pos ||
922 broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
923 "pos=%d indicating word wrap when none is expected.\n", pos);
924 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
925 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
927 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
928 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
929 ok(!pos ||
930 broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
931 "pos=%d indicating word wrap when none is expected.\n", pos);
932 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
933 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
934 DestroyWindow(hwnd);
936 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
937 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
938 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
939 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
940 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
941 ok(res, "WM_SETTEXT failed.\n");
942 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
943 ok(!pos ||
944 broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
945 "pos=%d indicating word wrap when none is expected.\n", pos);
946 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
947 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
949 SetWindowLong(hwnd, GWL_STYLE, dwCommonStyle);
950 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
951 ok(!pos ||
952 broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
953 "pos=%d indicating word wrap when none is expected.\n", pos);
954 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
955 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
957 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
958 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
959 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
960 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
961 ok(!pos ||
962 broken(pos == lstrlen(text)), /* Win9x, WinME and NT4 */
963 "pos=%d indicating word wrap when none is expected.\n", pos);
964 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
965 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
967 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
968 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
969 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
970 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
971 DestroyWindow(hwnd);
973 /* First lets see if the text would wrap normally (needed for reference) */
974 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
975 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
976 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
977 ok(IsWindowVisible(hwnd), "Window should be visible.\n");
978 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
979 ok(res, "EM_REPLACESEL failed.\n");
980 /* Should have wrapped */
981 reflines[0] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
982 ok(reflines[0] > 1, "Line was expected to wrap (%d lines).\n", reflines[0]);
983 /* Resize the window to fit the line */
984 MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
985 /* Text should not be wrapped */
986 reflines[1] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
987 ok(reflines[1] == 1, "Line wasn't expected to wrap (%d lines).\n", reflines[1]);
988 /* Resize the window again to make sure the line wraps again */
989 MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
990 reflines[2] = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
991 ok(reflines[2] > 1, "Line was expected to wrap (%d lines).\n", reflines[2]);
992 DestroyWindow(hwnd);
994 /* Same test with redraw disabled */
995 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
996 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
997 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
998 ok(IsWindowVisible(hwnd), "Window should be visible.\n");
999 /* Redraw is disabled by making the window invisible. */
1000 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
1001 ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
1002 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
1003 ok(res, "EM_REPLACESEL failed.\n");
1004 /* Should have wrapped */
1005 prevlines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
1006 ok(prevlines == reflines[0],
1007 "Line was expected to wrap (%d lines).\n", prevlines);
1008 /* Resize the window to fit the line, no change to the number of lines */
1009 MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
1010 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
1011 todo_wine
1012 ok(lines == prevlines ||
1013 broken(lines == reflines[1]), /* Win98, WinME and NT4 */
1014 "Expected no change in the number of lines\n");
1015 /* Resize the window again to make sure the line wraps again */
1016 MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
1017 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
1018 todo_wine
1019 ok(lines == prevlines ||
1020 broken(lines == reflines[2]), /* Win98, WinME and NT4 */
1021 "Expected no change in the number of lines\n");
1022 DestroyWindow(hwnd);
1025 static void test_EM_GETOPTIONS(void)
1027 HWND hwnd;
1028 DWORD options;
1030 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
1031 WS_POPUP,
1032 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1033 options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1034 ok(options == 0, "Incorrect options %x\n", options);
1035 DestroyWindow(hwnd);
1037 hwnd = CreateWindow(RICHEDIT_CLASS10A, NULL,
1038 WS_POPUP|WS_VSCROLL|WS_HSCROLL,
1039 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1040 options = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1041 ok(options == ECO_AUTOVSCROLL ||
1042 broken(options == 0), /* Win9x, WinME and NT4 */
1043 "Incorrect initial options %x\n", options);
1044 DestroyWindow(hwnd);
1047 static void test_autoscroll(void)
1049 HWND hwnd;
1050 UINT ret;
1052 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
1053 * auto vertical/horizontal scrolling options. */
1054 hwnd = CreateWindowEx(0, RICHEDIT_CLASS10A, NULL,
1055 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
1056 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1057 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1058 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1059 ok(ret & ECO_AUTOVSCROLL ||
1060 broken(!(ret & ECO_AUTOVSCROLL)), /* Win9x, WinME and NT4 */
1061 "ECO_AUTOVSCROLL isn't set.\n");
1062 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1063 ret = GetWindowLong(hwnd, GWL_STYLE);
1064 todo_wine
1065 ok(ret & ES_AUTOVSCROLL ||
1066 broken(!(ret & ES_AUTOVSCROLL)), /* Win9x, WinMe and NT4 */
1067 "ES_AUTOVSCROLL isn't set.\n");
1068 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1069 DestroyWindow(hwnd);
1071 hwnd = CreateWindowEx(0, RICHEDIT_CLASS10A, NULL,
1072 WS_POPUP|ES_MULTILINE,
1073 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1074 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1075 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
1076 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
1077 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1078 ret = GetWindowLong(hwnd, GWL_STYLE);
1079 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
1080 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1081 DestroyWindow(hwnd);
1084 static void simulate_typing_characters(HWND hwnd, const char* szChars)
1086 int ret;
1088 while (*szChars != '\0') {
1089 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
1090 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
1091 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
1092 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
1093 szChars++;
1098 * This test attempts to show the effect of enter on a richedit
1099 * control v1.0 inserts CRLF whereas for higher versions it only
1100 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
1101 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
1102 * does for higher. The same test is cloned in riched32 and riched20.
1104 static void test_enter(void)
1106 static const struct {
1107 const char *initialtext;
1108 const int cursor;
1109 const char *expectedtext;
1110 } testenteritems[] = {
1111 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n"},
1112 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n"},
1113 { "aa\rabbb\r\n", 7, "aa\rabbb\r\n\r\n"},
1114 { "aa\rabbb\r\n", 3, "aa\r\r\nabbb\r\n"},
1115 { "aa\rabbb\r\n", 2, "aa\r\n\rabbb\r\n"}
1118 char expectedbuf[1024];
1119 char resultbuf[1024];
1120 HWND hwndRichEdit = new_richedit(NULL);
1121 UINT i,j;
1123 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
1125 char buf[1024] = {0};
1126 LRESULT result;
1127 GETTEXTEX getText;
1128 const char *expected;
1130 /* Set the text to the initial text */
1131 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
1132 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1134 /* Send Enter */
1135 SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
1136 simulate_typing_characters(hwndRichEdit, "\r");
1138 /* 1. Retrieve with WM_GETTEXT */
1139 buf[0] = 0x00;
1140 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
1141 expected = testenteritems[i].expectedtext;
1143 resultbuf[0]=0x00;
1144 for (j = 0; j < (UINT)result; j++)
1145 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
1146 expectedbuf[0] = '\0';
1147 for (j = 0; j < strlen(expected); j++)
1148 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
1150 result = strcmp(expected, buf);
1151 ok (result == 0,
1152 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
1153 i, resultbuf, expectedbuf);
1155 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
1156 getText.cb = sizeof(buf);
1157 getText.flags = GT_DEFAULT;
1158 getText.codepage = CP_ACP;
1159 getText.lpDefaultChar = NULL;
1160 getText.lpUsedDefChar = NULL;
1161 buf[0] = 0x00;
1162 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1163 expected = testenteritems[i].expectedtext;
1165 resultbuf[0]=0x00;
1166 for (j = 0; j < (UINT)result; j++)
1167 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
1168 expectedbuf[0] = '\0';
1169 for (j = 0; j < strlen(expected); j++)
1170 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
1172 result = strcmp(expected, buf);
1173 ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1174 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
1175 i, resultbuf, expectedbuf);
1177 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
1178 getText.cb = sizeof(buf);
1179 getText.flags = GT_USECRLF;
1180 getText.codepage = CP_ACP;
1181 getText.lpDefaultChar = NULL;
1182 getText.lpUsedDefChar = NULL;
1183 buf[0] = 0x00;
1184 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
1185 expected = testenteritems[i].expectedtext;
1187 resultbuf[0]=0x00;
1188 for (j = 0; j < (UINT)result; j++)
1189 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
1190 expectedbuf[0] = '\0';
1191 for (j = 0; j < strlen(expected); j++)
1192 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
1194 result = strcmp(expected, buf);
1195 ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1196 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
1197 i, resultbuf, expectedbuf);
1200 DestroyWindow(hwndRichEdit);
1203 START_TEST( editor )
1205 MSG msg;
1206 time_t end;
1207 BOOL ret;
1209 /* Must explicitly LoadLibrary(). The test has no references to functions in
1210 * RICHED32.DLL, so the linker doesn't actually link to it. */
1211 hmoduleRichEdit = LoadLibrary("RICHED32.DLL");
1212 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1214 test_WM_SETTEXT();
1215 test_EM_GETTEXTRANGE();
1216 test_EM_GETSELTEXT();
1217 test_WM_GETTEXTLENGTH();
1218 test_EM_STREAMIN();
1219 test_EM_STREAMOUT();
1220 test_EM_GETLINE();
1221 test_EM_LINELENGTH();
1222 test_EM_FINDTEXT();
1223 test_EM_POSFROMCHAR();
1224 test_word_wrap();
1225 test_EM_GETOPTIONS();
1226 test_autoscroll();
1227 test_enter();
1229 /* Set the environment variable WINETEST_RICHED32 to keep windows
1230 * responsive and open for 30 seconds. This is useful for debugging.
1232 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1233 * 50ms before retrying the queue. */
1234 end = time(NULL) + 30;
1235 if (getenv( "WINETEST_RICHED32" )) {
1236 while (time(NULL) < end) {
1237 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1238 TranslateMessage(&msg);
1239 DispatchMessage(&msg);
1240 } else {
1241 Sleep(50);
1246 OleFlushClipboard();
1247 ret = FreeLibrary(hmoduleRichEdit);
1248 ok(ret, "error: %u\n", GetLastError());