dd9773c3f69bb1f7a0fb21dd2632fe2ca5fdd5d3
[reactos.git] / modules / rostests / winetests / riched32 / editor.c
1 /*
2 * Unit test suite for rich edit control 1.0
3 *
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
7 * Copyright 2007 Alex VillacĂ­s Lasso
8 *
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.
13 *
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.
18 *
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
22 */
23
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 <commdlg.h>
33 #include <richedit.h>
34 #include <time.h>
35 #include <wine/test.h>
36
37 static HMODULE hmoduleRichEdit;
38 static BOOL is_lang_japanese;
39
40 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
41 HWND hwnd;
42 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
43 |WS_VISIBLE, 0, 0, 500, 60, parent, NULL,
44 hmoduleRichEdit, NULL);
45 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
46 return hwnd;
47 }
48
49 static HWND new_richedit(HWND parent) {
50 return new_window(RICHEDIT_CLASS10A, ES_MULTILINE, parent);
51 }
52
53 static BOOL is_rtl(void) {
54 LOCALESIGNATURE sig;
55
56 return (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
57 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
58 (sig.lsUsb[3] & 0x08000000) != 0);
59 }
60
61 static void test_WM_SETTEXT(void)
62 {
63 static const struct {
64 const char *itemtext;
65 DWORD lines;
66 DWORD lines_rtl;
67 DWORD lines_broken;
68 } testitems[] = {
69 { "TestSomeText", 1, 1},
70 { "TestSomeText\r", 1, 1},
71 { "TestSomeText\rSomeMoreText\r", 2, 1, 1}, /* NT4 and below */
72 { "TestSomeText\n\nTestSomeText", 3, 3},
73 { "TestSomeText\r\r\nTestSomeText", 2, 2},
74 { "TestSomeText\r\r\n\rTestSomeText", 3, 2, 2}, /* NT4 and below */
75 { "TestSomeText\r\n\r\r\n\rTestSomeText", 4, 3, 3}, /* NT4 and below */
76 { "TestSomeText\r\n", 2, 2},
77 { "TestSomeText\r\nSomeMoreText\r\n", 3, 3},
78 { "TestSomeText\r\n\r\nTestSomeText", 3, 3},
79 { "TestSomeText TestSomeText", 1, 1},
80 { "TestSomeText \r\nTestSomeText", 2, 2},
81 { "TestSomeText\r\n \r\nTestSomeText", 3, 3},
82 { "TestSomeText\n", 2, 2},
83 { "TestSomeText\r\r\r", 3, 1, 1}, /* NT4 and below */
84 { "TestSomeText\r\r\rSomeMoreText", 4, 1, 1} /* NT4 and below */
85 };
86 HWND hwndRichEdit = new_richedit(NULL);
87 int i;
88 BOOL rtl = is_rtl();
89
90 /* This test attempts to show that WM_SETTEXT on a riched32 control does not
91 * attempt to modify the text that is pasted into the control, and should
92 * return it as is. In particular, \r\r\n is NOT converted, unlike riched20.
93 *
94 * For riched32, the rules for breaking lines seem to be the following:
95 * - \r\n is one line break. This is the normal case.
96 * - \r{0,2}\n is one line break. In particular, \n by itself is a line break.
97 * - \r{0,N-1}\r\r\n is N line breaks.
98 * - \n{1,N} are that many line breaks.
99 * - \r with text or other characters (except \n) past it, is a line break. That
100 * is, a run of \r{N} without a terminating \n is considered N line breaks
101 * - \r at the end of the text is NOT a line break. This differs from riched20,
102 * where \r at the end of the text is a proper line break.
103 * However, on RTL language versions, \r is simply skipped and never used
104 * for line breaking (only \n adds a line break)
105 */
106
107 for (i = 0; i < ARRAY_SIZE(testitems); i++) {
108
109 char buf[1024] = {0};
110 LRESULT result;
111
112 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testitems[i].itemtext);
113 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
114 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
115 ok (result == lstrlenA(buf),
116 "[%d] WM_GETTEXT returned %ld instead of expected %u\n",
117 i, result, lstrlenA(buf));
118 result = strcmp(testitems[i].itemtext, buf);
119 ok (result == 0,
120 "[%d] WM_SETTEXT round trip: strcmp = %ld\n", i, result);
121 result = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
122 ok (result == (rtl ? testitems[i].lines_rtl : testitems[i].lines) ||
123 broken(testitems[i].lines_broken && result == testitems[i].lines_broken),
124 "[%d] EM_GETLINECOUNT returned %ld, expected %d\n", i, result, testitems[i].lines);
125 }
126
127 DestroyWindow(hwndRichEdit);
128 }
129
130 static void test_WM_GETTEXTLENGTH(void)
131 {
132 HWND hwndRichEdit = new_richedit(NULL);
133 static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
134 static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
135 static const char text3[] = "abcdef\x8e\xf0";
136 int result;
137
138 /* Test for WM_GETTEXTLENGTH */
139 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
140 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
141 ok(result == lstrlenA(text1),
142 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
143 result, lstrlenA(text1));
144
145 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
146 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
147 ok(result == lstrlenA(text2),
148 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
149 result, lstrlenA(text2));
150
151 /* Test with multibyte character */
152 if (!is_lang_japanese)
153 skip("Skip multibyte character tests on non-Japanese platform\n");
154 else
155 {
156 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
157 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
158 ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
159 }
160
161 DestroyWindow(hwndRichEdit);
162 }
163
164 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
165 LPBYTE pbBuff,
166 LONG cb,
167 LONG *pcb)
168 {
169 const char** str = (const char**)dwCookie;
170 int size = strlen(*str);
171 *pcb = cb;
172 if (*pcb > size) {
173 *pcb = size;
174 }
175 if (*pcb > 0) {
176 memcpy(pbBuff, *str, *pcb);
177 *str += *pcb;
178 }
179 return 0;
180 }
181
182
183 static void test_EM_STREAMIN(void)
184 {
185 HWND hwndRichEdit = new_richedit(NULL);
186 LRESULT result;
187 EDITSTREAM es;
188 char buffer[1024] = {0};
189
190 const char * streamText0 = "{\\rtf1 TestSomeText}";
191 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
192 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
193
194 const char * streamText1 =
195 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
196 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
197 "}\r\n";
198
199 /* This should be accepted in richedit 1.0 emulation. See bug #8326 */
200 const char * streamText2 =
201 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
202 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
203 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
204 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
205 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
206 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
207 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
208
209 const char * streamText3 = "RichEdit1";
210
211 /* Minimal test without \par at the end */
212 es.dwCookie = (DWORD_PTR)&streamText0;
213 es.dwError = 0;
214 es.pfnCallback = test_EM_STREAMIN_esCallback;
215 SendMessageA(hwndRichEdit, EM_STREAMIN,
216 (WPARAM)(SF_RTF), (LPARAM)&es);
217
218 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
219 ok (result == 12,
220 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
221 result = strcmp (buffer,"TestSomeText");
222 ok (result == 0,
223 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
224 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
225
226 /* Native richedit 2.0 ignores last \par */
227 es.dwCookie = (DWORD_PTR)&streamText0a;
228 es.dwError = 0;
229 es.pfnCallback = test_EM_STREAMIN_esCallback;
230 SendMessageA(hwndRichEdit, EM_STREAMIN,
231 (WPARAM)(SF_RTF), (LPARAM)&es);
232
233 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
234 ok (result == 12,
235 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
236 result = strcmp (buffer,"TestSomeText");
237 ok (result == 0,
238 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
239 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
240
241 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
242 es.dwCookie = (DWORD_PTR)&streamText0b;
243 es.dwError = 0;
244 es.pfnCallback = test_EM_STREAMIN_esCallback;
245 SendMessageA(hwndRichEdit, EM_STREAMIN,
246 (WPARAM)(SF_RTF), (LPARAM)&es);
247
248 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
249 ok (result == 14,
250 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
251 result = strcmp (buffer,"TestSomeText\r\n");
252 ok (result == 0,
253 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
254 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
255
256 es.dwCookie = (DWORD_PTR)&streamText1;
257 es.dwError = 0;
258 es.pfnCallback = test_EM_STREAMIN_esCallback;
259 SendMessageA(hwndRichEdit, EM_STREAMIN,
260 (WPARAM)(SF_RTF), (LPARAM)&es);
261
262 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
263 ok (result == 12,
264 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
265 result = strcmp (buffer,"TestSomeText");
266 ok (result == 0,
267 "EM_STREAMIN: Test 1 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);
269
270
271 es.dwCookie = (DWORD_PTR)&streamText2;
272 es.dwError = 0;
273 SendMessageA(hwndRichEdit, EM_STREAMIN,
274 (WPARAM)(SF_RTF), (LPARAM)&es);
275
276 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
277 todo_wine {
278 ok (result == 9,
279 "EM_STREAMIN: Test 2 returned %ld, expected 9\n", result);
280 }
281 result = strcmp (buffer,"RichEdit1");
282 todo_wine {
283 ok (result == 0,
284 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
285 }
286 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
287
288 es.dwCookie = (DWORD_PTR)&streamText3;
289 es.dwError = 0;
290 SendMessageA(hwndRichEdit, EM_STREAMIN,
291 (WPARAM)(SF_RTF), (LPARAM)&es);
292
293 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
294 ok (result == 0,
295 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
296 ok (strlen(buffer) == 0,
297 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
298 ok(es.dwError == -16, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, -16);
299
300 DestroyWindow(hwndRichEdit);
301 }
302
303 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
304 LPBYTE pbBuff,
305 LONG cb,
306 LONG *pcb)
307 {
308 char** str = (char**)dwCookie;
309 *pcb = cb;
310 if (*pcb > 0) {
311 memcpy(*str, pbBuff, *pcb);
312 *str += *pcb;
313 }
314 return 0;
315 }
316
317 static void test_EM_STREAMOUT(void)
318 {
319 HWND hwndRichEdit = new_richedit(NULL);
320 int r;
321 EDITSTREAM es;
322 char buf[1024] = {0};
323 char * p;
324
325 const char * TestItem1 = "TestSomeText";
326 const char * TestItem2 = "TestSomeText\r";
327 const char * TestItem3 = "TestSomeText\r\n";
328
329 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
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 SendMessageA(hwndRichEdit, EM_STREAMOUT,
336 (WPARAM)(SF_TEXT), (LPARAM)&es);
337 r = strlen(buf);
338 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
339 ok(strcmp(buf, TestItem1) == 0,
340 "streamed text different, got %s\n", buf);
341
342 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
343 p = buf;
344 es.dwCookie = (DWORD_PTR)&p;
345 es.dwError = 0;
346 es.pfnCallback = test_WM_SETTEXT_esCallback;
347 memset(buf, 0, sizeof(buf));
348 SendMessageA(hwndRichEdit, EM_STREAMOUT,
349 (WPARAM)(SF_TEXT), (LPARAM)&es);
350 r = strlen(buf);
351
352 ok(r == 13, "streamed text length is %d, expecting 13\n", r);
353 ok(strcmp(buf, TestItem2) == 0,
354 "streamed text different, got %s\n", buf);
355
356 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
357 p = buf;
358 es.dwCookie = (DWORD_PTR)&p;
359 es.dwError = 0;
360 es.pfnCallback = test_WM_SETTEXT_esCallback;
361 memset(buf, 0, sizeof(buf));
362 SendMessageA(hwndRichEdit, EM_STREAMOUT,
363 (WPARAM)(SF_TEXT), (LPARAM)&es);
364 r = strlen(buf);
365 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
366 ok(strcmp(buf, TestItem3) == 0,
367 "streamed text different, got %s\n", buf);
368
369 DestroyWindow(hwndRichEdit);
370 }
371
372 static const struct getline_s {
373 int line;
374 size_t buffer_len;
375 const char *text;
376 const char *broken_text;
377 } gl[] = {
378 {0, 10, "foo bar\r\n", "foo bar\r\n"},
379 {1, 10, "\r", "\r\r\r\n"},
380 {2, 10, "\r\r\n", "bar\n"},
381 {3, 10, "bar\n", "\r\n"},
382 {4, 10, "\r\n"},
383
384 /* Buffer smaller than line length */
385 {0, 2, "foo bar\r"},
386 {0, 1, "foo bar\r"},
387 {0, 0, "foo bar\r"}
388 };
389
390 static void test_EM_GETLINE(void)
391 {
392 int i;
393 HWND hwndRichEdit = new_richedit(NULL);
394 static const int nBuf = 1024;
395 char dest[1024], origdest[1024];
396 LRESULT linecount;
397 const char text[] = "foo bar\r\n"
398 "\r"
399 "\r\r\n"
400 "bar\n";
401 BOOL broken_os = FALSE;
402 BOOL rtl = is_rtl();
403
404 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
405 linecount = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
406 if (linecount == 4)
407 {
408 broken_os = TRUE;
409 win_skip("Win9x, WinME and NT4 handle '\\r only' differently\n");
410 }
411
412 memset(origdest, 0xBB, nBuf);
413 for (i = 0; i < ARRAY_SIZE(gl); i++)
414 {
415 int nCopied, expected_nCopied, expected_bytes_written;
416 char gl_text[1024];
417
418 if (gl[i].line >= linecount)
419 continue; /* Win9x, WinME and NT4 */
420
421 if (broken_os && gl[i].broken_text)
422 /* Win9x, WinME and NT4 */
423 strcpy(gl_text, gl[i].broken_text);
424 else
425 strcpy(gl_text, gl[i].text);
426
427 expected_nCopied = min(gl[i].buffer_len, strlen(gl_text));
428 /* Cater for the fact that Win9x, WinME and NT4 don't append the '\0' */
429 expected_bytes_written = min(gl[i].buffer_len, strlen(gl_text) + (broken_os ? 0 : 1));
430
431 memset(dest, 0xBB, nBuf);
432 *(WORD *) dest = gl[i].buffer_len;
433
434 /* EM_GETLINE appends a "\r\0" to the end of the line
435 * nCopied counts up to and including the '\r' */
436 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
437 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
438 expected_nCopied);
439 /* two special cases since a parameter is passed via dest */
440 if (gl[i].buffer_len == 0)
441 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
442 "buffer_len=0\n");
443 else if (gl[i].buffer_len == 1)
444 ok(dest[0] == gl_text[0] && !dest[1] &&
445 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
446 else
447 {
448 ok(!strncmp(dest, gl_text, expected_bytes_written),
449 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
450 if (! rtl || expected_bytes_written == gl[i].buffer_len)
451 ok(!strncmp(dest + expected_bytes_written, origdest
452 + expected_bytes_written, nBuf - expected_bytes_written),
453 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
454 else
455 ok(dest[expected_bytes_written] == 0 &&
456 !strncmp(dest + expected_bytes_written + 1, origdest
457 + expected_bytes_written + 1, nBuf - (expected_bytes_written + 1)),
458 "%d: expected_bytes_written=%d\n", i, expected_bytes_written);
459 }
460 }
461
462 DestroyWindow(hwndRichEdit);
463 }
464
465 static void test_EM_LINELENGTH(void)
466 {
467 HWND hwndRichEdit = new_richedit(NULL);
468 const char * text =
469 "richedit1\r"
470 "richedit1\n"
471 "richedit1\r\n"
472 "short\r"
473 "richedit1\r"
474 "\r"
475 "\r"
476 "\r\r\n";
477 int offset_test[16][2] = {
478 {0, 9}, /* Line 1: |richedit1\r */
479 {5, 9}, /* Line 1: riche|dit1\r */
480 {10, 9}, /* Line 2: |richedit1\n */
481 {15, 9}, /* Line 2: riche|dit1\n */
482 {20, 9}, /* Line 3: |richedit1\r\n */
483 {25, 9}, /* Line 3: riche|dit1\r\n */
484 {30, 9}, /* Line 3: richedit1\r|\n */
485 {31, 5}, /* Line 4: |short\r */
486 {42, 9}, /* Line 5: riche|dit1\r */
487 {46, 9}, /* Line 5: richedit1|\r */
488 {47, 0}, /* Line 6: |\r */
489 {48, 0}, /* Line 7: |\r */
490 {49, 0}, /* Line 8: |\r\r\n */
491 {50, 0}, /* Line 8: \r|\r\n */
492 {51, 0}, /* Line 8: \r\r|\n */
493 {52, 0}, /* Line 9: \r\r\n| */
494 };
495 int i;
496 LRESULT result;
497
498 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
499
500 result = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
501 if (result == 4) {
502 win_skip("Win9x, WinME and NT4 don't handle '\\r only' correctly\n");
503 return;
504 }
505 ok(result == 9, "Incorrect line count of %ld\n", result);
506
507 for (i = 0; i < ARRAY_SIZE(offset_test); i++) {
508 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
509 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
510 offset_test[i][0], result, offset_test[i][1]);
511 }
512
513 /* Test with multibyte character */
514 if (!is_lang_japanese)
515 skip("Skip multibyte character tests on non-Japanese platform\n");
516 else
517 {
518 const char *text1 =
519 "wine\n"
520 "richedit\x8e\xf0\n"
521 "wine";
522 static int offset_test1[3][3] = {
523 {0, 4}, /* Line 1: |wine\n */
524 {5, 10, 1}, /* Line 2: |richedit\x8e\xf0\n */
525 {16, 4}, /* Line 3: |wine */
526 };
527 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
528 for (i = 0; i < ARRAY_SIZE(offset_test1); i++) {
529 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
530 todo_wine_if (offset_test1[i][2])
531 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
532 offset_test1[i][0], result, offset_test1[i][1]);
533 }
534 }
535
536 DestroyWindow(hwndRichEdit);
537 }
538
539 static void test_EM_GETTEXTRANGE(void)
540 {
541 HWND hwndRichEdit = new_richedit(NULL);
542 const char * text1 = "foo bar\r\nfoo bar";
543 const char * text3 = "foo bar\rfoo bar";
544 const char * expect1 = "bar\r\nfoo";
545 const char * expect2 = "\nfoo";
546 const char * expect3 = "bar\rfoo";
547 char buffer[1024] = {0};
548 LRESULT result;
549 TEXTRANGEA textRange;
550
551 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
552
553 textRange.lpstrText = buffer;
554 textRange.chrg.cpMin = 4;
555 textRange.chrg.cpMax = 12;
556 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
557 ok(result == 8, "EM_GETTEXTRANGE returned %ld\n", result);
558 ok(!strcmp(expect1, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
559
560 textRange.lpstrText = buffer;
561 textRange.chrg.cpMin = 8;
562 textRange.chrg.cpMax = 12;
563 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
564 ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
565 ok(!strcmp(expect2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
566
567 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
568
569 textRange.lpstrText = buffer;
570 textRange.chrg.cpMin = 4;
571 textRange.chrg.cpMax = 11;
572 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
573 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
574
575 ok(!strcmp(expect3, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
576
577 /* Test with multibyte character */
578 if (!is_lang_japanese)
579 skip("Skip multibyte character tests on non-Japanese platform\n");
580 else
581 {
582 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
583 textRange.chrg.cpMin = 4;
584 textRange.chrg.cpMax = 8;
585 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
586 ok(result == 4, "EM_GETTEXTRANGE returned %ld\n", result);
587 todo_wine ok(!strcmp("ef\x8e\xf0", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
588 }
589
590 DestroyWindow(hwndRichEdit);
591 }
592
593 static void test_EM_GETSELTEXT(void)
594 {
595 HWND hwndRichEdit = new_richedit(NULL);
596 const char * text1 = "foo bar\r\nfoo bar";
597 const char * text2 = "foo bar\rfoo bar";
598 const char * expect1 = "bar\r\nfoo";
599 const char * expect2 = "bar\rfoo";
600 char buffer[1024] = {0};
601 LRESULT result;
602
603 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
604
605 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 12);
606 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
607 ok(result == 8, "EM_GETSELTEXT returned %ld\n", result);
608 ok(!strcmp(expect1, buffer), "EM_GETSELTEXT filled %s\n", buffer);
609
610 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
611
612 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
613 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
614 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
615 ok(!strcmp(expect2, buffer), "EM_GETSELTEXT filled %s\n", buffer);
616
617 /* Test with multibyte character */
618 if (!is_lang_japanese)
619 skip("Skip multibyte character tests on non-Japanese platform\n");
620 else
621 {
622 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
623 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
624 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
625 ok(result == 4, "EM_GETSELTEXT returned %ld\n", result);
626 todo_wine ok(!strcmp("ef\x8e\xf0", buffer), "EM_GETSELTEXT filled %s\n", buffer);
627 }
628
629 DestroyWindow(hwndRichEdit);
630 }
631
632 static const char haystack[] = "WINEWine wineWine wine WineWine";
633 /* ^0 ^10 ^20 ^30 */
634
635 static const char haystack2[] = "first\r\r\nsecond";
636
637 struct find_s {
638 int start;
639 int end;
640 const char *needle;
641 int flags;
642 int expected_loc;
643 };
644
645
646 static struct find_s find_tests[] = {
647 /* Find in empty text */
648 {0, -1, "foo", FR_DOWN, -1},
649 {0, -1, "foo", 0, -1},
650 {0, -1, "", FR_DOWN, -1},
651 {20, 5, "foo", FR_DOWN, -1},
652 {5, 20, "foo", FR_DOWN, -1}
653 };
654
655 static struct find_s find_tests2[] = {
656 /* No-result find */
657 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
658 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
659
660 /* Subsequent finds */
661 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
662 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
663 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
664 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
665
666 /* Find backwards */
667 {19, 20, "Wine", FR_MATCHCASE, -1},
668 {10, 20, "Wine", FR_MATCHCASE, 13},
669 {20, 10, "Wine", FR_MATCHCASE, -1},
670
671 /* Case-insensitive */
672 {1, 31, "wInE", FR_DOWN, 4},
673 {1, 31, "Wine", FR_DOWN, 4},
674
675 /* High-to-low ranges */
676 {20, 5, "Wine", FR_DOWN, -1},
677 {2, 1, "Wine", FR_DOWN, -1},
678 {30, 29, "Wine", FR_DOWN, -1},
679 {20, 5, "Wine", 0, /*13*/ -1},
680
681 /* Find nothing */
682 {5, 10, "", FR_DOWN, -1},
683 {10, 5, "", FR_DOWN, -1},
684 {0, -1, "", FR_DOWN, -1},
685 {10, 5, "", 0, -1},
686
687 /* Whole-word search */
688 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
689 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
690 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
691 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
692 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
693 {11, -1, "winewine", FR_WHOLEWORD, 23},
694 {31, -1, "winewine", FR_WHOLEWORD, -1},
695
696 /* Bad ranges */
697 {5, 200, "XXX", FR_DOWN, -1},
698 {-20, 20, "Wine", FR_DOWN, -1},
699 {-20, 20, "Wine", FR_DOWN, -1},
700 {-15, -20, "Wine", FR_DOWN, -1},
701 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
702
703 /* Check the case noted in bug 4479 where matches at end aren't recognized */
704 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
705 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
706 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
707 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
708 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
709
710 /* The backwards case of bug 4479; bounds look right
711 * Fails because backward find is wrong */
712 {19, 20, "WINE", FR_MATCHCASE, -1},
713 {0, 20, "WINE", FR_MATCHCASE, 0},
714
715 {0, -1, "wineWine wine", FR_DOWN, 0},
716 {0, -1, "wineWine wine", 0, 0},
717 {0, -1, "INEW", 0, 1},
718 {0, 31, "INEW", 0, 1},
719 {4, -1, "INEW", 0, 10},
720 };
721
722 static struct find_s find_tests3[] = {
723 /* Searching for end of line characters */
724 {0, -1, "t\r\r\ns", FR_DOWN | FR_MATCHCASE, 4},
725 {6, -1, "\r\n", FR_DOWN | FR_MATCHCASE, 6},
726 {7, -1, "\n", FR_DOWN | FR_MATCHCASE, 7},
727 };
728
729 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
730 int findloc;
731 FINDTEXTA ft;
732 memset(&ft, 0, sizeof(ft));
733 ft.chrg.cpMin = f->start;
734 ft.chrg.cpMax = f->end;
735 ft.lpstrText = f->needle;
736 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ft);
737 ok(findloc == f->expected_loc,
738 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
739 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
740 }
741
742 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
743 int id) {
744 int findloc;
745 FINDTEXTEXA ft;
746 int expected_end_loc;
747
748 memset(&ft, 0, sizeof(ft));
749 ft.chrg.cpMin = f->start;
750 ft.chrg.cpMax = f->end;
751 ft.lpstrText = f->needle;
752 ft.chrgText.cpMax = 0xdeadbeef;
753 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ft);
754 ok(findloc == f->expected_loc,
755 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
756 name, id, f->needle, f->start, f->end, f->flags, findloc);
757 ok(ft.chrgText.cpMin == f->expected_loc,
758 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d, expected %d\n",
759 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin, f->expected_loc);
760 expected_end_loc = ((f->expected_loc == -1) ? -1
761 : f->expected_loc + strlen(f->needle));
762 ok(ft.chrgText.cpMax == expected_end_loc ||
763 broken(ft.chrgText.cpMin == -1 && ft.chrgText.cpMax == 0xdeadbeef), /* Win9x, WinME and NT4 */
764 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
765 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
766 }
767
768 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
769 int num_tests)
770 {
771 int i;
772
773 for (i = 0; i < num_tests; i++) {
774 check_EM_FINDTEXT(hwnd, name, &find[i], i);
775 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
776 }
777 }
778
779 static void test_EM_FINDTEXT(void)
780 {
781 HWND hwndRichEdit = new_richedit(NULL);
782
783 /* Empty rich edit control */
784 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, ARRAY_SIZE(find_tests));
785
786 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
787
788 /* Haystack text */
789 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, ARRAY_SIZE(find_tests2));
790
791 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack2);
792
793 /* Haystack text 2 (with EOL characters) */
794 run_tests_EM_FINDTEXT(hwndRichEdit, "3", find_tests3, ARRAY_SIZE(find_tests3));
795
796 DestroyWindow(hwndRichEdit);
797 }
798
799 static void test_EM_POSFROMCHAR(void)
800 {
801 HWND hwndRichEdit = new_richedit(NULL);
802 int i;
803 POINTL pl;
804 LRESULT result;
805 unsigned int height = 0;
806 int xpos = 0;
807 int xpos_rtl_adjusted = 0;
808 static const char text[] = "aa\n"
809 "this is a long line of text that should be longer than the "
810 "control's width\n"
811 "cc\n"
812 "dd\n"
813 "ee\n"
814 "ff\n"
815 "gg\n"
816 "hh\n";
817
818 /* Fill the control to lines to ensure that most of them are offscreen */
819 for (i = 0; i < 50; i++)
820 {
821 /* Do not modify the string; it is exactly 16 characters long. */
822 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
823 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCD\r\n");
824 }
825
826 /*
827 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
828 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
829 Richedit 3.0 accepts either of the above API conventions.
830 */
831
832 /* Testing Richedit 1.0 API format */
833
834 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
835 Since all lines are identical and drawn with the same font,
836 they should have the same height... right?
837 */
838 for (i = 0; i < 50; i++)
839 {
840 /* All the lines are 16 characters long */
841 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
842 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
843 if (i == 0)
844 {
845 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
846 ok(pl.x == 1 ||
847 broken(pl.x == 0), /* Win9x, WinME and NT4 */
848 "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
849 xpos = pl.x;
850 xpos_rtl_adjusted = xpos + (is_rtl() ? 7 : 0);
851 }
852 else if (i == 1)
853 {
854 ok(pl.y > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", pl.y);
855 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
856 height = pl.y;
857 }
858 else
859 {
860 ok(pl.y == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, i * height);
861 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
862 }
863 }
864
865 /* Testing position at end of text */
866 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
867 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
868 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
869 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
870
871 /* Testing position way past end of text */
872 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
873 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
874 ok(pl.y == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, 50 * height);
875
876 ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
877
878
879 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
880 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
881 for (i = 0; i < 50; i++)
882 {
883 /* All the lines are 16 characters long */
884 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, i * 16);
885 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
886 ok(pl.y == (i - 1) * height,
887 "EM_POSFROMCHAR reports y=%d, expected %d\n",
888 pl.y, (i - 1) * height);
889 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
890 }
891
892 /* Testing position at end of text */
893 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 50 * 16);
894 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
895 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
896 ok(pl.x == xpos, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos);
897
898 /* Testing position way past end of text */
899 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 55 * 16);
900 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
901 ok(pl.y == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", pl.y, (50 - 1) * height);
902 ok(pl.x == xpos_rtl_adjusted, "EM_POSFROMCHAR reports x=%d, expected %d\n", pl.x, xpos_rtl_adjusted);
903
904 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
905 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
906 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
907
908 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
909 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
910 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
911 ok(pl.x == 1 ||
912 broken(pl.x == 0), /* Win9x, WinME and NT4 */
913 "EM_POSFROMCHAR reports x=%d, expected 1\n", pl.x);
914 xpos = pl.x;
915
916 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
917 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pl, 0);
918 ok(result == 0, "EM_POSFROMCHAR returned %ld, expected 0\n", result);
919 ok(pl.y == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", pl.y);
920 todo_wine {
921 /* Fails on builtin because horizontal scrollbar is not being shown */
922 ok(pl.x < xpos ||
923 broken(pl.x == xpos), /* Win9x, WinME and NT4 */
924 "EM_POSFROMCHAR reports x=%d, expected value less than %d\n", pl.x, xpos);
925 }
926 DestroyWindow(hwndRichEdit);
927 }
928
929 static void test_word_wrap(void)
930 {
931 HWND hwnd;
932 POINTL point = {0, 60}; /* This point must be below the first line */
933 const char *text = "Must be long enough to test line wrapping";
934 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
935 int res, pos, lines, prevlines, reflines[3];
936
937 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
938 * when specified on window creation and set later. */
939 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
940 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
941 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
942 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
943 ok(res, "WM_SETTEXT failed.\n");
944 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
945 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
946 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
947 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
948
949 SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
950 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
951 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
952 DestroyWindow(hwnd);
953
954 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle|WS_HSCROLL,
955 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
956 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
957
958 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
959 ok(res, "WM_SETTEXT failed.\n");
960 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
961 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
962 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
963 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
964
965 SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
966 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
967 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
968 DestroyWindow(hwnd);
969
970 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
971 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
972 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
973 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
974 ok(res, "WM_SETTEXT failed.\n");
975 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
976 ok(!pos ||
977 broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
978 "pos=%d indicating word wrap when none is expected.\n", pos);
979 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
980 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
981
982 SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle);
983 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
984 ok(!pos ||
985 broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
986 "pos=%d indicating word wrap when none is expected.\n", pos);
987 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
988 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
989 DestroyWindow(hwnd);
990
991 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
992 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
993 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
994 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
995 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
996 ok(res, "WM_SETTEXT failed.\n");
997 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
998 ok(!pos ||
999 broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1000 "pos=%d indicating word wrap when none is expected.\n", pos);
1001 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1002 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1003
1004 SetWindowLongA(hwnd, GWL_STYLE, dwCommonStyle);
1005 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1006 ok(!pos ||
1007 broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1008 "pos=%d indicating word wrap when none is expected.\n", pos);
1009 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1010 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1011
1012 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
1013 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
1014 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
1015 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1016 ok(!pos ||
1017 broken(pos == lstrlenA(text)), /* Win9x, WinME and NT4 */
1018 "pos=%d indicating word wrap when none is expected.\n", pos);
1019 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1020 ok(lines == 1, "Line was not expected to wrap (lines=%d).\n", lines);
1021
1022 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
1023 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
1024 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
1025 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
1026 DestroyWindow(hwnd);
1027
1028 /* First lets see if the text would wrap normally (needed for reference) */
1029 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
1030 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
1031 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
1032 ok(IsWindowVisible(hwnd), "Window should be visible.\n");
1033 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
1034 ok(res, "EM_REPLACESEL failed.\n");
1035 /* Should have wrapped */
1036 reflines[0] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1037 ok(reflines[0] > 1, "Line was expected to wrap (%d lines).\n", reflines[0]);
1038 /* Resize the window to fit the line */
1039 MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
1040 /* Text should not be wrapped */
1041 reflines[1] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1042 ok(reflines[1] == 1, "Line wasn't expected to wrap (%d lines).\n", reflines[1]);
1043 /* Resize the window again to make sure the line wraps again */
1044 MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
1045 reflines[2] = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1046 ok(reflines[2] > 1, "Line was expected to wrap (%d lines).\n", reflines[2]);
1047 DestroyWindow(hwnd);
1048
1049 /* Same test with redraw disabled */
1050 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL, dwCommonStyle,
1051 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
1052 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
1053 ok(IsWindowVisible(hwnd), "Window should be visible.\n");
1054 /* Redraw is disabled by making the window invisible. */
1055 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
1056 ok(!IsWindowVisible(hwnd), "Window shouldn't be visible.\n");
1057 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
1058 ok(res, "EM_REPLACESEL failed.\n");
1059 /* Should have wrapped */
1060 prevlines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1061 ok(prevlines == reflines[0],
1062 "Line was expected to wrap (%d lines).\n", prevlines);
1063 /* Resize the window to fit the line, no change to the number of lines */
1064 MoveWindow(hwnd, 0, 0, 600, 80, TRUE);
1065 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1066 todo_wine
1067 ok(lines == prevlines ||
1068 broken(lines == reflines[1]), /* Win98, WinME and NT4 */
1069 "Expected no change in the number of lines\n");
1070 /* Resize the window again to make sure the line wraps again */
1071 MoveWindow(hwnd, 0, 0, 10, 80, TRUE);
1072 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
1073 todo_wine
1074 ok(lines == prevlines ||
1075 broken(lines == reflines[2]), /* Win98, WinME and NT4 */
1076 "Expected no change in the number of lines\n");
1077 DestroyWindow(hwnd);
1078 }
1079
1080 static void test_EM_GETOPTIONS(void)
1081 {
1082 HWND hwnd;
1083 DWORD options;
1084
1085 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
1086 WS_POPUP,
1087 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1088 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1089 ok(options == 0, "Incorrect options %x\n", options);
1090 DestroyWindow(hwnd);
1091
1092 hwnd = CreateWindowA(RICHEDIT_CLASS10A, NULL,
1093 WS_POPUP|WS_VSCROLL|WS_HSCROLL,
1094 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1095 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1096 ok(options == ECO_AUTOVSCROLL ||
1097 broken(options == 0), /* Win9x, WinME and NT4 */
1098 "Incorrect initial options %x\n", options);
1099 DestroyWindow(hwnd);
1100 }
1101
1102 static void test_autoscroll(void)
1103 {
1104 HWND hwnd;
1105 UINT ret;
1106
1107 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
1108 * auto vertical/horizontal scrolling options. */
1109 hwnd = CreateWindowExA(0, RICHEDIT_CLASS10A, NULL,
1110 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
1111 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1112 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1113 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1114 ok(ret & ECO_AUTOVSCROLL ||
1115 broken(!(ret & ECO_AUTOVSCROLL)), /* Win9x, WinME and NT4 */
1116 "ECO_AUTOVSCROLL isn't set.\n");
1117 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1118 ret = GetWindowLongA(hwnd, GWL_STYLE);
1119 todo_wine
1120 ok(ret & ES_AUTOVSCROLL ||
1121 broken(!(ret & ES_AUTOVSCROLL)), /* Win9x, WinMe and NT4 */
1122 "ES_AUTOVSCROLL isn't set.\n");
1123 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1124 DestroyWindow(hwnd);
1125
1126 hwnd = CreateWindowExA(0, RICHEDIT_CLASS10A, NULL,
1127 WS_POPUP|ES_MULTILINE,
1128 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
1129 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS10A, (int) GetLastError());
1130 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
1131 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
1132 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
1133 ret = GetWindowLongA(hwnd, GWL_STYLE);
1134 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
1135 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
1136 DestroyWindow(hwnd);
1137 }
1138
1139 static void simulate_typing_characters(HWND hwnd, const char* szChars)
1140 {
1141 int ret;
1142
1143 while (*szChars != '\0') {
1144 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
1145 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
1146 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
1147 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
1148 szChars++;
1149 }
1150 }
1151
1152 static void format_test_result(char *target, const char *src)
1153 {
1154 int i;
1155 for (i = 0; i < strlen(src); i++)
1156 sprintf(target + 2*i, "%02x", src[i] & 0xFF);
1157 target[2*i] = 0;
1158 }
1159
1160 /*
1161 * This test attempts to show the effect of enter on a richedit
1162 * control v1.0 inserts CRLF whereas for higher versions it only
1163 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
1164 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
1165 * does for higher. The same test is cloned in riched32 and riched20.
1166 * Also shows the difference between WM_CHAR/WM_KEYDOWN in v1.0 and higher versions
1167 */
1168 static void test_enter(void)
1169 {
1170 static const struct {
1171 const char *initialtext;
1172 const int cursor;
1173 const char *expectedtext;
1174 } testenteritems[] = {
1175 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n"},
1176 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n"},
1177 { "aa\rabbb\r\n", 7, "aa\rabbb\r\n\r\n"},
1178 { "aa\rabbb\r\n", 3, "aa\r\r\nabbb\r\n"},
1179 { "aa\rabbb\r\n", 2, "aa\r\n\rabbb\r\n"}
1180 };
1181
1182 char expectedbuf[1024];
1183 char resultbuf[1024];
1184 HWND hwndRichEdit = new_richedit(NULL);
1185 UINT i;
1186 char buf[1024] = {0};
1187 GETTEXTEX getText = {sizeof(buf)};
1188 LRESULT result;
1189 const char *expected;
1190
1191 for (i = 0; i < ARRAY_SIZE(testenteritems); i++)
1192 {
1193 /* Set the text to the initial text */
1194 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
1195 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1196
1197 /* Send Enter */
1198 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
1199 simulate_typing_characters(hwndRichEdit, "\r");
1200
1201 /* 1. Retrieve with WM_GETTEXT */
1202 buf[0] = 0x00;
1203 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
1204 expected = testenteritems[i].expectedtext;
1205
1206 format_test_result(resultbuf, buf);
1207 format_test_result(expectedbuf, expected);
1208
1209 result = strcmp(expected, buf);
1210 ok (result == 0,
1211 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
1212 i, resultbuf, expectedbuf);
1213
1214 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
1215 getText.flags = GT_DEFAULT;
1216 getText.codepage = CP_ACP;
1217 buf[0] = 0x00;
1218 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1219 expected = testenteritems[i].expectedtext;
1220
1221 format_test_result(resultbuf, buf);
1222 format_test_result(expectedbuf, expected);
1223
1224 result = strcmp(expected, buf);
1225 ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1226 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
1227 i, resultbuf, expectedbuf);
1228
1229 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
1230 getText.flags = GT_USECRLF;
1231 getText.codepage = CP_ACP;
1232 buf[0] = 0x00;
1233 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1234 expected = testenteritems[i].expectedtext;
1235
1236 format_test_result(resultbuf, buf);
1237 format_test_result(expectedbuf, expected);
1238
1239 result = strcmp(expected, buf);
1240 ok (result == 0 || broken(buf[0]==0x00 /* WinNT4 */),
1241 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
1242 i, resultbuf, expectedbuf);
1243 }
1244
1245 /* Show that WM_CHAR is handled differently from WM_KEYDOWN */
1246 getText.flags = GT_DEFAULT;
1247 getText.codepage = CP_ACP;
1248
1249 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1250 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1251 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
1252 SendMessageW(hwndRichEdit, WM_KEYDOWN, VK_RETURN, 0);
1253
1254 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1255 ok(result == 1, "Got %d\n", (int)result);
1256 format_test_result(resultbuf, buf);
1257 format_test_result(expectedbuf, "T");
1258 result = strcmp(resultbuf, expectedbuf);
1259 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
1260
1261 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1262 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
1263 SendMessageW(hwndRichEdit, WM_CHAR, 'T', 0);
1264 SendMessageW(hwndRichEdit, WM_CHAR, '\r', 0);
1265
1266 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
1267 ok(result == 1, "Got %d\n", (int)result);
1268 format_test_result(resultbuf, buf);
1269 format_test_result(expectedbuf, "T\r\n");
1270 result = strcmp(resultbuf, expectedbuf);
1271 ok (result == 0, "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n", i, resultbuf, expectedbuf);
1272
1273 DestroyWindow(hwndRichEdit);
1274 }
1275
1276 struct exsetsel_s {
1277 LONG min;
1278 LONG max;
1279 LRESULT expected_retval;
1280 int expected_getsel_start;
1281 int expected_getsel_end;
1282 BOOL result_todo;
1283 BOOL sel_todo;
1284 };
1285
1286 static const struct exsetsel_s exsetsel_tests[] = {
1287 /* sanity tests */
1288 {5, 10, 10, 5, 10, 0, 0 },
1289 {15, 17, 17, 15, 17, 0, 0 },
1290 /* test cpMax > strlen() */
1291 {0, 100, 19, 0, 19, 1, 0 },
1292 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
1293 {-1, 1, 17, 17, 17, 1, 0 },
1294 /* test cpMin == cpMax */
1295 {5, 5, 5, 5, 5, 0, 0 },
1296 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
1297 {-1, 0, 5, 5, 5, 0, 0 },
1298 {-1, 17, 5, 5, 5, 0, 0 },
1299 {-1, 18, 5, 5, 5, 0, 0 },
1300 /* test cpMin < 0 && cpMax < 0 */
1301 {-1, -1, 17, 17, 17, 0, 0 },
1302 {-4, -5, 17, 17, 17, 0, 0 },
1303 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
1304 {0, -1, 19, 0, 19, 1, 0 },
1305 {17, -5, 19, 17, 19, 1, 0 },
1306 {18, -3, 19, 17, 19, 1, 1 },
1307 /* test if cpMin > cpMax */
1308 {15, 19, 19, 15, 19, 1, 0 },
1309 {19, 15, 19, 15, 19, 1, 0 },
1310 /* cpMin == strlen() && cpMax > cpMin */
1311 {17, 18, 17, 17, 17, 1, 1 },
1312 {17, 50, 19, 17, 19, 1, 0 },
1313 };
1314
1315 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1316 CHARRANGE cr;
1317 LRESULT result;
1318 int start, end;
1319
1320 cr.cpMin = setsel->min;
1321 cr.cpMax = setsel->max;
1322 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
1323
1324 todo_wine_if (setsel->result_todo)
1325 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1326
1327 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
1328
1329 todo_wine_if (setsel->sel_todo)
1330 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end,
1331 "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
1332 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
1333 }
1334
1335 static void test_EM_EXSETSEL(void)
1336 {
1337 HWND hwndRichEdit = new_richedit(NULL);
1338 int i;
1339 const int num_tests = ARRAY_SIZE(exsetsel_tests);
1340
1341 /* sending some text to the window */
1342 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
1343 /* 01234567890123456 */
1344
1345 for (i = 0; i < num_tests; i++) {
1346 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1347 }
1348
1349 if (!is_lang_japanese)
1350 skip("Skip multibyte character tests on non-Japanese platform\n");
1351 else
1352 {
1353 CHARRANGE cr;
1354 LRESULT result;
1355 #define MAX_BUF_LEN 1024
1356 char bufA[MAX_BUF_LEN] = {0};
1357
1358 /* Test with multibyte character */
1359 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1360 /* 012345 6 7 8901 */
1361 cr.cpMin = 4, cr.cpMax = 8;
1362 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1363 todo_wine ok(result == 7, "EM_EXSETSEL return %ld expected 7\n", result);
1364 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
1365 todo_wine ok(!strcmp(bufA, "ef\x8e\xf0"), "EM_GETSELTEXT return incorrect string\n");
1366 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
1367 ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
1368 ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
1369 }
1370
1371 DestroyWindow(hwndRichEdit);
1372 }
1373
1374 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
1375 LRESULT result;
1376 int start, end;
1377
1378 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
1379
1380 todo_wine_if (setsel->result_todo)
1381 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
1382
1383 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
1384
1385 todo_wine_if (setsel->sel_todo)
1386 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end,
1387 "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
1388 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
1389 }
1390
1391 static void test_EM_SETSEL(void)
1392 {
1393 char buffA[32] = {0};
1394 HWND hwndRichEdit = new_richedit(NULL);
1395 int i;
1396 const int num_tests = ARRAY_SIZE(exsetsel_tests);
1397
1398 /* sending some text to the window */
1399 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
1400 /* 01234567890123456 */
1401
1402 for (i = 0; i < num_tests; i++) {
1403 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
1404 }
1405
1406 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
1407 buffA[0] = 123;
1408 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
1409 ok(buffA[0] == 0, "selection text %s\n", buffA);
1410
1411 if (!is_lang_japanese)
1412 skip("Skip multibyte character tests on non-Japanese platform\n");
1413 else
1414 {
1415 int sel_start, sel_end;
1416 LRESULT result;
1417
1418 /* Test with multibyte character */
1419 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1420 /* 012345 6 7 8901 */
1421 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1422 todo_wine ok(result == 7, "EM_SETSEL return %ld expected 7\n", result);
1423 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
1424 todo_wine ok(!strcmp(buffA, "ef\x8e\xf0"), "EM_GETSELTEXT return incorrect string\n");
1425 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
1426 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
1427 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
1428 }
1429
1430 DestroyWindow(hwndRichEdit);
1431 }
1432
1433 START_TEST( editor )
1434 {
1435 MSG msg;
1436 time_t end;
1437 BOOL ret;
1438
1439 /* Must explicitly LoadLibrary(). The test has no references to functions in
1440 * RICHED32.DLL, so the linker doesn't actually link to it. */
1441 hmoduleRichEdit = LoadLibraryA("riched32.dll");
1442 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
1443 is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
1444
1445 test_WM_SETTEXT();
1446 test_EM_GETTEXTRANGE();
1447 test_EM_GETSELTEXT();
1448 test_WM_GETTEXTLENGTH();
1449 test_EM_STREAMIN();
1450 test_EM_STREAMOUT();
1451 test_EM_GETLINE();
1452 test_EM_LINELENGTH();
1453 test_EM_FINDTEXT();
1454 test_EM_POSFROMCHAR();
1455 test_word_wrap();
1456 test_EM_GETOPTIONS();
1457 test_autoscroll();
1458 test_enter();
1459 test_EM_EXSETSEL();
1460 test_EM_SETSEL();
1461
1462 /* Set the environment variable WINETEST_RICHED32 to keep windows
1463 * responsive and open for 30 seconds. This is useful for debugging.
1464 *
1465 * The message pump uses PeekMessage() to empty the queue and then sleeps for
1466 * 50ms before retrying the queue. */
1467 end = time(NULL) + 30;
1468 if (getenv( "WINETEST_RICHED32" )) {
1469 while (time(NULL) < end) {
1470 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
1471 TranslateMessage(&msg);
1472 DispatchMessageA(&msg);
1473 } else {
1474 Sleep(50);
1475 }
1476 }
1477 }
1478
1479 OleFlushClipboard();
1480 ret = FreeLibrary(hmoduleRichEdit);
1481 ok(ret, "error: %u\n", GetLastError());
1482 }