riched20/tests: Use EqualRect() instead of open coding it.
[wine.git] / dlls / riched20 / tests / editor.c
blob92af700b5724f8356f7fadcd9bb30ee459d31d1d
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <commdlg.h>
34 #include <time.h>
35 #include <wine/test.h>
37 #define ID_RICHEDITTESTDBUTTON 0x123
39 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
41 #define ok_w3(format, szString1, szString2, szString3) \
42 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
44 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
45 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
46 format, string1, string2, string3);
48 static HMODULE hmoduleRichEdit;
49 static BOOL is_lang_japanese;
51 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
52 HWND hwnd;
53 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55 hmoduleRichEdit, NULL);
56 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
57 return hwnd;
60 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
61 HWND hwnd;
62 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
63 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
64 hmoduleRichEdit, NULL);
65 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
66 return hwnd;
69 static HWND new_richedit(HWND parent) {
70 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
73 static HWND new_richeditW(HWND parent) {
74 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
77 /* Keeps the window reponsive for the deley_time in seconds.
78 * This is useful for debugging a test to see what is happening. */
79 static void keep_responsive(time_t delay_time)
81 MSG msg;
82 time_t end;
84 /* The message pump uses PeekMessage() to empty the queue and then
85 * sleeps for 50ms before retrying the queue. */
86 end = time(NULL) + delay_time;
87 while (time(NULL) < end) {
88 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
89 TranslateMessage(&msg);
90 DispatchMessageA(&msg);
91 } else {
92 Sleep(50);
97 static void simulate_typing_characters(HWND hwnd, const char* szChars)
99 int ret;
101 while (*szChars != '\0') {
102 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
103 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
104 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
105 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
106 szChars++;
110 static BOOL hold_key(int vk)
112 BYTE key_state[256];
113 BOOL result;
115 result = GetKeyboardState(key_state);
116 ok(result, "GetKeyboardState failed.\n");
117 if (!result) return FALSE;
118 key_state[vk] |= 0x80;
119 result = SetKeyboardState(key_state);
120 ok(result, "SetKeyboardState failed.\n");
121 return result != 0;
124 static BOOL release_key(int vk)
126 BYTE key_state[256];
127 BOOL result;
129 result = GetKeyboardState(key_state);
130 ok(result, "GetKeyboardState failed.\n");
131 if (!result) return FALSE;
132 key_state[vk] &= ~0x80;
133 result = SetKeyboardState(key_state);
134 ok(result, "SetKeyboardState failed.\n");
135 return result != 0;
138 static const char haystack[] = "WINEWine wineWine wine WineWine";
139 /* ^0 ^10 ^20 ^30 */
141 struct find_s {
142 int start;
143 int end;
144 const char *needle;
145 int flags;
146 int expected_loc;
150 static struct find_s find_tests[] = {
151 /* Find in empty text */
152 {0, -1, "foo", FR_DOWN, -1},
153 {0, -1, "foo", 0, -1},
154 {0, -1, "", FR_DOWN, -1},
155 {20, 5, "foo", FR_DOWN, -1},
156 {5, 20, "foo", FR_DOWN, -1}
159 static struct find_s find_tests2[] = {
160 /* No-result find */
161 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
162 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
164 /* Subsequent finds */
165 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
166 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
167 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
168 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
170 /* Find backwards */
171 {19, 20, "Wine", FR_MATCHCASE, 13},
172 {10, 20, "Wine", FR_MATCHCASE, 4},
173 {20, 10, "Wine", FR_MATCHCASE, 13},
175 /* Case-insensitive */
176 {1, 31, "wInE", FR_DOWN, 4},
177 {1, 31, "Wine", FR_DOWN, 4},
179 /* High-to-low ranges */
180 {20, 5, "Wine", FR_DOWN, -1},
181 {2, 1, "Wine", FR_DOWN, -1},
182 {30, 29, "Wine", FR_DOWN, -1},
183 {20, 5, "Wine", 0, 13},
185 /* Find nothing */
186 {5, 10, "", FR_DOWN, -1},
187 {10, 5, "", FR_DOWN, -1},
188 {0, -1, "", FR_DOWN, -1},
189 {10, 5, "", 0, -1},
191 /* Whole-word search */
192 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
194 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
195 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
196 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
197 {11, -1, "winewine", FR_WHOLEWORD, 0},
198 {31, -1, "winewine", FR_WHOLEWORD, 23},
200 /* Bad ranges */
201 {5, 200, "XXX", FR_DOWN, -1},
202 {-20, 20, "Wine", FR_DOWN, -1},
203 {-20, 20, "Wine", FR_DOWN, -1},
204 {-15, -20, "Wine", FR_DOWN, -1},
205 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
207 /* Check the case noted in bug 4479 where matches at end aren't recognized */
208 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
209 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
210 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
211 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
214 /* The backwards case of bug 4479; bounds look right
215 * Fails because backward find is wrong */
216 {19, 20, "WINE", FR_MATCHCASE, 0},
217 {0, 20, "WINE", FR_MATCHCASE, -1},
219 {0, -1, "wineWine wine", 0, -1},
222 static WCHAR *atowstr(const char *str)
224 WCHAR *ret;
225 DWORD len;
226 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
227 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
228 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
229 return ret;
232 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
234 int findloc;
236 if(unicode){
237 FINDTEXTW ftw;
238 memset(&ftw, 0, sizeof(ftw));
239 ftw.chrg.cpMin = f->start;
240 ftw.chrg.cpMax = f->end;
241 ftw.lpstrText = atowstr(f->needle);
243 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
244 ok(findloc == f->expected_loc,
245 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
246 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
248 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
249 ok(findloc == f->expected_loc,
250 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
251 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
253 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
254 }else{
255 FINDTEXTA fta;
256 memset(&fta, 0, sizeof(fta));
257 fta.chrg.cpMin = f->start;
258 fta.chrg.cpMax = f->end;
259 fta.lpstrText = f->needle;
261 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
262 ok(findloc == f->expected_loc,
263 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
264 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
268 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
269 int id, BOOL unicode)
271 int findloc;
272 int expected_end_loc;
274 if(unicode){
275 FINDTEXTEXW ftw;
276 memset(&ftw, 0, sizeof(ftw));
277 ftw.chrg.cpMin = f->start;
278 ftw.chrg.cpMax = f->end;
279 ftw.lpstrText = atowstr(f->needle);
280 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
281 ok(findloc == f->expected_loc,
282 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
283 name, id, f->needle, f->start, f->end, f->flags, findloc);
284 ok(ftw.chrgText.cpMin == f->expected_loc,
285 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
286 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
287 expected_end_loc = ((f->expected_loc == -1) ? -1
288 : f->expected_loc + strlen(f->needle));
289 ok(ftw.chrgText.cpMax == expected_end_loc,
290 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
291 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
292 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
293 }else{
294 FINDTEXTEXA fta;
295 memset(&fta, 0, sizeof(fta));
296 fta.chrg.cpMin = f->start;
297 fta.chrg.cpMax = f->end;
298 fta.lpstrText = f->needle;
299 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
300 ok(findloc == f->expected_loc,
301 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
302 name, id, f->needle, f->start, f->end, f->flags, findloc);
303 ok(fta.chrgText.cpMin == f->expected_loc,
304 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
305 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
306 expected_end_loc = ((f->expected_loc == -1) ? -1
307 : f->expected_loc + strlen(f->needle));
308 ok(fta.chrgText.cpMax == expected_end_loc,
309 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
310 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
314 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
315 int num_tests, BOOL unicode)
317 int i;
319 for (i = 0; i < num_tests; i++) {
320 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
321 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
325 static void test_EM_FINDTEXT(BOOL unicode)
327 HWND hwndRichEdit;
328 CHARFORMAT2A cf2;
330 if(unicode)
331 hwndRichEdit = new_richeditW(NULL);
332 else
333 hwndRichEdit = new_richedit(NULL);
335 /* Empty rich edit control */
336 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
337 sizeof(find_tests)/sizeof(struct find_s), unicode);
339 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
341 /* Haystack text */
342 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
343 sizeof(find_tests2)/sizeof(struct find_s), unicode);
345 /* Setting a format on an arbitrary range should have no effect in search
346 results. This tests correct offset reporting across runs. */
347 cf2.cbSize = sizeof(CHARFORMAT2A);
348 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
349 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
350 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
351 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
352 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
354 /* Haystack text, again */
355 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
356 sizeof(find_tests2)/sizeof(struct find_s), unicode);
358 /* Yet another range */
359 cf2.dwMask = CFM_BOLD | cf2.dwMask;
360 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
361 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
362 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
364 /* Haystack text, again */
365 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
366 sizeof(find_tests2)/sizeof(struct find_s), unicode);
368 DestroyWindow(hwndRichEdit);
371 static const struct getline_s {
372 int line;
373 size_t buffer_len;
374 const char *text;
375 } gl[] = {
376 {0, 10, "foo bar\r"},
377 {1, 10, "\r"},
378 {2, 10, "bar\r"},
379 {3, 10, "\r"},
381 /* Buffer smaller than line length */
382 {0, 2, "foo bar\r"},
383 {0, 1, "foo bar\r"},
384 {0, 0, "foo bar\r"}
387 static void test_EM_GETLINE(void)
389 int i;
390 HWND hwndRichEdit = new_richedit(NULL);
391 static const int nBuf = 1024;
392 char dest[1024], origdest[1024];
393 const char text[] = "foo bar\n"
394 "\n"
395 "bar\n";
397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
399 memset(origdest, 0xBB, nBuf);
400 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
402 int nCopied;
403 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
404 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
405 memset(dest, 0xBB, nBuf);
406 *(WORD *) dest = gl[i].buffer_len;
408 /* EM_GETLINE appends a "\r\0" to the end of the line
409 * nCopied counts up to and including the '\r' */
410 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
411 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
412 expected_nCopied);
413 /* two special cases since a parameter is passed via dest */
414 if (gl[i].buffer_len == 0)
415 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
416 "buffer_len=0\n");
417 else if (gl[i].buffer_len == 1)
418 ok(dest[0] == gl[i].text[0] && !dest[1] &&
419 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
420 else
422 /* Prepare hex strings of buffers to dump on failure. */
423 char expectedbuf[1024];
424 char resultbuf[1024];
425 int j;
426 resultbuf[0] = '\0';
427 for (j = 0; j < 32; j++)
428 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
429 expectedbuf[0] = '\0';
430 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
431 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
432 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
433 sprintf(expectedbuf+strlen(expectedbuf), "??");
434 for (; j < 32; j++) /* Bytes after declared buffer size */
435 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
437 /* Test the part of the buffer that is expected to be written according
438 * to the MSDN documentation fo EM_GETLINE, which does not state that
439 * a NULL terminating character will be added unless no text is copied.
441 * Windows NT does not append a NULL terminating character, but
442 * Windows 2000 and up do append a NULL terminating character if there
443 * is space in the buffer. The test will ignore this difference. */
444 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
445 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
446 i, expected_bytes_written, expectedbuf, resultbuf);
447 /* Test the part of the buffer after the declared length to make sure
448 * there are no buffer overruns. */
449 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
450 nBuf - gl[i].buffer_len),
451 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
452 i, expected_bytes_written, expectedbuf, resultbuf);
456 DestroyWindow(hwndRichEdit);
459 static void test_EM_LINELENGTH(void)
461 HWND hwndRichEdit = new_richedit(NULL);
462 const char * text =
463 "richedit1\r"
464 "richedit1\n"
465 "richedit1\r\n"
466 "richedit1";
467 int offset_test[10][2] = {
468 {0, 9},
469 {5, 9},
470 {10, 9},
471 {15, 9},
472 {20, 9},
473 {25, 9},
474 {30, 9},
475 {35, 9},
476 {40, 0},
477 {45, 0},
479 int i;
480 LRESULT result;
482 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
484 for (i = 0; i < 10; i++) {
485 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
486 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
487 offset_test[i][0], result, offset_test[i][1]);
490 /* Test with multibyte character */
491 if (!is_lang_japanese)
492 skip("Skip multibyte character tests on non-Japanese platform\n");
493 else
495 const char *text1 =
496 "wine\n"
497 "richedit\x8e\xf0\n"
498 "wine";
499 int offset_test1[3][2] = {
500 {0, 4}, /* Line 1: |wine\n */
501 {5, 9}, /* Line 2: |richedit\x8e\xf0\n */
502 {15, 4}, /* Line 3: |wine */
504 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
505 for (i = 0; i < sizeof(offset_test1)/sizeof(offset_test1[0]); i++) {
506 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test1[i][0], 0);
507 ok(result == offset_test1[i][1], "Length of line at offset %d is %ld, expected %d\n",
508 offset_test1[i][0], result, offset_test1[i][1]);
512 DestroyWindow(hwndRichEdit);
515 static int get_scroll_pos_y(HWND hwnd)
517 POINT p = {-1, -1};
518 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
519 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
520 return p.y;
523 static void move_cursor(HWND hwnd, LONG charindex)
525 CHARRANGE cr;
526 cr.cpMax = charindex;
527 cr.cpMin = charindex;
528 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
531 static void line_scroll(HWND hwnd, int amount)
533 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
536 static void test_EM_SCROLLCARET(void)
538 int prevY, curY;
539 const char text[] = "aa\n"
540 "this is a long line of text that should be longer than the "
541 "control's width\n"
542 "cc\n"
543 "dd\n"
544 "ee\n"
545 "ff\n"
546 "gg\n"
547 "hh\n";
548 /* The richedit window height needs to be large enough vertically to fit in
549 * more than two lines of text, so the new_richedit function can't be used
550 * since a height of 60 was not large enough on some systems.
552 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
553 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
554 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
555 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
557 /* Can't verify this */
558 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
562 /* Caret above visible window */
563 line_scroll(hwndRichEdit, 3);
564 prevY = get_scroll_pos_y(hwndRichEdit);
565 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
566 curY = get_scroll_pos_y(hwndRichEdit);
567 ok(prevY != curY, "%d == %d\n", prevY, curY);
569 /* Caret below visible window */
570 move_cursor(hwndRichEdit, sizeof(text) - 1);
571 line_scroll(hwndRichEdit, -3);
572 prevY = get_scroll_pos_y(hwndRichEdit);
573 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
574 curY = get_scroll_pos_y(hwndRichEdit);
575 ok(prevY != curY, "%d == %d\n", prevY, curY);
577 /* Caret in visible window */
578 move_cursor(hwndRichEdit, sizeof(text) - 2);
579 prevY = get_scroll_pos_y(hwndRichEdit);
580 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
581 curY = get_scroll_pos_y(hwndRichEdit);
582 ok(prevY == curY, "%d != %d\n", prevY, curY);
584 /* Caret still in visible window */
585 line_scroll(hwndRichEdit, -1);
586 prevY = get_scroll_pos_y(hwndRichEdit);
587 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
588 curY = get_scroll_pos_y(hwndRichEdit);
589 ok(prevY == curY, "%d != %d\n", prevY, curY);
591 DestroyWindow(hwndRichEdit);
594 static void test_EM_POSFROMCHAR(void)
596 HWND hwndRichEdit = new_richedit(NULL);
597 int i, expected;
598 LRESULT result;
599 unsigned int height = 0;
600 int xpos = 0;
601 POINTL pt;
602 LOCALESIGNATURE sig;
603 BOOL rtl;
604 PARAFORMAT2 fmt;
605 static const char text[] = "aa\n"
606 "this is a long line of text that should be longer than the "
607 "control's width\n"
608 "cc\n"
609 "dd\n"
610 "ee\n"
611 "ff\n"
612 "gg\n"
613 "hh\n";
615 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
616 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
617 (sig.lsUsb[3] & 0x08000000) != 0);
619 /* Fill the control to lines to ensure that most of them are offscreen */
620 for (i = 0; i < 50; i++)
622 /* Do not modify the string; it is exactly 16 characters long. */
623 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
624 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
628 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
629 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
630 Richedit 3.0 accepts either of the above API conventions.
633 /* Testing Richedit 2.0 API format */
635 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
636 Since all lines are identical and drawn with the same font,
637 they should have the same height... right?
639 for (i = 0; i < 50; i++)
641 /* All the lines are 16 characters long */
642 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
643 if (i == 0)
645 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
646 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
647 xpos = LOWORD(result);
649 else if (i == 1)
651 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
652 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
653 height = HIWORD(result);
655 else
657 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
658 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
662 /* Testing position at end of text */
663 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
664 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
665 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
667 /* Testing position way past end of text */
668 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
669 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
670 expected = (rtl ? 8 : 1);
671 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
673 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
674 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
675 for (i = 0; i < 50; i++)
677 /* All the lines are 16 characters long */
678 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
679 ok((signed short)(HIWORD(result)) == (i - 1) * height,
680 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
681 (signed short)(HIWORD(result)), (i - 1) * height);
682 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
685 /* Testing position at end of text */
686 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
687 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
688 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
690 /* Testing position way past end of text */
691 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
692 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
693 expected = (rtl ? 8 : 1);
694 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
696 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
697 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
698 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
700 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
701 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
702 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
703 xpos = LOWORD(result);
705 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
706 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
707 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
708 ok((signed short)(LOWORD(result)) < xpos,
709 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
710 (signed short)(LOWORD(result)), xpos);
711 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
713 /* Test around end of text that doesn't end in a newline. */
714 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
715 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
716 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
717 ok(pt.x > 1, "pt.x = %d\n", pt.x);
718 xpos = pt.x;
719 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
720 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
721 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
722 xpos = (rtl ? pt.x + 7 : pt.x);
723 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
724 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
725 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
727 /* Try a negative position. */
728 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
729 ok(pt.x == 1, "pt.x = %d\n", pt.x);
731 /* test negative indentation */
732 SendMessageA(hwndRichEdit, WM_SETTEXT, 0,
733 (LPARAM)"{\\rtf1\\pard\\fi-200\\li-200\\f1 TestSomeText\\par}");
734 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, 0);
735 ok(pt.x == 1, "pt.x = %d\n", pt.x);
737 fmt.cbSize = sizeof(fmt);
738 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
739 ok(fmt.dxStartIndent == -400, "got %d\n", fmt.dxStartIndent);
740 ok(fmt.dxOffset == 200, "got %d\n", fmt.dxOffset);
741 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
743 DestroyWindow(hwndRichEdit);
746 static void test_EM_SETCHARFORMAT(void)
748 HWND hwndRichEdit = new_richedit(NULL);
749 CHARFORMAT2A cf2;
750 int rc = 0;
751 int tested_effects[] = {
752 CFE_BOLD,
753 CFE_ITALIC,
754 CFE_UNDERLINE,
755 CFE_STRIKEOUT,
756 CFE_PROTECTED,
757 CFE_LINK,
758 CFE_SUBSCRIPT,
759 CFE_SUPERSCRIPT,
762 int i;
763 CHARRANGE cr;
764 LOCALESIGNATURE sig;
765 BOOL rtl;
767 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
768 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
769 (sig.lsUsb[3] & 0x08000000) != 0);
771 /* Invalid flags, CHARFORMAT2 structure blanked out */
772 memset(&cf2, 0, sizeof(cf2));
773 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
774 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
776 /* A valid flag, CHARFORMAT2 structure blanked out */
777 memset(&cf2, 0, sizeof(cf2));
778 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
779 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
781 /* A valid flag, CHARFORMAT2 structure blanked out */
782 memset(&cf2, 0, sizeof(cf2));
783 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
784 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
786 /* A valid flag, CHARFORMAT2 structure blanked out */
787 memset(&cf2, 0, sizeof(cf2));
788 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
789 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
791 /* A valid flag, CHARFORMAT2 structure blanked out */
792 memset(&cf2, 0, sizeof(cf2));
793 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
794 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
796 /* Invalid flags, CHARFORMAT2 structure minimally filled */
797 memset(&cf2, 0, sizeof(cf2));
798 cf2.cbSize = sizeof(CHARFORMAT2A);
799 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
800 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
801 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
802 ok(rc == FALSE, "Should not be able to undo here.\n");
803 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
805 /* A valid flag, CHARFORMAT2 structure minimally filled */
806 memset(&cf2, 0, sizeof(cf2));
807 cf2.cbSize = sizeof(CHARFORMAT2A);
808 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
809 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
810 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
811 ok(rc == FALSE, "Should not be able to undo here.\n");
812 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
814 /* A valid flag, CHARFORMAT2 structure minimally filled */
815 memset(&cf2, 0, sizeof(cf2));
816 cf2.cbSize = sizeof(CHARFORMAT2A);
817 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
818 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
819 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
820 ok(rc == FALSE, "Should not be able to undo here.\n");
821 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
823 /* A valid flag, CHARFORMAT2 structure minimally filled */
824 memset(&cf2, 0, sizeof(cf2));
825 cf2.cbSize = sizeof(CHARFORMAT2A);
826 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
827 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
828 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
829 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
830 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
832 /* A valid flag, CHARFORMAT2 structure minimally filled */
833 memset(&cf2, 0, sizeof(cf2));
834 cf2.cbSize = sizeof(CHARFORMAT2A);
835 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
836 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
837 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
838 ok(rc == TRUE, "Should not be able to undo here.\n");
839 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
841 cf2.cbSize = sizeof(CHARFORMAT2A);
842 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
844 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
845 cf2.cbSize = sizeof(CHARFORMAT2A);
846 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
847 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
848 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
850 /* wParam==0 is default char format, does not set modify */
851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
852 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
853 ok(rc == 0, "Text marked as modified, expected not modified!\n");
854 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
855 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
856 if (! rtl)
858 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
859 ok(rc == 0, "Text marked as modified, expected not modified!\n");
861 else
862 skip("RTL language found\n");
864 /* wParam==SCF_SELECTION sets modify if nonempty selection */
865 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
866 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
867 ok(rc == 0, "Text marked as modified, expected not modified!\n");
868 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
869 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
870 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
871 ok(rc == 0, "Text marked as modified, expected not modified!\n");
873 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
874 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
875 ok(rc == 0, "Text marked as modified, expected not modified!\n");
876 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
877 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
878 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
879 ok(rc == 0, "Text marked as modified, expected not modified!\n");
880 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
881 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
882 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
883 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
884 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
886 /* wParam==SCF_ALL sets modify regardless of whether text is present */
887 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
888 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
889 ok(rc == 0, "Text marked as modified, expected not modified!\n");
890 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
891 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
892 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
893 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
895 DestroyWindow(hwndRichEdit);
897 /* EM_GETCHARFORMAT tests */
898 for (i = 0; tested_effects[i]; i++)
900 hwndRichEdit = new_richedit(NULL);
901 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
903 /* Need to set a TrueType font to get consistent CFM_BOLD results */
904 memset(&cf2, 0, sizeof(CHARFORMAT2A));
905 cf2.cbSize = sizeof(CHARFORMAT2A);
906 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
907 cf2.dwEffects = 0;
908 strcpy(cf2.szFaceName, "Courier New");
909 cf2.wWeight = FW_DONTCARE;
910 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
912 memset(&cf2, 0, sizeof(CHARFORMAT2A));
913 cf2.cbSize = sizeof(CHARFORMAT2A);
914 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
915 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
916 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
917 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
919 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
920 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
921 ok((cf2.dwEffects & tested_effects[i]) == 0,
922 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
924 memset(&cf2, 0, sizeof(CHARFORMAT2A));
925 cf2.cbSize = sizeof(CHARFORMAT2A);
926 cf2.dwMask = tested_effects[i];
927 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
928 cf2.dwMask = CFM_SUPERSCRIPT;
929 cf2.dwEffects = tested_effects[i];
930 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
931 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
933 memset(&cf2, 0, sizeof(CHARFORMAT2A));
934 cf2.cbSize = sizeof(CHARFORMAT2A);
935 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
936 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
937 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
938 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
940 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
941 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
942 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
943 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
945 memset(&cf2, 0, sizeof(CHARFORMAT2A));
946 cf2.cbSize = sizeof(CHARFORMAT2A);
947 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
948 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
949 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
950 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
952 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
953 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
954 ok((cf2.dwEffects & tested_effects[i]) == 0,
955 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
964 (cf2.dwMask & tested_effects[i]) == 0),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
967 DestroyWindow(hwndRichEdit);
970 for (i = 0; tested_effects[i]; i++)
972 hwndRichEdit = new_richedit(NULL);
973 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
975 /* Need to set a TrueType font to get consistent CFM_BOLD results */
976 memset(&cf2, 0, sizeof(CHARFORMAT2A));
977 cf2.cbSize = sizeof(CHARFORMAT2A);
978 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
979 cf2.dwEffects = 0;
980 strcpy(cf2.szFaceName, "Courier New");
981 cf2.wWeight = FW_DONTCARE;
982 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
984 memset(&cf2, 0, sizeof(CHARFORMAT2A));
985 cf2.cbSize = sizeof(CHARFORMAT2A);
986 cf2.dwMask = tested_effects[i];
987 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
988 cf2.dwMask = CFM_SUPERSCRIPT;
989 cf2.dwEffects = tested_effects[i];
990 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
991 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
993 memset(&cf2, 0, sizeof(CHARFORMAT2A));
994 cf2.cbSize = sizeof(CHARFORMAT2A);
995 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
996 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
997 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
998 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1000 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1001 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1002 ok((cf2.dwEffects & tested_effects[i]) == 0,
1003 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
1005 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1006 cf2.cbSize = sizeof(CHARFORMAT2A);
1007 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
1008 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1009 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1010 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
1012 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
1013 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
1014 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1015 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
1017 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1018 cf2.cbSize = sizeof(CHARFORMAT2A);
1019 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
1020 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1021 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
1022 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
1024 (cf2.dwMask & tested_effects[i]) == 0),
1025 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1026 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1027 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1029 DestroyWindow(hwndRichEdit);
1032 /* Effects applied on an empty selection should take effect when selection is
1033 replaced with text */
1034 hwndRichEdit = new_richedit(NULL);
1035 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1036 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1038 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1039 cf2.cbSize = sizeof(CHARFORMAT2A);
1040 cf2.dwMask = CFM_BOLD;
1041 cf2.dwEffects = CFE_BOLD;
1042 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1044 /* Selection is now nonempty */
1045 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1047 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1048 cf2.cbSize = sizeof(CHARFORMAT2A);
1049 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1050 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1052 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1053 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1054 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1055 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1058 /* Set two effects on an empty selection */
1059 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1062 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1063 cf2.cbSize = sizeof(CHARFORMAT2A);
1064 cf2.dwMask = CFM_BOLD;
1065 cf2.dwEffects = CFE_BOLD;
1066 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1067 cf2.dwMask = CFM_ITALIC;
1068 cf2.dwEffects = CFE_ITALIC;
1069 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1071 /* Selection is now nonempty */
1072 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1074 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1075 cf2.cbSize = sizeof(CHARFORMAT2A);
1076 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1077 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1079 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1080 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1081 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1082 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1084 /* Setting the (empty) selection to exactly the same place as before should
1085 NOT clear the insertion style! */
1086 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1087 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1089 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1090 cf2.cbSize = sizeof(CHARFORMAT2A);
1091 cf2.dwMask = CFM_BOLD;
1092 cf2.dwEffects = CFE_BOLD;
1093 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1095 /* Empty selection in same place, insert style should NOT be forgotten here. */
1096 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1098 /* Selection is now nonempty */
1099 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1101 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1102 cf2.cbSize = sizeof(CHARFORMAT2A);
1103 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1104 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1106 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1107 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1108 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1109 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1111 /* Ditto with EM_EXSETSEL */
1112 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1113 cr.cpMin = 2; cr.cpMax = 2;
1114 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1116 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1117 cf2.cbSize = sizeof(CHARFORMAT2A);
1118 cf2.dwMask = CFM_BOLD;
1119 cf2.dwEffects = CFE_BOLD;
1120 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1122 /* Empty selection in same place, insert style should NOT be forgotten here. */
1123 cr.cpMin = 2; cr.cpMax = 2;
1124 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1126 /* Selection is now nonempty */
1127 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1129 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1130 cf2.cbSize = sizeof(CHARFORMAT2A);
1131 cr.cpMin = 2; cr.cpMax = 6;
1132 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1133 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1135 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1136 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1137 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1138 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1140 DestroyWindow(hwndRichEdit);
1143 static void test_EM_SETTEXTMODE(void)
1145 HWND hwndRichEdit = new_richedit(NULL);
1146 CHARFORMAT2A cf2, cf2test;
1147 CHARRANGE cr;
1148 int rc = 0;
1150 /*Attempt to use mutually exclusive modes*/
1151 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1152 ok(rc == E_INVALIDARG,
1153 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1155 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1156 /*Insert text into the control*/
1158 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1160 /*Attempt to change the control to plain text mode*/
1161 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1162 ok(rc == E_UNEXPECTED,
1163 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1165 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1166 If rich text is pasted, it should have the same formatting as the rest
1167 of the text in the control*/
1169 /*Italicize the text
1170 *NOTE: If the default text was already italicized, the test will simply
1171 reverse; in other words, it will copy a regular "wine" into a plain
1172 text window that uses an italicized format*/
1173 cf2.cbSize = sizeof(CHARFORMAT2A);
1174 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1176 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1177 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1179 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1180 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1182 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1183 however, SCF_ALL has been implemented*/
1184 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1185 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1187 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1188 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1190 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1192 /*Select the string "wine"*/
1193 cr.cpMin = 0;
1194 cr.cpMax = 4;
1195 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1197 /*Copy the italicized "wine" to the clipboard*/
1198 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1200 /*Reset the formatting to default*/
1201 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1202 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1203 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1205 /*Clear the text in the control*/
1206 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1208 /*Switch to Plain Text Mode*/
1209 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1210 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1212 /*Input "wine" again in normal format*/
1213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1215 /*Paste the italicized "wine" into the control*/
1216 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1218 /*Select a character from the first "wine" string*/
1219 cr.cpMin = 2;
1220 cr.cpMax = 3;
1221 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1223 /*Retrieve its formatting*/
1224 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1226 /*Select a character from the second "wine" string*/
1227 cr.cpMin = 5;
1228 cr.cpMax = 6;
1229 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1231 /*Retrieve its formatting*/
1232 cf2test.cbSize = sizeof(CHARFORMAT2A);
1233 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1235 /*Compare the two formattings*/
1236 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1237 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1238 cf2.dwEffects, cf2test.dwEffects);
1239 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1240 printing "wine" in the current format(normal)
1241 pasting "wine" from the clipboard(italicized)
1242 comparing the two formats(should differ)*/
1244 /*Attempt to switch with text in control*/
1245 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1246 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1248 /*Clear control*/
1249 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1251 /*Switch into Rich Text mode*/
1252 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1253 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1255 /*Print "wine" in normal formatting into the control*/
1256 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1258 /*Paste italicized "wine" into the control*/
1259 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1261 /*Select text from the first "wine" string*/
1262 cr.cpMin = 1;
1263 cr.cpMax = 3;
1264 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1266 /*Retrieve its formatting*/
1267 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1269 /*Select text from the second "wine" string*/
1270 cr.cpMin = 6;
1271 cr.cpMax = 7;
1272 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1274 /*Retrieve its formatting*/
1275 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1277 /*Test that the two formattings are not the same*/
1278 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1279 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1280 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1282 DestroyWindow(hwndRichEdit);
1285 static void test_SETPARAFORMAT(void)
1287 HWND hwndRichEdit = new_richedit(NULL);
1288 PARAFORMAT2 fmt;
1289 HRESULT ret;
1290 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1291 fmt.cbSize = sizeof(PARAFORMAT2);
1292 fmt.dwMask = PFM_ALIGNMENT;
1293 fmt.wAlignment = PFA_LEFT;
1295 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1296 ok(ret != 0, "expected non-zero got %d\n", ret);
1298 fmt.cbSize = sizeof(PARAFORMAT2);
1299 fmt.dwMask = -1;
1300 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1301 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1302 * between richedit different native builds of riched20.dll
1303 * used on different Windows versions. */
1304 ret &= ~PFM_TABLEROWDELIMITER;
1305 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1307 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1308 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1310 DestroyWindow(hwndRichEdit);
1313 static void test_TM_PLAINTEXT(void)
1315 /*Tests plain text properties*/
1317 HWND hwndRichEdit = new_richedit(NULL);
1318 CHARFORMAT2A cf2, cf2test;
1319 CHARRANGE cr;
1320 int rc = 0;
1322 /*Switch to plain text mode*/
1324 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1325 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1327 /*Fill control with text*/
1329 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1331 /*Select some text and bold it*/
1333 cr.cpMin = 10;
1334 cr.cpMax = 20;
1335 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1336 cf2.cbSize = sizeof(CHARFORMAT2A);
1337 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1339 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1340 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1342 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1343 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1345 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1346 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1348 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1349 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1351 /*Get the formatting of those characters*/
1353 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1355 /*Get the formatting of some other characters*/
1356 cf2test.cbSize = sizeof(CHARFORMAT2A);
1357 cr.cpMin = 21;
1358 cr.cpMax = 30;
1359 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1360 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1362 /*Test that they are the same as plain text allows only one formatting*/
1364 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1365 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1366 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1368 /*Fill the control with a "wine" string, which when inserted will be bold*/
1370 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1372 /*Copy the bolded "wine" string*/
1374 cr.cpMin = 0;
1375 cr.cpMax = 4;
1376 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1377 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1379 /*Swap back to rich text*/
1381 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1382 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1384 /*Set the default formatting to bold italics*/
1386 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1387 cf2.dwMask |= CFM_ITALIC;
1388 cf2.dwEffects ^= CFE_ITALIC;
1389 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1390 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1392 /*Set the text in the control to "wine", which will be bold and italicized*/
1394 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1396 /*Paste the plain text "wine" string, which should take the insert
1397 formatting, which at the moment is bold italics*/
1399 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1401 /*Select the first "wine" string and retrieve its formatting*/
1403 cr.cpMin = 1;
1404 cr.cpMax = 3;
1405 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1406 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1408 /*Select the second "wine" string and retrieve its formatting*/
1410 cr.cpMin = 5;
1411 cr.cpMax = 7;
1412 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1413 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1415 /*Compare the two formattings. They should be the same.*/
1417 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1418 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1419 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1420 DestroyWindow(hwndRichEdit);
1423 static void test_WM_GETTEXT(void)
1425 HWND hwndRichEdit = new_richedit(NULL);
1426 static const char text[] = "Hello. My name is RichEdit!";
1427 static const char text2[] = "Hello. My name is RichEdit!\r";
1428 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1429 char buffer[1024] = {0};
1430 int result;
1432 /* Baseline test with normal-sized buffer */
1433 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1434 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1435 ok(result == lstrlenA(buffer),
1436 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1437 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1438 result = strcmp(buffer,text);
1439 ok(result == 0,
1440 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1442 /* Test for returned value of WM_GETTEXTLENGTH */
1443 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1444 ok(result == lstrlenA(text),
1445 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1446 result, lstrlenA(text));
1448 /* Test for behavior in overflow case */
1449 memset(buffer, 0, 1024);
1450 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1451 ok(result == 0 ||
1452 result == lstrlenA(text) - 1, /* XP, win2k3 */
1453 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1454 result = strcmp(buffer,text);
1455 if (result)
1456 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1457 ok(result == 0,
1458 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1460 /* Baseline test with normal-sized buffer and carriage return */
1461 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1462 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1463 ok(result == lstrlenA(buffer),
1464 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1465 result = strcmp(buffer,text2_after);
1466 ok(result == 0,
1467 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1469 /* Test for returned value of WM_GETTEXTLENGTH */
1470 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1471 ok(result == lstrlenA(text2_after),
1472 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1473 result, lstrlenA(text2_after));
1475 /* Test for behavior of CRLF conversion in case of overflow */
1476 memset(buffer, 0, 1024);
1477 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1478 ok(result == 0 ||
1479 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1480 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1481 result = strcmp(buffer,text2);
1482 if (result)
1483 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1484 ok(result == 0,
1485 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1487 DestroyWindow(hwndRichEdit);
1490 static void test_EM_GETTEXTRANGE(void)
1492 HWND hwndRichEdit = new_richedit(NULL);
1493 const char * text1 = "foo bar\r\nfoo bar";
1494 const char * text2 = "foo bar\rfoo bar";
1495 const char * expect = "bar\rfoo";
1496 char buffer[1024] = {0};
1497 LRESULT result;
1498 TEXTRANGEA textRange;
1500 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1502 textRange.lpstrText = buffer;
1503 textRange.chrg.cpMin = 4;
1504 textRange.chrg.cpMax = 11;
1505 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1506 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1507 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1509 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1511 textRange.lpstrText = buffer;
1512 textRange.chrg.cpMin = 4;
1513 textRange.chrg.cpMax = 11;
1514 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1515 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1516 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1518 /* cpMax of text length is used instead of -1 in this case */
1519 textRange.lpstrText = buffer;
1520 textRange.chrg.cpMin = 0;
1521 textRange.chrg.cpMax = -1;
1522 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1523 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1524 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1526 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1527 textRange.lpstrText = buffer;
1528 textRange.chrg.cpMin = -1;
1529 textRange.chrg.cpMax = 1;
1530 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1531 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1532 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1534 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1535 textRange.lpstrText = buffer;
1536 textRange.chrg.cpMin = 1;
1537 textRange.chrg.cpMax = -1;
1538 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1539 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1540 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1542 /* no end character is copied if cpMax - cpMin < 0 */
1543 textRange.lpstrText = buffer;
1544 textRange.chrg.cpMin = 5;
1545 textRange.chrg.cpMax = 5;
1546 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1547 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1548 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1550 /* cpMax of text length is used if cpMax > text length*/
1551 textRange.lpstrText = buffer;
1552 textRange.chrg.cpMin = 0;
1553 textRange.chrg.cpMax = 1000;
1554 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1555 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1556 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1558 /* Test with multibyte character */
1559 if (!is_lang_japanese)
1560 skip("Skip multibyte character tests on non-Japanese platform\n");
1561 else
1563 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1564 textRange.chrg.cpMin = 4;
1565 textRange.chrg.cpMax = 8;
1566 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1567 todo_wine ok(result == 5, "EM_GETTEXTRANGE returned %ld\n", result);
1568 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1571 DestroyWindow(hwndRichEdit);
1574 static void test_EM_GETSELTEXT(void)
1576 HWND hwndRichEdit = new_richedit(NULL);
1577 const char * text1 = "foo bar\r\nfoo bar";
1578 const char * text2 = "foo bar\rfoo bar";
1579 const char * expect = "bar\rfoo";
1580 char buffer[1024] = {0};
1581 LRESULT result;
1583 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1585 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1586 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1587 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1588 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1590 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1592 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1593 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1594 ok(result == 7, "EM_GETSELTEXT returned %ld\n", result);
1595 ok(!strcmp(expect, buffer), "EM_GETSELTEXT filled %s\n", buffer);
1597 /* Test with multibyte character */
1598 if (!is_lang_japanese)
1599 skip("Skip multibyte character tests on non-Japanese platform\n");
1600 else
1602 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
1603 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
1604 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1605 todo_wine ok(result == 5, "EM_GETSELTEXT returned %ld\n", result);
1606 todo_wine ok(!strcmp("ef\x8e\xf0g", buffer), "EM_GETSELTEXT filled %s\n", buffer);
1609 DestroyWindow(hwndRichEdit);
1612 /* FIXME: need to test unimplemented options and robustly test wparam */
1613 static void test_EM_SETOPTIONS(void)
1615 HWND hwndRichEdit;
1616 static const char text[] = "Hello. My name is RichEdit!";
1617 char buffer[1024] = {0};
1618 DWORD dwStyle, options, oldOptions;
1619 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1620 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1621 ES_SELECTIONBAR|ES_VERTICAL;
1623 /* Test initial options. */
1624 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1625 0, 0, 200, 60, NULL, NULL,
1626 hmoduleRichEdit, NULL);
1627 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1628 RICHEDIT_CLASS20A, (int) GetLastError());
1629 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1630 ok(options == 0, "Incorrect initial options %x\n", options);
1631 DestroyWindow(hwndRichEdit);
1633 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1634 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1635 0, 0, 200, 60, NULL, NULL,
1636 hmoduleRichEdit, NULL);
1637 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1638 RICHEDIT_CLASS20A, (int) GetLastError());
1639 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1640 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1641 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1642 "Incorrect initial options %x\n", options);
1644 /* NEGATIVE TESTING - NO OPTIONS SET */
1645 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1646 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1648 /* testing no readonly by sending 'a' to the control*/
1649 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1650 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1651 ok(buffer[0]=='a',
1652 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1653 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1655 /* READONLY - sending 'a' to the control */
1656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1657 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1658 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1659 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1660 ok(buffer[0]==text[0],
1661 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1663 /* EM_SETOPTIONS changes the window style, but changing the
1664 * window style does not change the options. */
1665 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1666 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1667 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1668 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1669 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1670 /* Confirm that the text is still read only. */
1671 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1672 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1673 ok(buffer[0]==text[0],
1674 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1676 oldOptions = options;
1677 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1678 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1679 ok(options == oldOptions,
1680 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1682 DestroyWindow(hwndRichEdit);
1685 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1687 CHARFORMAT2A text_format;
1688 text_format.cbSize = sizeof(text_format);
1689 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1690 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1691 return (text_format.dwEffects & CFE_LINK) != 0;
1694 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1696 BOOL link_present = FALSE;
1698 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1699 if (is_url)
1700 { /* control text is url; should get CFE_LINK */
1701 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1703 else
1705 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1709 static HWND new_static_wnd(HWND parent) {
1710 return new_window("Static", 0, parent);
1713 static void test_EM_AUTOURLDETECT(void)
1715 /* DO NOT change the properties of the first two elements. To shorten the
1716 tests, all tests after WM_SETTEXT test just the first two elements -
1717 one non-URL and one URL */
1718 struct urls_s {
1719 const char *text;
1720 BOOL is_url;
1721 } urls[12] = {
1722 {"winehq.org", FALSE},
1723 {"http://www.winehq.org", TRUE},
1724 {"http//winehq.org", FALSE},
1725 {"ww.winehq.org", FALSE},
1726 {"www.winehq.org", TRUE},
1727 {"ftp://192.168.1.1", TRUE},
1728 {"ftp//192.168.1.1", FALSE},
1729 {"mailto:your@email.com", TRUE},
1730 {"prospero:prosperoserver", TRUE},
1731 {"telnet:test", TRUE},
1732 {"news:newserver", TRUE},
1733 {"wais:waisserver", TRUE}
1736 int i, j;
1737 int urlRet=-1;
1738 HWND hwndRichEdit, parent;
1740 /* All of the following should cause the URL to be detected */
1741 const char * templates_delim[] = {
1742 "This is some text with X on it",
1743 "This is some text with (X) on it",
1744 "This is some text with X\r on it",
1745 "This is some text with ---X--- on it",
1746 "This is some text with \"X\" on it",
1747 "This is some text with 'X' on it",
1748 "This is some text with 'X' on it",
1749 "This is some text with :X: on it",
1751 "This text ends with X",
1753 "This is some text with X) on it",
1754 "This is some text with X--- on it",
1755 "This is some text with X\" on it",
1756 "This is some text with X' on it",
1757 "This is some text with X: on it",
1759 "This is some text with (X on it",
1760 "This is some text with \rX on it",
1761 "This is some text with ---X on it",
1762 "This is some text with \"X on it",
1763 "This is some text with 'X on it",
1764 "This is some text with :X on it",
1766 /* None of these should cause the URL to be detected */
1767 const char * templates_non_delim[] = {
1768 "This is some text with |X| on it",
1769 "This is some text with *X* on it",
1770 "This is some text with /X/ on it",
1771 "This is some text with +X+ on it",
1772 "This is some text with %X% on it",
1773 "This is some text with #X# on it",
1774 "This is some text with @X@ on it",
1775 "This is some text with \\X\\ on it",
1776 "This is some text with |X on it",
1777 "This is some text with *X on it",
1778 "This is some text with /X on it",
1779 "This is some text with +X on it",
1780 "This is some text with %X on it",
1781 "This is some text with #X on it",
1782 "This is some text with @X on it",
1783 "This is some text with \\X on it",
1784 "This is some text with _X on it",
1786 /* All of these cause the URL detection to be extended by one more byte,
1787 thus demonstrating that the tested character is considered as part
1788 of the URL. */
1789 const char * templates_xten_delim[] = {
1790 "This is some text with X| on it",
1791 "This is some text with X* on it",
1792 "This is some text with X/ on it",
1793 "This is some text with X+ on it",
1794 "This is some text with X% on it",
1795 "This is some text with X# on it",
1796 "This is some text with X@ on it",
1797 "This is some text with X\\ on it",
1798 "This is some text with X_ on it",
1800 /* These delims act as neutral breaks. Whether the url is ended
1801 or not depends on the next non-neutral character. We'll test
1802 with Y unchanged, in which case the url should include the
1803 deliminator and the Y. We'll also test with the Y changed
1804 to a space, in which case the url stops before the
1805 deliminator. */
1806 const char * templates_neutral_delim[] = {
1807 "This is some text with X-Y on it",
1808 "This is some text with X--Y on it",
1809 "This is some text with X!Y on it",
1810 "This is some text with X[Y on it",
1811 "This is some text with X]Y on it",
1812 "This is some text with X{Y on it",
1813 "This is some text with X}Y on it",
1814 "This is some text with X(Y on it",
1815 "This is some text with X)Y on it",
1816 "This is some text with X\"Y on it",
1817 "This is some text with X;Y on it",
1818 "This is some text with X:Y on it",
1819 "This is some text with X'Y on it",
1820 "This is some text with X?Y on it",
1821 "This is some text with X<Y on it",
1822 "This is some text with X>Y on it",
1823 "This is some text with X.Y on it",
1824 "This is some text with X,Y on it",
1826 char buffer[1024];
1828 parent = new_static_wnd(NULL);
1829 hwndRichEdit = new_richedit(parent);
1830 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1831 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1832 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1833 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1834 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1835 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1836 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1837 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1838 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1839 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1840 /* for each url, check the text to see if CFE_LINK effect is present */
1841 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1843 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1844 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1845 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1847 /* Link detection should happen immediately upon WM_SETTEXT */
1848 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1849 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1850 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1852 DestroyWindow(hwndRichEdit);
1854 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1855 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1856 hwndRichEdit = new_richedit(parent);
1858 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1859 char * at_pos;
1860 int at_offset;
1861 int end_offset;
1863 at_pos = strchr(templates_delim[j], 'X');
1864 at_offset = at_pos - templates_delim[j];
1865 memcpy(buffer, templates_delim[j], at_offset);
1866 buffer[at_offset] = '\0';
1867 strcat(buffer, urls[i].text);
1868 strcat(buffer, templates_delim[j] + at_offset + 1);
1869 end_offset = at_offset + strlen(urls[i].text);
1871 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1872 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1874 /* This assumes no templates start with the URL itself, and that they
1875 have at least two characters before the URL text */
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1880 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1881 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1883 if (urls[i].is_url)
1885 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1886 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1887 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1888 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1890 else
1892 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1893 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1894 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1895 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1897 if (buffer[end_offset] != '\0')
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1901 if (buffer[end_offset +1] != '\0')
1903 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1904 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1909 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1910 char * at_pos;
1911 int at_offset;
1912 int end_offset;
1914 at_pos = strchr(templates_non_delim[j], 'X');
1915 at_offset = at_pos - templates_non_delim[j];
1916 memcpy(buffer, templates_non_delim[j], at_offset);
1917 buffer[at_offset] = '\0';
1918 strcat(buffer, urls[i].text);
1919 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1920 end_offset = at_offset + strlen(urls[i].text);
1922 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1923 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1925 /* This assumes no templates start with the URL itself, and that they
1926 have at least two characters before the URL text */
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1931 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1932 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1934 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1935 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1938 if (buffer[end_offset] != '\0')
1940 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1941 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1942 if (buffer[end_offset +1] != '\0')
1944 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1945 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1950 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1951 char * at_pos;
1952 int at_offset;
1953 int end_offset;
1955 at_pos = strchr(templates_xten_delim[j], 'X');
1956 at_offset = at_pos - templates_xten_delim[j];
1957 memcpy(buffer, templates_xten_delim[j], at_offset);
1958 buffer[at_offset] = '\0';
1959 strcat(buffer, urls[i].text);
1960 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1961 end_offset = at_offset + strlen(urls[i].text);
1963 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1964 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1966 /* This assumes no templates start with the URL itself, and that they
1967 have at least two characters before the URL text */
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1970 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1971 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1972 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1973 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1975 if (urls[i].is_url)
1977 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1978 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1979 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1980 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1981 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1982 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1984 else
1986 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1987 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1993 if (buffer[end_offset +1] != '\0')
1995 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1996 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1997 if (buffer[end_offset +2] != '\0')
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2005 for (j = 0; j < sizeof(templates_neutral_delim) / sizeof(const char *); j++) {
2006 char * at_pos, * end_pos;
2007 int at_offset;
2008 int end_offset;
2010 if (!urls[i].is_url) continue;
2012 at_pos = strchr(templates_neutral_delim[j], 'X');
2013 at_offset = at_pos - templates_neutral_delim[j];
2014 memcpy(buffer, templates_neutral_delim[j], at_offset);
2015 buffer[at_offset] = '\0';
2016 strcat(buffer, urls[i].text);
2017 strcat(buffer, templates_neutral_delim[j] + at_offset + 1);
2019 end_pos = strchr(buffer, 'Y');
2020 end_offset = end_pos - buffer;
2022 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2023 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2025 /* This assumes no templates start with the URL itself, and that they
2026 have at least two characters before the URL text */
2027 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2028 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2029 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2030 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2031 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2032 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2034 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2035 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2036 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2037 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2038 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2039 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2041 *end_pos = ' ';
2043 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2044 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
2046 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2047 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2048 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2049 "CFE_LINK set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2050 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2051 "CFE_LINK set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
2054 DestroyWindow(hwndRichEdit);
2055 hwndRichEdit = NULL;
2058 /* Test detection of URLs within normal text - WM_CHAR case. */
2059 /* Test only the first two URL examples for brevity */
2060 for (i = 0; i < 2; i++) {
2061 hwndRichEdit = new_richedit(parent);
2063 /* Also for brevity, test only the first three delimiters */
2064 for (j = 0; j < 3; j++) {
2065 char * at_pos;
2066 int at_offset;
2067 int end_offset;
2068 int u, v;
2070 at_pos = strchr(templates_delim[j], 'X');
2071 at_offset = at_pos - templates_delim[j];
2072 end_offset = at_offset + strlen(urls[i].text);
2074 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2075 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2076 for (u = 0; templates_delim[j][u]; u++) {
2077 if (templates_delim[j][u] == '\r') {
2078 simulate_typing_characters(hwndRichEdit, "\r");
2079 } else if (templates_delim[j][u] != 'X') {
2080 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
2081 } else {
2082 for (v = 0; urls[i].text[v]; v++) {
2083 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
2087 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2089 /* This assumes no templates start with the URL itself, and that they
2090 have at least two characters before the URL text */
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2098 if (urls[i].is_url)
2100 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2101 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2102 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2103 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2105 else
2107 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2108 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2109 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2110 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2112 if (buffer[end_offset] != '\0')
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2116 if (buffer[end_offset +1] != '\0')
2118 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2119 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2123 /* The following will insert a paragraph break after the first character
2124 of the URL candidate, thus breaking the URL. It is expected that the
2125 CFE_LINK attribute should break across both pieces of the URL */
2126 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2127 simulate_typing_characters(hwndRichEdit, "\r");
2128 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2132 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2133 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2134 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2135 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2137 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2138 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2139 /* end_offset moved because of paragraph break */
2140 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2141 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2142 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2143 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2145 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2147 if (buffer[end_offset +2] != '\0')
2149 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2150 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2154 /* The following will remove the just-inserted paragraph break, thus
2155 restoring the URL */
2156 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2157 simulate_typing_characters(hwndRichEdit, "\b");
2158 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2162 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2163 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2164 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2165 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2167 if (urls[i].is_url)
2169 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2170 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2171 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2172 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2174 else
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2178 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2179 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2181 if (buffer[end_offset] != '\0')
2183 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2184 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2185 if (buffer[end_offset +1] != '\0')
2187 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2188 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2192 DestroyWindow(hwndRichEdit);
2193 hwndRichEdit = NULL;
2196 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2197 /* Test just the first two URL examples for brevity */
2198 for (i = 0; i < 2; i++) {
2199 SETTEXTEX st;
2201 hwndRichEdit = new_richedit(parent);
2203 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2204 be detected:
2205 1) Set entire text, a la WM_SETTEXT
2206 2) Set a selection of the text to the URL
2207 3) Set a portion of the text at a time, which eventually results in
2208 an URL
2209 All of them should give equivalent results
2212 /* Set entire text in one go, like WM_SETTEXT */
2213 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2214 char * at_pos;
2215 int at_offset;
2216 int end_offset;
2218 st.codepage = CP_ACP;
2219 st.flags = ST_DEFAULT;
2221 at_pos = strchr(templates_delim[j], 'X');
2222 at_offset = at_pos - templates_delim[j];
2223 memcpy(buffer, templates_delim[j], at_offset);
2224 buffer[at_offset] = '\0';
2225 strcat(buffer, urls[i].text);
2226 strcat(buffer, templates_delim[j] + at_offset + 1);
2227 end_offset = at_offset + strlen(urls[i].text);
2229 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2230 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2232 /* This assumes no templates start with the URL itself, and that they
2233 have at least two characters before the URL text */
2234 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2235 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2236 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2237 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2238 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2239 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2241 if (urls[i].is_url)
2243 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2244 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2245 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2246 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2248 else
2250 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2251 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2255 if (buffer[end_offset] != '\0')
2257 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2258 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2259 if (buffer[end_offset +1] != '\0')
2261 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2262 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2267 /* Set selection with X to the URL */
2268 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2269 char * at_pos;
2270 int at_offset;
2271 int end_offset;
2273 at_pos = strchr(templates_delim[j], 'X');
2274 at_offset = at_pos - templates_delim[j];
2275 end_offset = at_offset + strlen(urls[i].text);
2277 st.codepage = CP_ACP;
2278 st.flags = ST_DEFAULT;
2279 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2280 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2281 st.flags = ST_SELECTION;
2282 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2283 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2284 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2286 /* This assumes no templates start with the URL itself, and that they
2287 have at least two characters before the URL text */
2288 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2289 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2290 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2291 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2292 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2293 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2295 if (urls[i].is_url)
2297 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2298 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2299 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2300 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2302 else
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2306 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2307 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2309 if (buffer[end_offset] != '\0')
2311 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2312 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2313 if (buffer[end_offset +1] != '\0')
2315 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2316 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2321 /* Set selection with X to the first character of the URL, then the rest */
2322 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2323 char * at_pos;
2324 int at_offset;
2325 int end_offset;
2327 at_pos = strchr(templates_delim[j], 'X');
2328 at_offset = at_pos - templates_delim[j];
2329 end_offset = at_offset + strlen(urls[i].text);
2331 strcpy(buffer, "YY");
2332 buffer[0] = urls[i].text[0];
2334 st.codepage = CP_ACP;
2335 st.flags = ST_DEFAULT;
2336 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2337 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2338 st.flags = ST_SELECTION;
2339 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2340 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2341 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2342 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2343 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2345 /* This assumes no templates start with the URL itself, and that they
2346 have at least two characters before the URL text */
2347 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2348 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2349 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2350 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2351 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2352 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2354 if (urls[i].is_url)
2356 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2357 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2358 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2359 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2361 else
2363 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2364 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2365 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2366 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2368 if (buffer[end_offset] != '\0')
2370 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2371 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2372 if (buffer[end_offset +1] != '\0')
2374 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2375 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2380 DestroyWindow(hwndRichEdit);
2381 hwndRichEdit = NULL;
2384 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2385 /* Test just the first two URL examples for brevity */
2386 for (i = 0; i < 2; i++) {
2387 hwndRichEdit = new_richedit(parent);
2389 /* Set selection with X to the URL */
2390 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2391 char * at_pos;
2392 int at_offset;
2393 int end_offset;
2395 at_pos = strchr(templates_delim[j], 'X');
2396 at_offset = at_pos - templates_delim[j];
2397 end_offset = at_offset + strlen(urls[i].text);
2399 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2400 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2401 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2402 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2403 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2405 /* This assumes no templates start with the URL itself, and that they
2406 have at least two characters before the URL text */
2407 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2408 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2409 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2410 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2411 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2412 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2414 if (urls[i].is_url)
2416 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2417 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2418 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2419 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2421 else
2423 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2424 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2425 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2426 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2428 if (buffer[end_offset] != '\0')
2430 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2431 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2432 if (buffer[end_offset +1] != '\0')
2434 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2435 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2440 /* Set selection with X to the first character of the URL, then the rest */
2441 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2442 char * at_pos;
2443 int at_offset;
2444 int end_offset;
2446 at_pos = strchr(templates_delim[j], 'X');
2447 at_offset = at_pos - templates_delim[j];
2448 end_offset = at_offset + strlen(urls[i].text);
2450 strcpy(buffer, "YY");
2451 buffer[0] = urls[i].text[0];
2453 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2454 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2455 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2456 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2457 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2458 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2459 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2461 /* This assumes no templates start with the URL itself, and that they
2462 have at least two characters before the URL text */
2463 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2464 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2465 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2466 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2467 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2468 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2470 if (urls[i].is_url)
2472 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2473 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2474 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2475 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2477 else
2479 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2480 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2481 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2482 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2484 if (buffer[end_offset] != '\0')
2486 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2487 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2488 if (buffer[end_offset +1] != '\0')
2490 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2491 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2496 DestroyWindow(hwndRichEdit);
2497 hwndRichEdit = NULL;
2500 DestroyWindow(parent);
2503 static void test_EM_SCROLL(void)
2505 int i, j;
2506 int r; /* return value */
2507 int expr; /* expected return value */
2508 HWND hwndRichEdit = new_richedit(NULL);
2509 int y_before, y_after; /* units of lines of text */
2511 /* test a richedit box containing a single line of text */
2512 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2513 expr = 0x00010000;
2514 for (i = 0; i < 4; i++) {
2515 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2517 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2518 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2519 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2520 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2521 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2522 "(i == %d)\n", y_after, i);
2526 * test a richedit box that will scroll. There are two general
2527 * cases: the case without any long lines and the case with a long
2528 * line.
2530 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2531 if (i == 0)
2532 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2533 else
2534 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2535 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2536 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2537 "LONG LINE \nb\nc\nd\ne");
2538 for (j = 0; j < 12; j++) /* reset scroll position to top */
2539 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2541 /* get first visible line */
2542 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2543 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2545 /* get new current first visible line */
2546 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2548 ok(((r & 0xffffff00) == 0x00010000) &&
2549 ((r & 0x000000ff) != 0x00000000),
2550 "EM_SCROLL page down didn't scroll by a small positive number of "
2551 "lines (r == 0x%08x)\n", r);
2552 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2553 "(line %d scrolled to line %d\n", y_before, y_after);
2555 y_before = y_after;
2557 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2558 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2559 ok(((r & 0xffffff00) == 0x0001ff00),
2560 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2561 "(r == 0x%08x)\n", r);
2562 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2563 "%d scrolled to line %d\n", y_before, y_after);
2565 y_before = y_after;
2567 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2569 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2571 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2572 "(r == 0x%08x)\n", r);
2573 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2574 "1 line (%d scrolled to %d)\n", y_before, y_after);
2576 y_before = y_after;
2578 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2580 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2582 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2583 "(r == 0x%08x)\n", r);
2584 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2585 "line (%d scrolled to %d)\n", y_before, y_after);
2587 y_before = y_after;
2589 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2590 SB_LINEUP, 0); /* lineup beyond top */
2592 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2594 ok(r == 0x00010000,
2595 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2596 ok(y_before == y_after,
2597 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2599 y_before = y_after;
2601 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2602 SB_PAGEUP, 0);/*page up beyond top */
2604 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2606 ok(r == 0x00010000,
2607 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2608 ok(y_before == y_after,
2609 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2611 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2612 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2613 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2614 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2615 SB_PAGEDOWN, 0); /* page down beyond bot */
2616 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2618 ok(r == 0x00010000,
2619 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2620 ok(y_before == y_after,
2621 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2622 y_before, y_after);
2624 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2625 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2626 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2628 ok(r == 0x00010000,
2629 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2630 ok(y_before == y_after,
2631 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2632 y_before, y_after);
2634 DestroyWindow(hwndRichEdit);
2637 static unsigned int recursionLevel = 0;
2638 static unsigned int WM_SIZE_recursionLevel = 0;
2639 static BOOL bailedOutOfRecursion = FALSE;
2640 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2642 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2644 LRESULT r;
2646 if (bailedOutOfRecursion) return 0;
2647 if (recursionLevel >= 32) {
2648 bailedOutOfRecursion = TRUE;
2649 return 0;
2652 recursionLevel++;
2653 switch (message) {
2654 case WM_SIZE:
2655 WM_SIZE_recursionLevel++;
2656 r = richeditProc(hwnd, message, wParam, lParam);
2657 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2658 ShowScrollBar(hwnd, SB_VERT, TRUE);
2659 WM_SIZE_recursionLevel--;
2660 break;
2661 default:
2662 r = richeditProc(hwnd, message, wParam, lParam);
2663 break;
2665 recursionLevel--;
2666 return r;
2669 static void test_scrollbar_visibility(void)
2671 HWND hwndRichEdit;
2672 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2673 SCROLLINFO si;
2674 WNDCLASSA cls;
2675 BOOL r;
2677 /* These tests show that richedit should temporarily refrain from automatically
2678 hiding or showing its scrollbars (vertical at least) when an explicit request
2679 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2680 Some applications depend on forced showing (when otherwise richedit would
2681 hide the vertical scrollbar) and are thrown on an endless recursive loop
2682 if richedit auto-hides the scrollbar again. Apparently they never heard of
2683 the ES_DISABLENOSCROLL style... */
2685 hwndRichEdit = new_richedit(NULL);
2687 /* Test default scrollbar visibility behavior */
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2693 "Vertical scrollbar is visible, should be invisible.\n");
2694 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2695 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2696 si.nPage, si.nMin, si.nMax);
2698 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2699 memset(&si, 0, sizeof(si));
2700 si.cbSize = sizeof(si);
2701 si.fMask = SIF_PAGE | SIF_RANGE;
2702 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2703 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2704 "Vertical scrollbar is visible, should be invisible.\n");
2705 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2706 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2707 si.nPage, si.nMin, si.nMax);
2709 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2710 memset(&si, 0, sizeof(si));
2711 si.cbSize = sizeof(si);
2712 si.fMask = SIF_PAGE | SIF_RANGE;
2713 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2714 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2715 "Vertical scrollbar is invisible, should be visible.\n");
2716 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2717 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2718 si.nPage, si.nMin, si.nMax);
2720 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2721 even though it hides the scrollbar */
2722 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2723 memset(&si, 0, sizeof(si));
2724 si.cbSize = sizeof(si);
2725 si.fMask = SIF_PAGE | SIF_RANGE;
2726 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2727 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2728 "Vertical scrollbar is visible, should be invisible.\n");
2729 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2730 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2731 si.nPage, si.nMin, si.nMax);
2733 /* Setting non-scrolling text again does *not* reset scrollbar range */
2734 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2735 memset(&si, 0, sizeof(si));
2736 si.cbSize = sizeof(si);
2737 si.fMask = SIF_PAGE | SIF_RANGE;
2738 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2739 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2740 "Vertical scrollbar is visible, should be invisible.\n");
2741 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2742 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2743 si.nPage, si.nMin, si.nMax);
2745 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2746 memset(&si, 0, sizeof(si));
2747 si.cbSize = sizeof(si);
2748 si.fMask = SIF_PAGE | SIF_RANGE;
2749 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2750 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2751 "Vertical scrollbar is visible, should be invisible.\n");
2752 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2753 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2754 si.nPage, si.nMin, si.nMax);
2756 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2757 memset(&si, 0, sizeof(si));
2758 si.cbSize = sizeof(si);
2759 si.fMask = SIF_PAGE | SIF_RANGE;
2760 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2761 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2762 "Vertical scrollbar is visible, should be invisible.\n");
2763 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2764 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2765 si.nPage, si.nMin, si.nMax);
2767 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2768 memset(&si, 0, sizeof(si));
2769 si.cbSize = sizeof(si);
2770 si.fMask = SIF_PAGE | SIF_RANGE;
2771 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2772 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2773 "Vertical scrollbar is visible, should be invisible.\n");
2774 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2775 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2776 si.nPage, si.nMin, si.nMax);
2778 DestroyWindow(hwndRichEdit);
2780 /* Test again, with ES_DISABLENOSCROLL style */
2781 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2783 /* Test default scrollbar visibility behavior */
2784 memset(&si, 0, sizeof(si));
2785 si.cbSize = sizeof(si);
2786 si.fMask = SIF_PAGE | SIF_RANGE;
2787 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2788 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2789 "Vertical scrollbar is invisible, should be visible.\n");
2790 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2791 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2792 si.nPage, si.nMin, si.nMax);
2794 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2795 memset(&si, 0, sizeof(si));
2796 si.cbSize = sizeof(si);
2797 si.fMask = SIF_PAGE | SIF_RANGE;
2798 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2799 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2800 "Vertical scrollbar is invisible, should be visible.\n");
2801 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2802 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2803 si.nPage, si.nMin, si.nMax);
2805 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2806 memset(&si, 0, sizeof(si));
2807 si.cbSize = sizeof(si);
2808 si.fMask = SIF_PAGE | SIF_RANGE;
2809 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2810 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2811 "Vertical scrollbar is invisible, should be visible.\n");
2812 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2813 "reported page/range is %d (%d..%d)\n",
2814 si.nPage, si.nMin, si.nMax);
2816 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2817 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2818 memset(&si, 0, sizeof(si));
2819 si.cbSize = sizeof(si);
2820 si.fMask = SIF_PAGE | SIF_RANGE;
2821 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2822 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2823 "Vertical scrollbar is invisible, should be visible.\n");
2824 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2825 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2826 si.nPage, si.nMin, si.nMax);
2828 /* Setting non-scrolling text again does *not* reset scrollbar range */
2829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2830 memset(&si, 0, sizeof(si));
2831 si.cbSize = sizeof(si);
2832 si.fMask = SIF_PAGE | SIF_RANGE;
2833 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2834 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2835 "Vertical scrollbar is invisible, should be visible.\n");
2836 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2837 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2838 si.nPage, si.nMin, si.nMax);
2840 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2841 memset(&si, 0, sizeof(si));
2842 si.cbSize = sizeof(si);
2843 si.fMask = SIF_PAGE | SIF_RANGE;
2844 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2845 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2846 "Vertical scrollbar is invisible, should be visible.\n");
2847 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2848 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2849 si.nPage, si.nMin, si.nMax);
2851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2852 memset(&si, 0, sizeof(si));
2853 si.cbSize = sizeof(si);
2854 si.fMask = SIF_PAGE | SIF_RANGE;
2855 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2856 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2857 "Vertical scrollbar is invisible, should be visible.\n");
2858 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2859 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2860 si.nPage, si.nMin, si.nMax);
2862 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2863 memset(&si, 0, sizeof(si));
2864 si.cbSize = sizeof(si);
2865 si.fMask = SIF_PAGE | SIF_RANGE;
2866 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2867 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2868 "Vertical scrollbar is invisible, should be visible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2870 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2871 si.nPage, si.nMin, si.nMax);
2873 DestroyWindow(hwndRichEdit);
2875 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2876 hwndRichEdit = new_richedit(NULL);
2878 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2879 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2885 "Vertical scrollbar is invisible, should be visible.\n");
2886 todo_wine {
2887 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2888 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2889 si.nPage, si.nMin, si.nMax);
2892 /* Ditto, see above */
2893 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2894 memset(&si, 0, sizeof(si));
2895 si.cbSize = sizeof(si);
2896 si.fMask = SIF_PAGE | SIF_RANGE;
2897 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2898 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2899 "Vertical scrollbar is invisible, should be visible.\n");
2900 todo_wine {
2901 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2902 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2903 si.nPage, si.nMin, si.nMax);
2906 /* Ditto, see above */
2907 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2908 memset(&si, 0, sizeof(si));
2909 si.cbSize = sizeof(si);
2910 si.fMask = SIF_PAGE | SIF_RANGE;
2911 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2912 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2913 "Vertical scrollbar is invisible, should be visible.\n");
2914 todo_wine {
2915 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2916 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2917 si.nPage, si.nMin, si.nMax);
2920 /* Ditto, see above */
2921 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2922 memset(&si, 0, sizeof(si));
2923 si.cbSize = sizeof(si);
2924 si.fMask = SIF_PAGE | SIF_RANGE;
2925 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2926 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2927 "Vertical scrollbar is invisible, should be visible.\n");
2928 todo_wine {
2929 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2930 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2931 si.nPage, si.nMin, si.nMax);
2934 /* Ditto, see above */
2935 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2936 memset(&si, 0, sizeof(si));
2937 si.cbSize = sizeof(si);
2938 si.fMask = SIF_PAGE | SIF_RANGE;
2939 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2940 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2941 "Vertical scrollbar is invisible, should be visible.\n");
2942 todo_wine {
2943 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2944 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2945 si.nPage, si.nMin, si.nMax);
2948 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2949 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2950 memset(&si, 0, sizeof(si));
2951 si.cbSize = sizeof(si);
2952 si.fMask = SIF_PAGE | SIF_RANGE;
2953 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2954 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2955 "Vertical scrollbar is visible, should be invisible.\n");
2956 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2957 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2958 si.nPage, si.nMin, si.nMax);
2960 DestroyWindow(hwndRichEdit);
2962 hwndRichEdit = new_richedit(NULL);
2964 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2965 memset(&si, 0, sizeof(si));
2966 si.cbSize = sizeof(si);
2967 si.fMask = SIF_PAGE | SIF_RANGE;
2968 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2969 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2970 "Vertical scrollbar is visible, should be invisible.\n");
2971 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2972 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2973 si.nPage, si.nMin, si.nMax);
2975 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2976 memset(&si, 0, sizeof(si));
2977 si.cbSize = sizeof(si);
2978 si.fMask = SIF_PAGE | SIF_RANGE;
2979 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2981 "Vertical scrollbar is visible, should be invisible.\n");
2982 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2983 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2984 si.nPage, si.nMin, si.nMax);
2986 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2992 "Vertical scrollbar is visible, should be invisible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2997 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2998 memset(&si, 0, sizeof(si));
2999 si.cbSize = sizeof(si);
3000 si.fMask = SIF_PAGE | SIF_RANGE;
3001 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3002 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3003 "Vertical scrollbar is visible, should be invisible.\n");
3004 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3005 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3006 si.nPage, si.nMin, si.nMax);
3008 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3009 memset(&si, 0, sizeof(si));
3010 si.cbSize = sizeof(si);
3011 si.fMask = SIF_PAGE | SIF_RANGE;
3012 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3013 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3014 "Vertical scrollbar is invisible, should be visible.\n");
3015 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3016 "reported page/range is %d (%d..%d)\n",
3017 si.nPage, si.nMin, si.nMax);
3019 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3020 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3021 memset(&si, 0, sizeof(si));
3022 si.cbSize = sizeof(si);
3023 si.fMask = SIF_PAGE | SIF_RANGE;
3024 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3025 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3026 "Vertical scrollbar is visible, should be invisible.\n");
3027 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3028 "reported page/range is %d (%d..%d)\n",
3029 si.nPage, si.nMin, si.nMax);
3031 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3032 memset(&si, 0, sizeof(si));
3033 si.cbSize = sizeof(si);
3034 si.fMask = SIF_PAGE | SIF_RANGE;
3035 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3036 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3037 "Vertical scrollbar is visible, should be invisible.\n");
3038 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3039 "reported page/range is %d (%d..%d)\n",
3040 si.nPage, si.nMin, si.nMax);
3042 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3043 EM_SCROLL will make visible any forcefully invisible scrollbar */
3044 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3045 memset(&si, 0, sizeof(si));
3046 si.cbSize = sizeof(si);
3047 si.fMask = SIF_PAGE | SIF_RANGE;
3048 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3049 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3050 "Vertical scrollbar is invisible, should be visible.\n");
3051 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3052 "reported page/range is %d (%d..%d)\n",
3053 si.nPage, si.nMin, si.nMax);
3055 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
3056 memset(&si, 0, sizeof(si));
3057 si.cbSize = sizeof(si);
3058 si.fMask = SIF_PAGE | SIF_RANGE;
3059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3060 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3061 "Vertical scrollbar is visible, should be invisible.\n");
3062 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3063 "reported page/range is %d (%d..%d)\n",
3064 si.nPage, si.nMin, si.nMax);
3066 /* Again, EM_SCROLL, with SB_LINEUP */
3067 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3068 memset(&si, 0, sizeof(si));
3069 si.cbSize = sizeof(si);
3070 si.fMask = SIF_PAGE | SIF_RANGE;
3071 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3072 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3073 "Vertical scrollbar is invisible, should be visible.\n");
3074 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3075 "reported page/range is %d (%d..%d)\n",
3076 si.nPage, si.nMin, si.nMax);
3078 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3084 "Vertical scrollbar is visible, should be invisible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3086 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3087 si.nPage, si.nMin, si.nMax);
3089 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3090 memset(&si, 0, sizeof(si));
3091 si.cbSize = sizeof(si);
3092 si.fMask = SIF_PAGE | SIF_RANGE;
3093 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3094 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3095 "Vertical scrollbar is invisible, should be visible.\n");
3096 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3097 "reported page/range is %d (%d..%d)\n",
3098 si.nPage, si.nMin, si.nMax);
3100 DestroyWindow(hwndRichEdit);
3103 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
3104 hwndRichEdit = new_richedit(NULL);
3106 #define ENABLE_WS_VSCROLL(hwnd) \
3107 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
3108 #define DISABLE_WS_VSCROLL(hwnd) \
3109 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
3111 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
3112 ENABLE_WS_VSCROLL(hwndRichEdit);
3113 memset(&si, 0, sizeof(si));
3114 si.cbSize = sizeof(si);
3115 si.fMask = SIF_PAGE | SIF_RANGE;
3116 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3117 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3118 "Vertical scrollbar is invisible, should be visible.\n");
3119 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3120 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3121 si.nPage, si.nMin, si.nMax);
3123 /* Ditto, see above */
3124 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3125 memset(&si, 0, sizeof(si));
3126 si.cbSize = sizeof(si);
3127 si.fMask = SIF_PAGE | SIF_RANGE;
3128 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3129 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3130 "Vertical scrollbar is invisible, should be visible.\n");
3131 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3132 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3133 si.nPage, si.nMin, si.nMax);
3135 /* Ditto, see above */
3136 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3137 memset(&si, 0, sizeof(si));
3138 si.cbSize = sizeof(si);
3139 si.fMask = SIF_PAGE | SIF_RANGE;
3140 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3141 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3142 "Vertical scrollbar is invisible, should be visible.\n");
3143 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3144 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3145 si.nPage, si.nMin, si.nMax);
3147 /* Ditto, see above */
3148 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3149 memset(&si, 0, sizeof(si));
3150 si.cbSize = sizeof(si);
3151 si.fMask = SIF_PAGE | SIF_RANGE;
3152 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3153 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3154 "Vertical scrollbar is invisible, should be visible.\n");
3155 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3156 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3157 si.nPage, si.nMin, si.nMax);
3159 /* Ditto, see above */
3160 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3161 memset(&si, 0, sizeof(si));
3162 si.cbSize = sizeof(si);
3163 si.fMask = SIF_PAGE | SIF_RANGE;
3164 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3165 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3166 "Vertical scrollbar is invisible, should be visible.\n");
3167 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3168 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3169 si.nPage, si.nMin, si.nMax);
3171 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3172 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3173 memset(&si, 0, sizeof(si));
3174 si.cbSize = sizeof(si);
3175 si.fMask = SIF_PAGE | SIF_RANGE;
3176 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3177 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3178 "Vertical scrollbar is visible, should be invisible.\n");
3179 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3180 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3181 si.nPage, si.nMin, si.nMax);
3183 DestroyWindow(hwndRichEdit);
3185 hwndRichEdit = new_richedit(NULL);
3187 DISABLE_WS_VSCROLL(hwndRichEdit);
3188 memset(&si, 0, sizeof(si));
3189 si.cbSize = sizeof(si);
3190 si.fMask = SIF_PAGE | SIF_RANGE;
3191 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3192 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3193 "Vertical scrollbar is visible, should be invisible.\n");
3194 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3195 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3196 si.nPage, si.nMin, si.nMax);
3198 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3199 memset(&si, 0, sizeof(si));
3200 si.cbSize = sizeof(si);
3201 si.fMask = SIF_PAGE | SIF_RANGE;
3202 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3203 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3204 "Vertical scrollbar is visible, should be invisible.\n");
3205 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3206 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3207 si.nPage, si.nMin, si.nMax);
3209 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3210 memset(&si, 0, sizeof(si));
3211 si.cbSize = sizeof(si);
3212 si.fMask = SIF_PAGE | SIF_RANGE;
3213 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3214 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3215 "Vertical scrollbar is visible, should be invisible.\n");
3216 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3217 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3218 si.nPage, si.nMin, si.nMax);
3220 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3221 memset(&si, 0, sizeof(si));
3222 si.cbSize = sizeof(si);
3223 si.fMask = SIF_PAGE | SIF_RANGE;
3224 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3225 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3226 "Vertical scrollbar is visible, should be invisible.\n");
3227 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3228 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3229 si.nPage, si.nMin, si.nMax);
3231 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3232 memset(&si, 0, sizeof(si));
3233 si.cbSize = sizeof(si);
3234 si.fMask = SIF_PAGE | SIF_RANGE;
3235 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3236 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3237 "Vertical scrollbar is invisible, should be visible.\n");
3238 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3239 "reported page/range is %d (%d..%d)\n",
3240 si.nPage, si.nMin, si.nMax);
3242 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3243 DISABLE_WS_VSCROLL(hwndRichEdit);
3244 memset(&si, 0, sizeof(si));
3245 si.cbSize = sizeof(si);
3246 si.fMask = SIF_PAGE | SIF_RANGE;
3247 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3248 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3249 "Vertical scrollbar is visible, should be invisible.\n");
3250 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3251 "reported page/range is %d (%d..%d)\n",
3252 si.nPage, si.nMin, si.nMax);
3254 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3255 memset(&si, 0, sizeof(si));
3256 si.cbSize = sizeof(si);
3257 si.fMask = SIF_PAGE | SIF_RANGE;
3258 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3259 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3260 "Vertical scrollbar is visible, should be invisible.\n");
3261 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3262 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3263 si.nPage, si.nMin, si.nMax);
3265 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3266 memset(&si, 0, sizeof(si));
3267 si.cbSize = sizeof(si);
3268 si.fMask = SIF_PAGE | SIF_RANGE;
3269 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3270 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3271 "Vertical scrollbar is invisible, should be visible.\n");
3272 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3273 "reported page/range is %d (%d..%d)\n",
3274 si.nPage, si.nMin, si.nMax);
3276 DISABLE_WS_VSCROLL(hwndRichEdit);
3277 memset(&si, 0, sizeof(si));
3278 si.cbSize = sizeof(si);
3279 si.fMask = SIF_PAGE | SIF_RANGE;
3280 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3281 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3282 "Vertical scrollbar is visible, should be invisible.\n");
3283 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3284 "reported page/range is %d (%d..%d)\n",
3285 si.nPage, si.nMin, si.nMax);
3287 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3288 EM_SCROLL will make visible any forcefully invisible scrollbar */
3289 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3290 memset(&si, 0, sizeof(si));
3291 si.cbSize = sizeof(si);
3292 si.fMask = SIF_PAGE | SIF_RANGE;
3293 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3294 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3295 "Vertical scrollbar is invisible, should be visible.\n");
3296 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3297 "reported page/range is %d (%d..%d)\n",
3298 si.nPage, si.nMin, si.nMax);
3300 DISABLE_WS_VSCROLL(hwndRichEdit);
3301 memset(&si, 0, sizeof(si));
3302 si.cbSize = sizeof(si);
3303 si.fMask = SIF_PAGE | SIF_RANGE;
3304 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3305 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3306 "Vertical scrollbar is visible, should be invisible.\n");
3307 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3308 "reported page/range is %d (%d..%d)\n",
3309 si.nPage, si.nMin, si.nMax);
3311 /* Again, EM_SCROLL, with SB_LINEUP */
3312 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3313 memset(&si, 0, sizeof(si));
3314 si.cbSize = sizeof(si);
3315 si.fMask = SIF_PAGE | SIF_RANGE;
3316 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3317 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3318 "Vertical scrollbar is invisible, should be visible.\n");
3319 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3320 "reported page/range is %d (%d..%d)\n",
3321 si.nPage, si.nMin, si.nMax);
3323 DestroyWindow(hwndRichEdit);
3325 /* This window proc models what is going on with Corman Lisp 3.0.
3326 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3327 force the scrollbar into visibility. Recursion should NOT happen
3328 as a result of this action.
3330 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3331 if (r) {
3332 richeditProc = cls.lpfnWndProc;
3333 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3334 cls.lpszClassName = "RicheditStupidOverride";
3335 if(!RegisterClassA(&cls)) assert(0);
3337 recursionLevel = 0;
3338 WM_SIZE_recursionLevel = 0;
3339 bailedOutOfRecursion = FALSE;
3340 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3341 ok(!bailedOutOfRecursion,
3342 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3344 recursionLevel = 0;
3345 WM_SIZE_recursionLevel = 0;
3346 bailedOutOfRecursion = FALSE;
3347 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3348 ok(!bailedOutOfRecursion,
3349 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3351 /* Unblock window in order to process WM_DESTROY */
3352 recursionLevel = 0;
3353 bailedOutOfRecursion = FALSE;
3354 WM_SIZE_recursionLevel = 0;
3355 DestroyWindow(hwndRichEdit);
3359 static void test_EM_SETUNDOLIMIT(void)
3361 /* cases we test for:
3362 * default behaviour - limiting at 100 undo's
3363 * undo disabled - setting a limit of 0
3364 * undo limited - undo limit set to some to some number, like 2
3365 * bad input - sending a negative number should default to 100 undo's */
3367 HWND hwndRichEdit = new_richedit(NULL);
3368 CHARRANGE cr;
3369 int i;
3370 int result;
3372 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3373 cr.cpMin = 0;
3374 cr.cpMax = -1;
3375 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3377 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3378 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3379 also, multiple pastes don't combine like WM_CHAR would */
3381 /* first case - check the default */
3382 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3383 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3384 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3385 for (i=0; i<100; i++) /* Undo 100 of them */
3386 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3387 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3388 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3390 /* second case - cannot undo */
3391 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3392 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3393 SendMessageA(hwndRichEdit,
3394 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3395 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3396 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3398 /* third case - set it to an arbitrary number */
3399 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3400 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3401 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3402 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3403 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3404 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3405 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3406 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3407 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3408 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3409 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3410 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3411 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3412 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3414 /* fourth case - setting negative numbers should default to 100 undos */
3415 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3416 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3417 ok (result == 100,
3418 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3420 DestroyWindow(hwndRichEdit);
3423 static void test_ES_PASSWORD(void)
3425 /* This isn't hugely testable, so we're just going to run it through its paces */
3427 HWND hwndRichEdit = new_richedit(NULL);
3428 WCHAR result;
3430 /* First, check the default of a regular control */
3431 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3432 ok (result == 0,
3433 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3435 /* Now, set it to something normal */
3436 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3437 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3438 ok (result == 120,
3439 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3441 /* Now, set it to something odd */
3442 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3443 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3444 ok (result == 1234,
3445 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3446 DestroyWindow(hwndRichEdit);
3449 LONG streamout_written = 0;
3451 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3452 LPBYTE pbBuff,
3453 LONG cb,
3454 LONG *pcb)
3456 char** str = (char**)dwCookie;
3457 *pcb = cb;
3458 if (*pcb > 0) {
3459 memcpy(*str, pbBuff, *pcb);
3460 *str += *pcb;
3462 streamout_written = *pcb;
3463 return 0;
3466 static void test_WM_SETTEXT(void)
3468 HWND hwndRichEdit = new_richedit(NULL);
3469 const char * TestItem1 = "TestSomeText";
3470 const char * TestItem2 = "TestSomeText\r";
3471 const char * TestItem2_after = "TestSomeText\r\n";
3472 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3473 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3474 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3475 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3476 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3477 const char * TestItem5_after = "TestSomeText TestSomeText";
3478 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3479 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3480 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3481 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3483 const char rtftextA[] = "{\\rtf sometext}";
3484 const char urtftextA[] = "{\\urtf sometext}";
3485 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3486 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3487 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3489 char buf[1024] = {0};
3490 WCHAR bufW[1024] = {0};
3491 LRESULT result;
3493 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3494 any solitary \r to be converted to \r\n on return. Properly paired
3495 \r\n are not affected. It also shows that the special sequence \r\r\n
3496 gets converted to a single space.
3499 #define TEST_SETTEXT(a, b) \
3500 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3501 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3502 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3503 ok (result == lstrlenA(buf), \
3504 "WM_GETTEXT returned %ld instead of expected %u\n", \
3505 result, lstrlenA(buf)); \
3506 result = strcmp(b, buf); \
3507 ok(result == 0, \
3508 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3510 TEST_SETTEXT(TestItem1, TestItem1)
3511 TEST_SETTEXT(TestItem2, TestItem2_after)
3512 TEST_SETTEXT(TestItem3, TestItem3_after)
3513 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3514 TEST_SETTEXT(TestItem4, TestItem4_after)
3515 TEST_SETTEXT(TestItem5, TestItem5_after)
3516 TEST_SETTEXT(TestItem6, TestItem6_after)
3517 TEST_SETTEXT(TestItem7, TestItem7_after)
3519 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3520 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3521 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3522 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3523 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3524 DestroyWindow(hwndRichEdit);
3525 #undef TEST_SETTEXT
3527 #define TEST_SETTEXTW(a, b) \
3528 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3529 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3530 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3531 ok (result == lstrlenW(bufW), \
3532 "WM_GETTEXT returned %ld instead of expected %u\n", \
3533 result, lstrlenW(bufW)); \
3534 result = lstrcmpW(b, bufW); \
3535 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3537 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3538 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3539 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3540 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3541 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3542 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3543 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3544 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3545 DestroyWindow(hwndRichEdit);
3546 #undef TEST_SETTEXTW
3549 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3550 resent to the callkack. */
3551 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3552 LPBYTE pbBuff,
3553 LONG cb,
3554 LONG *pcb)
3556 char** str = (char**)dwCookie;
3557 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3558 *pcb = 0;
3559 if (cb > 0) {
3560 memcpy(*str, pbBuff, cb);
3561 *str += cb;
3562 *pcb = 1;
3564 return 0;
3567 static int count_pars(const char *buf)
3569 const char *p = buf;
3570 int count = 0;
3571 while ((p = strstr( p, "\\par" )) != NULL)
3573 if (!isalpha( p[4] ))
3574 count++;
3575 p++;
3577 return count;
3580 static void test_EM_STREAMOUT(void)
3582 HWND hwndRichEdit = new_richedit(NULL);
3583 int r;
3584 EDITSTREAM es;
3585 char buf[1024] = {0};
3586 char * p;
3587 LRESULT result;
3589 const char * TestItem1 = "TestSomeText";
3590 const char * TestItem2 = "TestSomeText\r";
3591 const char * TestItem3 = "TestSomeText\r\n";
3593 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3594 p = buf;
3595 es.dwCookie = (DWORD_PTR)&p;
3596 es.dwError = 0;
3597 es.pfnCallback = test_WM_SETTEXT_esCallback;
3598 memset(buf, 0, sizeof(buf));
3599 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3600 r = strlen(buf);
3601 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3602 ok(strcmp(buf, TestItem1) == 0,
3603 "streamed text different, got %s\n", buf);
3604 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3606 /* RTF mode writes the final end of para \r if it's part of the selection */
3607 p = buf;
3608 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3609 ok (count_pars(buf) == 1, "got %s\n", buf);
3610 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3611 p = buf;
3612 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3613 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3614 ok (count_pars(buf) == 0, "got %s\n", buf);
3615 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3616 p = buf;
3617 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3618 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3619 ok (count_pars(buf) == 1, "got %s\n", buf);
3620 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3622 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3623 p = buf;
3624 es.dwCookie = (DWORD_PTR)&p;
3625 es.dwError = 0;
3626 es.pfnCallback = test_WM_SETTEXT_esCallback;
3627 memset(buf, 0, sizeof(buf));
3628 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3629 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3630 r = strlen(buf);
3631 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3632 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3633 ok(strcmp(buf, TestItem3) == 0,
3634 "streamed text different from, got %s\n", buf);
3636 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3637 p = buf;
3638 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3639 ok (count_pars(buf) == 2, "got %s\n", buf);
3640 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3641 p = buf;
3642 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3643 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3644 ok (count_pars(buf) == 1, "got %s\n", buf);
3645 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3646 p = buf;
3647 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3648 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3649 ok (count_pars(buf) == 2, "got %s\n", buf);
3650 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3652 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3653 p = buf;
3654 es.dwCookie = (DWORD_PTR)&p;
3655 es.dwError = 0;
3656 es.pfnCallback = test_WM_SETTEXT_esCallback;
3657 memset(buf, 0, sizeof(buf));
3658 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3659 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3660 r = strlen(buf);
3661 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3662 ok(strcmp(buf, TestItem3) == 0,
3663 "streamed text different, got %s\n", buf);
3665 /* Use a callback that sets *pcb to one */
3666 p = buf;
3667 es.dwCookie = (DWORD_PTR)&p;
3668 es.dwError = 0;
3669 es.pfnCallback = test_esCallback_written_1;
3670 memset(buf, 0, sizeof(buf));
3671 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3672 r = strlen(buf);
3673 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3674 ok(strcmp(buf, TestItem3) == 0,
3675 "streamed text different, got %s\n", buf);
3676 ok(result == 0, "got %ld expected 0\n", result);
3679 DestroyWindow(hwndRichEdit);
3682 static void test_EM_STREAMOUT_FONTTBL(void)
3684 HWND hwndRichEdit = new_richedit(NULL);
3685 EDITSTREAM es;
3686 char buf[1024] = {0};
3687 char * p;
3688 char * fontTbl;
3689 int brackCount;
3691 const char * TestItem = "TestSomeText";
3693 /* fills in the richedit control with some text */
3694 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3696 /* streams out the text in rtf format */
3697 p = buf;
3698 es.dwCookie = (DWORD_PTR)&p;
3699 es.dwError = 0;
3700 es.pfnCallback = test_WM_SETTEXT_esCallback;
3701 memset(buf, 0, sizeof(buf));
3702 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3704 /* scans for \fonttbl, error if not found */
3705 fontTbl = strstr(buf, "\\fonttbl");
3706 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3707 if(fontTbl)
3709 /* scans for terminating closing bracket */
3710 brackCount = 1;
3711 while(*fontTbl && brackCount)
3713 if(*fontTbl == '{')
3714 brackCount++;
3715 else if(*fontTbl == '}')
3716 brackCount--;
3717 fontTbl++;
3719 /* checks whether closing bracket is ok */
3720 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3721 if(!brackCount)
3723 /* char before closing fonttbl block should be a closed bracket */
3724 fontTbl -= 2;
3725 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3727 /* char after fonttbl block should be a crlf */
3728 fontTbl += 2;
3729 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3732 DestroyWindow(hwndRichEdit);
3736 static void test_EM_SETTEXTEX(void)
3738 HWND hwndRichEdit, parent;
3739 SCROLLINFO si;
3740 int sel_start, sel_end;
3741 SETTEXTEX setText;
3742 GETTEXTEX getText;
3743 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3744 'S', 'o', 'm', 'e',
3745 'T', 'e', 'x', 't', 0};
3746 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3747 't', 'S', 'o', 'm',
3748 'e', 'T', 'e', 'x',
3749 't', 't', 'S', 'o',
3750 'm', 'e', 'T', 'e',
3751 'x', 't', 0};
3752 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3753 '\r','t','S','o','m','e','T','e','x','t',0};
3754 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3755 'S', 'o', 'm', 'e',
3756 'T', 'e', 'x', 't',
3757 '\r', 0};
3758 const char * TestItem2_after = "TestSomeText\r\n";
3759 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3760 'S', 'o', 'm', 'e',
3761 'T', 'e', 'x', 't',
3762 '\r','\n','\r','\n', 0};
3763 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3764 'S', 'o', 'm', 'e',
3765 'T', 'e', 'x', 't',
3766 '\n','\n', 0};
3767 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3768 'S', 'o', 'm', 'e',
3769 'T', 'e', 'x', 't',
3770 '\r','\r', 0};
3771 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3772 'S', 'o', 'm', 'e',
3773 'T', 'e', 'x', 't',
3774 '\r','\r','\n','\r',
3775 '\n', 0};
3776 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3777 'S', 'o', 'm', 'e',
3778 'T', 'e', 'x', 't',
3779 ' ','\r', 0};
3780 #define MAX_BUF_LEN 1024
3781 WCHAR buf[MAX_BUF_LEN];
3782 char bufACP[MAX_BUF_LEN];
3783 char * p;
3784 int result;
3785 CHARRANGE cr;
3786 EDITSTREAM es;
3787 WNDCLASSA cls;
3789 /* Test the scroll position with and without a parent window.
3791 * For some reason the scroll position is 0 after EM_SETTEXTEX
3792 * with the ST_SELECTION flag only when the control has a parent
3793 * window, even though the selection is at the end. */
3794 cls.style = 0;
3795 cls.lpfnWndProc = DefWindowProcA;
3796 cls.cbClsExtra = 0;
3797 cls.cbWndExtra = 0;
3798 cls.hInstance = GetModuleHandleA(0);
3799 cls.hIcon = 0;
3800 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3801 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3802 cls.lpszMenuName = NULL;
3803 cls.lpszClassName = "ParentTestClass";
3804 if(!RegisterClassA(&cls)) assert(0);
3806 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3807 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3808 ok (parent != 0, "Failed to create parent window\n");
3810 hwndRichEdit = CreateWindowExA(0,
3811 RICHEDIT_CLASS20A, NULL,
3812 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3813 0, 0, 200, 60, parent, NULL,
3814 hmoduleRichEdit, NULL);
3816 setText.codepage = CP_ACP;
3817 setText.flags = ST_SELECTION;
3818 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3819 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3820 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
3821 si.cbSize = sizeof(si);
3822 si.fMask = SIF_ALL;
3823 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3824 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3825 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3826 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3827 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3829 DestroyWindow(parent);
3831 /* Test without a parent window */
3832 hwndRichEdit = new_richedit(NULL);
3833 setText.codepage = CP_ACP;
3834 setText.flags = ST_SELECTION;
3835 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3836 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3837 todo_wine ok(result == 18, "EM_SETTEXTEX returned %d, expected 18\n", result);
3838 si.cbSize = sizeof(si);
3839 si.fMask = SIF_ALL;
3840 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3841 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3842 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3843 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3844 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3846 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3847 * but this time it is because the selection is at the beginning. */
3848 setText.codepage = CP_ACP;
3849 setText.flags = ST_DEFAULT;
3850 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3851 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3852 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3853 si.cbSize = sizeof(si);
3854 si.fMask = SIF_ALL;
3855 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3856 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3857 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3858 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3859 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3861 setText.codepage = 1200; /* no constant for unicode */
3862 getText.codepage = 1200; /* no constant for unicode */
3863 getText.cb = MAX_BUF_LEN;
3864 getText.flags = GT_DEFAULT;
3865 getText.lpDefaultChar = NULL;
3866 getText.lpUsedDefChar = NULL;
3868 setText.flags = 0;
3869 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3870 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3871 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3872 ok(lstrcmpW(buf, TestItem1) == 0,
3873 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3875 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3876 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3878 setText.codepage = 1200; /* no constant for unicode */
3879 getText.codepage = 1200; /* no constant for unicode */
3880 getText.cb = MAX_BUF_LEN;
3881 getText.flags = GT_DEFAULT;
3882 getText.lpDefaultChar = NULL;
3883 getText.lpUsedDefChar = NULL;
3884 setText.flags = 0;
3885 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3886 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3887 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3888 ok(lstrcmpW(buf, TestItem2) == 0,
3889 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3891 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3892 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3893 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3894 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3896 /* Baseline test for just-enough buffer space for string */
3897 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3898 getText.codepage = 1200; /* no constant for unicode */
3899 getText.flags = GT_DEFAULT;
3900 getText.lpDefaultChar = NULL;
3901 getText.lpUsedDefChar = NULL;
3902 memset(buf, 0, sizeof(buf));
3903 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3904 ok(lstrcmpW(buf, TestItem2) == 0,
3905 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3907 /* When there is enough space for one character, but not both, of the CRLF
3908 pair at the end of the string, the CR is not copied at all. That is,
3909 the caller must not see CRLF pairs truncated to CR at the end of the
3910 string.
3912 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3913 getText.codepage = 1200; /* no constant for unicode */
3914 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3915 getText.lpDefaultChar = NULL;
3916 getText.lpUsedDefChar = NULL;
3917 memset(buf, 0, sizeof(buf));
3918 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3919 ok(lstrcmpW(buf, TestItem1) == 0,
3920 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3923 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3924 setText.codepage = 1200; /* no constant for unicode */
3925 getText.codepage = 1200; /* no constant for unicode */
3926 getText.cb = MAX_BUF_LEN;
3927 getText.flags = GT_DEFAULT;
3928 getText.lpDefaultChar = NULL;
3929 getText.lpUsedDefChar = NULL;
3930 setText.flags = 0;
3931 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3932 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3933 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3934 ok(lstrcmpW(buf, TestItem3_after) == 0,
3935 "EM_SETTEXTEX did not convert properly\n");
3937 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3938 setText.codepage = 1200; /* no constant for unicode */
3939 getText.codepage = 1200; /* no constant for unicode */
3940 getText.cb = MAX_BUF_LEN;
3941 getText.flags = GT_DEFAULT;
3942 getText.lpDefaultChar = NULL;
3943 getText.lpUsedDefChar = NULL;
3944 setText.flags = 0;
3945 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3946 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3947 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3948 ok(lstrcmpW(buf, TestItem3_after) == 0,
3949 "EM_SETTEXTEX did not convert properly\n");
3951 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3952 setText.codepage = 1200; /* no constant for unicode */
3953 getText.codepage = 1200; /* no constant for unicode */
3954 getText.cb = MAX_BUF_LEN;
3955 getText.flags = GT_DEFAULT;
3956 getText.lpDefaultChar = NULL;
3957 getText.lpUsedDefChar = NULL;
3958 setText.flags = 0;
3959 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3960 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3961 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3962 ok(lstrcmpW(buf, TestItem4_after) == 0,
3963 "EM_SETTEXTEX did not convert properly\n");
3965 /* !ST_SELECTION && Unicode && !\rtf */
3966 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3967 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3969 ok (result == 1,
3970 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3971 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3973 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3974 setText.flags = 0;
3975 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3976 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3977 /* select some text */
3978 cr.cpMax = 1;
3979 cr.cpMin = 3;
3980 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3981 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3982 setText.flags = ST_SELECTION;
3983 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3984 ok(result == 0,
3985 "EM_SETTEXTEX with NULL lParam to replace selection"
3986 " with no text should return 0. Got %i\n",
3987 result);
3989 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3990 setText.flags = 0;
3991 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3992 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
3993 /* select some text */
3994 cr.cpMax = 1;
3995 cr.cpMin = 3;
3996 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3997 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3998 setText.flags = ST_SELECTION;
3999 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4000 /* get text */
4001 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4002 ok(result == lstrlenW(TestItem1),
4003 "EM_SETTEXTEX with NULL lParam to replace selection"
4004 " with no text should return 0. Got %i\n",
4005 result);
4006 ok(lstrlenW(buf) == 22,
4007 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
4008 lstrlenW(buf) );
4010 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
4011 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4012 p = (char *)buf;
4013 es.dwCookie = (DWORD_PTR)&p;
4014 es.dwError = 0;
4015 es.pfnCallback = test_WM_SETTEXT_esCallback;
4016 memset(buf, 0, sizeof(buf));
4017 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4018 (WPARAM)(SF_RTF), (LPARAM)&es);
4019 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4021 /* !ST_SELECTION && !Unicode && \rtf */
4022 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4023 getText.codepage = 1200; /* no constant for unicode */
4024 getText.cb = MAX_BUF_LEN;
4025 getText.flags = GT_DEFAULT;
4026 getText.lpDefaultChar = NULL;
4027 getText.lpUsedDefChar = NULL;
4029 setText.flags = 0;
4030 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4031 ok(result == 1, "EM_SETTEXTEX returned %d, expected 1\n", result);
4032 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4033 ok(lstrcmpW(buf, TestItem1) == 0,
4034 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
4036 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
4037 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
4038 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
4039 getText.codepage = CP_ACP;
4040 getText.cb = MAX_BUF_LEN;
4041 getText.flags = GT_DEFAULT;
4042 getText.lpDefaultChar = NULL;
4043 getText.lpUsedDefChar = NULL;
4045 setText.flags = ST_SELECTION;
4046 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4047 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
4048 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
4049 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4050 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
4052 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
4053 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
4054 p = (char *)buf;
4055 es.dwCookie = (DWORD_PTR)&p;
4056 es.dwError = 0;
4057 es.pfnCallback = test_WM_SETTEXT_esCallback;
4058 memset(buf, 0, sizeof(buf));
4059 SendMessageA(hwndRichEdit, EM_STREAMOUT,
4060 (WPARAM)(SF_RTF), (LPARAM)&es);
4061 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
4063 /* select some text */
4064 cr.cpMax = 1;
4065 cr.cpMin = 3;
4066 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4068 /* ST_SELECTION && !Unicode && \rtf */
4069 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
4070 getText.codepage = 1200; /* no constant for unicode */
4071 getText.cb = MAX_BUF_LEN;
4072 getText.flags = GT_DEFAULT;
4073 getText.lpDefaultChar = NULL;
4074 getText.lpUsedDefChar = NULL;
4076 setText.flags = ST_SELECTION;
4077 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
4078 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4079 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
4081 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
4082 setText.codepage = 1200; /* no constant for unicode */
4083 getText.codepage = CP_ACP;
4084 getText.cb = MAX_BUF_LEN;
4086 setText.flags = 0;
4087 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
4088 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4090 /* select some text */
4091 cr.cpMax = 1;
4092 cr.cpMin = 3;
4093 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4095 /* ST_SELECTION && !Unicode && !\rtf */
4096 setText.codepage = CP_ACP;
4097 getText.codepage = 1200; /* no constant for unicode */
4098 getText.cb = MAX_BUF_LEN;
4099 getText.flags = GT_DEFAULT;
4100 getText.lpDefaultChar = NULL;
4101 getText.lpUsedDefChar = NULL;
4103 setText.flags = ST_SELECTION;
4104 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
4105 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
4106 ok(lstrcmpW(buf, TestItem1alt) == 0,
4107 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
4108 " using ST_SELECTION and non-Unicode\n");
4110 /* Test setting text using rich text format */
4111 setText.flags = 0;
4112 setText.codepage = CP_ACP;
4113 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
4114 getText.codepage = CP_ACP;
4115 getText.cb = MAX_BUF_LEN;
4116 getText.flags = GT_DEFAULT;
4117 getText.lpDefaultChar = NULL;
4118 getText.lpUsedDefChar = NULL;
4119 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4120 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
4122 setText.flags = 0;
4123 setText.codepage = CP_ACP;
4124 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
4125 getText.codepage = CP_ACP;
4126 getText.cb = MAX_BUF_LEN;
4127 getText.flags = GT_DEFAULT;
4128 getText.lpDefaultChar = NULL;
4129 getText.lpUsedDefChar = NULL;
4130 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
4131 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
4133 /* test for utf8 text with BOM */
4134 setText.flags = 0;
4135 setText.codepage = CP_ACP;
4136 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4137 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4138 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4139 result = strcmp(bufACP, "TestUTF8WithBOM");
4140 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4142 setText.flags = 0;
4143 setText.codepage = CP_UTF8;
4144 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
4145 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4146 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
4147 result = strcmp(bufACP, "TestUTF8WithBOM");
4148 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4150 /* Test multibyte character */
4151 if (!is_lang_japanese)
4152 skip("Skip multibyte character tests on non-Japanese platform\n");
4153 else
4155 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4156 setText.flags = ST_SELECTION;
4157 setText.codepage = CP_ACP;
4158 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4159 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4160 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4161 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4162 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4163 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4165 setText.flags = ST_DEFAULT;
4166 setText.codepage = CP_ACP;
4167 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4168 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4169 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4170 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4171 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4172 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4174 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4175 setText.flags = ST_SELECTION;
4176 setText.codepage = CP_ACP;
4177 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4178 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4179 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4180 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4181 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4182 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4185 DestroyWindow(hwndRichEdit);
4188 static void test_EM_LIMITTEXT(void)
4190 int ret;
4192 HWND hwndRichEdit = new_richedit(NULL);
4194 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4195 * about setting the length to -1 for multiline edit controls doesn't happen.
4198 /* Don't check default gettextlimit case. That's done in other tests */
4200 /* Set textlimit to 100 */
4201 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4202 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4203 ok (ret == 100,
4204 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4206 /* Set textlimit to 0 */
4207 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4208 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4209 ok (ret == 65536,
4210 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4212 /* Set textlimit to -1 */
4213 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4214 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4215 ok (ret == -1,
4216 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4218 /* Set textlimit to -2 */
4219 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4220 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4221 ok (ret == -2,
4222 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4224 DestroyWindow (hwndRichEdit);
4228 static void test_EM_EXLIMITTEXT(void)
4230 int i, selBegin, selEnd, len1, len2;
4231 int result;
4232 char text[1024 + 1];
4233 char buffer[1024 + 1];
4234 int textlimit = 0; /* multiple of 100 */
4235 HWND hwndRichEdit = new_richedit(NULL);
4237 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4238 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4240 textlimit = 256000;
4241 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4242 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4243 /* set higher */
4244 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4246 textlimit = 1000;
4247 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4248 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4249 /* set lower */
4250 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4252 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4253 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4254 /* default for WParam = 0 */
4255 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4257 textlimit = sizeof(text)-1;
4258 memset(text, 'W', textlimit);
4259 text[sizeof(text)-1] = 0;
4260 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4261 /* maxed out text */
4262 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4264 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4265 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4266 len1 = selEnd - selBegin;
4268 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4269 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4270 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4271 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4272 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4273 len2 = selEnd - selBegin;
4275 ok(len1 != len2,
4276 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4277 len1,len2,i);
4279 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4280 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4281 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4282 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4283 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4284 len1 = selEnd - selBegin;
4286 ok(len1 != len2,
4287 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4288 len1,len2,i);
4290 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4291 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4292 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4293 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4294 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4295 len2 = selEnd - selBegin;
4297 ok(len1 == len2,
4298 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4299 len1,len2,i);
4301 /* set text up to the limit, select all the text, then add a char */
4302 textlimit = 5;
4303 memset(text, 'W', textlimit);
4304 text[textlimit] = 0;
4305 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4306 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4307 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4308 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4309 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4310 result = strcmp(buffer, "A");
4311 ok(0 == result, "got string = \"%s\"\n", buffer);
4313 /* WM_SETTEXT not limited */
4314 textlimit = 10;
4315 memset(text, 'W', textlimit);
4316 text[textlimit] = 0;
4317 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4318 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4319 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4320 i = strlen(buffer);
4321 ok(10 == i, "expected 10 chars\n");
4322 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4323 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4325 /* try inserting more text at end */
4326 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4327 ok(0 == i, "WM_CHAR wasn't processed\n");
4328 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4329 i = strlen(buffer);
4330 ok(10 == i, "expected 10 chars, got %i\n", i);
4331 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4332 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4334 /* try inserting text at beginning */
4335 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4336 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4337 ok(0 == i, "WM_CHAR wasn't processed\n");
4338 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4339 i = strlen(buffer);
4340 ok(10 == i, "expected 10 chars, got %i\n", i);
4341 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4342 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4344 /* WM_CHAR is limited */
4345 textlimit = 1;
4346 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4347 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4348 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4349 ok(0 == i, "WM_CHAR wasn't processed\n");
4350 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4351 ok(0 == i, "WM_CHAR wasn't processed\n");
4352 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4353 i = strlen(buffer);
4354 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4356 DestroyWindow(hwndRichEdit);
4359 static void test_EM_GETLIMITTEXT(void)
4361 int i;
4362 HWND hwndRichEdit = new_richedit(NULL);
4364 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4365 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4367 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4368 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4369 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4371 DestroyWindow(hwndRichEdit);
4374 static void test_WM_SETFONT(void)
4376 /* There is no invalid input or error conditions for this function.
4377 * NULL wParam and lParam just fall back to their default values
4378 * It should be noted that even if you use a gibberish name for your fonts
4379 * here, it will still work because the name is stored. They will display as
4380 * System, but will report their name to be whatever they were created as */
4382 HWND hwndRichEdit = new_richedit(NULL);
4383 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4384 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4385 FF_DONTCARE, "Marlett");
4386 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4387 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4388 FF_DONTCARE, "MS Sans Serif");
4389 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4390 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4391 FF_DONTCARE, "Courier");
4392 LOGFONTA sentLogFont;
4393 CHARFORMAT2A returnedCF2A;
4395 returnedCF2A.cbSize = sizeof(returnedCF2A);
4397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4398 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4399 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4401 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4402 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4403 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4404 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4406 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4407 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4408 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4409 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4410 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4411 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4413 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4414 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4415 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4416 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4417 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4418 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4420 /* This last test is special since we send in NULL. We clear the variables
4421 * and just compare to "System" instead of the sent in font name. */
4422 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4423 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4424 returnedCF2A.cbSize = sizeof(returnedCF2A);
4426 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4427 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4428 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4429 ok (!strcmp("System",returnedCF2A.szFaceName),
4430 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4432 DestroyWindow(hwndRichEdit);
4436 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4437 LPBYTE pbBuff,
4438 LONG cb,
4439 LONG *pcb)
4441 const char** str = (const char**)dwCookie;
4442 int size = strlen(*str);
4443 if(size > 3) /* let's make it piecemeal for fun */
4444 size = 3;
4445 *pcb = cb;
4446 if (*pcb > size) {
4447 *pcb = size;
4449 if (*pcb > 0) {
4450 memcpy(pbBuff, *str, *pcb);
4451 *str += *pcb;
4453 return 0;
4456 static void test_EM_GETMODIFY(void)
4458 HWND hwndRichEdit = new_richedit(NULL);
4459 LRESULT result;
4460 SETTEXTEX setText;
4461 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4462 'S', 'o', 'm', 'e',
4463 'T', 'e', 'x', 't', 0};
4464 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4465 'S', 'o', 'm', 'e',
4466 'O', 't', 'h', 'e', 'r',
4467 'T', 'e', 'x', 't', 0};
4468 const char* streamText = "hello world";
4469 CHARFORMAT2A cf2;
4470 PARAFORMAT2 pf2;
4471 EDITSTREAM es;
4473 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4474 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4475 FF_DONTCARE, "Courier");
4477 setText.codepage = 1200; /* no constant for unicode */
4478 setText.flags = ST_KEEPUNDO;
4481 /* modify flag shouldn't be set when richedit is first created */
4482 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4483 ok (result == 0,
4484 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4486 /* setting modify flag should actually set it */
4487 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4488 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4489 ok (result != 0,
4490 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4492 /* clearing modify flag should actually clear it */
4493 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4494 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4495 ok (result == 0,
4496 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4498 /* setting font doesn't change modify flag */
4499 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4500 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4501 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4502 ok (result == 0,
4503 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4505 /* setting text should set modify flag */
4506 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4507 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4508 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4509 ok (result != 0,
4510 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4512 /* undo previous text doesn't reset modify flag */
4513 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4514 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4515 ok (result != 0,
4516 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4518 /* set text with no flag to keep undo stack should not set modify flag */
4519 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4520 setText.flags = 0;
4521 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4522 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4523 ok (result == 0,
4524 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4526 /* WM_SETTEXT doesn't modify */
4527 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4528 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4529 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4530 ok (result == 0,
4531 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4533 /* clear the text */
4534 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4535 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4536 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4537 ok (result == 0,
4538 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4540 /* replace text */
4541 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4542 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4543 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4544 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4545 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4546 ok (result != 0,
4547 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4549 /* copy/paste text 1 */
4550 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4551 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4552 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4553 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4554 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4555 ok (result != 0,
4556 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4558 /* copy/paste text 2 */
4559 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4560 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4561 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4562 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4563 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4564 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4565 ok (result != 0,
4566 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4568 /* press char */
4569 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4570 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4571 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4572 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4573 ok (result != 0,
4574 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4576 /* press del */
4577 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4578 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4579 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4580 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4581 ok (result != 0,
4582 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4584 /* set char format */
4585 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4586 cf2.cbSize = sizeof(CHARFORMAT2A);
4587 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4588 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4589 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4590 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4591 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4592 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4593 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4594 ok (result != 0,
4595 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4597 /* set para format */
4598 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4599 pf2.cbSize = sizeof(PARAFORMAT2);
4600 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4601 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4602 pf2.wAlignment = PFA_RIGHT;
4603 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4604 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4605 ok (result == 0,
4606 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4608 /* EM_STREAM */
4609 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4610 es.dwCookie = (DWORD_PTR)&streamText;
4611 es.dwError = 0;
4612 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4613 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4614 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4615 ok (result != 0,
4616 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4618 DestroyWindow(hwndRichEdit);
4621 struct exsetsel_s {
4622 LONG min;
4623 LONG max;
4624 LRESULT expected_retval;
4625 int expected_getsel_start;
4626 int expected_getsel_end;
4627 BOOL todo;
4630 static const struct exsetsel_s exsetsel_tests[] = {
4631 /* sanity tests */
4632 {5, 10, 10, 5, 10 },
4633 {15, 17, 17, 15, 17 },
4634 /* test cpMax > strlen() */
4635 {0, 100, 18, 0, 18 },
4636 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4637 {-1, 1, 17, 17, 17 },
4638 /* test cpMin == cpMax */
4639 {5, 5, 5, 5, 5 },
4640 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4641 {-1, 0, 5, 5, 5 },
4642 {-1, 17, 5, 5, 5 },
4643 {-1, 18, 5, 5, 5 },
4644 /* test cpMin < 0 && cpMax < 0 */
4645 {-1, -1, 17, 17, 17 },
4646 {-4, -5, 17, 17, 17 },
4647 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4648 {0, -1, 18, 0, 18 },
4649 {17, -5, 18, 17, 18 },
4650 {18, -3, 17, 17, 17 },
4651 /* test if cpMin > cpMax */
4652 {15, 19, 18, 15, 18 },
4653 {19, 15, 18, 15, 18 },
4654 /* cpMin == strlen() && cpMax > cpMin */
4655 {17, 18, 18, 17, 18 },
4656 {17, 50, 18, 17, 18 },
4659 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4660 CHARRANGE cr;
4661 LRESULT result;
4662 int start, end;
4664 cr.cpMin = setsel->min;
4665 cr.cpMax = setsel->max;
4666 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4668 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4670 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4672 todo_wine_if (setsel->todo)
4673 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4674 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4677 static void test_EM_EXSETSEL(void)
4679 HWND hwndRichEdit = new_richedit(NULL);
4680 int i;
4681 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4683 /* sending some text to the window */
4684 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4685 /* 01234567890123456*/
4686 /* 10 */
4688 for (i = 0; i < num_tests; i++) {
4689 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4692 if (!is_lang_japanese)
4693 skip("Skip multibyte character tests on non-Japanese platform\n");
4694 else
4696 CHARRANGE cr;
4697 char bufA[MAX_BUF_LEN] = {0};
4698 LRESULT result;
4700 /* Test with multibyte character */
4701 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4702 /* 012345 6 78901 */
4703 cr.cpMin = 4, cr.cpMax = 8;
4704 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
4705 ok(result == 8, "EM_EXSETSEL return %ld expected 8\n", result);
4706 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(bufA), (LPARAM)bufA);
4707 ok(!strcmp(bufA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4708 SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4709 ok(cr.cpMin == 4, "Selection start incorrectly: %d expected 4\n", cr.cpMin);
4710 ok(cr.cpMax == 8, "Selection end incorrectly: %d expected 8\n", cr.cpMax);
4713 DestroyWindow(hwndRichEdit);
4716 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4717 LRESULT result;
4718 int start, end;
4720 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4722 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4724 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4726 todo_wine_if (setsel->todo)
4727 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n",
4728 id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4731 static void test_EM_SETSEL(void)
4733 char buffA[32] = {0};
4734 HWND hwndRichEdit = new_richedit(NULL);
4735 int i;
4736 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4738 /* sending some text to the window */
4739 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4740 /* 01234567890123456*/
4741 /* 10 */
4743 for (i = 0; i < num_tests; i++) {
4744 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4747 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
4748 buffA[0] = 123;
4749 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
4750 ok(buffA[0] == 0, "selection text %s\n", buffA);
4752 if (!is_lang_japanese)
4753 skip("Skip multibyte character tests on non-Japanese platform\n");
4754 else
4756 int sel_start, sel_end;
4757 LRESULT result;
4759 /* Test with multibyte character */
4760 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"abcdef\x8e\xf0ghijk");
4761 /* 012345 6 78901 */
4762 result = SendMessageA(hwndRichEdit, EM_SETSEL, 4, 8);
4763 ok(result == 8, "EM_SETSEL return %ld expected 8\n", result);
4764 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, sizeof(buffA), (LPARAM)buffA);
4765 ok(!strcmp(buffA, "ef\x8e\xf0g"), "EM_GETSELTEXT return incorrect string\n");
4766 result = SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
4767 ok(sel_start == 4, "Selection start incorrectly: %d expected 4\n", sel_start);
4768 ok(sel_end == 8, "Selection end incorrectly: %d expected 8\n", sel_end);
4771 DestroyWindow(hwndRichEdit);
4774 static void test_EM_REPLACESEL(int redraw)
4776 HWND hwndRichEdit = new_richedit(NULL);
4777 char buffer[1024] = {0};
4778 int r;
4779 GETTEXTEX getText;
4780 CHARRANGE cr;
4781 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
4782 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
4784 /* sending some text to the window */
4785 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4786 /* 01234567890123456*/
4787 /* 10 */
4789 /* FIXME add more tests */
4790 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4791 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4792 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4793 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4794 r = strcmp(buffer, "testing");
4795 ok(0 == r, "expected %d, got %d\n", 0, r);
4797 DestroyWindow(hwndRichEdit);
4799 hwndRichEdit = new_richedit(NULL);
4801 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4802 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4804 /* Test behavior with carriage returns and newlines */
4805 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4806 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4807 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4808 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4809 r = strcmp(buffer, "RichEdit1");
4810 ok(0 == r, "expected %d, got %d\n", 0, r);
4811 getText.cb = 1024;
4812 getText.codepage = CP_ACP;
4813 getText.flags = GT_DEFAULT;
4814 getText.lpDefaultChar = NULL;
4815 getText.lpUsedDefChar = NULL;
4816 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4817 ok(strcmp(buffer, "RichEdit1") == 0,
4818 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4820 /* Test number of lines reported after EM_REPLACESEL */
4821 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4822 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4824 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4825 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4826 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4827 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4828 r = strcmp(buffer, "RichEdit1\r\n");
4829 ok(0 == r, "expected %d, got %d\n", 0, r);
4830 getText.cb = 1024;
4831 getText.codepage = CP_ACP;
4832 getText.flags = GT_DEFAULT;
4833 getText.lpDefaultChar = NULL;
4834 getText.lpUsedDefChar = NULL;
4835 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4836 ok(strcmp(buffer, "RichEdit1\r") == 0,
4837 "EM_GETTEXTEX returned incorrect string\n");
4839 /* Test number of lines reported after EM_REPLACESEL */
4840 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4841 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4843 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4844 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4845 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4847 /* Test number of lines reported after EM_REPLACESEL */
4848 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4849 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4851 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4852 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4853 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4854 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4856 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4857 r = strcmp(buffer, "RichEdit1\r\n");
4858 ok(0 == r, "expected %d, got %d\n", 0, r);
4859 getText.cb = 1024;
4860 getText.codepage = CP_ACP;
4861 getText.flags = GT_DEFAULT;
4862 getText.lpDefaultChar = NULL;
4863 getText.lpUsedDefChar = NULL;
4864 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4865 ok(strcmp(buffer, "RichEdit1\r") == 0,
4866 "EM_GETTEXTEX returned incorrect string\n");
4868 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4869 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4870 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4871 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4873 /* The following tests show that richedit should handle the special \r\r\n
4874 sequence by turning it into a single space on insertion. However,
4875 EM_REPLACESEL on WinXP returns the number of characters in the original
4876 string.
4879 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4880 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4881 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4882 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4883 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4884 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4885 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4887 /* Test the actual string */
4888 getText.cb = 1024;
4889 getText.codepage = CP_ACP;
4890 getText.flags = GT_DEFAULT;
4891 getText.lpDefaultChar = NULL;
4892 getText.lpUsedDefChar = NULL;
4893 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4894 ok(strcmp(buffer, "\r\r") == 0,
4895 "EM_GETTEXTEX returned incorrect string\n");
4897 /* Test number of lines reported after EM_REPLACESEL */
4898 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4899 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4901 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4902 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4903 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4904 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4905 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4906 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4907 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4909 /* Test the actual string */
4910 getText.cb = 1024;
4911 getText.codepage = CP_ACP;
4912 getText.flags = GT_DEFAULT;
4913 getText.lpDefaultChar = NULL;
4914 getText.lpUsedDefChar = NULL;
4915 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4916 ok(strcmp(buffer, " ") == 0,
4917 "EM_GETTEXTEX returned incorrect string\n");
4919 /* Test number of lines reported after EM_REPLACESEL */
4920 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4921 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4923 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4924 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4925 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4926 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4927 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4928 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4929 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4931 /* Test the actual string */
4932 getText.cb = 1024;
4933 getText.codepage = CP_ACP;
4934 getText.flags = GT_DEFAULT;
4935 getText.lpDefaultChar = NULL;
4936 getText.lpUsedDefChar = NULL;
4937 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4938 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4939 "EM_GETTEXTEX returned incorrect string\n");
4941 /* Test number of lines reported after EM_REPLACESEL */
4942 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4943 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4945 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4946 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4947 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4948 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4949 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4950 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4951 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4953 /* Test the actual string */
4954 getText.cb = 1024;
4955 getText.codepage = CP_ACP;
4956 getText.flags = GT_DEFAULT;
4957 getText.lpDefaultChar = NULL;
4958 getText.lpUsedDefChar = NULL;
4959 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4960 ok(strcmp(buffer, " \r") == 0,
4961 "EM_GETTEXTEX returned incorrect string\n");
4963 /* Test number of lines reported after EM_REPLACESEL */
4964 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4965 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4967 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4968 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4969 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4970 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4971 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4972 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4973 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4975 /* Test the actual string */
4976 getText.cb = 1024;
4977 getText.codepage = CP_ACP;
4978 getText.flags = GT_DEFAULT;
4979 getText.lpDefaultChar = NULL;
4980 getText.lpUsedDefChar = NULL;
4981 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4982 ok(strcmp(buffer, " \r\r") == 0,
4983 "EM_GETTEXTEX returned incorrect string\n");
4985 /* Test number of lines reported after EM_REPLACESEL */
4986 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4987 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4989 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4990 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4991 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4992 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4993 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4994 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4995 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4997 /* Test the actual string */
4998 getText.cb = 1024;
4999 getText.codepage = CP_ACP;
5000 getText.flags = GT_DEFAULT;
5001 getText.lpDefaultChar = NULL;
5002 getText.lpUsedDefChar = NULL;
5003 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5004 ok(strcmp(buffer, "\rX\r\r\r") == 0,
5005 "EM_GETTEXTEX returned incorrect string\n");
5007 /* Test number of lines reported after EM_REPLACESEL */
5008 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5009 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
5011 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5012 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
5013 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
5014 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5015 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5016 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
5017 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
5019 /* Test the actual string */
5020 getText.cb = 1024;
5021 getText.codepage = CP_ACP;
5022 getText.flags = GT_DEFAULT;
5023 getText.lpDefaultChar = NULL;
5024 getText.lpUsedDefChar = NULL;
5025 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5026 ok(strcmp(buffer, "\r\r") == 0,
5027 "EM_GETTEXTEX returned incorrect string\n");
5029 /* Test number of lines reported after EM_REPLACESEL */
5030 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5031 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
5033 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5034 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
5035 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
5036 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5037 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5038 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
5039 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
5041 /* Test the actual string */
5042 getText.cb = 1024;
5043 getText.codepage = CP_ACP;
5044 getText.flags = GT_DEFAULT;
5045 getText.lpDefaultChar = NULL;
5046 getText.lpUsedDefChar = NULL;
5047 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
5048 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
5049 "EM_GETTEXTEX returned incorrect string\n");
5051 /* Test number of lines reported after EM_REPLACESEL */
5052 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
5053 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
5055 /* Test with multibyte character */
5056 if (!is_lang_japanese)
5057 skip("Skip multibyte character tests on non-Japanese platform\n");
5058 else
5060 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5061 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
5062 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
5063 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5064 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5065 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5066 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5067 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5068 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5069 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5071 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5072 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
5073 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
5074 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5075 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
5076 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
5077 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
5078 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5079 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
5080 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
5083 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5084 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5085 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5086 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5087 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5088 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5089 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5090 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5091 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5093 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5094 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
5095 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5096 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5097 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5098 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
5099 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
5100 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5101 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
5103 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
5104 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5105 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5106 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5107 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5108 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5109 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5110 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5111 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5112 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5114 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
5115 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
5116 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
5117 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
5118 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
5119 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
5120 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
5121 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
5122 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5123 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
5125 if (!redraw)
5126 /* This is needed to avoid interfering with keybd_event calls
5127 * on other tests that simulate keyboard events. */
5128 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
5130 DestroyWindow(hwndRichEdit);
5133 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
5134 * to test the state of the modifiers (Ctrl/Alt/Shift).
5136 * Therefore Ctrl-<key> keystrokes need to be simulated with
5137 * keybd_event or by using SetKeyboardState to set the modifiers
5138 * and SendMessage to simulate the keystrokes.
5140 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
5142 LRESULT result;
5143 hold_key(VK_CONTROL);
5144 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
5145 release_key(VK_CONTROL);
5146 return result;
5149 static void test_WM_PASTE(void)
5151 int result;
5152 char buffer[1024] = {0};
5153 const char* text1 = "testing paste\r";
5154 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
5155 const char* text1_after = "testing paste\r\n";
5156 const char* text2 = "testing paste\r\rtesting paste";
5157 const char* text2_after = "testing paste\r\n\r\ntesting paste";
5158 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
5159 HWND hwndRichEdit = new_richedit(NULL);
5161 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5162 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
5164 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5165 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5166 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5167 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5168 /* Pasted text should be visible at this step */
5169 result = strcmp(text1_step1, buffer);
5170 ok(result == 0,
5171 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5173 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5174 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5175 /* Text should be the same as before (except for \r -> \r\n conversion) */
5176 result = strcmp(text1_after, buffer);
5177 ok(result == 0,
5178 "test paste: strcmp = %i, text='%s'\n", result, buffer);
5180 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
5181 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5182 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5183 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5184 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5185 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5186 /* Pasted text should be visible at this step */
5187 result = strcmp(text3, buffer);
5188 ok(result == 0,
5189 "test paste: strcmp = %i\n", result);
5190 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5191 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5192 /* Text should be the same as before (except for \r -> \r\n conversion) */
5193 result = strcmp(text2_after, buffer);
5194 ok(result == 0,
5195 "test paste: strcmp = %i\n", result);
5196 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5197 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5198 /* Text should revert to post-paste state */
5199 result = strcmp(buffer,text3);
5200 ok(result == 0,
5201 "test paste: strcmp = %i\n", result);
5203 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5204 /* Send WM_CHAR to simulate Ctrl-V */
5205 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5206 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5207 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5208 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5209 result = strcmp(buffer,"");
5210 ok(result == 0,
5211 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5213 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5214 * with SetKeyboard state. */
5216 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5217 /* Simulates paste (Ctrl-V) */
5218 hold_key(VK_CONTROL);
5219 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5220 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5221 release_key(VK_CONTROL);
5222 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5223 result = strcmp(buffer,"paste");
5224 ok(result == 0,
5225 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5227 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5228 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5229 /* Simulates copy (Ctrl-C) */
5230 hold_key(VK_CONTROL);
5231 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5232 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5233 release_key(VK_CONTROL);
5234 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5235 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5236 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5237 result = strcmp(buffer,"testing");
5238 ok(result == 0,
5239 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5241 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5242 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5243 /* Simulates select all (Ctrl-A) */
5244 hold_key(VK_CONTROL);
5245 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5246 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5247 /* Simulates select cut (Ctrl-X) */
5248 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5249 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5250 release_key(VK_CONTROL);
5251 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5252 result = strcmp(buffer,"");
5253 ok(result == 0,
5254 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5255 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5256 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5257 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5258 result = strcmp(buffer,"cut\r\n");
5259 ok(result == 0,
5260 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5261 /* Simulates undo (Ctrl-Z) */
5262 hold_key(VK_CONTROL);
5263 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5264 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5265 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5266 result = strcmp(buffer,"");
5267 ok(result == 0,
5268 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5269 /* Simulates redo (Ctrl-Y) */
5270 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5271 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5272 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5273 result = strcmp(buffer,"cut\r\n");
5274 ok(result == 0,
5275 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5276 release_key(VK_CONTROL);
5278 DestroyWindow(hwndRichEdit);
5281 static void test_EM_FORMATRANGE(void)
5283 int r, i, tpp_x, tpp_y;
5284 HDC hdc;
5285 HWND hwndRichEdit = new_richedit(NULL);
5286 FORMATRANGE fr;
5287 BOOL skip_non_english;
5288 static const struct {
5289 const char *string; /* The string */
5290 int first; /* First 'pagebreak', 0 for don't care */
5291 int second; /* Second 'pagebreak', 0 for don't care */
5292 } fmtstrings[] = {
5293 {"WINE wine", 0, 0},
5294 {"WINE wineWine", 0, 0},
5295 {"WINE\r\nwine\r\nwine", 5, 10},
5296 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5297 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5300 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5301 if (skip_non_english)
5302 skip("Skipping some tests on non-English platform\n");
5304 hdc = GetDC(hwndRichEdit);
5305 ok(hdc != NULL, "Could not get HDC\n");
5307 /* Calculate the twips per pixel */
5308 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5309 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5311 /* Test the simple case where all the text fits in the page rect. */
5312 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5313 fr.hdc = fr.hdcTarget = hdc;
5314 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5315 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5316 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5317 fr.chrg.cpMin = 0;
5318 fr.chrg.cpMax = -1;
5319 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5320 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5322 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5323 fr.rc.bottom = fr.rcPage.bottom;
5324 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5325 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5327 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5329 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5331 GETTEXTLENGTHEX gtl;
5332 SIZE stringsize;
5333 int len;
5335 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5337 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5338 gtl.codepage = CP_ACP;
5339 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5341 /* Get some size information for the string */
5342 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5344 /* Define the box to be half the width needed and a bit larger than the height.
5345 * Changes to the width means we have at least 2 pages. Changes to the height
5346 * is done so we can check the changing of fr.rc.bottom.
5348 fr.hdc = fr.hdcTarget = hdc;
5349 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5350 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5351 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5353 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5354 todo_wine {
5355 ok(r == len, "Expected %d, got %d\n", len, r);
5358 /* We know that the page can't hold the full string. See how many characters
5359 * are on the first one
5361 fr.chrg.cpMin = 0;
5362 fr.chrg.cpMax = -1;
5363 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5364 todo_wine {
5365 if (! skip_non_english)
5366 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5368 if (fmtstrings[i].first)
5369 todo_wine {
5370 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5372 else
5373 ok(r < len, "Expected < %d, got %d\n", len, r);
5375 /* Do another page */
5376 fr.chrg.cpMin = r;
5377 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5378 if (fmtstrings[i].second)
5379 todo_wine {
5380 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5382 else if (! skip_non_english)
5383 ok (r < len, "Expected < %d, got %d\n", len, r);
5385 /* There is at least on more page, but we don't care */
5387 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5388 todo_wine {
5389 ok(r == len, "Expected %d, got %d\n", len, r);
5393 ReleaseDC(NULL, hdc);
5394 DestroyWindow(hwndRichEdit);
5397 static int nCallbackCount = 0;
5399 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5400 LONG cb, LONG* pcb)
5402 const char text[] = {'t','e','s','t'};
5404 if (sizeof(text) <= cb)
5406 if ((int)dwCookie != nCallbackCount)
5408 *pcb = 0;
5409 return 0;
5412 memcpy (pbBuff, text, sizeof(text));
5413 *pcb = sizeof(text);
5415 nCallbackCount++;
5417 return 0;
5419 else
5420 return 1; /* indicates callback failed */
5423 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5424 LPBYTE pbBuff,
5425 LONG cb,
5426 LONG *pcb)
5428 const char** str = (const char**)dwCookie;
5429 int size = strlen(*str);
5430 *pcb = cb;
5431 if (*pcb > size) {
5432 *pcb = size;
5434 if (*pcb > 0) {
5435 memcpy(pbBuff, *str, *pcb);
5436 *str += *pcb;
5438 return 0;
5441 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5442 LPBYTE pbBuff,
5443 LONG cb,
5444 LONG *pcb)
5446 DWORD *phase = (DWORD *)dwCookie;
5448 if(*phase == 0){
5449 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5450 *pcb = sizeof(first) - 1;
5451 memcpy(pbBuff, first, *pcb);
5452 }else if(*phase == 1){
5453 static const char second[] = "\x8f\xc3\x8b";
5454 *pcb = sizeof(second) - 1;
5455 memcpy(pbBuff, second, *pcb);
5456 }else
5457 *pcb = 0;
5459 ++*phase;
5461 return 0;
5464 struct StringWithLength {
5465 int length;
5466 char *buffer;
5469 /* This callback is used to handled the null characters in a string. */
5470 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5471 LPBYTE pbBuff,
5472 LONG cb,
5473 LONG *pcb)
5475 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5476 int size = str->length;
5477 *pcb = cb;
5478 if (*pcb > size) {
5479 *pcb = size;
5481 if (*pcb > 0) {
5482 memcpy(pbBuff, str->buffer, *pcb);
5483 str->buffer += *pcb;
5484 str->length -= *pcb;
5486 return 0;
5489 static void test_EM_STREAMIN(void)
5491 HWND hwndRichEdit = new_richedit(NULL);
5492 DWORD phase;
5493 LRESULT result;
5494 EDITSTREAM es;
5495 char buffer[1024] = {0}, tmp[16];
5496 CHARRANGE range;
5497 PARAFORMAT2 fmt;
5499 const char * streamText0 = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText}";
5500 const char * streamText0a = "{\\rtf1\\fi100\\li200\\rtlpar\\qr TestSomeText\\par}";
5501 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5502 const char * ptr;
5504 const char * streamText1 =
5505 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5506 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5507 "}\r\n";
5509 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5510 const char * streamText2 =
5511 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5512 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5513 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5514 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5515 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5516 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5517 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5519 const char * streamText3 = "RichEdit1";
5521 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5523 const char * streamText4 =
5524 "This text just needs to be long enough to cause run to be split onto "
5525 "two separate lines and make sure the null terminating character is "
5526 "handled properly.\0";
5528 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5530 int length4 = strlen(streamText4) + 1;
5531 struct StringWithLength cookieForStream4 = {
5532 length4,
5533 (char *)streamText4,
5536 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5537 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5538 struct StringWithLength cookieForStream5 = {
5539 sizeof(streamText5),
5540 (char *)streamText5,
5543 /* Minimal test without \par at the end */
5544 es.dwCookie = (DWORD_PTR)&streamText0;
5545 es.dwError = 0;
5546 es.pfnCallback = test_EM_STREAMIN_esCallback;
5547 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5548 ok(result == 12, "got %ld, expected %d\n", result, 12);
5550 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5551 ok (result == 12,
5552 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5553 result = strcmp (buffer,"TestSomeText");
5554 ok (result == 0,
5555 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5556 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5557 /* Show that para fmts are ignored */
5558 range.cpMin = 2;
5559 range.cpMax = 2;
5560 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5561 memset(&fmt, 0xcc, sizeof(fmt));
5562 fmt.cbSize = sizeof(fmt);
5563 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5564 ok(fmt.dxStartIndent == 0, "got %d\n", fmt.dxStartIndent);
5565 ok(fmt.dxOffset == 0, "got %d\n", fmt.dxOffset);
5566 ok(fmt.wAlignment == PFA_LEFT, "got %d\n", fmt.wAlignment);
5567 ok((fmt.wEffects & PFE_RTLPARA) == 0, "got %x\n", fmt.wEffects);
5569 /* Native richedit 2.0 ignores last \par */
5570 ptr = streamText0a;
5571 es.dwCookie = (DWORD_PTR)&ptr;
5572 es.dwError = 0;
5573 es.pfnCallback = test_EM_STREAMIN_esCallback;
5574 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5575 ok(result == 12, "got %ld, expected %d\n", result, 12);
5577 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5578 ok (result == 12,
5579 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5580 result = strcmp (buffer,"TestSomeText");
5581 ok (result == 0,
5582 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5583 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5584 /* This time para fmts are processed */
5585 range.cpMin = 2;
5586 range.cpMax = 2;
5587 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5588 memset(&fmt, 0xcc, sizeof(fmt));
5589 fmt.cbSize = sizeof(fmt);
5590 result = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
5591 ok(fmt.dxStartIndent == 300, "got %d\n", fmt.dxStartIndent);
5592 ok(fmt.dxOffset == -100, "got %d\n", fmt.dxOffset);
5593 ok(fmt.wAlignment == PFA_RIGHT, "got %d\n", fmt.wAlignment);
5594 ok((fmt.wEffects & PFE_RTLPARA) == PFE_RTLPARA, "got %x\n", fmt.wEffects);
5596 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5597 es.dwCookie = (DWORD_PTR)&streamText0b;
5598 es.dwError = 0;
5599 es.pfnCallback = test_EM_STREAMIN_esCallback;
5600 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5601 ok(result == 13, "got %ld, expected %d\n", result, 13);
5603 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5604 ok (result == 14,
5605 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5606 result = strcmp (buffer,"TestSomeText\r\n");
5607 ok (result == 0,
5608 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5609 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5611 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5612 ptr = streamText0a;
5613 es.dwCookie = (DWORD_PTR)&ptr;
5614 es.dwError = 0;
5615 es.pfnCallback = test_EM_STREAMIN_esCallback;
5616 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5617 ok(result == 12, "got %ld, expected %d\n", result, 12);
5619 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5620 ok (result == 12,
5621 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5622 result = strcmp (buffer,"TestSomeText");
5623 ok (result == 0,
5624 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5625 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5627 range.cpMin = 0;
5628 range.cpMax = -1;
5629 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5630 ok (result == 13, "got %ld\n", result);
5632 ptr = streamText0a;
5633 es.dwCookie = (DWORD_PTR)&ptr;
5634 es.dwError = 0;
5635 es.pfnCallback = test_EM_STREAMIN_esCallback;
5637 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5638 ok(result == 13, "got %ld, expected 13\n", result);
5640 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5641 ok (result == 14,
5642 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5643 result = strcmp (buffer,"TestSomeText\r\n");
5644 ok (result == 0,
5645 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5646 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5648 es.dwCookie = (DWORD_PTR)&streamText1;
5649 es.dwError = 0;
5650 es.pfnCallback = test_EM_STREAMIN_esCallback;
5651 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5652 ok(result == 12, "got %ld, expected %d\n", result, 12);
5654 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5655 ok (result == 12,
5656 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5657 result = strcmp (buffer,"TestSomeText");
5658 ok (result == 0,
5659 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5660 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5662 es.dwCookie = (DWORD_PTR)&streamText2;
5663 es.dwError = 0;
5664 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5665 ok(result == 0, "got %ld, expected %d\n", result, 0);
5667 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5668 ok (result == 0,
5669 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5670 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5671 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5673 es.dwCookie = (DWORD_PTR)&streamText3;
5674 es.dwError = 0;
5675 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5676 ok(result == 0, "got %ld, expected %d\n", result, 0);
5678 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5679 ok (result == 0,
5680 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5681 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5682 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5684 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5685 es.dwError = 0;
5686 es.pfnCallback = test_EM_STREAMIN_esCallback;
5687 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5688 ok(result == 18, "got %ld, expected %d\n", result, 18);
5690 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5691 ok(result == 15,
5692 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5693 result = strcmp (buffer,"TestUTF8WithBOM");
5694 ok(result == 0,
5695 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5696 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5698 phase = 0;
5699 es.dwCookie = (DWORD_PTR)&phase;
5700 es.dwError = 0;
5701 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5702 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5703 ok(result == 8, "got %ld\n", result);
5705 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5707 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5708 ok(result == 3,
5709 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5710 result = memcmp (buffer, tmp, 3);
5711 ok(result == 0,
5712 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5713 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5715 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5716 es.dwError = 0;
5717 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5718 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5719 ok(result == length4, "got %ld, expected %d\n", result, length4);
5721 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5722 ok (result == length4,
5723 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5724 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5726 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5727 es.dwError = 0;
5728 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5729 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5730 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5732 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5733 ok (result == length5,
5734 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5735 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5737 DestroyWindow(hwndRichEdit);
5740 static void test_EM_StreamIn_Undo(void)
5742 /* The purpose of this test is to determine when a EM_StreamIn should be
5743 * undoable. This is important because WM_PASTE currently uses StreamIn and
5744 * pasting should always be undoable but streaming isn't always.
5746 * cases to test:
5747 * StreamIn plain text without SFF_SELECTION.
5748 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5749 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5750 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5751 * Feel free to add tests for other text modes or StreamIn things.
5755 HWND hwndRichEdit = new_richedit(NULL);
5756 LRESULT result;
5757 EDITSTREAM es;
5758 char buffer[1024] = {0};
5759 const char randomtext[] = "Some text";
5761 es.pfnCallback = EditStreamCallback;
5763 /* StreamIn, no SFF_SELECTION */
5764 es.dwCookie = nCallbackCount;
5765 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5766 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5767 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5768 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5769 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5770 result = strcmp (buffer,"test");
5771 ok (result == 0,
5772 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5774 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5775 ok (result == FALSE,
5776 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5778 /* StreamIn, SFF_SELECTION, but nothing selected */
5779 es.dwCookie = nCallbackCount;
5780 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5781 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5782 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5783 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5784 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5785 result = strcmp (buffer,"testSome text");
5786 ok (result == 0,
5787 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5789 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5790 ok (result == TRUE,
5791 "EM_STREAMIN with SFF_SELECTION but no selection set "
5792 "should create an undo\n");
5794 /* StreamIn, SFF_SELECTION, with a selection */
5795 es.dwCookie = nCallbackCount;
5796 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5797 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5798 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5799 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5800 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5801 result = strcmp (buffer,"Sometesttext");
5802 ok (result == 0,
5803 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5805 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5806 ok (result == TRUE,
5807 "EM_STREAMIN with SFF_SELECTION and selection set "
5808 "should create an undo\n");
5810 DestroyWindow(hwndRichEdit);
5813 static BOOL is_em_settextex_supported(HWND hwnd)
5815 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5816 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5819 static void test_unicode_conversions(void)
5821 static const WCHAR tW[] = {'t',0};
5822 static const WCHAR teW[] = {'t','e',0};
5823 static const WCHAR textW[] = {'t','e','s','t',0};
5824 static const char textA[] = "test";
5825 char bufA[64];
5826 WCHAR bufW[64];
5827 HWND hwnd;
5828 int em_settextex_supported, ret;
5830 #define set_textA(hwnd, wm_set_text, txt) \
5831 do { \
5832 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5833 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5834 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5835 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5836 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5837 } while(0)
5838 #define expect_textA(hwnd, wm_get_text, txt) \
5839 do { \
5840 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5841 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5842 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5843 memset(bufA, 0xAA, sizeof(bufA)); \
5844 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5845 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5846 ret = lstrcmpA(bufA, txt); \
5847 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5848 } while(0)
5850 #define set_textW(hwnd, wm_set_text, txt) \
5851 do { \
5852 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5853 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5854 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5855 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5856 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5857 } while(0)
5858 #define expect_textW(hwnd, wm_get_text, txt) \
5859 do { \
5860 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5861 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5862 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5863 memset(bufW, 0xAA, sizeof(bufW)); \
5864 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5865 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5866 ret = lstrcmpW(bufW, txt); \
5867 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5868 } while(0)
5869 #define expect_empty(hwnd, wm_get_text) \
5870 do { \
5871 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5872 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5873 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5874 memset(bufA, 0xAA, sizeof(bufA)); \
5875 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5876 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5877 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5878 } while(0)
5880 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5881 0, 0, 200, 60, 0, 0, 0, 0);
5882 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5884 ret = IsWindowUnicode(hwnd);
5885 ok(ret, "RichEdit20W should be unicode under NT\n");
5887 /* EM_SETTEXTEX is supported starting from version 3.0 */
5888 em_settextex_supported = is_em_settextex_supported(hwnd);
5889 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5890 em_settextex_supported ? "" : "NOT ");
5892 expect_empty(hwnd, WM_GETTEXT);
5893 expect_empty(hwnd, EM_GETTEXTEX);
5895 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5896 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5897 expect_textA(hwnd, WM_GETTEXT, "t");
5898 expect_textA(hwnd, EM_GETTEXTEX, "t");
5899 expect_textW(hwnd, EM_GETTEXTEX, tW);
5901 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5902 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5903 expect_textA(hwnd, WM_GETTEXT, "te");
5904 expect_textA(hwnd, EM_GETTEXTEX, "te");
5905 expect_textW(hwnd, EM_GETTEXTEX, teW);
5907 set_textA(hwnd, WM_SETTEXT, NULL);
5908 expect_empty(hwnd, WM_GETTEXT);
5909 expect_empty(hwnd, EM_GETTEXTEX);
5911 set_textA(hwnd, WM_SETTEXT, textA);
5912 expect_textA(hwnd, WM_GETTEXT, textA);
5913 expect_textA(hwnd, EM_GETTEXTEX, textA);
5914 expect_textW(hwnd, EM_GETTEXTEX, textW);
5916 if (em_settextex_supported)
5918 set_textA(hwnd, EM_SETTEXTEX, textA);
5919 expect_textA(hwnd, WM_GETTEXT, textA);
5920 expect_textA(hwnd, EM_GETTEXTEX, textA);
5921 expect_textW(hwnd, EM_GETTEXTEX, textW);
5924 set_textW(hwnd, WM_SETTEXT, textW);
5925 expect_textW(hwnd, WM_GETTEXT, textW);
5926 expect_textA(hwnd, WM_GETTEXT, textA);
5927 expect_textW(hwnd, EM_GETTEXTEX, textW);
5928 expect_textA(hwnd, EM_GETTEXTEX, textA);
5930 if (em_settextex_supported)
5932 set_textW(hwnd, EM_SETTEXTEX, textW);
5933 expect_textW(hwnd, WM_GETTEXT, textW);
5934 expect_textA(hwnd, WM_GETTEXT, textA);
5935 expect_textW(hwnd, EM_GETTEXTEX, textW);
5936 expect_textA(hwnd, EM_GETTEXTEX, textA);
5938 DestroyWindow(hwnd);
5940 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5941 0, 0, 200, 60, 0, 0, 0, 0);
5942 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5944 ret = IsWindowUnicode(hwnd);
5945 ok(!ret, "RichEdit20A should NOT be unicode\n");
5947 set_textA(hwnd, WM_SETTEXT, textA);
5948 expect_textA(hwnd, WM_GETTEXT, textA);
5949 expect_textA(hwnd, EM_GETTEXTEX, textA);
5950 expect_textW(hwnd, EM_GETTEXTEX, textW);
5952 if (em_settextex_supported)
5954 set_textA(hwnd, EM_SETTEXTEX, textA);
5955 expect_textA(hwnd, WM_GETTEXT, textA);
5956 expect_textA(hwnd, EM_GETTEXTEX, textA);
5957 expect_textW(hwnd, EM_GETTEXTEX, textW);
5960 set_textW(hwnd, WM_SETTEXT, textW);
5961 expect_textW(hwnd, WM_GETTEXT, textW);
5962 expect_textA(hwnd, WM_GETTEXT, textA);
5963 expect_textW(hwnd, EM_GETTEXTEX, textW);
5964 expect_textA(hwnd, EM_GETTEXTEX, textA);
5966 if (em_settextex_supported)
5968 set_textW(hwnd, EM_SETTEXTEX, textW);
5969 expect_textW(hwnd, WM_GETTEXT, textW);
5970 expect_textA(hwnd, WM_GETTEXT, textA);
5971 expect_textW(hwnd, EM_GETTEXTEX, textW);
5972 expect_textA(hwnd, EM_GETTEXTEX, textA);
5974 DestroyWindow(hwnd);
5977 static void test_WM_CHAR(void)
5979 HWND hwnd;
5980 int ret;
5981 const char * char_list = "abc\rabc\r";
5982 const char * expected_content_single = "abcabc";
5983 const char * expected_content_multi = "abc\r\nabc\r\n";
5984 char buffer[64] = {0};
5985 const char * p;
5987 /* single-line control must IGNORE carriage returns */
5988 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5989 0, 0, 200, 60, 0, 0, 0, 0);
5990 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5992 p = char_list;
5993 while (*p != '\0') {
5994 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5995 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5996 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5997 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5998 p++;
6001 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6002 ret = strcmp(buffer, expected_content_single);
6003 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6005 DestroyWindow(hwnd);
6007 /* multi-line control inserts CR normally */
6008 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6009 0, 0, 200, 60, 0, 0, 0, 0);
6010 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6012 p = char_list;
6013 while (*p != '\0') {
6014 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
6015 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
6016 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
6017 SendMessageA(hwnd, WM_KEYUP, *p, 1);
6018 p++;
6021 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6022 ret = strcmp(buffer, expected_content_multi);
6023 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6025 DestroyWindow(hwnd);
6028 static void test_EM_GETTEXTLENGTHEX(void)
6030 HWND hwnd;
6031 GETTEXTLENGTHEX gtl;
6032 int ret;
6033 const char * base_string = "base string";
6034 const char * test_string = "a\nb\n\n\r\n";
6035 const char * test_string_after = "a";
6036 const char * test_string_2 = "a\rtest\rstring";
6037 char buffer[64] = {0};
6039 /* single line */
6040 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
6041 0, 0, 200, 60, 0, 0, 0, 0);
6042 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6044 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6045 gtl.codepage = CP_ACP;
6046 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6047 ok(ret == 0, "ret %d\n",ret);
6049 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6050 gtl.codepage = CP_ACP;
6051 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6052 ok(ret == 0, "ret %d\n",ret);
6054 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6056 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6057 gtl.codepage = CP_ACP;
6058 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6059 ok(ret == strlen(base_string), "ret %d\n",ret);
6061 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6062 gtl.codepage = CP_ACP;
6063 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6064 ok(ret == strlen(base_string), "ret %d\n",ret);
6066 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6068 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6069 gtl.codepage = CP_ACP;
6070 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6071 ok(ret == 1, "ret %d\n",ret);
6073 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6074 gtl.codepage = CP_ACP;
6075 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6076 ok(ret == 1, "ret %d\n",ret);
6078 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6079 ret = strcmp(buffer, test_string_after);
6080 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
6082 DestroyWindow(hwnd);
6084 /* multi line */
6085 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
6086 0, 0, 200, 60, 0, 0, 0, 0);
6087 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6089 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6090 gtl.codepage = CP_ACP;
6091 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6092 ok(ret == 0, "ret %d\n",ret);
6094 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6095 gtl.codepage = CP_ACP;
6096 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6097 ok(ret == 0, "ret %d\n",ret);
6099 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
6101 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6102 gtl.codepage = CP_ACP;
6103 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6104 ok(ret == strlen(base_string), "ret %d\n",ret);
6106 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6107 gtl.codepage = CP_ACP;
6108 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6109 ok(ret == strlen(base_string), "ret %d\n",ret);
6111 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6113 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6114 gtl.codepage = CP_ACP;
6115 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6116 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
6118 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6119 gtl.codepage = CP_ACP;
6120 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6121 ok(ret == strlen(test_string_2), "ret %d\n",ret);
6123 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
6125 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
6126 gtl.codepage = CP_ACP;
6127 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6128 ok(ret == 10, "ret %d\n",ret);
6130 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6131 gtl.codepage = CP_ACP;
6132 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6133 ok(ret == 6, "ret %d\n",ret);
6135 /* Unicode/NUMCHARS/NUMBYTES */
6136 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
6138 gtl.flags = GTL_DEFAULT;
6139 gtl.codepage = 1200;
6140 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6141 ok(ret == lstrlenA(test_string_2),
6142 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6144 gtl.flags = GTL_NUMCHARS;
6145 gtl.codepage = 1200;
6146 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6147 ok(ret == lstrlenA(test_string_2),
6148 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6150 gtl.flags = GTL_NUMBYTES;
6151 gtl.codepage = 1200;
6152 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6153 ok(ret == lstrlenA(test_string_2)*2,
6154 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6156 gtl.flags = GTL_PRECISE;
6157 gtl.codepage = 1200;
6158 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6159 ok(ret == lstrlenA(test_string_2)*2,
6160 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
6162 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
6163 gtl.codepage = 1200;
6164 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6165 ok(ret == lstrlenA(test_string_2),
6166 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
6168 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
6169 gtl.codepage = 1200;
6170 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
6171 ok(ret == E_INVALIDARG,
6172 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
6174 DestroyWindow(hwnd);
6178 /* globals that parent and child access when checking event masks & notifications */
6179 static HWND eventMaskEditHwnd = 0;
6180 static int queriedEventMask;
6181 static int watchForEventMask = 0;
6183 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
6184 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6186 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
6188 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6190 return DefWindowProcA(hwnd, message, wParam, lParam);
6193 /* test event masks in combination with WM_COMMAND */
6194 static void test_eventMask(void)
6196 HWND parent;
6197 int ret, style;
6198 WNDCLASSA cls;
6199 const char text[] = "foo bar\n";
6200 int eventMask;
6202 /* register class to capture WM_COMMAND */
6203 cls.style = 0;
6204 cls.lpfnWndProc = ParentMsgCheckProcA;
6205 cls.cbClsExtra = 0;
6206 cls.cbWndExtra = 0;
6207 cls.hInstance = GetModuleHandleA(0);
6208 cls.hIcon = 0;
6209 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6210 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6211 cls.lpszMenuName = NULL;
6212 cls.lpszClassName = "EventMaskParentClass";
6213 if(!RegisterClassA(&cls)) assert(0);
6215 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6216 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6217 ok (parent != 0, "Failed to create parent window\n");
6219 eventMaskEditHwnd = new_richedit(parent);
6220 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6222 eventMask = ENM_CHANGE | ENM_UPDATE;
6223 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6224 ok(ret == ENM_NONE, "wrong event mask\n");
6225 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6226 ok(ret == eventMask, "failed to set event mask\n");
6228 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6229 queriedEventMask = 0; /* initialize to something other than we expect */
6230 watchForEventMask = EN_CHANGE;
6231 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6232 ok(ret == TRUE, "failed to set text\n");
6233 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6234 notification in response to WM_SETTEXT */
6235 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6236 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6238 /* check to see if EN_CHANGE is sent when redraw is turned off */
6239 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6240 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6241 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6242 /* redraw is disabled by making the window invisible. */
6243 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6244 queriedEventMask = 0; /* initialize to something other than we expect */
6245 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6246 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6247 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6248 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6249 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6251 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6252 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6253 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6254 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6255 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6256 watchForEventMask = EN_UPDATE;
6257 queriedEventMask = 0; /* initialize to something other than we expect */
6258 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6259 ok(queriedEventMask == 0,
6260 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6261 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6262 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6263 queriedEventMask = 0; /* initialize to something other than we expect */
6264 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6265 ok(queriedEventMask == eventMask,
6266 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6269 DestroyWindow(parent);
6272 static int received_WM_NOTIFY = 0;
6273 static int modify_at_WM_NOTIFY = 0;
6274 static BOOL filter_on_WM_NOTIFY = FALSE;
6275 static HWND hwndRichedit_WM_NOTIFY;
6277 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6279 if(message == WM_NOTIFY)
6281 received_WM_NOTIFY = 1;
6282 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6283 if (filter_on_WM_NOTIFY) return TRUE;
6285 return DefWindowProcA(hwnd, message, wParam, lParam);
6288 static void test_WM_NOTIFY(void)
6290 HWND parent;
6291 WNDCLASSA cls;
6292 CHARFORMAT2A cf2;
6293 int sel_start, sel_end;
6295 /* register class to capture WM_NOTIFY */
6296 cls.style = 0;
6297 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6298 cls.cbClsExtra = 0;
6299 cls.cbWndExtra = 0;
6300 cls.hInstance = GetModuleHandleA(0);
6301 cls.hIcon = 0;
6302 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6303 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6304 cls.lpszMenuName = NULL;
6305 cls.lpszClassName = "WM_NOTIFY_ParentClass";
6306 if(!RegisterClassA(&cls)) assert(0);
6308 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6309 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6310 ok (parent != 0, "Failed to create parent window\n");
6312 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6313 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6315 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6317 /* Notifications for selection change should only be sent when selection
6318 actually changes. EM_SETCHARFORMAT is one message that calls
6319 ME_CommitUndo, which should check whether message should be sent */
6320 received_WM_NOTIFY = 0;
6321 cf2.cbSize = sizeof(CHARFORMAT2A);
6322 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6323 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6324 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6325 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6326 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6328 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6329 already at 0. */
6330 received_WM_NOTIFY = 0;
6331 modify_at_WM_NOTIFY = 0;
6332 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6333 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6334 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6336 received_WM_NOTIFY = 0;
6337 modify_at_WM_NOTIFY = 0;
6338 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6339 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6341 received_WM_NOTIFY = 0;
6342 modify_at_WM_NOTIFY = 0;
6343 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6344 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6345 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6347 /* Test for WM_NOTIFY messages with redraw disabled. */
6348 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6349 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6350 received_WM_NOTIFY = 0;
6351 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6352 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6353 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6355 /* Test filtering key events. */
6356 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6357 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6358 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6359 received_WM_NOTIFY = 0;
6360 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6361 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6362 ok(sel_start == 1 && sel_end == 1,
6363 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6364 filter_on_WM_NOTIFY = TRUE;
6365 received_WM_NOTIFY = 0;
6366 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6367 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6368 ok(sel_start == 1 && sel_end == 1,
6369 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6371 /* test with owner set to NULL */
6372 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6373 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6374 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6375 ok(sel_start == 1 && sel_end == 1,
6376 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6378 DestroyWindow(hwndRichedit_WM_NOTIFY);
6379 DestroyWindow(parent);
6382 static ENLINK enlink;
6383 #define CURSOR_CLIENT_X 5
6384 #define CURSOR_CLIENT_Y 5
6385 #define WP_PARENT 1
6386 #define WP_CHILD 2
6388 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6390 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6392 enlink = *(ENLINK*)lParam;
6394 return DefWindowProcA(hwnd, message, wParam, lParam);
6397 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6398 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6400 ENLINK junk_enlink;
6402 switch (msg)
6404 case WM_LBUTTONDBLCLK:
6405 case WM_LBUTTONDOWN:
6406 case WM_LBUTTONUP:
6407 case WM_MOUSEHOVER:
6408 case WM_MOUSEMOVE:
6409 case WM_MOUSEWHEEL:
6410 case WM_RBUTTONDBLCLK:
6411 case WM_RBUTTONDOWN:
6412 case WM_RBUTTONUP:
6413 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6414 break;
6415 case WM_SETCURSOR:
6416 if (wParam == WP_PARENT)
6417 wParam = (WPARAM)parent;
6418 else if (wParam == WP_CHILD)
6419 wParam = (WPARAM)hwnd;
6420 break;
6423 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6424 enlink = junk_enlink;
6426 SendMessageA(hwnd, msg, wParam, lParam);
6428 if (notifies)
6430 ok(enlink.nmhdr.hwndFrom == hwnd,
6431 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6432 ok(enlink.nmhdr.idFrom == 0,
6433 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6434 ok(enlink.msg == msg,
6435 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6436 if (msg == WM_SETCURSOR)
6438 ok(enlink.wParam == 0,
6439 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6441 else
6443 ok(enlink.wParam == wParam,
6444 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6446 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6447 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6448 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6449 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6450 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6452 else
6454 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6455 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6459 static void test_EN_LINK(void)
6461 HWND hwnd, parent;
6462 WNDCLASSA cls;
6463 CHARFORMAT2A cf2;
6464 POINT orig_cursor_pos;
6465 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6466 int i;
6468 static const struct
6470 UINT msg;
6471 WPARAM wParam;
6472 LPARAM lParam;
6473 BOOL notifies;
6475 link_notify_tests[] =
6477 /* hold down the left button and try some messages */
6478 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6479 { EM_LINESCROLL, 0, 1, FALSE },
6480 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6481 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6482 { WM_MOUSEHOVER, 0, 0, FALSE },
6483 { WM_MOUSEMOVE, 0, 0, FALSE },
6484 { WM_MOUSEWHEEL, 0, 0, FALSE },
6485 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6486 { WM_RBUTTONDOWN, 0, 0, TRUE },
6487 { WM_RBUTTONUP, 0, 0, TRUE },
6488 { WM_SETCURSOR, 0, 0, FALSE },
6489 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6490 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6491 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6492 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6493 { WM_LBUTTONUP, 0, 0, TRUE },
6494 /* hold down the right button and try some messages */
6495 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6496 { EM_LINESCROLL, 0, 1, FALSE },
6497 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6498 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6499 { WM_LBUTTONDOWN, 0, 0, TRUE },
6500 { WM_LBUTTONUP, 0, 0, TRUE },
6501 { WM_MOUSEHOVER, 0, 0, FALSE },
6502 { WM_MOUSEMOVE, 0, 0, TRUE },
6503 { WM_MOUSEWHEEL, 0, 0, FALSE },
6504 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6505 { WM_SETCURSOR, 0, 0, FALSE },
6506 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6507 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6508 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6509 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6510 { WM_RBUTTONUP, 0, 0, TRUE },
6511 /* try the messages with both buttons released */
6512 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6513 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6514 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6515 { WM_LBUTTONDOWN, 0, 0, TRUE },
6516 { WM_LBUTTONUP, 0, 0, TRUE },
6517 { WM_MOUSEHOVER, 0, 0, FALSE },
6518 { WM_MOUSEMOVE, 0, 0, TRUE },
6519 { WM_MOUSEWHEEL, 0, 0, FALSE },
6520 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6521 { WM_RBUTTONDOWN, 0, 0, TRUE },
6522 { WM_RBUTTONUP, 0, 0, TRUE },
6523 { WM_SETCURSOR, 0, 0, FALSE },
6524 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6525 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6526 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6527 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6530 /* register class to capture WM_NOTIFY */
6531 cls.style = 0;
6532 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6533 cls.cbClsExtra = 0;
6534 cls.cbWndExtra = 0;
6535 cls.hInstance = GetModuleHandleA(0);
6536 cls.hIcon = 0;
6537 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6538 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6539 cls.lpszMenuName = NULL;
6540 cls.lpszClassName = "EN_LINK_ParentClass";
6541 if(!RegisterClassA(&cls)) assert(0);
6543 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6544 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6545 ok(parent != 0, "Failed to create parent window\n");
6547 hwnd = new_richedit(parent);
6548 ok(hwnd != 0, "Failed to create edit window\n");
6550 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6552 cf2.cbSize = sizeof(CHARFORMAT2A);
6553 cf2.dwMask = CFM_LINK;
6554 cf2.dwEffects = CFE_LINK;
6555 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6556 /* mixing letters and numbers causes runs to be split */
6557 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6559 GetCursorPos(&orig_cursor_pos);
6560 SetCursorPos(0, 0);
6562 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6564 link_notify_test("cursor position simulated", i, hwnd, parent,
6565 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6566 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6569 ClientToScreen(hwnd, &cursor_screen_pos);
6570 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6572 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6574 link_notify_test("cursor position set", i, hwnd, parent,
6575 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6576 link_notify_tests[i].notifies);
6579 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6580 DestroyWindow(hwnd);
6581 DestroyWindow(parent);
6584 static void test_undo_coalescing(void)
6586 HWND hwnd;
6587 int result;
6588 char buffer[64] = {0};
6590 /* multi-line control inserts CR normally */
6591 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6592 0, 0, 200, 60, 0, 0, 0, 0);
6593 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6595 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6596 ok (result == FALSE, "Can undo after window creation.\n");
6597 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6598 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6599 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6600 ok (result == FALSE, "Can redo after window creation.\n");
6601 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6602 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6604 /* Test the effect of arrows keys during typing on undo transactions*/
6605 simulate_typing_characters(hwnd, "one two three");
6606 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6607 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6608 simulate_typing_characters(hwnd, " four five six");
6610 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6611 ok (result == FALSE, "Can redo before anything is undone.\n");
6612 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6613 ok (result == TRUE, "Cannot undo typed characters.\n");
6614 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6615 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6616 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6617 ok (result == TRUE, "Cannot redo after undo.\n");
6618 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6619 result = strcmp(buffer, "one two three");
6620 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6622 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6623 ok (result == TRUE, "Cannot undo typed characters.\n");
6624 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6625 ok (result == TRUE, "Failed to undo typed characters.\n");
6626 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6627 result = strcmp(buffer, "");
6628 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6630 /* Test the effect of focus changes during typing on undo transactions*/
6631 simulate_typing_characters(hwnd, "one two three");
6632 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6633 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6634 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6635 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6636 simulate_typing_characters(hwnd, " four five six");
6637 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6638 ok (result == TRUE, "Failed to undo typed characters.\n");
6639 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6640 result = strcmp(buffer, "one two three");
6641 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6643 /* Test the effect of the back key during typing on undo transactions */
6644 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6645 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6646 ok (result == TRUE, "Failed to clear the text.\n");
6647 simulate_typing_characters(hwnd, "one two threa");
6648 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6649 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6650 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6651 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6652 simulate_typing_characters(hwnd, "e four five six");
6653 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6654 ok (result == TRUE, "Failed to undo typed characters.\n");
6655 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6656 result = strcmp(buffer, "");
6657 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6659 /* Test the effect of the delete key during typing on undo transactions */
6660 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6661 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6662 ok(result == TRUE, "Failed to set the text.\n");
6663 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6664 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6665 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6666 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6667 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6668 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6669 ok (result == TRUE, "Failed to undo typed characters.\n");
6670 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6671 result = strcmp(buffer, "acd");
6672 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6673 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6674 ok (result == TRUE, "Failed to undo typed characters.\n");
6675 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6676 result = strcmp(buffer, "abcd");
6677 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6679 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6680 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6681 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6682 ok (result == TRUE, "Failed to clear the text.\n");
6683 simulate_typing_characters(hwnd, "one two three");
6684 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6685 ok (result == 0, "expected %d but got %d\n", 0, result);
6686 simulate_typing_characters(hwnd, " four five six");
6687 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6688 ok (result == TRUE, "Failed to undo typed characters.\n");
6689 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6690 result = strcmp(buffer, "one two three");
6691 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6692 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6693 ok (result == TRUE, "Failed to undo typed characters.\n");
6694 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6695 result = strcmp(buffer, "");
6696 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6698 DestroyWindow(hwnd);
6701 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6703 int length;
6705 /* MSDN lied, length is actually the number of bytes. */
6706 length = bytes / sizeof(WCHAR);
6707 switch(code)
6709 case WB_ISDELIMITER:
6710 return text[pos] == 'X';
6711 case WB_LEFT:
6712 case WB_MOVEWORDLEFT:
6713 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6714 return pos-1;
6715 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6716 case WB_LEFTBREAK:
6717 pos--;
6718 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6719 pos--;
6720 return pos;
6721 case WB_RIGHT:
6722 case WB_MOVEWORDRIGHT:
6723 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6724 return pos+1;
6725 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6726 case WB_RIGHTBREAK:
6727 pos++;
6728 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6729 pos++;
6730 return pos;
6731 default:
6732 ok(FALSE, "Unexpected code %d\n", code);
6733 break;
6735 return 0;
6738 static void test_word_movement(void)
6740 HWND hwnd;
6741 int result;
6742 int sel_start, sel_end;
6743 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6745 /* multi-line control inserts CR normally */
6746 hwnd = new_richedit(NULL);
6748 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6749 ok (result == TRUE, "Failed to clear the text.\n");
6750 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6751 /* |one two three */
6753 send_ctrl_key(hwnd, VK_RIGHT);
6754 /* one |two three */
6755 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6756 ok(sel_start == sel_end, "Selection should be empty\n");
6757 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6759 send_ctrl_key(hwnd, VK_RIGHT);
6760 /* one two |three */
6761 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6762 ok(sel_start == sel_end, "Selection should be empty\n");
6763 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6765 send_ctrl_key(hwnd, VK_LEFT);
6766 /* one |two three */
6767 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6768 ok(sel_start == sel_end, "Selection should be empty\n");
6769 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6771 send_ctrl_key(hwnd, VK_LEFT);
6772 /* |one two three */
6773 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6774 ok(sel_start == sel_end, "Selection should be empty\n");
6775 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6777 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6778 /* one two | three */
6779 send_ctrl_key(hwnd, VK_RIGHT);
6780 /* one two |three */
6781 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6782 ok(sel_start == sel_end, "Selection should be empty\n");
6783 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6785 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6786 /* one two th|ree */
6787 send_ctrl_key(hwnd, VK_LEFT);
6788 /* one two |three */
6789 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6790 ok(sel_start == sel_end, "Selection should be empty\n");
6791 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6793 /* Test with a custom word break procedure that uses X as the delimiter. */
6794 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6795 ok (result == TRUE, "Failed to clear the text.\n");
6796 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6797 /* |one twoXthree */
6798 send_ctrl_key(hwnd, VK_RIGHT);
6799 /* one twoX|three */
6800 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6801 ok(sel_start == sel_end, "Selection should be empty\n");
6802 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6804 DestroyWindow(hwnd);
6806 /* Make sure the behaviour is the same with a unicode richedit window,
6807 * and using unicode functions. */
6809 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6810 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6811 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6813 /* Test with a custom word break procedure that uses X as the delimiter. */
6814 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6815 ok (result == TRUE, "Failed to clear the text.\n");
6816 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6817 /* |one twoXthree */
6818 send_ctrl_key(hwnd, VK_RIGHT);
6819 /* one twoX|three */
6820 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6821 ok(sel_start == sel_end, "Selection should be empty\n");
6822 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6824 DestroyWindow(hwnd);
6827 static void test_EM_CHARFROMPOS(void)
6829 HWND hwnd;
6830 int result;
6831 RECT rcClient;
6832 POINTL point;
6833 point.x = 0;
6834 point.y = 40;
6836 /* multi-line control inserts CR normally */
6837 hwnd = new_richedit(NULL);
6838 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6839 (LPARAM)"one two three four five six seven\reight");
6840 ok(result == 1, "Expected 1, got %d\n", result);
6841 GetClientRect(hwnd, &rcClient);
6843 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6844 ok(result == 34, "expected character index of 34 but got %d\n", result);
6846 /* Test with points outside the bounds of the richedit control. */
6847 point.x = -1;
6848 point.y = 40;
6849 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6850 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6852 point.x = 1000;
6853 point.y = 0;
6854 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6855 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6857 point.x = 1000;
6858 point.y = 36;
6859 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6860 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6862 point.x = 1000;
6863 point.y = -1;
6864 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6865 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6867 point.x = 1000;
6868 point.y = rcClient.bottom + 1;
6869 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6870 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6872 point.x = 1000;
6873 point.y = rcClient.bottom;
6874 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6875 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6877 DestroyWindow(hwnd);
6880 static void test_word_wrap(void)
6882 HWND hwnd;
6883 POINTL point = {0, 60}; /* This point must be below the first line */
6884 const char *text = "Must be long enough to test line wrapping";
6885 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6886 int res, pos, lines;
6888 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6889 * when specified on window creation and set later. */
6890 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6891 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6892 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6893 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6894 ok(res, "WM_SETTEXT failed.\n");
6895 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6896 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6897 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6898 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6900 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6901 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6902 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6903 DestroyWindow(hwnd);
6905 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6906 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6907 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6909 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6910 ok(res, "WM_SETTEXT failed.\n");
6911 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6912 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6913 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6914 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6916 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6917 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6918 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6919 DestroyWindow(hwnd);
6921 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6922 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6923 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6924 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6925 ok(res, "WM_SETTEXT failed.\n");
6926 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6927 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6929 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6930 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6931 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6932 DestroyWindow(hwnd);
6934 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6935 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6936 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6937 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6938 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6939 ok(res, "WM_SETTEXT failed.\n");
6940 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6941 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6943 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6944 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6945 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6947 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6948 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6949 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6950 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6951 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6953 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6954 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6955 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6956 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6957 DestroyWindow(hwnd);
6959 /* Test to see if wrapping happens with redraw disabled. */
6960 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6961 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6962 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6963 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6964 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6965 ok(res, "EM_REPLACESEL failed.\n");
6966 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6967 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6968 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6969 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6970 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6972 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6973 DestroyWindow(hwnd);
6976 static void test_autoscroll(void)
6978 HWND hwnd = new_richedit(NULL);
6979 int lines, ret, redraw;
6980 POINT pt;
6982 for (redraw = 0; redraw <= 1; redraw++) {
6983 trace("testing with WM_SETREDRAW=%d\n", redraw);
6984 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6985 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6986 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6987 ok(lines == 8, "%d lines instead of 8\n", lines);
6988 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6989 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6990 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6991 ret = GetWindowLongA(hwnd, GWL_STYLE);
6992 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6994 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6995 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6996 ok(lines == 1, "%d lines instead of 1\n", lines);
6997 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6998 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6999 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
7000 ret = GetWindowLongA(hwnd, GWL_STYLE);
7001 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
7004 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
7005 DestroyWindow(hwnd);
7007 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
7008 * auto vertical/horizontal scrolling options. */
7009 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7010 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
7011 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7012 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7013 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7014 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
7015 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
7016 ret = GetWindowLongA(hwnd, GWL_STYLE);
7017 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7018 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7019 DestroyWindow(hwnd);
7021 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7022 WS_POPUP|ES_MULTILINE,
7023 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7024 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7025 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7026 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
7027 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
7028 ret = GetWindowLongA(hwnd, GWL_STYLE);
7029 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
7030 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
7031 DestroyWindow(hwnd);
7035 static void test_format_rect(void)
7037 HWND hwnd;
7038 RECT rc, expected, clientRect;
7039 int n;
7040 DWORD options;
7042 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7043 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7044 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7045 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7047 GetClientRect(hwnd, &clientRect);
7049 expected = clientRect;
7050 expected.left += 1;
7051 expected.right -= 1;
7052 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7053 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7054 rc.top, rc.left, rc.bottom, rc.right,
7055 expected.top, expected.left, expected.bottom, expected.right);
7057 for (n = -3; n <= 3; n++)
7059 rc = clientRect;
7060 rc.top += n;
7061 rc.left += n;
7062 rc.bottom -= n;
7063 rc.right -= n;
7064 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7066 expected = rc;
7067 expected.top = max(0, rc.top);
7068 expected.left = max(0, rc.left);
7069 expected.bottom = min(clientRect.bottom, rc.bottom);
7070 expected.right = min(clientRect.right, rc.right);
7071 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7072 ok(EqualRect(&rc, &expected),
7073 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7074 n, rc.top, rc.left, rc.bottom, rc.right,
7075 expected.top, expected.left, expected.bottom, expected.right);
7078 rc = clientRect;
7079 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7080 expected = clientRect;
7081 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7082 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7083 rc.top, rc.left, rc.bottom, rc.right,
7084 expected.top, expected.left, expected.bottom, expected.right);
7086 /* Adding the selectionbar adds the selectionbar width to the left side. */
7087 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
7088 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7089 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
7090 expected.left += 8; /* selection bar width */
7091 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7092 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7093 rc.top, rc.left, rc.bottom, rc.right,
7094 expected.top, expected.left, expected.bottom, expected.right);
7096 rc = clientRect;
7097 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7098 expected = clientRect;
7099 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7100 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7101 rc.top, rc.left, rc.bottom, rc.right,
7102 expected.top, expected.left, expected.bottom, expected.right);
7104 /* Removing the selectionbar subtracts the selectionbar width from the left side,
7105 * even if the left side is already 0. */
7106 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
7107 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
7108 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
7109 expected.left -= 8; /* selection bar width */
7110 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7111 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7112 rc.top, rc.left, rc.bottom, rc.right,
7113 expected.top, expected.left, expected.bottom, expected.right);
7115 /* Set the absolute value of the formatting rectangle. */
7116 rc = clientRect;
7117 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7118 expected = clientRect;
7119 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7120 ok(EqualRect(&rc, &expected),
7121 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7122 n, rc.top, rc.left, rc.bottom, rc.right,
7123 expected.top, expected.left, expected.bottom, expected.right);
7125 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
7126 * LPARAM as being a relative offset when the WPARAM value is 1, but these
7127 * tests show that this isn't true. */
7128 rc.top = 15;
7129 rc.left = 15;
7130 rc.bottom = clientRect.bottom - 15;
7131 rc.right = clientRect.right - 15;
7132 expected = rc;
7133 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7134 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7135 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7136 rc.top, rc.left, rc.bottom, rc.right,
7137 expected.top, expected.left, expected.bottom, expected.right);
7139 /* For some reason it does not limit the values to the client rect with
7140 * a WPARAM value of 1. */
7141 rc.top = -15;
7142 rc.left = -15;
7143 rc.bottom = clientRect.bottom + 15;
7144 rc.right = clientRect.right + 15;
7145 expected = rc;
7146 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
7147 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7148 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7149 rc.top, rc.left, rc.bottom, rc.right,
7150 expected.top, expected.left, expected.bottom, expected.right);
7152 /* Reset to default rect and check how the format rect adjusts to window
7153 * resize and how it copes with very small windows */
7154 SendMessageA(hwnd, EM_SETRECT, 0, 0);
7156 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
7157 GetClientRect(hwnd, &clientRect);
7159 expected = clientRect;
7160 expected.left += 1;
7161 expected.right -= 1;
7162 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7163 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7164 rc.top, rc.left, rc.bottom, rc.right,
7165 expected.top, expected.left, expected.bottom, expected.right);
7167 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
7168 GetClientRect(hwnd, &clientRect);
7170 expected = clientRect;
7171 expected.left += 1;
7172 expected.right -= 1;
7173 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7174 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7175 rc.top, rc.left, rc.bottom, rc.right,
7176 expected.top, expected.left, expected.bottom, expected.right);
7178 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
7179 GetClientRect(hwnd, &clientRect);
7181 expected = clientRect;
7182 expected.left += 1;
7183 expected.right -= 1;
7184 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7185 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7186 rc.top, rc.left, rc.bottom, rc.right,
7187 expected.top, expected.left, expected.bottom, expected.right);
7189 DestroyWindow(hwnd);
7191 /* The extended window style affects the formatting rectangle. */
7192 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7193 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7194 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7195 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7197 GetClientRect(hwnd, &clientRect);
7199 expected = clientRect;
7200 expected.left += 1;
7201 expected.top += 1;
7202 expected.right -= 1;
7203 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7204 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7205 rc.top, rc.left, rc.bottom, rc.right,
7206 expected.top, expected.left, expected.bottom, expected.right);
7208 rc = clientRect;
7209 rc.top += 5;
7210 rc.left += 5;
7211 rc.bottom -= 5;
7212 rc.right -= 5;
7213 expected = rc;
7214 expected.top -= 1;
7215 expected.left -= 1;
7216 expected.right += 1;
7217 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7218 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7219 ok(EqualRect(&rc, &expected), "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7220 rc.top, rc.left, rc.bottom, rc.right,
7221 expected.top, expected.left, expected.bottom, expected.right);
7223 DestroyWindow(hwnd);
7226 static void test_WM_GETDLGCODE(void)
7228 HWND hwnd;
7229 UINT res, expected;
7230 MSG msg;
7232 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7234 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7235 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7236 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7237 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7238 msg.hwnd = hwnd;
7239 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7240 expected = expected | DLGC_WANTMESSAGE;
7241 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7242 res, expected);
7243 DestroyWindow(hwnd);
7245 msg.message = WM_KEYDOWN;
7246 msg.wParam = VK_RETURN;
7247 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7248 msg.pt.x = 0;
7249 msg.pt.y = 0;
7250 msg.time = GetTickCount();
7252 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7253 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7254 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7255 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7256 msg.hwnd = hwnd;
7257 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7258 expected = expected | DLGC_WANTMESSAGE;
7259 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7260 res, expected);
7261 DestroyWindow(hwnd);
7263 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7264 ES_MULTILINE|WS_POPUP,
7265 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7266 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7267 msg.hwnd = hwnd;
7268 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7269 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7270 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7271 res, expected);
7272 DestroyWindow(hwnd);
7274 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7275 ES_WANTRETURN|WS_POPUP,
7276 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7277 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7278 msg.hwnd = hwnd;
7279 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7280 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7281 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7282 res, expected);
7283 DestroyWindow(hwnd);
7285 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7286 WS_POPUP,
7287 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7288 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7289 msg.hwnd = hwnd;
7290 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7291 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7292 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7293 res, expected);
7294 DestroyWindow(hwnd);
7296 msg.wParam = VK_TAB;
7297 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7299 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7300 ES_MULTILINE|WS_POPUP,
7301 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7302 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7303 msg.hwnd = hwnd;
7304 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7305 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7306 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7307 res, expected);
7308 DestroyWindow(hwnd);
7310 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7311 WS_POPUP,
7312 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7313 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7314 msg.hwnd = hwnd;
7315 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7316 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7317 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7318 res, expected);
7319 DestroyWindow(hwnd);
7321 hold_key(VK_CONTROL);
7323 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7324 ES_MULTILINE|WS_POPUP,
7325 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7326 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7327 msg.hwnd = hwnd;
7328 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7329 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7330 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7331 res, expected);
7332 DestroyWindow(hwnd);
7334 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7335 WS_POPUP,
7336 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7337 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7338 msg.hwnd = hwnd;
7339 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7340 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7341 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7342 res, expected);
7343 DestroyWindow(hwnd);
7345 release_key(VK_CONTROL);
7347 msg.wParam = 'a';
7348 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7350 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7351 ES_MULTILINE|WS_POPUP,
7352 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7353 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7354 msg.hwnd = hwnd;
7355 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7356 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7357 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7358 res, expected);
7359 DestroyWindow(hwnd);
7361 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7362 WS_POPUP,
7363 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7364 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7365 msg.hwnd = hwnd;
7366 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7367 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7368 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7369 res, expected);
7370 DestroyWindow(hwnd);
7372 msg.message = WM_CHAR;
7374 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7375 ES_MULTILINE|WS_POPUP,
7376 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7377 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7378 msg.hwnd = hwnd;
7379 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7380 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7381 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7382 res, expected);
7383 DestroyWindow(hwnd);
7385 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7386 WS_POPUP,
7387 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7388 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7389 msg.hwnd = hwnd;
7390 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7391 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7392 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7393 res, expected);
7394 DestroyWindow(hwnd);
7396 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7397 WS_POPUP|ES_SAVESEL,
7398 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7399 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7400 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7401 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7402 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7403 res, expected);
7404 DestroyWindow(hwnd);
7407 static void test_zoom(void)
7409 HWND hwnd;
7410 UINT ret;
7411 RECT rc;
7412 POINT pt;
7413 int numerator, denominator;
7415 hwnd = new_richedit(NULL);
7416 GetClientRect(hwnd, &rc);
7417 pt.x = (rc.right - rc.left) / 2;
7418 pt.y = (rc.bottom - rc.top) / 2;
7419 ClientToScreen(hwnd, &pt);
7421 /* Test initial zoom value */
7422 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7423 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7424 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7425 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7427 /* test scroll wheel */
7428 hold_key(VK_CONTROL);
7429 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7430 MAKELPARAM(pt.x, pt.y));
7431 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7432 release_key(VK_CONTROL);
7434 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7435 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7436 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7437 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7439 /* Test how much the mouse wheel can zoom in and out. */
7440 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7441 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7443 hold_key(VK_CONTROL);
7444 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7445 MAKELPARAM(pt.x, pt.y));
7446 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7447 release_key(VK_CONTROL);
7449 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7450 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7451 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7452 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7454 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7455 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7457 hold_key(VK_CONTROL);
7458 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7459 MAKELPARAM(pt.x, pt.y));
7460 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7461 release_key(VK_CONTROL);
7463 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7464 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7465 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7466 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7468 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7469 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7471 hold_key(VK_CONTROL);
7472 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7473 MAKELPARAM(pt.x, pt.y));
7474 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7475 release_key(VK_CONTROL);
7477 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7478 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7479 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7480 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7482 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7483 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7485 hold_key(VK_CONTROL);
7486 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7487 MAKELPARAM(pt.x, pt.y));
7488 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7489 release_key(VK_CONTROL);
7491 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7492 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7493 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7494 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7496 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7497 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7498 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7500 hold_key(VK_CONTROL);
7501 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7502 MAKELPARAM(pt.x, pt.y));
7503 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7504 release_key(VK_CONTROL);
7506 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7507 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7508 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7509 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7511 /* Test bounds checking on EM_SETZOOM */
7512 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7513 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7515 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7516 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7518 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7519 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7521 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7522 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7523 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7524 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7526 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7527 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7529 /* See if negative numbers are accepted. */
7530 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7531 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7533 /* See if negative numbers are accepted. */
7534 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7535 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7537 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7538 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7539 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7540 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7542 /* Reset the zoom value */
7543 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7544 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7546 DestroyWindow(hwnd);
7549 struct dialog_mode_messages
7551 int wm_getdefid, wm_close, wm_nextdlgctl;
7554 static struct dialog_mode_messages dm_messages;
7556 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7557 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7558 "got %d\n", wmclose, dm_messages.wm_close); \
7559 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7560 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7561 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7562 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7564 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7566 switch (iMsg)
7568 case DM_GETDEFID:
7569 dm_messages.wm_getdefid++;
7570 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7571 case WM_NEXTDLGCTL:
7572 dm_messages.wm_nextdlgctl++;
7573 break;
7574 case WM_CLOSE:
7575 dm_messages.wm_close++;
7576 break;
7579 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7582 static void test_dialogmode(void)
7584 HWND hwRichEdit, hwParent, hwButton;
7585 MSG msg= {0};
7586 int lcount, r;
7587 WNDCLASSA cls;
7589 cls.style = 0;
7590 cls.lpfnWndProc = dialog_mode_wnd_proc;
7591 cls.cbClsExtra = 0;
7592 cls.cbWndExtra = 0;
7593 cls.hInstance = GetModuleHandleA(0);
7594 cls.hIcon = 0;
7595 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7596 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7597 cls.lpszMenuName = NULL;
7598 cls.lpszClassName = "DialogModeParentClass";
7599 if(!RegisterClassA(&cls)) assert(0);
7601 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7602 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7604 /* Test richedit(ES_MULTILINE) */
7606 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7608 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7609 ok(0 == r, "expected 0, got %d\n", r);
7610 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7611 ok(2 == lcount, "expected 2, got %d\n", lcount);
7613 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7614 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7616 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7617 ok(0 == r, "expected 0, got %d\n", r);
7618 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7619 ok(3 == lcount, "expected 3, got %d\n", lcount);
7621 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7622 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7623 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7624 ok(0 == r, "expected 0, got %d\n", r);
7625 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7626 ok(3 == lcount, "expected 3, got %d\n", lcount);
7628 DestroyWindow(hwRichEdit);
7630 /* Test standalone richedit(ES_MULTILINE) */
7632 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7634 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7635 ok(0 == r, "expected 0, got %d\n", r);
7636 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7637 ok(2 == lcount, "expected 2, got %d\n", lcount);
7639 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7640 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7642 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7643 ok(0 == r, "expected 0, got %d\n", r);
7644 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7645 ok(2 == lcount, "expected 2, got %d\n", lcount);
7647 DestroyWindow(hwRichEdit);
7649 /* Check a destination for messages */
7651 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7653 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7654 SetParent( hwRichEdit, NULL);
7656 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7657 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7659 memset(&dm_messages, 0, sizeof(dm_messages));
7660 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7661 ok(0 == r, "expected 0, got %d\n", r);
7662 test_dm_messages(0, 1, 0);
7664 memset(&dm_messages, 0, sizeof(dm_messages));
7665 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7666 ok(0 == r, "expected 0, got %d\n", r);
7667 test_dm_messages(0, 0, 1);
7669 DestroyWindow(hwRichEdit);
7671 /* Check messages from richedit(ES_MULTILINE) */
7673 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7675 memset(&dm_messages, 0, sizeof(dm_messages));
7676 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7677 ok(0 == r, "expected 0, got %d\n", r);
7678 test_dm_messages(0, 0, 0);
7680 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7681 ok(2 == lcount, "expected 2, got %d\n", lcount);
7683 memset(&dm_messages, 0, sizeof(dm_messages));
7684 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7685 ok(0 == r, "expected 0, got %d\n", r);
7686 test_dm_messages(0, 0, 0);
7688 memset(&dm_messages, 0, sizeof(dm_messages));
7689 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7690 ok(0 == r, "expected 0, got %d\n", r);
7691 test_dm_messages(0, 0, 0);
7693 memset(&dm_messages, 0, sizeof(dm_messages));
7694 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7695 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7696 test_dm_messages(0, 0, 0);
7698 memset(&dm_messages, 0, sizeof(dm_messages));
7699 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7700 ok(0 == r, "expected 0, got %d\n", r);
7701 test_dm_messages(0, 1, 0);
7703 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7704 ok(2 == lcount, "expected 2, got %d\n", lcount);
7706 memset(&dm_messages, 0, sizeof(dm_messages));
7707 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7708 ok(0 == r, "expected 0, got %d\n", r);
7709 test_dm_messages(0, 0, 0);
7711 memset(&dm_messages, 0, sizeof(dm_messages));
7712 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7713 ok(0 == r, "expected 0, got %d\n", r);
7714 test_dm_messages(0, 0, 1);
7716 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7717 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7718 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7720 memset(&dm_messages, 0, sizeof(dm_messages));
7721 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7722 ok(0 == r, "expected 0, got %d\n", r);
7723 test_dm_messages(0, 1, 1);
7725 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7726 ok(2 == lcount, "expected 2, got %d\n", lcount);
7728 DestroyWindow(hwButton);
7729 DestroyWindow(hwRichEdit);
7731 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7733 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7735 memset(&dm_messages, 0, sizeof(dm_messages));
7736 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7737 ok(0 == r, "expected 0, got %d\n", r);
7738 test_dm_messages(0, 0, 0);
7740 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7741 ok(2 == lcount, "expected 2, got %d\n", lcount);
7743 memset(&dm_messages, 0, sizeof(dm_messages));
7744 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7745 ok(0 == r, "expected 0, got %d\n", r);
7746 test_dm_messages(0, 0, 0);
7748 memset(&dm_messages, 0, sizeof(dm_messages));
7749 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7750 ok(0 == r, "expected 0, got %d\n", r);
7751 test_dm_messages(0, 0, 0);
7753 memset(&dm_messages, 0, sizeof(dm_messages));
7754 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7755 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7756 test_dm_messages(0, 0, 0);
7758 memset(&dm_messages, 0, sizeof(dm_messages));
7759 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7760 ok(0 == r, "expected 0, got %d\n", r);
7761 test_dm_messages(0, 0, 0);
7763 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7764 ok(3 == lcount, "expected 3, got %d\n", lcount);
7766 memset(&dm_messages, 0, sizeof(dm_messages));
7767 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7768 ok(0 == r, "expected 0, got %d\n", r);
7769 test_dm_messages(0, 0, 0);
7771 memset(&dm_messages, 0, sizeof(dm_messages));
7772 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7773 ok(0 == r, "expected 0, got %d\n", r);
7774 test_dm_messages(0, 0, 1);
7776 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7777 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7778 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7780 memset(&dm_messages, 0, sizeof(dm_messages));
7781 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7782 ok(0 == r, "expected 0, got %d\n", r);
7783 test_dm_messages(0, 0, 0);
7785 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7786 ok(4 == lcount, "expected 4, got %d\n", lcount);
7788 DestroyWindow(hwButton);
7789 DestroyWindow(hwRichEdit);
7791 /* Check messages from richedit(0) */
7793 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7795 memset(&dm_messages, 0, sizeof(dm_messages));
7796 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7797 ok(0 == r, "expected 0, got %d\n", r);
7798 test_dm_messages(0, 0, 0);
7800 memset(&dm_messages, 0, sizeof(dm_messages));
7801 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7802 ok(0 == r, "expected 0, got %d\n", r);
7803 test_dm_messages(0, 0, 0);
7805 memset(&dm_messages, 0, sizeof(dm_messages));
7806 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7807 ok(0 == r, "expected 0, got %d\n", r);
7808 test_dm_messages(0, 0, 0);
7810 memset(&dm_messages, 0, sizeof(dm_messages));
7811 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7812 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7813 test_dm_messages(0, 0, 0);
7815 memset(&dm_messages, 0, sizeof(dm_messages));
7816 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7817 ok(0 == r, "expected 0, got %d\n", r);
7818 test_dm_messages(0, 1, 0);
7820 memset(&dm_messages, 0, sizeof(dm_messages));
7821 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7822 ok(0 == r, "expected 0, got %d\n", r);
7823 test_dm_messages(0, 0, 0);
7825 memset(&dm_messages, 0, sizeof(dm_messages));
7826 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7827 ok(0 == r, "expected 0, got %d\n", r);
7828 test_dm_messages(0, 0, 1);
7830 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7831 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7832 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7834 memset(&dm_messages, 0, sizeof(dm_messages));
7835 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7836 ok(0 == r, "expected 0, got %d\n", r);
7837 test_dm_messages(0, 1, 1);
7839 DestroyWindow(hwRichEdit);
7841 /* Check messages from richedit(ES_WANTRETURN) */
7843 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7845 memset(&dm_messages, 0, sizeof(dm_messages));
7846 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7847 ok(0 == r, "expected 0, got %d\n", r);
7848 test_dm_messages(0, 0, 0);
7850 memset(&dm_messages, 0, sizeof(dm_messages));
7851 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7852 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7853 test_dm_messages(0, 0, 0);
7855 memset(&dm_messages, 0, sizeof(dm_messages));
7856 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7857 ok(0 == r, "expected 0, got %d\n", r);
7858 test_dm_messages(0, 0, 0);
7860 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7861 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7862 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7864 memset(&dm_messages, 0, sizeof(dm_messages));
7865 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7866 ok(0 == r, "expected 0, got %d\n", r);
7867 test_dm_messages(0, 0, 0);
7869 DestroyWindow(hwRichEdit);
7870 DestroyWindow(hwParent);
7873 static void test_EM_FINDWORDBREAK_W(void)
7875 static const struct {
7876 WCHAR c;
7877 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7878 } delimiter_tests[] = {
7879 {0x0a, FALSE}, /* newline */
7880 {0x0b, FALSE}, /* vertical tab */
7881 {0x0c, FALSE}, /* form feed */
7882 {0x0d, FALSE}, /* carriage return */
7883 {0x20, TRUE}, /* space */
7884 {0x61, FALSE}, /* capital letter a */
7885 {0xa0, FALSE}, /* no-break space */
7886 {0x2000, FALSE}, /* en quad */
7887 {0x3000, FALSE}, /* Ideographic space */
7888 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7889 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7890 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7891 {0xac00, FALSE}, /* Hangul character GA*/
7892 {0xd7af, FALSE}, /* End of Hangul character chart */
7893 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7894 {0xff20, FALSE}, /* fullwidth commercial @ */
7895 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7897 int i;
7898 HWND hwndRichEdit = new_richeditW(NULL);
7899 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7900 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7902 WCHAR wbuf[2];
7903 int result;
7905 wbuf[0] = delimiter_tests[i].c;
7906 wbuf[1] = 0;
7907 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7908 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7909 todo_wine_if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7910 ok(result == delimiter_tests[i].isdelimiter,
7911 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7912 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7914 DestroyWindow(hwndRichEdit);
7917 static void test_EM_FINDWORDBREAK_A(void)
7919 static const struct {
7920 WCHAR c;
7921 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7922 } delimiter_tests[] = {
7923 {0x0a, FALSE}, /* newline */
7924 {0x0b, FALSE}, /* vertical tab */
7925 {0x0c, FALSE}, /* form feed */
7926 {0x0d, FALSE}, /* carriage return */
7927 {0x20, TRUE}, /* space */
7928 {0x61, FALSE}, /* capital letter a */
7930 int i;
7931 HWND hwndRichEdit = new_richedit(NULL);
7933 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7934 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7936 int result;
7937 char buf[2];
7938 buf[0] = delimiter_tests[i].c;
7939 buf[1] = 0;
7940 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7941 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7942 todo_wine_if (buf[0] == 0x20)
7943 ok(result == delimiter_tests[i].isdelimiter,
7944 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7945 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7947 DestroyWindow(hwndRichEdit);
7951 * This test attempts to show the effect of enter on a richedit
7952 * control v1.0 inserts CRLF whereas for higher versions it only
7953 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7954 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7955 * does for higher. The same test is cloned in riched32 and riched20.
7957 static void test_enter(void)
7959 static const struct {
7960 const char *initialtext;
7961 const int cursor;
7962 const char *expectedwmtext;
7963 const char *expectedemtext;
7964 const char *expectedemtextcrlf;
7965 } testenteritems[] = {
7966 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7967 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7968 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7969 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7970 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7973 char expectedbuf[1024];
7974 char resultbuf[1024];
7975 HWND hwndRichEdit = new_richedit(NULL);
7976 UINT i,j;
7978 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7980 char buf[1024] = {0};
7981 LRESULT result;
7982 GETTEXTEX getText;
7983 const char *expected;
7985 /* Set the text to the initial text */
7986 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7987 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7989 /* Send Enter */
7990 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7991 simulate_typing_characters(hwndRichEdit, "\r");
7993 /* 1. Retrieve with WM_GETTEXT */
7994 buf[0] = 0x00;
7995 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7996 expected = testenteritems[i].expectedwmtext;
7998 resultbuf[0]=0x00;
7999 for (j = 0; j < (UINT)result; j++)
8000 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8001 expectedbuf[0] = '\0';
8002 for (j = 0; j < strlen(expected); j++)
8003 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8005 result = strcmp(expected, buf);
8006 ok (result == 0,
8007 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
8008 i, resultbuf, expectedbuf);
8010 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
8011 getText.cb = sizeof(buf);
8012 getText.flags = GT_DEFAULT;
8013 getText.codepage = CP_ACP;
8014 getText.lpDefaultChar = NULL;
8015 getText.lpUsedDefChar = NULL;
8016 buf[0] = 0x00;
8017 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8018 expected = testenteritems[i].expectedemtext;
8020 resultbuf[0]=0x00;
8021 for (j = 0; j < (UINT)result; j++)
8022 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8023 expectedbuf[0] = '\0';
8024 for (j = 0; j < strlen(expected); j++)
8025 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8027 result = strcmp(expected, buf);
8028 ok (result == 0,
8029 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
8030 i, resultbuf, expectedbuf);
8032 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
8033 getText.cb = sizeof(buf);
8034 getText.flags = GT_USECRLF;
8035 getText.codepage = CP_ACP;
8036 getText.lpDefaultChar = NULL;
8037 getText.lpUsedDefChar = NULL;
8038 buf[0] = 0x00;
8039 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
8040 expected = testenteritems[i].expectedemtextcrlf;
8042 resultbuf[0]=0x00;
8043 for (j = 0; j < (UINT)result; j++)
8044 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
8045 expectedbuf[0] = '\0';
8046 for (j = 0; j < strlen(expected); j++)
8047 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
8049 result = strcmp(expected, buf);
8050 ok (result == 0,
8051 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
8052 i, resultbuf, expectedbuf);
8055 DestroyWindow(hwndRichEdit);
8058 static void test_WM_CREATE(void)
8060 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
8061 static const char title[] = "line1\nline2";
8063 HWND rich_edit;
8064 LRESULT res;
8065 char buf[64];
8066 int len;
8068 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
8069 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8070 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
8072 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8073 ok(len == 5, "GetWindowText returned %d\n", len);
8074 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
8076 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8077 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8079 DestroyWindow(rich_edit);
8081 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
8082 0, 0, 200, 80, NULL, NULL, NULL, NULL);
8083 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
8085 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
8086 ok(len == 12, "GetWindowText returned %d\n", len);
8087 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
8089 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
8090 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
8092 DestroyWindow(rich_edit);
8095 /*******************************************************************
8096 * Test that after deleting all of the text, the first paragraph
8097 * format reverts to the default.
8099 static void test_reset_default_para_fmt( void )
8101 HWND richedit = new_richeditW( NULL );
8102 PARAFORMAT2 fmt;
8103 WORD def_align, new_align;
8105 memset( &fmt, 0, sizeof(fmt) );
8106 fmt.cbSize = sizeof(PARAFORMAT2);
8107 fmt.dwMask = -1;
8108 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8109 def_align = fmt.wAlignment;
8110 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8112 simulate_typing_characters( richedit, "123" );
8114 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8115 fmt.dwMask = PFM_ALIGNMENT;
8116 fmt.wAlignment = new_align;
8117 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
8119 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8120 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
8122 SendMessageA( richedit, EM_SETSEL, 0, -1 );
8123 SendMessageA( richedit, WM_CUT, 0, 0 );
8125 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8126 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
8128 DestroyWindow( richedit );
8131 static void test_EM_SETREADONLY(void)
8133 HWND richedit = new_richeditW(NULL);
8134 DWORD dwStyle;
8135 LRESULT res;
8137 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
8138 ok(res == 1, "EM_SETREADONLY\n");
8139 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8140 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
8142 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
8143 ok(res == 1, "EM_SETREADONLY\n");
8144 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
8145 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
8147 DestroyWindow(richedit);
8150 static inline LONG twips2points(LONG value)
8152 return value / 20;
8155 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
8156 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
8157 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
8158 LRESULT expected_res, BOOL expected_undo)
8160 CHARFORMAT2A cf;
8161 LRESULT res;
8162 BOOL isundo;
8164 cf.cbSize = sizeof(cf);
8165 cf.dwMask = CFM_SIZE;
8167 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
8168 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8169 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8170 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8171 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8172 twips2points(cf.yHeight), expected_size);
8173 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8174 isundo, expected_undo);
8177 static void test_EM_SETFONTSIZE(void)
8179 HWND richedit = new_richedit(NULL);
8180 CHAR text[] = "wine";
8181 CHARFORMAT2A tmp_cf;
8182 LONG default_size;
8184 tmp_cf.cbSize = sizeof(tmp_cf);
8185 tmp_cf.dwMask = CFM_SIZE;
8186 tmp_cf.yHeight = 9 * 20.0;
8187 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8189 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8191 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8192 /* without selection */
8193 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8194 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8195 default_size = twips2points(tmp_cf.yHeight);
8196 ok(default_size == 9, "Default font size should not be changed.\n");
8197 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8199 SendMessageA(richedit, EM_SETSEL, 0, 2);
8201 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8203 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8204 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8205 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8207 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8208 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8209 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8210 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8211 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8212 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8213 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8214 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8215 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8216 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8217 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8218 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8219 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8220 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8221 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8222 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8223 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8224 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8225 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8226 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8227 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8228 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8229 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8230 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8231 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8232 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8233 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8234 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8236 /* return FALSE when richedit is TM_PLAINTEXT mode */
8237 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8238 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8239 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8241 DestroyWindow(richedit);
8244 static void test_alignment_style(void)
8246 HWND richedit = NULL;
8247 PARAFORMAT2 pf;
8248 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8249 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8250 ES_LEFT | ES_RIGHT | ES_CENTER};
8251 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8252 PFA_RIGHT, PFA_CENTER};
8253 const char * streamtext =
8254 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8255 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8256 "}\r\n";
8257 EDITSTREAM es;
8258 int i;
8260 for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8262 DWORD dwStyle, new_align;
8264 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8265 memset(&pf, 0, sizeof(pf));
8266 pf.cbSize = sizeof(PARAFORMAT2);
8267 pf.dwMask = -1;
8269 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8270 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8271 i, pf.wAlignment, align_mask[i]);
8272 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8273 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8274 "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8277 /* Based on test_reset_default_para_fmt() */
8278 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8279 simulate_typing_characters(richedit, "123");
8281 SendMessageW(richedit, EM_SETSEL, 0, -1);
8282 pf.dwMask = PFM_ALIGNMENT;
8283 pf.wAlignment = new_align;
8284 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8286 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8287 ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8289 SendMessageW(richedit, EM_SETSEL, 0, -1);
8290 SendMessageW(richedit, WM_CUT, 0, 0);
8292 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8293 ok(pf.wAlignment == align_mask[i], "got %d exppect %d\n", pf.wAlignment, align_mask[i]);
8295 DestroyWindow(richedit);
8298 /* test with EM_STREAMIN */
8299 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8300 simulate_typing_characters(richedit, "abc");
8301 es.dwCookie = (DWORD_PTR)&streamtext;
8302 es.dwError = 0;
8303 es.pfnCallback = test_EM_STREAMIN_esCallback;
8304 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8305 SendMessageW(richedit, EM_SETSEL, 0, -1);
8306 memset(&pf, 0, sizeof(pf));
8307 pf.cbSize = sizeof(PARAFORMAT2);
8308 pf.dwMask = -1;
8309 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8310 ok(pf.wAlignment == PFA_LEFT, "got %d expected PFA_LEFT\n", pf.wAlignment);
8311 DestroyWindow(richedit);
8314 static void test_WM_GETTEXTLENGTH(void)
8316 HWND hwndRichEdit = new_richedit(NULL);
8317 static const char text1[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee";
8318 static const char text2[] = "aaa\r\nbbb\r\nccc\r\nddd\r\neee\r\n";
8319 static const char text3[] = "abcdef\x8e\xf0";
8320 int result;
8322 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
8323 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8324 ok(result == lstrlenA(text1), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8325 result, lstrlenA(text1));
8327 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
8328 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8329 ok(result == lstrlenA(text2), "WM_GETTEXTLENGTH returned %d, expected %d\n",
8330 result, lstrlenA(text2));
8332 /* Test with multibyte character */
8333 if (!is_lang_japanese)
8334 skip("Skip multibyte character tests on non-Japanese platform\n");
8335 else
8337 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text3);
8338 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
8339 todo_wine ok(result == 8, "WM_GETTEXTLENGTH returned %d, expected 8\n", result);
8342 DestroyWindow(hwndRichEdit);
8345 static void test_rtf_specials(void)
8347 const char *specials = "{\\rtf1\\emspace\\enspace\\bullet\\lquote"
8348 "\\rquote\\ldblquote\\rdblquote\\ltrmark\\rtlmark\\zwj\\zwnj}";
8349 const WCHAR expect_specials[] = {' ',' ',0x2022,0x2018,0x2019,0x201c,
8350 0x201d,0x200e,0x200f,0x200d,0x200c};
8351 const char *pard = "{\\rtf1 ABC\\rtlpar\\par DEF\\par HIJ\\pard\\par}";
8352 HWND edit = new_richeditW( NULL );
8353 EDITSTREAM es;
8354 WCHAR buf[80];
8355 LRESULT result;
8356 PARAFORMAT2 fmt;
8358 es.dwCookie = (DWORD_PTR)&specials;
8359 es.dwError = 0;
8360 es.pfnCallback = test_EM_STREAMIN_esCallback;
8361 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8362 ok( result == 11, "got %ld\n", result );
8364 result = SendMessageW( edit, WM_GETTEXT, sizeof(buf)/sizeof(buf[0]), (LPARAM)buf );
8365 ok( result == sizeof(expect_specials)/sizeof(expect_specials[0]), "got %ld\n", result );
8366 ok( !memcmp( buf, expect_specials, sizeof(expect_specials) ), "got %s\n", wine_dbgstr_w(buf) );
8368 /* Show that \rtlpar propagates to the second paragraph and is
8369 reset by \pard in the third. */
8370 es.dwCookie = (DWORD_PTR)&pard;
8371 result = SendMessageA( edit, EM_STREAMIN, SF_RTF, (LPARAM)&es );
8372 ok( result == 11, "got %ld\n", result );
8374 fmt.cbSize = sizeof(fmt);
8375 SendMessageW( edit, EM_SETSEL, 1, 1 );
8376 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8377 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8378 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8379 SendMessageW( edit, EM_SETSEL, 5, 5 );
8380 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8381 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8382 ok( fmt.wEffects & PFE_RTLPARA, "rtl para not set\n" );
8383 SendMessageW( edit, EM_SETSEL, 9, 9 );
8384 SendMessageW( edit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
8385 ok( fmt.dwMask & PFM_RTLPARA, "rtl para mask not set\n" );
8386 ok( !(fmt.wEffects & PFE_RTLPARA), "rtl para set\n" );
8388 DestroyWindow( edit );
8391 static void test_background(void)
8393 HWND hwndRichEdit = new_richedit(NULL);
8395 /* set the background color to black */
8396 ValidateRect(hwndRichEdit, NULL);
8397 SendMessageA(hwndRichEdit, EM_SETBKGNDCOLOR, FALSE, RGB(0, 0, 0));
8398 ok(GetUpdateRect(hwndRichEdit, NULL, FALSE), "Update rectangle is empty!\n");
8400 DestroyWindow(hwndRichEdit);
8403 START_TEST( editor )
8405 BOOL ret;
8406 /* Must explicitly LoadLibrary(). The test has no references to functions in
8407 * RICHED20.DLL, so the linker doesn't actually link to it. */
8408 hmoduleRichEdit = LoadLibraryA("riched20.dll");
8409 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8410 is_lang_japanese = (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_JAPANESE);
8412 test_WM_CHAR();
8413 test_EM_FINDTEXT(FALSE);
8414 test_EM_FINDTEXT(TRUE);
8415 test_EM_GETLINE();
8416 test_EM_POSFROMCHAR();
8417 test_EM_SCROLLCARET();
8418 test_EM_SCROLL();
8419 test_scrollbar_visibility();
8420 test_WM_SETTEXT();
8421 test_EM_LINELENGTH();
8422 test_EM_SETCHARFORMAT();
8423 test_EM_SETTEXTMODE();
8424 test_TM_PLAINTEXT();
8425 test_EM_SETOPTIONS();
8426 test_WM_GETTEXT();
8427 test_EM_GETTEXTRANGE();
8428 test_EM_GETSELTEXT();
8429 test_EM_SETUNDOLIMIT();
8430 test_ES_PASSWORD();
8431 test_EM_SETTEXTEX();
8432 test_EM_LIMITTEXT();
8433 test_EM_EXLIMITTEXT();
8434 test_EM_GETLIMITTEXT();
8435 test_WM_SETFONT();
8436 test_EM_GETMODIFY();
8437 test_EM_SETSEL();
8438 test_EM_EXSETSEL();
8439 test_WM_PASTE();
8440 test_EM_STREAMIN();
8441 test_EM_STREAMOUT();
8442 test_EM_STREAMOUT_FONTTBL();
8443 test_EM_StreamIn_Undo();
8444 test_EM_FORMATRANGE();
8445 test_unicode_conversions();
8446 test_EM_GETTEXTLENGTHEX();
8447 test_WM_GETTEXTLENGTH();
8448 test_EM_REPLACESEL(1);
8449 test_EM_REPLACESEL(0);
8450 test_WM_NOTIFY();
8451 test_EN_LINK();
8452 test_EM_AUTOURLDETECT();
8453 test_eventMask();
8454 test_undo_coalescing();
8455 test_word_movement();
8456 test_EM_CHARFROMPOS();
8457 test_SETPARAFORMAT();
8458 test_word_wrap();
8459 test_autoscroll();
8460 test_format_rect();
8461 test_WM_GETDLGCODE();
8462 test_zoom();
8463 test_dialogmode();
8464 test_EM_FINDWORDBREAK_W();
8465 test_EM_FINDWORDBREAK_A();
8466 test_enter();
8467 test_WM_CREATE();
8468 test_reset_default_para_fmt();
8469 test_EM_SETREADONLY();
8470 test_EM_SETFONTSIZE();
8471 test_alignment_style();
8472 test_rtf_specials();
8473 test_background();
8475 /* Set the environment variable WINETEST_RICHED20 to keep windows
8476 * responsive and open for 30 seconds. This is useful for debugging.
8478 if (getenv( "WINETEST_RICHED20" )) {
8479 keep_responsive(30);
8482 OleFlushClipboard();
8483 ret = FreeLibrary(hmoduleRichEdit);
8484 ok(ret, "error: %d\n", (int) GetLastError());