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
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
51 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
58 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
60 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62 hmoduleRichEdit, NULL);
63 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 /* Keeps the window reponsive for the deley_time in seconds.
76 * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
82 /* The message pump uses PeekMessage() to empty the queue and then
83 * sleeps for 50ms before retrying the queue. */
84 end = time(NULL) + delay_time;
85 while (time(NULL) < end) {
86 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessage(&msg);
95 static void processPendingMessages(void)
98 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
99 TranslateMessage(&msg);
100 DispatchMessage(&msg);
104 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
106 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
107 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
109 keybd_event(mod_vk, mod_scan_code, 0, 0);
110 keybd_event(vk, scan_code, 0, 0);
111 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
112 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
113 processPendingMessages();
116 static void simulate_typing_characters(HWND hwnd, const char* szChars)
120 while (*szChars != '\0') {
121 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
122 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
123 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
124 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
129 static BOOL hold_key(int vk)
134 result = GetKeyboardState(key_state);
135 ok(result, "GetKeyboardState failed.\n");
136 if (!result) return FALSE;
137 key_state[vk] |= 0x80;
138 result = SetKeyboardState(key_state);
139 ok(result, "SetKeyboardState failed.\n");
143 static BOOL release_key(int vk)
148 result = GetKeyboardState(key_state);
149 ok(result, "GetKeyboardState failed.\n");
150 if (!result) return FALSE;
151 key_state[vk] &= ~0x80;
152 result = SetKeyboardState(key_state);
153 ok(result, "SetKeyboardState failed.\n");
157 static const char haystack[] = "WINEWine wineWine wine WineWine";
169 static struct find_s find_tests[] = {
170 /* Find in empty text */
171 {0, -1, "foo", FR_DOWN, -1},
172 {0, -1, "foo", 0, -1},
173 {0, -1, "", FR_DOWN, -1},
174 {20, 5, "foo", FR_DOWN, -1},
175 {5, 20, "foo", FR_DOWN, -1}
178 static struct find_s find_tests2[] = {
180 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
181 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
183 /* Subsequent finds */
184 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
185 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
186 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
187 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
190 {19, 20, "Wine", FR_MATCHCASE, 13},
191 {10, 20, "Wine", FR_MATCHCASE, 4},
192 {20, 10, "Wine", FR_MATCHCASE, 13},
194 /* Case-insensitive */
195 {1, 31, "wInE", FR_DOWN, 4},
196 {1, 31, "Wine", FR_DOWN, 4},
198 /* High-to-low ranges */
199 {20, 5, "Wine", FR_DOWN, -1},
200 {2, 1, "Wine", FR_DOWN, -1},
201 {30, 29, "Wine", FR_DOWN, -1},
202 {20, 5, "Wine", 0, 13},
205 {5, 10, "", FR_DOWN, -1},
206 {10, 5, "", FR_DOWN, -1},
207 {0, -1, "", FR_DOWN, -1},
210 /* Whole-word search */
211 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
212 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
213 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
214 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
215 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
216 {11, -1, "winewine", FR_WHOLEWORD, 0},
217 {31, -1, "winewine", FR_WHOLEWORD, 23},
220 {5, 200, "XXX", FR_DOWN, -1},
221 {-20, 20, "Wine", FR_DOWN, -1},
222 {-20, 20, "Wine", FR_DOWN, -1},
223 {-15, -20, "Wine", FR_DOWN, -1},
224 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
226 /* Check the case noted in bug 4479 where matches at end aren't recognized */
227 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
228 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
229 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
230 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
231 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
233 /* The backwards case of bug 4479; bounds look right
234 * Fails because backward find is wrong */
235 {19, 20, "WINE", FR_MATCHCASE, 0},
236 {0, 20, "WINE", FR_MATCHCASE, -1},
238 {0, -1, "wineWine wine", 0, -1},
241 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
244 memset(&ft, 0, sizeof(ft));
245 ft.chrg.cpMin = f->start;
246 ft.chrg.cpMax = f->end;
247 ft.lpstrText = f->needle;
248 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
249 ok(findloc == f->expected_loc,
250 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
251 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
254 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
258 int expected_end_loc;
260 memset(&ft, 0, sizeof(ft));
261 ft.chrg.cpMin = f->start;
262 ft.chrg.cpMax = f->end;
263 ft.lpstrText = f->needle;
264 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
265 ok(findloc == f->expected_loc,
266 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
267 name, id, f->needle, f->start, f->end, f->flags, findloc);
268 ok(ft.chrgText.cpMin == f->expected_loc,
269 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
270 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
271 expected_end_loc = ((f->expected_loc == -1) ? -1
272 : f->expected_loc + strlen(f->needle));
273 ok(ft.chrgText.cpMax == expected_end_loc,
274 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
275 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
278 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
283 for (i = 0; i < num_tests; i++) {
284 check_EM_FINDTEXT(hwnd, name, &find[i], i);
285 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
289 static void test_EM_FINDTEXT(void)
291 HWND hwndRichEdit = new_richedit(NULL);
294 /* Empty rich edit control */
295 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
296 sizeof(find_tests)/sizeof(struct find_s));
298 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
301 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
302 sizeof(find_tests2)/sizeof(struct find_s));
304 /* Setting a format on an arbitrary range should have no effect in search
305 results. This tests correct offset reporting across runs. */
306 cf2.cbSize = sizeof(CHARFORMAT2);
307 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
308 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
309 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
310 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
311 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
313 /* Haystack text, again */
314 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
315 sizeof(find_tests2)/sizeof(struct find_s));
317 /* Yet another range */
318 cf2.dwMask = CFM_BOLD | cf2.dwMask;
319 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
320 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
321 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
323 /* Haystack text, again */
324 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
325 sizeof(find_tests2)/sizeof(struct find_s));
327 DestroyWindow(hwndRichEdit);
330 static const struct getline_s {
335 {0, 10, "foo bar\r"},
340 /* Buffer smaller than line length */
346 static void test_EM_GETLINE(void)
349 HWND hwndRichEdit = new_richedit(NULL);
350 static const int nBuf = 1024;
351 char dest[1024], origdest[1024];
352 const char text[] = "foo bar\n"
356 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
358 memset(origdest, 0xBB, nBuf);
359 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
362 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
363 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
364 memset(dest, 0xBB, nBuf);
365 *(WORD *) dest = gl[i].buffer_len;
367 /* EM_GETLINE appends a "\r\0" to the end of the line
368 * nCopied counts up to and including the '\r' */
369 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
370 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
372 /* two special cases since a parameter is passed via dest */
373 if (gl[i].buffer_len == 0)
374 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
376 else if (gl[i].buffer_len == 1)
377 ok(dest[0] == gl[i].text[0] && !dest[1] &&
378 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
381 /* Prepare hex strings of buffers to dump on failure. */
382 char expectedbuf[1024];
383 char resultbuf[1024];
386 for (j = 0; j < 32; j++)
387 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
388 expectedbuf[0] = '\0';
389 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
390 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
391 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
392 sprintf(expectedbuf+strlen(expectedbuf), "??");
393 for (; j < 32; j++) /* Bytes after declared buffer size */
394 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
396 /* Test the part of the buffer that is expected to be written according
397 * to the MSDN documentation fo EM_GETLINE, which does not state that
398 * a NULL terminating character will be added unless no text is copied.
400 * Windows NT does not append a NULL terminating character, but
401 * Windows 2000 and up do append a NULL terminating character if there
402 * is space in the buffer. The test will ignore this difference. */
403 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
404 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
405 i, expected_bytes_written, expectedbuf, resultbuf);
406 /* Test the part of the buffer after the declared length to make sure
407 * there are no buffer overruns. */
408 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
409 nBuf - gl[i].buffer_len),
410 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
411 i, expected_bytes_written, expectedbuf, resultbuf);
415 DestroyWindow(hwndRichEdit);
418 static void test_EM_LINELENGTH(void)
420 HWND hwndRichEdit = new_richedit(NULL);
426 int offset_test[10][2] = {
441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
443 for (i = 0; i < 10; i++) {
444 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
445 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
446 offset_test[i][0], result, offset_test[i][1]);
449 DestroyWindow(hwndRichEdit);
452 static int get_scroll_pos_y(HWND hwnd)
455 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
456 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
460 static void move_cursor(HWND hwnd, LONG charindex)
463 cr.cpMax = charindex;
464 cr.cpMin = charindex;
465 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
468 static void line_scroll(HWND hwnd, int amount)
470 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
473 static void test_EM_SCROLLCARET(void)
476 const char text[] = "aa\n"
477 "this is a long line of text that should be longer than the "
485 /* The richedit window height needs to be large enough vertically to fit in
486 * more than two lines of text, so the new_richedit function can't be used
487 * since a height of 60 was not large enough on some systems.
489 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
490 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
491 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
492 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
494 /* Can't verify this */
495 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
499 /* Caret above visible window */
500 line_scroll(hwndRichEdit, 3);
501 prevY = get_scroll_pos_y(hwndRichEdit);
502 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
503 curY = get_scroll_pos_y(hwndRichEdit);
504 ok(prevY != curY, "%d == %d\n", prevY, curY);
506 /* Caret below visible window */
507 move_cursor(hwndRichEdit, sizeof(text) - 1);
508 line_scroll(hwndRichEdit, -3);
509 prevY = get_scroll_pos_y(hwndRichEdit);
510 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
511 curY = get_scroll_pos_y(hwndRichEdit);
512 ok(prevY != curY, "%d == %d\n", prevY, curY);
514 /* Caret in visible window */
515 move_cursor(hwndRichEdit, sizeof(text) - 2);
516 prevY = get_scroll_pos_y(hwndRichEdit);
517 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
518 curY = get_scroll_pos_y(hwndRichEdit);
519 ok(prevY == curY, "%d != %d\n", prevY, curY);
521 /* Caret still in visible window */
522 line_scroll(hwndRichEdit, -1);
523 prevY = get_scroll_pos_y(hwndRichEdit);
524 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
525 curY = get_scroll_pos_y(hwndRichEdit);
526 ok(prevY == curY, "%d != %d\n", prevY, curY);
528 DestroyWindow(hwndRichEdit);
531 static void test_EM_POSFROMCHAR(void)
533 HWND hwndRichEdit = new_richedit(NULL);
536 unsigned int height = 0;
541 static const char text[] = "aa\n"
542 "this is a long line of text that should be longer than the "
551 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
552 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
553 (sig.lsUsb[3] & 0x08000000) != 0);
555 /* Fill the control to lines to ensure that most of them are offscreen */
556 for (i = 0; i < 50; i++)
558 /* Do not modify the string; it is exactly 16 characters long. */
559 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
560 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
564 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
565 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
566 Richedit 3.0 accepts either of the above API conventions.
569 /* Testing Richedit 2.0 API format */
571 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
572 Since all lines are identical and drawn with the same font,
573 they should have the same height... right?
575 for (i = 0; i < 50; i++)
577 /* All the lines are 16 characters long */
578 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
581 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
582 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
583 xpos = LOWORD(result);
587 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
589 height = HIWORD(result);
593 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
594 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
598 /* Testing position at end of text */
599 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
600 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
601 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
603 /* Testing position way past end of text */
604 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
605 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
606 expected = (rtl ? 8 : 1);
607 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
609 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
610 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
611 for (i = 0; i < 50; i++)
613 /* All the lines are 16 characters long */
614 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
615 ok((signed short)(HIWORD(result)) == (i - 1) * height,
616 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
617 (signed short)(HIWORD(result)), (i - 1) * height);
618 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
621 /* Testing position at end of text */
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
623 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
624 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
626 /* Testing position way past end of text */
627 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
628 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
629 expected = (rtl ? 8 : 1);
630 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
632 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
633 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
634 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
636 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
637 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
638 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
639 xpos = LOWORD(result);
641 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
642 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
643 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
644 ok((signed short)(LOWORD(result)) < xpos,
645 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
646 (signed short)(LOWORD(result)), xpos);
647 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
649 /* Test around end of text that doesn't end in a newline. */
650 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
651 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
652 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
653 ok(pt.x > 1, "pt.x = %d\n", pt.x);
655 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
656 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
657 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
658 xpos = (rtl ? pt.x + 7 : pt.x);
659 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
660 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
661 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
663 /* Try a negative position. */
664 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
665 ok(pt.x == 1, "pt.x = %d\n", pt.x);
667 DestroyWindow(hwndRichEdit);
670 static void test_EM_SETCHARFORMAT(void)
672 HWND hwndRichEdit = new_richedit(NULL);
675 int tested_effects[] = {
691 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
692 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
693 (sig.lsUsb[3] & 0x08000000) != 0);
695 /* Invalid flags, CHARFORMAT2 structure blanked out */
696 memset(&cf2, 0, sizeof(cf2));
697 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
699 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
701 /* A valid flag, CHARFORMAT2 structure blanked out */
702 memset(&cf2, 0, sizeof(cf2));
703 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
705 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
707 /* A valid flag, CHARFORMAT2 structure blanked out */
708 memset(&cf2, 0, sizeof(cf2));
709 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
711 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
713 /* A valid flag, CHARFORMAT2 structure blanked out */
714 memset(&cf2, 0, sizeof(cf2));
715 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
717 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
719 /* A valid flag, CHARFORMAT2 structure blanked out */
720 memset(&cf2, 0, sizeof(cf2));
721 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
723 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
725 /* Invalid flags, CHARFORMAT2 structure minimally filled */
726 memset(&cf2, 0, sizeof(cf2));
727 cf2.cbSize = sizeof(CHARFORMAT2);
728 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
730 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
731 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
732 ok(rc == FALSE, "Should not be able to undo here.\n");
733 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
735 /* A valid flag, CHARFORMAT2 structure minimally filled */
736 memset(&cf2, 0, sizeof(cf2));
737 cf2.cbSize = sizeof(CHARFORMAT2);
738 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
740 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
741 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
742 ok(rc == FALSE, "Should not be able to undo here.\n");
743 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
745 /* A valid flag, CHARFORMAT2 structure minimally filled */
746 memset(&cf2, 0, sizeof(cf2));
747 cf2.cbSize = sizeof(CHARFORMAT2);
748 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
750 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
751 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
752 ok(rc == FALSE, "Should not be able to undo here.\n");
753 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
755 /* A valid flag, CHARFORMAT2 structure minimally filled */
756 memset(&cf2, 0, sizeof(cf2));
757 cf2.cbSize = sizeof(CHARFORMAT2);
758 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
760 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
761 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
762 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
763 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
765 /* A valid flag, CHARFORMAT2 structure minimally filled */
766 memset(&cf2, 0, sizeof(cf2));
767 cf2.cbSize = sizeof(CHARFORMAT2);
768 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
770 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
772 ok(rc == TRUE, "Should not be able to undo here.\n");
773 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
775 cf2.cbSize = sizeof(CHARFORMAT2);
776 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
779 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
780 cf2.cbSize = sizeof(CHARFORMAT2);
781 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
783 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
784 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
786 /* wParam==0 is default char format, does not set modify */
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == 0, "Text marked as modified, expected not modified!\n");
790 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
794 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
795 ok(rc == 0, "Text marked as modified, expected not modified!\n");
798 skip("RTL language found\n");
800 /* wParam==SCF_SELECTION sets modify if nonempty selection */
801 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
802 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803 ok(rc == 0, "Text marked as modified, expected not modified!\n");
804 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
807 ok(rc == 0, "Text marked as modified, expected not modified!\n");
809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
810 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
811 ok(rc == 0, "Text marked as modified, expected not modified!\n");
812 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
813 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
814 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
815 ok(rc == 0, "Text marked as modified, expected not modified!\n");
816 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
817 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
818 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
819 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
820 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
822 /* wParam==SCF_ALL sets modify regardless of whether text is present */
823 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
824 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
825 ok(rc == 0, "Text marked as modified, expected not modified!\n");
826 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
827 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
828 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
829 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
831 DestroyWindow(hwndRichEdit);
833 /* EM_GETCHARFORMAT tests */
834 for (i = 0; tested_effects[i]; i++)
836 hwndRichEdit = new_richedit(NULL);
837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
839 /* Need to set a TrueType font to get consistent CFM_BOLD results */
840 memset(&cf2, 0, sizeof(CHARFORMAT2));
841 cf2.cbSize = sizeof(CHARFORMAT2);
842 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
844 strcpy(cf2.szFaceName, "Courier New");
845 cf2.wWeight = FW_DONTCARE;
846 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
848 memset(&cf2, 0, sizeof(CHARFORMAT2));
849 cf2.cbSize = sizeof(CHARFORMAT2);
850 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
851 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
853 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
855 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
856 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
857 ok((cf2.dwEffects & tested_effects[i]) == 0,
858 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
860 memset(&cf2, 0, sizeof(CHARFORMAT2));
861 cf2.cbSize = sizeof(CHARFORMAT2);
862 cf2.dwMask = tested_effects[i];
863 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
864 cf2.dwMask = CFM_SUPERSCRIPT;
865 cf2.dwEffects = tested_effects[i];
866 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
867 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
869 memset(&cf2, 0, sizeof(CHARFORMAT2));
870 cf2.cbSize = sizeof(CHARFORMAT2);
871 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
872 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
873 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
874 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
876 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
877 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
878 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
879 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
884 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
885 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
886 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
888 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
889 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
890 ok((cf2.dwEffects & tested_effects[i]) == 0,
891 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
893 memset(&cf2, 0, sizeof(CHARFORMAT2));
894 cf2.cbSize = sizeof(CHARFORMAT2);
895 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
896 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
897 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
898 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
900 (cf2.dwMask & tested_effects[i]) == 0),
901 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
903 DestroyWindow(hwndRichEdit);
906 for (i = 0; tested_effects[i]; i++)
908 hwndRichEdit = new_richedit(NULL);
909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
911 /* Need to set a TrueType font to get consistent CFM_BOLD results */
912 memset(&cf2, 0, sizeof(CHARFORMAT2));
913 cf2.cbSize = sizeof(CHARFORMAT2);
914 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
916 strcpy(cf2.szFaceName, "Courier New");
917 cf2.wWeight = FW_DONTCARE;
918 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
920 memset(&cf2, 0, sizeof(CHARFORMAT2));
921 cf2.cbSize = sizeof(CHARFORMAT2);
922 cf2.dwMask = tested_effects[i];
923 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
924 cf2.dwMask = CFM_SUPERSCRIPT;
925 cf2.dwEffects = tested_effects[i];
926 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
927 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
929 memset(&cf2, 0, sizeof(CHARFORMAT2));
930 cf2.cbSize = sizeof(CHARFORMAT2);
931 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
932 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
933 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
934 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
936 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
937 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
938 ok((cf2.dwEffects & tested_effects[i]) == 0,
939 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
941 memset(&cf2, 0, sizeof(CHARFORMAT2));
942 cf2.cbSize = sizeof(CHARFORMAT2);
943 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
944 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
945 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
946 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
948 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
949 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
950 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
951 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
953 memset(&cf2, 0, sizeof(CHARFORMAT2));
954 cf2.cbSize = sizeof(CHARFORMAT2);
955 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
956 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
958 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
960 (cf2.dwMask & tested_effects[i]) == 0),
961 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
962 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
963 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
965 DestroyWindow(hwndRichEdit);
968 /* Effects applied on an empty selection should take effect when selection is
969 replaced with text */
970 hwndRichEdit = new_richedit(NULL);
971 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
972 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
974 memset(&cf2, 0, sizeof(CHARFORMAT2));
975 cf2.cbSize = sizeof(CHARFORMAT2);
976 cf2.dwMask = CFM_BOLD;
977 cf2.dwEffects = CFE_BOLD;
978 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
980 /* Selection is now nonempty */
981 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
983 memset(&cf2, 0, sizeof(CHARFORMAT2));
984 cf2.cbSize = sizeof(CHARFORMAT2);
985 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
986 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
988 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
990 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
991 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
994 /* Set two effects on an empty selection */
995 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
996 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
998 memset(&cf2, 0, sizeof(CHARFORMAT2));
999 cf2.cbSize = sizeof(CHARFORMAT2);
1000 cf2.dwMask = CFM_BOLD;
1001 cf2.dwEffects = CFE_BOLD;
1002 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1003 cf2.dwMask = CFM_ITALIC;
1004 cf2.dwEffects = CFE_ITALIC;
1005 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1007 /* Selection is now nonempty */
1008 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1010 memset(&cf2, 0, sizeof(CHARFORMAT2));
1011 cf2.cbSize = sizeof(CHARFORMAT2);
1012 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1013 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1015 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1016 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1017 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1018 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1020 /* Setting the (empty) selection to exactly the same place as before should
1021 NOT clear the insertion style! */
1022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1025 memset(&cf2, 0, sizeof(CHARFORMAT2));
1026 cf2.cbSize = sizeof(CHARFORMAT2);
1027 cf2.dwMask = CFM_BOLD;
1028 cf2.dwEffects = CFE_BOLD;
1029 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1031 /* Empty selection in same place, insert style should NOT be forgotten here. */
1032 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1034 /* Selection is now nonempty */
1035 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1037 memset(&cf2, 0, sizeof(CHARFORMAT2));
1038 cf2.cbSize = sizeof(CHARFORMAT2);
1039 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1040 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1043 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1044 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1045 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1047 /* Ditto with EM_EXSETSEL */
1048 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1049 cr.cpMin = 2; cr.cpMax = 2;
1050 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1052 memset(&cf2, 0, sizeof(CHARFORMAT2));
1053 cf2.cbSize = sizeof(CHARFORMAT2);
1054 cf2.dwMask = CFM_BOLD;
1055 cf2.dwEffects = CFE_BOLD;
1056 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1058 /* Empty selection in same place, insert style should NOT be forgotten here. */
1059 cr.cpMin = 2; cr.cpMax = 2;
1060 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1062 /* Selection is now nonempty */
1063 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1065 memset(&cf2, 0, sizeof(CHARFORMAT2));
1066 cf2.cbSize = sizeof(CHARFORMAT2);
1067 cr.cpMin = 2; cr.cpMax = 6;
1068 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1069 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1071 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1072 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1073 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1074 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1076 DestroyWindow(hwndRichEdit);
1079 static void test_EM_SETTEXTMODE(void)
1081 HWND hwndRichEdit = new_richedit(NULL);
1082 CHARFORMAT2 cf2, cf2test;
1086 /*Attempt to use mutually exclusive modes*/
1087 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1088 ok(rc == E_INVALIDARG,
1089 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1091 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1092 /*Insert text into the control*/
1094 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1096 /*Attempt to change the control to plain text mode*/
1097 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1098 ok(rc == E_UNEXPECTED,
1099 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1101 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1102 If rich text is pasted, it should have the same formatting as the rest
1103 of the text in the control*/
1105 /*Italicize the text
1106 *NOTE: If the default text was already italicized, the test will simply
1107 reverse; in other words, it will copy a regular "wine" into a plain
1108 text window that uses an italicized format*/
1109 cf2.cbSize = sizeof(CHARFORMAT2);
1110 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1113 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1114 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1116 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1117 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1119 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1120 however, SCF_ALL has been implemented*/
1121 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1122 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1124 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1125 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1127 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1129 /*Select the string "wine"*/
1132 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1134 /*Copy the italicized "wine" to the clipboard*/
1135 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1137 /*Reset the formatting to default*/
1138 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1139 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1140 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1142 /*Clear the text in the control*/
1143 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1145 /*Switch to Plain Text Mode*/
1146 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1147 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1149 /*Input "wine" again in normal format*/
1150 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1152 /*Paste the italicized "wine" into the control*/
1153 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1155 /*Select a character from the first "wine" string*/
1158 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1160 /*Retrieve its formatting*/
1161 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1164 /*Select a character from the second "wine" string*/
1167 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1169 /*Retrieve its formatting*/
1170 cf2test.cbSize = sizeof(CHARFORMAT2);
1171 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1174 /*Compare the two formattings*/
1175 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1176 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1177 cf2.dwEffects, cf2test.dwEffects);
1178 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1179 printing "wine" in the current format(normal)
1180 pasting "wine" from the clipboard(italicized)
1181 comparing the two formats(should differ)*/
1183 /*Attempt to switch with text in control*/
1184 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1185 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1188 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1190 /*Switch into Rich Text mode*/
1191 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1192 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1194 /*Print "wine" in normal formatting into the control*/
1195 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1197 /*Paste italicized "wine" into the control*/
1198 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1200 /*Select text from the first "wine" string*/
1203 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1205 /*Retrieve its formatting*/
1206 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1209 /*Select text from the second "wine" string*/
1212 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1214 /*Retrieve its formatting*/
1215 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1218 /*Test that the two formattings are not the same*/
1219 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1220 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1221 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1223 DestroyWindow(hwndRichEdit);
1226 static void test_SETPARAFORMAT(void)
1228 HWND hwndRichEdit = new_richedit(NULL);
1231 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1232 fmt.cbSize = sizeof(PARAFORMAT2);
1233 fmt.dwMask = PFM_ALIGNMENT;
1234 fmt.wAlignment = PFA_LEFT;
1236 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1237 ok(ret != 0, "expected non-zero got %d\n", ret);
1239 fmt.cbSize = sizeof(PARAFORMAT2);
1241 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1242 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1243 * between richedit different native builds of riched20.dll
1244 * used on different Windows versions. */
1245 ret &= ~PFM_TABLEROWDELIMITER;
1246 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1248 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1249 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1251 DestroyWindow(hwndRichEdit);
1254 static void test_TM_PLAINTEXT(void)
1256 /*Tests plain text properties*/
1258 HWND hwndRichEdit = new_richedit(NULL);
1259 CHARFORMAT2 cf2, cf2test;
1263 /*Switch to plain text mode*/
1265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1266 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1268 /*Fill control with text*/
1270 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1272 /*Select some text and bold it*/
1276 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1277 cf2.cbSize = sizeof(CHARFORMAT2);
1278 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1280 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1281 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1283 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1284 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1286 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1287 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1289 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1290 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1292 /*Get the formatting of those characters*/
1294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1296 /*Get the formatting of some other characters*/
1297 cf2test.cbSize = sizeof(CHARFORMAT2);
1300 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1301 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1303 /*Test that they are the same as plain text allows only one formatting*/
1305 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1306 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1307 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1309 /*Fill the control with a "wine" string, which when inserted will be bold*/
1311 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1313 /*Copy the bolded "wine" string*/
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1320 /*Swap back to rich text*/
1322 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1323 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1325 /*Set the default formatting to bold italics*/
1327 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1328 cf2.dwMask |= CFM_ITALIC;
1329 cf2.dwEffects ^= CFE_ITALIC;
1330 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1331 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1333 /*Set the text in the control to "wine", which will be bold and italicized*/
1335 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1337 /*Paste the plain text "wine" string, which should take the insert
1338 formatting, which at the moment is bold italics*/
1340 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1342 /*Select the first "wine" string and retrieve its formatting*/
1346 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1347 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1349 /*Select the second "wine" string and retrieve its formatting*/
1353 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1354 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1356 /*Compare the two formattings. They should be the same.*/
1358 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1359 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1360 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1361 DestroyWindow(hwndRichEdit);
1364 static void test_WM_GETTEXT(void)
1366 HWND hwndRichEdit = new_richedit(NULL);
1367 static const char text[] = "Hello. My name is RichEdit!";
1368 static const char text2[] = "Hello. My name is RichEdit!\r";
1369 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1370 char buffer[1024] = {0};
1373 /* Baseline test with normal-sized buffer */
1374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1375 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1376 ok(result == lstrlen(buffer),
1377 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1378 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1379 result = strcmp(buffer,text);
1381 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1383 /* Test for returned value of WM_GETTEXTLENGTH */
1384 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1385 ok(result == lstrlen(text),
1386 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1387 result, lstrlen(text));
1389 /* Test for behavior in overflow case */
1390 memset(buffer, 0, 1024);
1391 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1393 result == lstrlenA(text) - 1, /* XP, win2k3 */
1394 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1395 result = strcmp(buffer,text);
1397 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1399 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1401 /* Baseline test with normal-sized buffer and carriage return */
1402 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1403 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1404 ok(result == lstrlen(buffer),
1405 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1406 result = strcmp(buffer,text2_after);
1408 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1410 /* Test for returned value of WM_GETTEXTLENGTH */
1411 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1412 ok(result == lstrlen(text2_after),
1413 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1414 result, lstrlen(text2_after));
1416 /* Test for behavior of CRLF conversion in case of overflow */
1417 memset(buffer, 0, 1024);
1418 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1420 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1421 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1422 result = strcmp(buffer,text2);
1424 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1426 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1428 DestroyWindow(hwndRichEdit);
1431 static void test_EM_GETTEXTRANGE(void)
1433 HWND hwndRichEdit = new_richedit(NULL);
1434 const char * text1 = "foo bar\r\nfoo bar";
1435 const char * text2 = "foo bar\rfoo bar";
1436 const char * expect = "bar\rfoo";
1437 char buffer[1024] = {0};
1439 TEXTRANGEA textRange;
1441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1443 textRange.lpstrText = buffer;
1444 textRange.chrg.cpMin = 4;
1445 textRange.chrg.cpMax = 11;
1446 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1447 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1448 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1450 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1452 textRange.lpstrText = buffer;
1453 textRange.chrg.cpMin = 4;
1454 textRange.chrg.cpMax = 11;
1455 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1456 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1457 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1459 /* cpMax of text length is used instead of -1 in this case */
1460 textRange.lpstrText = buffer;
1461 textRange.chrg.cpMin = 0;
1462 textRange.chrg.cpMax = -1;
1463 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1464 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1465 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1467 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1468 textRange.lpstrText = buffer;
1469 textRange.chrg.cpMin = -1;
1470 textRange.chrg.cpMax = 1;
1471 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1472 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1473 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1475 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1476 textRange.lpstrText = buffer;
1477 textRange.chrg.cpMin = 1;
1478 textRange.chrg.cpMax = -1;
1479 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1480 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1481 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1483 /* no end character is copied if cpMax - cpMin < 0 */
1484 textRange.lpstrText = buffer;
1485 textRange.chrg.cpMin = 5;
1486 textRange.chrg.cpMax = 5;
1487 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1488 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1489 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1491 /* cpMax of text length is used if cpMax > text length*/
1492 textRange.lpstrText = buffer;
1493 textRange.chrg.cpMin = 0;
1494 textRange.chrg.cpMax = 1000;
1495 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1496 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1497 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1499 DestroyWindow(hwndRichEdit);
1502 static void test_EM_GETSELTEXT(void)
1504 HWND hwndRichEdit = new_richedit(NULL);
1505 const char * text1 = "foo bar\r\nfoo bar";
1506 const char * text2 = "foo bar\rfoo bar";
1507 const char * expect = "bar\rfoo";
1508 char buffer[1024] = {0};
1511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1513 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1514 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1515 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1516 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1518 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1520 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1521 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1522 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1523 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1525 DestroyWindow(hwndRichEdit);
1528 /* FIXME: need to test unimplemented options and robustly test wparam */
1529 static void test_EM_SETOPTIONS(void)
1532 static const char text[] = "Hello. My name is RichEdit!";
1533 char buffer[1024] = {0};
1534 DWORD dwStyle, options, oldOptions;
1535 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1536 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1537 ES_SELECTIONBAR|ES_VERTICAL;
1539 /* Test initial options. */
1540 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1541 0, 0, 200, 60, NULL, NULL,
1542 hmoduleRichEdit, NULL);
1543 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1544 RICHEDIT_CLASS, (int) GetLastError());
1545 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1546 ok(options == 0, "Incorrect initial options %x\n", options);
1547 DestroyWindow(hwndRichEdit);
1549 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1550 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1551 0, 0, 200, 60, NULL, NULL,
1552 hmoduleRichEdit, NULL);
1553 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1554 RICHEDIT_CLASS, (int) GetLastError());
1555 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1556 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1557 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1558 "Incorrect initial options %x\n", options);
1560 /* NEGATIVE TESTING - NO OPTIONS SET */
1561 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1562 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1564 /* testing no readonly by sending 'a' to the control*/
1565 SetFocus(hwndRichEdit);
1566 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1567 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1569 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1572 /* READONLY - sending 'a' to the control */
1573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1574 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1575 SetFocus(hwndRichEdit);
1576 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1577 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1578 ok(buffer[0]==text[0],
1579 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1581 /* EM_SETOPTIONS changes the window style, but changing the
1582 * window style does not change the options. */
1583 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1584 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1585 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1586 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1587 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1588 /* Confirm that the text is still read only. */
1589 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1590 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1591 ok(buffer[0]==text[0],
1592 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1594 oldOptions = options;
1595 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1596 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1597 ok(options == oldOptions,
1598 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1600 DestroyWindow(hwndRichEdit);
1603 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1605 CHARFORMAT2W text_format;
1606 text_format.cbSize = sizeof(text_format);
1607 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1608 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1609 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1612 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1614 int link_present = 0;
1616 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1618 { /* control text is url; should get CFE_LINK */
1619 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1623 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1627 static HWND new_static_wnd(HWND parent) {
1628 return new_window("Static", 0, parent);
1631 static void test_EM_AUTOURLDETECT(void)
1633 /* DO NOT change the properties of the first two elements. To shorten the
1634 tests, all tests after WM_SETTEXT test just the first two elements -
1635 one non-URL and one URL */
1641 {"http://www.winehq.org", 1},
1642 {"http//winehq.org", 0},
1643 {"ww.winehq.org", 0},
1644 {"www.winehq.org", 1},
1645 {"ftp://192.168.1.1", 1},
1646 {"ftp//192.168.1.1", 0},
1647 {"mailto:your@email.com", 1},
1648 {"prospero:prosperoserver", 1},
1650 {"news:newserver", 1},
1651 {"wais:waisserver", 1}
1656 HWND hwndRichEdit, parent;
1658 /* All of the following should cause the URL to be detected */
1659 const char * templates_delim[] = {
1660 "This is some text with X on it",
1661 "This is some text with (X) on it",
1662 "This is some text with X\r on it",
1663 "This is some text with ---X--- on it",
1664 "This is some text with \"X\" on it",
1665 "This is some text with 'X' on it",
1666 "This is some text with 'X' on it",
1667 "This is some text with :X: on it",
1669 "This text ends with X",
1671 "This is some text with X) on it",
1672 "This is some text with X--- on it",
1673 "This is some text with X\" on it",
1674 "This is some text with X' on it",
1675 "This is some text with X: on it",
1677 "This is some text with (X on it",
1678 "This is some text with \rX on it",
1679 "This is some text with ---X on it",
1680 "This is some text with \"X on it",
1681 "This is some text with 'X on it",
1682 "This is some text with :X on it",
1684 /* None of these should cause the URL to be detected */
1685 const char * templates_non_delim[] = {
1686 "This is some text with |X| on it",
1687 "This is some text with *X* on it",
1688 "This is some text with /X/ on it",
1689 "This is some text with +X+ on it",
1690 "This is some text with %X% on it",
1691 "This is some text with #X# on it",
1692 "This is some text with @X@ on it",
1693 "This is some text with \\X\\ on it",
1694 "This is some text with |X on it",
1695 "This is some text with *X on it",
1696 "This is some text with /X on it",
1697 "This is some text with +X on it",
1698 "This is some text with %X on it",
1699 "This is some text with #X on it",
1700 "This is some text with @X on it",
1701 "This is some text with \\X on it",
1703 /* All of these cause the URL detection to be extended by one more byte,
1704 thus demonstrating that the tested character is considered as part
1706 const char * templates_xten_delim[] = {
1707 "This is some text with X| on it",
1708 "This is some text with X* on it",
1709 "This is some text with X/ on it",
1710 "This is some text with X+ on it",
1711 "This is some text with X% on it",
1712 "This is some text with X# on it",
1713 "This is some text with X@ on it",
1714 "This is some text with X\\ on it",
1718 parent = new_static_wnd(NULL);
1719 hwndRichEdit = new_richedit(parent);
1720 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1721 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1722 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1723 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1724 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1725 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1726 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1727 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1728 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1729 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1730 /* for each url, check the text to see if CFE_LINK effect is present */
1731 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1733 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1735 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1737 /* Link detection should happen immediately upon WM_SETTEXT */
1738 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1740 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1742 DestroyWindow(hwndRichEdit);
1744 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1745 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1746 hwndRichEdit = new_richedit(parent);
1748 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1753 at_pos = strchr(templates_delim[j], 'X');
1754 at_offset = at_pos - templates_delim[j];
1755 strncpy(buffer, templates_delim[j], at_offset);
1756 buffer[at_offset] = '\0';
1757 strcat(buffer, urls[i].text);
1758 strcat(buffer, templates_delim[j] + at_offset + 1);
1759 end_offset = at_offset + strlen(urls[i].text);
1761 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1764 /* This assumes no templates start with the URL itself, and that they
1765 have at least two characters before the URL text */
1766 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1767 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1768 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1769 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1775 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1776 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1777 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1778 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1787 if (buffer[end_offset] != '\0')
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1791 if (buffer[end_offset +1] != '\0')
1793 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1794 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1799 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1804 at_pos = strchr(templates_non_delim[j], 'X');
1805 at_offset = at_pos - templates_non_delim[j];
1806 strncpy(buffer, templates_non_delim[j], at_offset);
1807 buffer[at_offset] = '\0';
1808 strcat(buffer, urls[i].text);
1809 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1810 end_offset = at_offset + strlen(urls[i].text);
1812 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1815 /* This assumes no templates start with the URL itself, and that they
1816 have at least two characters before the URL text */
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1828 if (buffer[end_offset] != '\0')
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1832 if (buffer[end_offset +1] != '\0')
1834 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1835 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1840 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1845 at_pos = strchr(templates_xten_delim[j], 'X');
1846 at_offset = at_pos - templates_xten_delim[j];
1847 strncpy(buffer, templates_xten_delim[j], at_offset);
1848 buffer[at_offset] = '\0';
1849 strcat(buffer, urls[i].text);
1850 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1851 end_offset = at_offset + strlen(urls[i].text);
1853 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1854 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1856 /* This assumes no templates start with the URL itself, and that they
1857 have at least two characters before the URL text */
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1867 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1868 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1869 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1870 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1871 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1872 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1876 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1877 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1878 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1879 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1880 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1881 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1883 if (buffer[end_offset +1] != '\0')
1885 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1886 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1887 if (buffer[end_offset +2] != '\0')
1889 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1890 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1895 DestroyWindow(hwndRichEdit);
1896 hwndRichEdit = NULL;
1899 /* Test detection of URLs within normal text - WM_CHAR case. */
1900 /* Test only the first two URL examples for brevity */
1901 for (i = 0; i < 2; i++) {
1902 hwndRichEdit = new_richedit(parent);
1904 /* Also for brevity, test only the first three delimiters */
1905 for (j = 0; j < 3; j++) {
1911 at_pos = strchr(templates_delim[j], 'X');
1912 at_offset = at_pos - templates_delim[j];
1913 end_offset = at_offset + strlen(urls[i].text);
1915 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1916 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1917 for (u = 0; templates_delim[j][u]; u++) {
1918 if (templates_delim[j][u] == '\r') {
1919 simulate_typing_characters(hwndRichEdit, "\r");
1920 } else if (templates_delim[j][u] != 'X') {
1921 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1923 for (v = 0; urls[i].text[v]; v++) {
1924 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1928 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1930 /* This assumes no templates start with the URL itself, and that they
1931 have at least two characters before the URL text */
1932 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1933 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1934 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1935 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1936 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1937 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1941 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1942 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1943 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1944 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1953 if (buffer[end_offset] != '\0')
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1957 if (buffer[end_offset +1] != '\0')
1959 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1960 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1964 /* The following will insert a paragraph break after the first character
1965 of the URL candidate, thus breaking the URL. It is expected that the
1966 CFE_LINK attribute should break across both pieces of the URL */
1967 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1968 simulate_typing_characters(hwndRichEdit, "\r");
1969 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1975 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1976 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1980 /* end_offset moved because of paragraph break */
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1983 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1984 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1986 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1987 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1988 if (buffer[end_offset +2] != '\0')
1990 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1991 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1995 /* The following will remove the just-inserted paragraph break, thus
1996 restoring the URL */
1997 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1998 simulate_typing_characters(hwndRichEdit, "\b");
1999 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2005 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2006 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2010 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2012 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2013 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2019 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2020 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2022 if (buffer[end_offset] != '\0')
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2026 if (buffer[end_offset +1] != '\0')
2028 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2029 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2033 DestroyWindow(hwndRichEdit);
2034 hwndRichEdit = NULL;
2037 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2038 /* Test just the first two URL examples for brevity */
2039 for (i = 0; i < 2; i++) {
2042 hwndRichEdit = new_richedit(parent);
2044 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2046 1) Set entire text, a la WM_SETTEXT
2047 2) Set a selection of the text to the URL
2048 3) Set a portion of the text at a time, which eventually results in
2050 All of them should give equivalent results
2053 /* Set entire text in one go, like WM_SETTEXT */
2054 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2059 st.codepage = CP_ACP;
2060 st.flags = ST_DEFAULT;
2062 at_pos = strchr(templates_delim[j], 'X');
2063 at_offset = at_pos - templates_delim[j];
2064 strncpy(buffer, templates_delim[j], at_offset);
2065 buffer[at_offset] = '\0';
2066 strcat(buffer, urls[i].text);
2067 strcat(buffer, templates_delim[j] + at_offset + 1);
2068 end_offset = at_offset + strlen(urls[i].text);
2070 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2071 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2073 /* This assumes no templates start with the URL itself, and that they
2074 have at least two characters before the URL text */
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2084 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2085 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2086 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2087 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2096 if (buffer[end_offset] != '\0')
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2100 if (buffer[end_offset +1] != '\0')
2102 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2103 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2108 /* Set selection with X to the URL */
2109 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2114 at_pos = strchr(templates_delim[j], 'X');
2115 at_offset = at_pos - templates_delim[j];
2116 end_offset = at_offset + strlen(urls[i].text);
2118 st.codepage = CP_ACP;
2119 st.flags = ST_DEFAULT;
2120 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2121 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2122 st.flags = ST_SELECTION;
2123 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2124 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2125 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2127 /* This assumes no templates start with the URL itself, and that they
2128 have at least two characters before the URL text */
2129 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2130 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2131 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2132 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2138 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2139 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2140 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2141 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2145 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2146 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2147 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2148 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2150 if (buffer[end_offset] != '\0')
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2154 if (buffer[end_offset +1] != '\0')
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2162 /* Set selection with X to the first character of the URL, then the rest */
2163 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2168 at_pos = strchr(templates_delim[j], 'X');
2169 at_offset = at_pos - templates_delim[j];
2170 end_offset = at_offset + strlen(urls[i].text);
2172 strcpy(buffer, "YY");
2173 buffer[0] = urls[i].text[0];
2175 st.codepage = CP_ACP;
2176 st.flags = ST_DEFAULT;
2177 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2178 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2179 st.flags = ST_SELECTION;
2180 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2181 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2182 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2183 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2184 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2186 /* This assumes no templates start with the URL itself, and that they
2187 have at least two characters before the URL text */
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2192 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2193 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2197 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2198 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2199 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2200 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2204 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2205 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2206 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2207 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2209 if (buffer[end_offset] != '\0')
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2213 if (buffer[end_offset +1] != '\0')
2215 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2216 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2221 DestroyWindow(hwndRichEdit);
2222 hwndRichEdit = NULL;
2225 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2226 /* Test just the first two URL examples for brevity */
2227 for (i = 0; i < 2; i++) {
2228 hwndRichEdit = new_richedit(parent);
2230 /* Set selection with X to the URL */
2231 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2236 at_pos = strchr(templates_delim[j], 'X');
2237 at_offset = at_pos - templates_delim[j];
2238 end_offset = at_offset + strlen(urls[i].text);
2240 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2241 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2242 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2243 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2244 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2246 /* This assumes no templates start with the URL itself, and that they
2247 have at least two characters before the URL text */
2248 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2249 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2250 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2251 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2257 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2258 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2259 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2260 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2264 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2265 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2266 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2267 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2269 if (buffer[end_offset] != '\0')
2271 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2272 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2273 if (buffer[end_offset +1] != '\0')
2275 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2276 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2281 /* Set selection with X to the first character of the URL, then the rest */
2282 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2287 at_pos = strchr(templates_delim[j], 'X');
2288 at_offset = at_pos - templates_delim[j];
2289 end_offset = at_offset + strlen(urls[i].text);
2291 strcpy(buffer, "YY");
2292 buffer[0] = urls[i].text[0];
2294 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2296 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2297 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2298 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2299 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2300 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2302 /* This assumes no templates start with the URL itself, and that they
2303 have at least two characters before the URL text */
2304 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2305 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2306 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2307 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2313 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2314 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2315 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2316 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2320 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2321 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2322 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2323 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2325 if (buffer[end_offset] != '\0')
2327 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2328 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2329 if (buffer[end_offset +1] != '\0')
2331 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2332 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2337 DestroyWindow(hwndRichEdit);
2338 hwndRichEdit = NULL;
2341 DestroyWindow(parent);
2344 static void test_EM_SCROLL(void)
2347 int r; /* return value */
2348 int expr; /* expected return value */
2349 HWND hwndRichEdit = new_richedit(NULL);
2350 int y_before, y_after; /* units of lines of text */
2352 /* test a richedit box containing a single line of text */
2353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2355 for (i = 0; i < 4; i++) {
2356 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2358 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2359 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2360 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2361 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2362 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2363 "(i == %d)\n", y_after, i);
2367 * test a richedit box that will scroll. There are two general
2368 * cases: the case without any long lines and the case with a long
2371 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2373 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2376 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2377 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2378 "LONG LINE \nb\nc\nd\ne");
2379 for (j = 0; j < 12; j++) /* reset scroll position to top */
2380 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2382 /* get first visible line */
2383 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2384 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2386 /* get new current first visible line */
2387 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2389 ok(((r & 0xffffff00) == 0x00010000) &&
2390 ((r & 0x000000ff) != 0x00000000),
2391 "EM_SCROLL page down didn't scroll by a small positive number of "
2392 "lines (r == 0x%08x)\n", r);
2393 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2394 "(line %d scrolled to line %d\n", y_before, y_after);
2398 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2399 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2400 ok(((r & 0xffffff00) == 0x0001ff00),
2401 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2402 "(r == 0x%08x)\n", r);
2403 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2404 "%d scrolled to line %d\n", y_before, y_after);
2408 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2410 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2413 "(r == 0x%08x)\n", r);
2414 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2415 "1 line (%d scrolled to %d)\n", y_before, y_after);
2419 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2421 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2423 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2424 "(r == 0x%08x)\n", r);
2425 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2426 "line (%d scrolled to %d)\n", y_before, y_after);
2430 r = SendMessage(hwndRichEdit, EM_SCROLL,
2431 SB_LINEUP, 0); /* lineup beyond top */
2433 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2437 ok(y_before == y_after,
2438 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2442 r = SendMessage(hwndRichEdit, EM_SCROLL,
2443 SB_PAGEUP, 0);/*page up beyond top */
2445 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2448 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2449 ok(y_before == y_after,
2450 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2452 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2453 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2454 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2455 r = SendMessage(hwndRichEdit, EM_SCROLL,
2456 SB_PAGEDOWN, 0); /* page down beyond bot */
2457 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2460 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2461 ok(y_before == y_after,
2462 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2465 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2466 SendMessage(hwndRichEdit, EM_SCROLL,
2467 SB_LINEDOWN, 0); /* line down beyond bot */
2468 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2471 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2472 ok(y_before == y_after,
2473 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2476 DestroyWindow(hwndRichEdit);
2479 static unsigned int recursionLevel = 0;
2480 static unsigned int WM_SIZE_recursionLevel = 0;
2481 static BOOL bailedOutOfRecursion = FALSE;
2482 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2484 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2488 if (bailedOutOfRecursion) return 0;
2489 if (recursionLevel >= 32) {
2490 bailedOutOfRecursion = TRUE;
2497 WM_SIZE_recursionLevel++;
2498 r = richeditProc(hwnd, message, wParam, lParam);
2499 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2500 ShowScrollBar(hwnd, SB_VERT, TRUE);
2501 WM_SIZE_recursionLevel--;
2504 r = richeditProc(hwnd, message, wParam, lParam);
2511 static void test_scrollbar_visibility(void)
2514 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2519 /* These tests show that richedit should temporarily refrain from automatically
2520 hiding or showing its scrollbars (vertical at least) when an explicit request
2521 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2522 Some applications depend on forced showing (when otherwise richedit would
2523 hide the vertical scrollbar) and are thrown on an endless recursive loop
2524 if richedit auto-hides the scrollbar again. Apparently they never heard of
2525 the ES_DISABLENOSCROLL style... */
2527 hwndRichEdit = new_richedit(NULL);
2529 /* Test default scrollbar visibility behavior */
2530 memset(&si, 0, sizeof(si));
2531 si.cbSize = sizeof(si);
2532 si.fMask = SIF_PAGE | SIF_RANGE;
2533 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2534 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2535 "Vertical scrollbar is visible, should be invisible.\n");
2536 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2537 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2538 si.nPage, si.nMin, si.nMax);
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2548 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2549 si.nPage, si.nMin, si.nMax);
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2557 "Vertical scrollbar is invisible, should be visible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2562 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2563 even though it hides the scrollbar */
2564 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2565 memset(&si, 0, sizeof(si));
2566 si.cbSize = sizeof(si);
2567 si.fMask = SIF_PAGE | SIF_RANGE;
2568 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2569 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2570 "Vertical scrollbar is visible, should be invisible.\n");
2571 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2572 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2573 si.nPage, si.nMin, si.nMax);
2575 /* Setting non-scrolling text again does *not* reset scrollbar range */
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582 "Vertical scrollbar is visible, should be invisible.\n");
2583 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2584 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2593 "Vertical scrollbar is visible, should be invisible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2599 memset(&si, 0, sizeof(si));
2600 si.cbSize = sizeof(si);
2601 si.fMask = SIF_PAGE | SIF_RANGE;
2602 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2603 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2604 "Vertical scrollbar is visible, should be invisible.\n");
2605 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2606 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2607 si.nPage, si.nMin, si.nMax);
2609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2610 memset(&si, 0, sizeof(si));
2611 si.cbSize = sizeof(si);
2612 si.fMask = SIF_PAGE | SIF_RANGE;
2613 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2614 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2615 "Vertical scrollbar is visible, should be invisible.\n");
2616 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2617 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2618 si.nPage, si.nMin, si.nMax);
2620 DestroyWindow(hwndRichEdit);
2622 /* Test again, with ES_DISABLENOSCROLL style */
2623 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2625 /* Test default scrollbar visibility behavior */
2626 memset(&si, 0, sizeof(si));
2627 si.cbSize = sizeof(si);
2628 si.fMask = SIF_PAGE | SIF_RANGE;
2629 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2630 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2631 "Vertical scrollbar is invisible, should be visible.\n");
2632 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2633 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2634 si.nPage, si.nMin, si.nMax);
2636 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2637 memset(&si, 0, sizeof(si));
2638 si.cbSize = sizeof(si);
2639 si.fMask = SIF_PAGE | SIF_RANGE;
2640 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2641 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2642 "Vertical scrollbar is invisible, should be visible.\n");
2643 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2644 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2645 si.nPage, si.nMin, si.nMax);
2647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2648 memset(&si, 0, sizeof(si));
2649 si.cbSize = sizeof(si);
2650 si.fMask = SIF_PAGE | SIF_RANGE;
2651 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2652 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2653 "Vertical scrollbar is invisible, should be visible.\n");
2654 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2655 "reported page/range is %d (%d..%d)\n",
2656 si.nPage, si.nMin, si.nMax);
2658 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2666 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2667 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2668 si.nPage, si.nMin, si.nMax);
2670 /* Setting non-scrolling text again does *not* reset scrollbar range */
2671 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2672 memset(&si, 0, sizeof(si));
2673 si.cbSize = sizeof(si);
2674 si.fMask = SIF_PAGE | SIF_RANGE;
2675 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2676 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2677 "Vertical scrollbar is invisible, should be visible.\n");
2678 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2679 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2680 si.nPage, si.nMin, si.nMax);
2682 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2683 memset(&si, 0, sizeof(si));
2684 si.cbSize = sizeof(si);
2685 si.fMask = SIF_PAGE | SIF_RANGE;
2686 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2687 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2688 "Vertical scrollbar is invisible, should be visible.\n");
2689 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2690 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2691 si.nPage, si.nMin, si.nMax);
2693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2694 memset(&si, 0, sizeof(si));
2695 si.cbSize = sizeof(si);
2696 si.fMask = SIF_PAGE | SIF_RANGE;
2697 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2698 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2699 "Vertical scrollbar is invisible, should be visible.\n");
2700 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2701 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2702 si.nPage, si.nMin, si.nMax);
2704 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2705 memset(&si, 0, sizeof(si));
2706 si.cbSize = sizeof(si);
2707 si.fMask = SIF_PAGE | SIF_RANGE;
2708 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2709 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2710 "Vertical scrollbar is invisible, should be visible.\n");
2711 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2712 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2713 si.nPage, si.nMin, si.nMax);
2715 DestroyWindow(hwndRichEdit);
2717 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2718 hwndRichEdit = new_richedit(NULL);
2720 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2721 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2722 memset(&si, 0, sizeof(si));
2723 si.cbSize = sizeof(si);
2724 si.fMask = SIF_PAGE | SIF_RANGE;
2725 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2726 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2727 "Vertical scrollbar is invisible, should be visible.\n");
2729 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2730 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2731 si.nPage, si.nMin, si.nMax);
2734 /* Ditto, see above */
2735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2736 memset(&si, 0, sizeof(si));
2737 si.cbSize = sizeof(si);
2738 si.fMask = SIF_PAGE | SIF_RANGE;
2739 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2740 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2741 "Vertical scrollbar is invisible, should be visible.\n");
2743 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2744 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2745 si.nPage, si.nMin, si.nMax);
2748 /* Ditto, see above */
2749 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2750 memset(&si, 0, sizeof(si));
2751 si.cbSize = sizeof(si);
2752 si.fMask = SIF_PAGE | SIF_RANGE;
2753 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2754 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2755 "Vertical scrollbar is invisible, should be visible.\n");
2757 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2758 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2759 si.nPage, si.nMin, si.nMax);
2762 /* Ditto, see above */
2763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2764 memset(&si, 0, sizeof(si));
2765 si.cbSize = sizeof(si);
2766 si.fMask = SIF_PAGE | SIF_RANGE;
2767 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2768 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2769 "Vertical scrollbar is invisible, should be visible.\n");
2771 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2772 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2773 si.nPage, si.nMin, si.nMax);
2776 /* Ditto, see above */
2777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2778 memset(&si, 0, sizeof(si));
2779 si.cbSize = sizeof(si);
2780 si.fMask = SIF_PAGE | SIF_RANGE;
2781 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2782 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2783 "Vertical scrollbar is invisible, should be visible.\n");
2785 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2786 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2787 si.nPage, si.nMin, si.nMax);
2790 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2792 memset(&si, 0, sizeof(si));
2793 si.cbSize = sizeof(si);
2794 si.fMask = SIF_PAGE | SIF_RANGE;
2795 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2796 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2797 "Vertical scrollbar is visible, should be invisible.\n");
2798 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2799 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2800 si.nPage, si.nMin, si.nMax);
2802 DestroyWindow(hwndRichEdit);
2804 hwndRichEdit = new_richedit(NULL);
2806 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2807 memset(&si, 0, sizeof(si));
2808 si.cbSize = sizeof(si);
2809 si.fMask = SIF_PAGE | SIF_RANGE;
2810 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2811 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2812 "Vertical scrollbar is visible, should be invisible.\n");
2813 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2814 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2815 si.nPage, si.nMin, si.nMax);
2817 SendMessage(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 visible, should be invisible.\n");
2824 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2825 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2826 si.nPage, si.nMin, si.nMax);
2828 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2829 memset(&si, 0, sizeof(si));
2830 si.cbSize = sizeof(si);
2831 si.fMask = SIF_PAGE | SIF_RANGE;
2832 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2833 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2834 "Vertical scrollbar is visible, should be invisible.\n");
2835 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2836 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2837 si.nPage, si.nMin, si.nMax);
2839 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2840 memset(&si, 0, sizeof(si));
2841 si.cbSize = sizeof(si);
2842 si.fMask = SIF_PAGE | SIF_RANGE;
2843 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2845 "Vertical scrollbar is visible, should be invisible.\n");
2846 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2847 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2848 si.nPage, si.nMin, si.nMax);
2850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856 "Vertical scrollbar is invisible, should be visible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2861 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2862 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
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 visible, should be invisible.\n");
2869 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2870 "reported page/range is %d (%d..%d)\n",
2871 si.nPage, si.nMin, si.nMax);
2873 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2879 "Vertical scrollbar is visible, should be invisible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2885 EM_SCROLL will make visible any forcefully invisible scrollbar */
2886 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892 "Vertical scrollbar is invisible, should be visible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2898 memset(&si, 0, sizeof(si));
2899 si.cbSize = sizeof(si);
2900 si.fMask = SIF_PAGE | SIF_RANGE;
2901 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2902 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2903 "Vertical scrollbar is visible, should be invisible.\n");
2904 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2905 "reported page/range is %d (%d..%d)\n",
2906 si.nPage, si.nMin, si.nMax);
2908 /* Again, EM_SCROLL, with SB_LINEUP */
2909 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2915 "Vertical scrollbar is invisible, should be visible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d)\n",
2918 si.nPage, si.nMin, si.nMax);
2920 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2921 memset(&si, 0, sizeof(si));
2922 si.cbSize = sizeof(si);
2923 si.fMask = SIF_PAGE | SIF_RANGE;
2924 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2925 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2926 "Vertical scrollbar is visible, should be invisible.\n");
2927 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2928 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2929 si.nPage, si.nMin, si.nMax);
2931 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2932 memset(&si, 0, sizeof(si));
2933 si.cbSize = sizeof(si);
2934 si.fMask = SIF_PAGE | SIF_RANGE;
2935 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2936 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2937 "Vertical scrollbar is invisible, should be visible.\n");
2938 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2939 "reported page/range is %d (%d..%d)\n",
2940 si.nPage, si.nMin, si.nMax);
2942 DestroyWindow(hwndRichEdit);
2945 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2946 hwndRichEdit = new_richedit(NULL);
2948 #define ENABLE_WS_VSCROLL(hwnd) \
2949 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2950 #define DISABLE_WS_VSCROLL(hwnd) \
2951 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2953 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2954 ENABLE_WS_VSCROLL(hwndRichEdit);
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2977 /* Ditto, see above */
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2984 "Vertical scrollbar is invisible, should be visible.\n");
2985 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2986 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2987 si.nPage, si.nMin, si.nMax);
2989 /* Ditto, see above */
2990 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2991 memset(&si, 0, sizeof(si));
2992 si.cbSize = sizeof(si);
2993 si.fMask = SIF_PAGE | SIF_RANGE;
2994 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996 "Vertical scrollbar is invisible, should be visible.\n");
2997 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2998 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2999 si.nPage, si.nMin, si.nMax);
3001 /* Ditto, see above */
3002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3003 memset(&si, 0, sizeof(si));
3004 si.cbSize = sizeof(si);
3005 si.fMask = SIF_PAGE | SIF_RANGE;
3006 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008 "Vertical scrollbar is invisible, should be visible.\n");
3009 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3010 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3011 si.nPage, si.nMin, si.nMax);
3013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3015 memset(&si, 0, sizeof(si));
3016 si.cbSize = sizeof(si);
3017 si.fMask = SIF_PAGE | SIF_RANGE;
3018 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3020 "Vertical scrollbar is visible, should be invisible.\n");
3021 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3022 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3023 si.nPage, si.nMin, si.nMax);
3025 DestroyWindow(hwndRichEdit);
3027 hwndRichEdit = new_richedit(NULL);
3029 DISABLE_WS_VSCROLL(hwndRichEdit);
3030 memset(&si, 0, sizeof(si));
3031 si.cbSize = sizeof(si);
3032 si.fMask = SIF_PAGE | SIF_RANGE;
3033 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3034 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3035 "Vertical scrollbar is visible, should be invisible.\n");
3036 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3037 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3038 si.nPage, si.nMin, si.nMax);
3040 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3041 memset(&si, 0, sizeof(si));
3042 si.cbSize = sizeof(si);
3043 si.fMask = SIF_PAGE | SIF_RANGE;
3044 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3045 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3046 "Vertical scrollbar is visible, should be invisible.\n");
3047 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3048 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3049 si.nPage, si.nMin, si.nMax);
3051 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3052 memset(&si, 0, sizeof(si));
3053 si.cbSize = sizeof(si);
3054 si.fMask = SIF_PAGE | SIF_RANGE;
3055 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3056 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3057 "Vertical scrollbar is visible, should be invisible.\n");
3058 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3059 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3060 si.nPage, si.nMin, si.nMax);
3062 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3063 memset(&si, 0, sizeof(si));
3064 si.cbSize = sizeof(si);
3065 si.fMask = SIF_PAGE | SIF_RANGE;
3066 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3067 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3068 "Vertical scrollbar is visible, should be invisible.\n");
3069 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3070 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3071 si.nPage, si.nMin, si.nMax);
3073 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3074 memset(&si, 0, sizeof(si));
3075 si.cbSize = sizeof(si);
3076 si.fMask = SIF_PAGE | SIF_RANGE;
3077 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3078 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3079 "Vertical scrollbar is invisible, should be visible.\n");
3080 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3081 "reported page/range is %d (%d..%d)\n",
3082 si.nPage, si.nMin, si.nMax);
3084 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3085 DISABLE_WS_VSCROLL(hwndRichEdit);
3086 memset(&si, 0, sizeof(si));
3087 si.cbSize = sizeof(si);
3088 si.fMask = SIF_PAGE | SIF_RANGE;
3089 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3090 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3091 "Vertical scrollbar is visible, should be invisible.\n");
3092 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3093 "reported page/range is %d (%d..%d)\n",
3094 si.nPage, si.nMin, si.nMax);
3096 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3097 memset(&si, 0, sizeof(si));
3098 si.cbSize = sizeof(si);
3099 si.fMask = SIF_PAGE | SIF_RANGE;
3100 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3101 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3102 "Vertical scrollbar is visible, should be invisible.\n");
3103 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3104 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3105 si.nPage, si.nMin, si.nMax);
3107 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3108 memset(&si, 0, sizeof(si));
3109 si.cbSize = sizeof(si);
3110 si.fMask = SIF_PAGE | SIF_RANGE;
3111 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3112 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3113 "Vertical scrollbar is invisible, should be visible.\n");
3114 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3115 "reported page/range is %d (%d..%d)\n",
3116 si.nPage, si.nMin, si.nMax);
3118 DISABLE_WS_VSCROLL(hwndRichEdit);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3124 "Vertical scrollbar is visible, should be invisible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3130 EM_SCROLL will make visible any forcefully invisible scrollbar */
3131 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3132 memset(&si, 0, sizeof(si));
3133 si.cbSize = sizeof(si);
3134 si.fMask = SIF_PAGE | SIF_RANGE;
3135 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3136 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3137 "Vertical scrollbar is invisible, should be visible.\n");
3138 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3139 "reported page/range is %d (%d..%d)\n",
3140 si.nPage, si.nMin, si.nMax);
3142 DISABLE_WS_VSCROLL(hwndRichEdit);
3143 memset(&si, 0, sizeof(si));
3144 si.cbSize = sizeof(si);
3145 si.fMask = SIF_PAGE | SIF_RANGE;
3146 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3147 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3148 "Vertical scrollbar is visible, should be invisible.\n");
3149 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3150 "reported page/range is %d (%d..%d)\n",
3151 si.nPage, si.nMin, si.nMax);
3153 /* Again, EM_SCROLL, with SB_LINEUP */
3154 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3155 memset(&si, 0, sizeof(si));
3156 si.cbSize = sizeof(si);
3157 si.fMask = SIF_PAGE | SIF_RANGE;
3158 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3159 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3160 "Vertical scrollbar is invisible, should be visible.\n");
3161 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3162 "reported page/range is %d (%d..%d)\n",
3163 si.nPage, si.nMin, si.nMax);
3165 DestroyWindow(hwndRichEdit);
3167 /* This window proc models what is going on with Corman Lisp 3.0.
3168 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3169 force the scrollbar into visibility. Recursion should NOT happen
3170 as a result of this action.
3172 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3174 richeditProc = cls.lpfnWndProc;
3175 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3176 cls.lpszClassName = "RicheditStupidOverride";
3177 if(!RegisterClassA(&cls)) assert(0);
3180 WM_SIZE_recursionLevel = 0;
3181 bailedOutOfRecursion = FALSE;
3182 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3183 ok(!bailedOutOfRecursion,
3184 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3187 WM_SIZE_recursionLevel = 0;
3188 bailedOutOfRecursion = FALSE;
3189 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3190 ok(!bailedOutOfRecursion,
3191 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3193 /* Unblock window in order to process WM_DESTROY */
3195 bailedOutOfRecursion = FALSE;
3196 WM_SIZE_recursionLevel = 0;
3197 DestroyWindow(hwndRichEdit);
3201 static void test_EM_SETUNDOLIMIT(void)
3203 /* cases we test for:
3204 * default behaviour - limiting at 100 undo's
3205 * undo disabled - setting a limit of 0
3206 * undo limited - undo limit set to some to some number, like 2
3207 * bad input - sending a negative number should default to 100 undo's */
3209 HWND hwndRichEdit = new_richedit(NULL);
3214 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3217 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3218 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3219 also, multiple pastes don't combine like WM_CHAR would */
3220 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3222 /* first case - check the default */
3223 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3224 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3225 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3226 for (i=0; i<100; i++) /* Undo 100 of them */
3227 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3228 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3229 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3231 /* second case - cannot undo */
3232 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3233 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3234 SendMessage(hwndRichEdit,
3235 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3236 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3237 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3239 /* third case - set it to an arbitrary number */
3240 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3241 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3242 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3243 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3244 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3245 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3246 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3247 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3248 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3249 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3250 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3251 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3252 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3253 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3255 /* fourth case - setting negative numbers should default to 100 undos */
3256 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3257 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3259 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3261 DestroyWindow(hwndRichEdit);
3264 static void test_ES_PASSWORD(void)
3266 /* This isn't hugely testable, so we're just going to run it through its paces */
3268 HWND hwndRichEdit = new_richedit(NULL);
3271 /* First, check the default of a regular control */
3272 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3274 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3276 /* Now, set it to something normal */
3277 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3278 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3280 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3282 /* Now, set it to something odd */
3283 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3284 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3286 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3287 DestroyWindow(hwndRichEdit);
3290 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3295 char** str = (char**)dwCookie;
3298 memcpy(*str, pbBuff, *pcb);
3304 static void test_WM_SETTEXT(void)
3306 HWND hwndRichEdit = new_richedit(NULL);
3307 const char * TestItem1 = "TestSomeText";
3308 const char * TestItem2 = "TestSomeText\r";
3309 const char * TestItem2_after = "TestSomeText\r\n";
3310 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3311 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3312 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3313 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3314 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3315 const char * TestItem5_after = "TestSomeText TestSomeText";
3316 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3317 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3318 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3319 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3321 const char rtftextA[] = "{\\rtf sometext}";
3322 const char urtftextA[] = "{\\urtf sometext}";
3323 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3324 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3325 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3327 char buf[1024] = {0};
3328 WCHAR bufW[1024] = {0};
3331 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3332 any solitary \r to be converted to \r\n on return. Properly paired
3333 \r\n are not affected. It also shows that the special sequence \r\r\n
3334 gets converted to a single space.
3337 #define TEST_SETTEXT(a, b) \
3338 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3339 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3340 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3341 ok (result == lstrlen(buf), \
3342 "WM_GETTEXT returned %ld instead of expected %u\n", \
3343 result, lstrlen(buf)); \
3344 result = strcmp(b, buf); \
3346 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3348 TEST_SETTEXT(TestItem1, TestItem1)
3349 TEST_SETTEXT(TestItem2, TestItem2_after)
3350 TEST_SETTEXT(TestItem3, TestItem3_after)
3351 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3352 TEST_SETTEXT(TestItem4, TestItem4_after)
3353 TEST_SETTEXT(TestItem5, TestItem5_after)
3354 TEST_SETTEXT(TestItem6, TestItem6_after)
3355 TEST_SETTEXT(TestItem7, TestItem7_after)
3357 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3358 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3359 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3360 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3361 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3362 DestroyWindow(hwndRichEdit);
3365 #define TEST_SETTEXTW(a, b) \
3366 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3367 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3368 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3369 ok (result == lstrlenW(bufW), \
3370 "WM_GETTEXT returned %ld instead of expected %u\n", \
3371 result, lstrlenW(bufW)); \
3372 result = lstrcmpW(b, bufW); \
3373 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3375 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3376 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3377 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3378 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3379 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3380 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3381 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3382 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3383 DestroyWindow(hwndRichEdit);
3384 #undef TEST_SETTEXTW
3387 static void test_EM_STREAMOUT(void)
3389 HWND hwndRichEdit = new_richedit(NULL);
3392 char buf[1024] = {0};
3395 const char * TestItem1 = "TestSomeText";
3396 const char * TestItem2 = "TestSomeText\r";
3397 const char * TestItem3 = "TestSomeText\r\n";
3399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3401 es.dwCookie = (DWORD_PTR)&p;
3403 es.pfnCallback = test_WM_SETTEXT_esCallback;
3404 memset(buf, 0, sizeof(buf));
3405 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3407 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3408 ok(strcmp(buf, TestItem1) == 0,
3409 "streamed text different, got %s\n", buf);
3411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3413 es.dwCookie = (DWORD_PTR)&p;
3415 es.pfnCallback = test_WM_SETTEXT_esCallback;
3416 memset(buf, 0, sizeof(buf));
3417 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3419 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3420 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3421 ok(strcmp(buf, TestItem3) == 0,
3422 "streamed text different from, got %s\n", buf);
3423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3425 es.dwCookie = (DWORD_PTR)&p;
3427 es.pfnCallback = test_WM_SETTEXT_esCallback;
3428 memset(buf, 0, sizeof(buf));
3429 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3431 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3432 ok(strcmp(buf, TestItem3) == 0,
3433 "streamed text different, got %s\n", buf);
3435 DestroyWindow(hwndRichEdit);
3438 static void test_EM_STREAMOUT_FONTTBL(void)
3440 HWND hwndRichEdit = new_richedit(NULL);
3442 char buf[1024] = {0};
3447 const char * TestItem = "TestSomeText";
3449 /* fills in the richedit control with some text */
3450 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3452 /* streams out the text in rtf format */
3454 es.dwCookie = (DWORD_PTR)&p;
3456 es.pfnCallback = test_WM_SETTEXT_esCallback;
3457 memset(buf, 0, sizeof(buf));
3458 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3460 /* scans for \fonttbl, error if not found */
3461 fontTbl = strstr(buf, "\\fonttbl");
3462 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3465 /* scans for terminating closing bracket */
3467 while(*fontTbl && brackCount)
3471 else if(*fontTbl == '}')
3475 /* checks whether closing bracket is ok */
3476 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3479 /* char before closing fonttbl block should be a closed bracket */
3481 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3483 /* char after fonttbl block should be a crlf */
3485 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3488 DestroyWindow(hwndRichEdit);
3492 static void test_EM_SETTEXTEX(void)
3494 HWND hwndRichEdit, parent;
3496 int sel_start, sel_end;
3499 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3501 'T', 'e', 'x', 't', 0};
3502 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3508 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3509 '\r','t','S','o','m','e','T','e','x','t',0};
3510 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3514 const char * TestItem2_after = "TestSomeText\r\n";
3515 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3518 '\r','\n','\r','\n', 0};
3519 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3523 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3527 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3530 '\r','\r','\n','\r',
3532 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3536 #define MAX_BUF_LEN 1024
3537 WCHAR buf[MAX_BUF_LEN];
3538 char bufACP[MAX_BUF_LEN];
3545 /* Test the scroll position with and without a parent window.
3547 * For some reason the scroll position is 0 after EM_SETTEXTEX
3548 * with the ST_SELECTION flag only when the control has a parent
3549 * window, even though the selection is at the end. */
3551 cls.lpfnWndProc = DefWindowProcA;
3554 cls.hInstance = GetModuleHandleA(0);
3556 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3557 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3558 cls.lpszMenuName = NULL;
3559 cls.lpszClassName = "ParentTestClass";
3560 if(!RegisterClassA(&cls)) assert(0);
3562 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3563 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3564 ok (parent != 0, "Failed to create parent window\n");
3566 hwndRichEdit = CreateWindowEx(0,
3567 RICHEDIT_CLASS, NULL,
3568 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3569 0, 0, 200, 60, parent, NULL,
3570 hmoduleRichEdit, NULL);
3572 setText.codepage = CP_ACP;
3573 setText.flags = ST_SELECTION;
3574 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3575 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3576 si.cbSize = sizeof(si);
3578 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3579 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3580 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3581 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3582 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3584 DestroyWindow(parent);
3586 /* Test without a parent window */
3587 hwndRichEdit = new_richedit(NULL);
3588 setText.codepage = CP_ACP;
3589 setText.flags = ST_SELECTION;
3590 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3591 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3592 si.cbSize = sizeof(si);
3594 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3595 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3596 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3597 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3598 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3600 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3601 * but this time it is because the selection is at the beginning. */
3602 setText.codepage = CP_ACP;
3603 setText.flags = ST_DEFAULT;
3604 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3605 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3606 si.cbSize = sizeof(si);
3608 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3609 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3610 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3611 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3612 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3614 setText.codepage = 1200; /* no constant for unicode */
3615 getText.codepage = 1200; /* no constant for unicode */
3616 getText.cb = MAX_BUF_LEN;
3617 getText.flags = GT_DEFAULT;
3618 getText.lpDefaultChar = NULL;
3619 getText.lpUsedDefChar = NULL;
3622 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3623 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3624 ok(lstrcmpW(buf, TestItem1) == 0,
3625 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3627 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3628 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3630 setText.codepage = 1200; /* no constant for unicode */
3631 getText.codepage = 1200; /* no constant for unicode */
3632 getText.cb = MAX_BUF_LEN;
3633 getText.flags = GT_DEFAULT;
3634 getText.lpDefaultChar = NULL;
3635 getText.lpUsedDefChar = NULL;
3637 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3638 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3639 ok(lstrcmpW(buf, TestItem2) == 0,
3640 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3642 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3643 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3644 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3645 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3647 /* Baseline test for just-enough buffer space for string */
3648 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3649 getText.codepage = 1200; /* no constant for unicode */
3650 getText.flags = GT_DEFAULT;
3651 getText.lpDefaultChar = NULL;
3652 getText.lpUsedDefChar = NULL;
3653 memset(buf, 0, MAX_BUF_LEN);
3654 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3655 ok(lstrcmpW(buf, TestItem2) == 0,
3656 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3658 /* When there is enough space for one character, but not both, of the CRLF
3659 pair at the end of the string, the CR is not copied at all. That is,
3660 the caller must not see CRLF pairs truncated to CR at the end of the
3663 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3664 getText.codepage = 1200; /* no constant for unicode */
3665 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3666 getText.lpDefaultChar = NULL;
3667 getText.lpUsedDefChar = NULL;
3668 memset(buf, 0, MAX_BUF_LEN);
3669 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3670 ok(lstrcmpW(buf, TestItem1) == 0,
3671 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3674 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3675 setText.codepage = 1200; /* no constant for unicode */
3676 getText.codepage = 1200; /* no constant for unicode */
3677 getText.cb = MAX_BUF_LEN;
3678 getText.flags = GT_DEFAULT;
3679 getText.lpDefaultChar = NULL;
3680 getText.lpUsedDefChar = NULL;
3682 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3683 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3684 ok(lstrcmpW(buf, TestItem3_after) == 0,
3685 "EM_SETTEXTEX did not convert properly\n");
3687 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3688 setText.codepage = 1200; /* no constant for unicode */
3689 getText.codepage = 1200; /* no constant for unicode */
3690 getText.cb = MAX_BUF_LEN;
3691 getText.flags = GT_DEFAULT;
3692 getText.lpDefaultChar = NULL;
3693 getText.lpUsedDefChar = NULL;
3695 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3696 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3697 ok(lstrcmpW(buf, TestItem3_after) == 0,
3698 "EM_SETTEXTEX did not convert properly\n");
3700 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3701 setText.codepage = 1200; /* no constant for unicode */
3702 getText.codepage = 1200; /* no constant for unicode */
3703 getText.cb = MAX_BUF_LEN;
3704 getText.flags = GT_DEFAULT;
3705 getText.lpDefaultChar = NULL;
3706 getText.lpUsedDefChar = NULL;
3708 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3709 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3710 ok(lstrcmpW(buf, TestItem4_after) == 0,
3711 "EM_SETTEXTEX did not convert properly\n");
3713 /* !ST_SELECTION && Unicode && !\rtf */
3714 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3715 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3718 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3719 ok(lstrlenW(buf) == 0,
3720 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3722 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3724 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3725 /* select some text */
3728 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3729 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3730 setText.flags = ST_SELECTION;
3731 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3733 "EM_SETTEXTEX with NULL lParam to replace selection"
3734 " with no text should return 0. Got %i\n",
3737 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3739 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3740 /* select some text */
3743 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3744 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3745 setText.flags = ST_SELECTION;
3746 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3747 (WPARAM)&setText, (LPARAM) TestItem1);
3749 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3750 ok(result == lstrlenW(TestItem1),
3751 "EM_SETTEXTEX with NULL lParam to replace selection"
3752 " with no text should return 0. Got %i\n",
3754 ok(lstrlenW(buf) == 22,
3755 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3758 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3759 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3761 es.dwCookie = (DWORD_PTR)&p;
3763 es.pfnCallback = test_WM_SETTEXT_esCallback;
3764 memset(buf, 0, sizeof(buf));
3765 SendMessage(hwndRichEdit, EM_STREAMOUT,
3766 (WPARAM)(SF_RTF), (LPARAM)&es);
3767 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3769 /* !ST_SELECTION && !Unicode && \rtf */
3770 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3771 getText.codepage = 1200; /* no constant for unicode */
3772 getText.cb = MAX_BUF_LEN;
3773 getText.flags = GT_DEFAULT;
3774 getText.lpDefaultChar = NULL;
3775 getText.lpUsedDefChar = NULL;
3778 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3779 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3780 ok(lstrcmpW(buf, TestItem1) == 0,
3781 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3783 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3784 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3785 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3786 getText.codepage = CP_ACP;
3787 getText.cb = MAX_BUF_LEN;
3788 getText.flags = GT_DEFAULT;
3789 getText.lpDefaultChar = NULL;
3790 getText.lpUsedDefChar = NULL;
3792 setText.flags = ST_SELECTION;
3793 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3794 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3795 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3796 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3797 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3799 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3800 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3802 es.dwCookie = (DWORD_PTR)&p;
3804 es.pfnCallback = test_WM_SETTEXT_esCallback;
3805 memset(buf, 0, sizeof(buf));
3806 SendMessage(hwndRichEdit, EM_STREAMOUT,
3807 (WPARAM)(SF_RTF), (LPARAM)&es);
3808 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3810 /* select some text */
3813 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3815 /* ST_SELECTION && !Unicode && \rtf */
3816 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3817 getText.codepage = 1200; /* no constant for unicode */
3818 getText.cb = MAX_BUF_LEN;
3819 getText.flags = GT_DEFAULT;
3820 getText.lpDefaultChar = NULL;
3821 getText.lpUsedDefChar = NULL;
3823 setText.flags = ST_SELECTION;
3824 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3825 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3826 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3828 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3829 setText.codepage = 1200; /* no constant for unicode */
3830 getText.codepage = CP_ACP;
3831 getText.cb = MAX_BUF_LEN;
3834 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3835 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3837 /* select some text */
3840 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3842 /* ST_SELECTION && !Unicode && !\rtf */
3843 setText.codepage = CP_ACP;
3844 getText.codepage = 1200; /* no constant for unicode */
3845 getText.cb = MAX_BUF_LEN;
3846 getText.flags = GT_DEFAULT;
3847 getText.lpDefaultChar = NULL;
3848 getText.lpUsedDefChar = NULL;
3850 setText.flags = ST_SELECTION;
3851 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3852 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3853 ok(lstrcmpW(buf, TestItem1alt) == 0,
3854 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3855 " using ST_SELECTION and non-Unicode\n");
3857 /* Test setting text using rich text format */
3859 setText.codepage = CP_ACP;
3860 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3861 getText.codepage = CP_ACP;
3862 getText.cb = MAX_BUF_LEN;
3863 getText.flags = GT_DEFAULT;
3864 getText.lpDefaultChar = NULL;
3865 getText.lpUsedDefChar = NULL;
3866 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3867 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3870 setText.codepage = CP_ACP;
3871 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3872 getText.codepage = CP_ACP;
3873 getText.cb = MAX_BUF_LEN;
3874 getText.flags = GT_DEFAULT;
3875 getText.lpDefaultChar = NULL;
3876 getText.lpUsedDefChar = NULL;
3877 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3878 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3880 DestroyWindow(hwndRichEdit);
3883 static void test_EM_LIMITTEXT(void)
3887 HWND hwndRichEdit = new_richedit(NULL);
3889 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3890 * about setting the length to -1 for multiline edit controls doesn't happen.
3893 /* Don't check default gettextlimit case. That's done in other tests */
3895 /* Set textlimit to 100 */
3896 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3897 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3899 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3901 /* Set textlimit to 0 */
3902 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3903 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3905 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3907 /* Set textlimit to -1 */
3908 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3909 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3911 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3913 /* Set textlimit to -2 */
3914 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3915 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3917 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3919 DestroyWindow (hwndRichEdit);
3923 static void test_EM_EXLIMITTEXT(void)
3925 int i, selBegin, selEnd, len1, len2;
3927 char text[1024 + 1];
3928 char buffer[1024 + 1];
3929 int textlimit = 0; /* multiple of 100 */
3930 HWND hwndRichEdit = new_richedit(NULL);
3932 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3933 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3936 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3937 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3939 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3942 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3943 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3945 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3947 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3948 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3949 /* default for WParam = 0 */
3950 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3952 textlimit = sizeof(text)-1;
3953 memset(text, 'W', textlimit);
3954 text[sizeof(text)-1] = 0;
3955 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3956 /* maxed out text */
3957 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3959 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3960 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3961 len1 = selEnd - selBegin;
3963 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3964 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3965 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3966 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3967 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3968 len2 = selEnd - selBegin;
3971 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3974 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3975 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3976 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3977 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3978 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3979 len1 = selEnd - selBegin;
3982 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3985 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3986 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3987 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3988 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3989 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3990 len2 = selEnd - selBegin;
3993 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3996 /* set text up to the limit, select all the text, then add a char */
3998 memset(text, 'W', textlimit);
3999 text[textlimit] = 0;
4000 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4001 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4002 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4003 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4004 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4005 result = strcmp(buffer, "A");
4006 ok(0 == result, "got string = \"%s\"\n", buffer);
4008 /* WM_SETTEXT not limited */
4010 memset(text, 'W', textlimit);
4011 text[textlimit] = 0;
4012 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4013 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4014 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4016 ok(10 == i, "expected 10 chars\n");
4017 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4018 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4020 /* try inserting more text at end */
4021 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4022 ok(0 == i, "WM_CHAR wasn't processed\n");
4023 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4025 ok(10 == i, "expected 10 chars, got %i\n", i);
4026 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4027 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4029 /* try inserting text at beginning */
4030 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4031 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4032 ok(0 == i, "WM_CHAR wasn't processed\n");
4033 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4035 ok(10 == i, "expected 10 chars, got %i\n", i);
4036 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4037 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4039 /* WM_CHAR is limited */
4041 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4042 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4043 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4044 ok(0 == i, "WM_CHAR wasn't processed\n");
4045 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4046 ok(0 == i, "WM_CHAR wasn't processed\n");
4047 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4049 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4051 DestroyWindow(hwndRichEdit);
4054 static void test_EM_GETLIMITTEXT(void)
4057 HWND hwndRichEdit = new_richedit(NULL);
4059 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4060 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4062 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4063 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4064 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4066 DestroyWindow(hwndRichEdit);
4069 static void test_WM_SETFONT(void)
4071 /* There is no invalid input or error conditions for this function.
4072 * NULL wParam and lParam just fall back to their default values
4073 * It should be noted that even if you use a gibberish name for your fonts
4074 * here, it will still work because the name is stored. They will display as
4075 * System, but will report their name to be whatever they were created as */
4077 HWND hwndRichEdit = new_richedit(NULL);
4078 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4079 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4080 FF_DONTCARE, "Marlett");
4081 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4082 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4083 FF_DONTCARE, "MS Sans Serif");
4084 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4085 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4086 FF_DONTCARE, "Courier");
4087 LOGFONTA sentLogFont;
4088 CHARFORMAT2A returnedCF2A;
4090 returnedCF2A.cbSize = sizeof(returnedCF2A);
4092 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4093 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4094 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4096 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4097 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4098 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4099 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4101 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4102 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4103 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4104 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4105 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4106 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4108 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4109 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4110 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4111 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4112 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4113 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4115 /* This last test is special since we send in NULL. We clear the variables
4116 * and just compare to "System" instead of the sent in font name. */
4117 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4118 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4119 returnedCF2A.cbSize = sizeof(returnedCF2A);
4121 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4122 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4123 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4124 ok (!strcmp("System",returnedCF2A.szFaceName),
4125 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4127 DestroyWindow(hwndRichEdit);
4131 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4136 const char** str = (const char**)dwCookie;
4137 int size = strlen(*str);
4138 if(size > 3) /* let's make it piecemeal for fun */
4145 memcpy(pbBuff, *str, *pcb);
4151 static void test_EM_GETMODIFY(void)
4153 HWND hwndRichEdit = new_richedit(NULL);
4156 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4158 'T', 'e', 'x', 't', 0};
4159 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4161 'O', 't', 'h', 'e', 'r',
4162 'T', 'e', 'x', 't', 0};
4163 const char* streamText = "hello world";
4168 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4169 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4170 FF_DONTCARE, "Courier");
4172 setText.codepage = 1200; /* no constant for unicode */
4173 setText.flags = ST_KEEPUNDO;
4176 /* modify flag shouldn't be set when richedit is first created */
4177 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4179 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4181 /* setting modify flag should actually set it */
4182 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4183 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4185 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4187 /* clearing modify flag should actually clear it */
4188 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4191 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4193 /* setting font doesn't change modify flag */
4194 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4195 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4196 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4198 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4200 /* setting text should set modify flag */
4201 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4202 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4203 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4205 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4207 /* undo previous text doesn't reset modify flag */
4208 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4209 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4211 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4213 /* set text with no flag to keep undo stack should not set modify flag */
4214 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4216 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4217 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4219 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4221 /* WM_SETTEXT doesn't modify */
4222 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4223 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4224 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4228 /* clear the text */
4229 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4231 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4233 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4236 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4237 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4238 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4239 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4240 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4242 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4244 /* copy/paste text 1 */
4245 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4246 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4247 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4248 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4249 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4251 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4253 /* copy/paste text 2 */
4254 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4255 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4256 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4257 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4258 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4259 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4261 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4264 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4265 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4266 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4267 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4269 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4272 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4273 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4274 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4275 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4279 /* set char format */
4280 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 cf2.cbSize = sizeof(CHARFORMAT2);
4282 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4283 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4284 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4285 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4286 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4287 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4288 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4290 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4292 /* set para format */
4293 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4294 pf2.cbSize = sizeof(PARAFORMAT2);
4295 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4297 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4298 pf2.wAlignment = PFA_RIGHT;
4299 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4300 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4302 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4305 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4306 es.dwCookie = (DWORD_PTR)&streamText;
4308 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4309 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4310 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4312 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4314 DestroyWindow(hwndRichEdit);
4320 LRESULT expected_retval;
4321 int expected_getsel_start;
4322 int expected_getsel_end;
4323 int _getsel_todo_wine;
4326 const struct exsetsel_s exsetsel_tests[] = {
4328 {5, 10, 10, 5, 10, 0},
4329 {15, 17, 17, 15, 17, 0},
4330 /* test cpMax > strlen() */
4331 {0, 100, 18, 0, 18, 1},
4332 /* test cpMin == cpMax */
4334 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4335 {-1, 0, 5, 5, 5, 0},
4336 {-1, 17, 5, 5, 5, 0},
4337 {-1, 18, 5, 5, 5, 0},
4338 /* test cpMin < 0 && cpMax < 0 */
4339 {-1, -1, 17, 17, 17, 0},
4340 {-4, -5, 17, 17, 17, 0},
4341 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4342 {0, -1, 18, 0, 18, 1},
4343 {17, -5, 18, 17, 18, 1},
4344 {18, -3, 17, 17, 17, 0},
4345 /* test if cpMin > cpMax */
4346 {15, 19, 18, 15, 18, 1},
4347 {19, 15, 18, 15, 18, 1}
4350 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4355 cr.cpMin = setsel->min;
4356 cr.cpMax = setsel->max;
4357 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4359 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4361 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4363 if (setsel->_getsel_todo_wine) {
4365 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4368 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4372 static void test_EM_EXSETSEL(void)
4374 HWND hwndRichEdit = new_richedit(NULL);
4376 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4378 /* sending some text to the window */
4379 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4380 /* 01234567890123456*/
4383 for (i = 0; i < num_tests; i++) {
4384 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4387 DestroyWindow(hwndRichEdit);
4390 static void test_EM_REPLACESEL(int redraw)
4392 HWND hwndRichEdit = new_richedit(NULL);
4393 char buffer[1024] = {0};
4398 /* sending some text to the window */
4399 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4400 /* 01234567890123456*/
4403 /* FIXME add more tests */
4404 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4405 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4406 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4407 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4408 r = strcmp(buffer, "testing");
4409 ok(0 == r, "expected %d, got %d\n", 0, r);
4411 DestroyWindow(hwndRichEdit);
4413 hwndRichEdit = new_richedit(NULL);
4415 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4416 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4418 /* Test behavior with carriage returns and newlines */
4419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4420 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4421 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4422 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4423 r = strcmp(buffer, "RichEdit1");
4424 ok(0 == r, "expected %d, got %d\n", 0, r);
4426 getText.codepage = CP_ACP;
4427 getText.flags = GT_DEFAULT;
4428 getText.lpDefaultChar = NULL;
4429 getText.lpUsedDefChar = NULL;
4430 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4431 ok(strcmp(buffer, "RichEdit1") == 0,
4432 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4434 /* Test number of lines reported after EM_REPLACESEL */
4435 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4436 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4438 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4439 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4440 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4441 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4442 r = strcmp(buffer, "RichEdit1\r\n");
4443 ok(0 == r, "expected %d, got %d\n", 0, r);
4445 getText.codepage = CP_ACP;
4446 getText.flags = GT_DEFAULT;
4447 getText.lpDefaultChar = NULL;
4448 getText.lpUsedDefChar = NULL;
4449 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4450 ok(strcmp(buffer, "RichEdit1\r") == 0,
4451 "EM_GETTEXTEX returned incorrect string\n");
4453 /* Test number of lines reported after EM_REPLACESEL */
4454 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4455 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4457 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4458 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4459 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4461 /* Test number of lines reported after EM_REPLACESEL */
4462 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4463 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4465 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4466 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4467 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4468 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4470 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4471 r = strcmp(buffer, "RichEdit1\r\n");
4472 ok(0 == r, "expected %d, got %d\n", 0, r);
4474 getText.codepage = CP_ACP;
4475 getText.flags = GT_DEFAULT;
4476 getText.lpDefaultChar = NULL;
4477 getText.lpUsedDefChar = NULL;
4478 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4479 ok(strcmp(buffer, "RichEdit1\r") == 0,
4480 "EM_GETTEXTEX returned incorrect string\n");
4482 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4483 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4484 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4485 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4487 /* The following tests show that richedit should handle the special \r\r\n
4488 sequence by turning it into a single space on insertion. However,
4489 EM_REPLACESEL on WinXP returns the number of characters in the original
4493 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4494 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4495 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4496 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4497 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4498 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4499 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4501 /* Test the actual string */
4503 getText.codepage = CP_ACP;
4504 getText.flags = GT_DEFAULT;
4505 getText.lpDefaultChar = NULL;
4506 getText.lpUsedDefChar = NULL;
4507 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4508 ok(strcmp(buffer, "\r\r") == 0,
4509 "EM_GETTEXTEX returned incorrect string\n");
4511 /* Test number of lines reported after EM_REPLACESEL */
4512 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4513 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4516 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4517 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4518 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4519 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4520 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4521 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4523 /* Test the actual string */
4525 getText.codepage = CP_ACP;
4526 getText.flags = GT_DEFAULT;
4527 getText.lpDefaultChar = NULL;
4528 getText.lpUsedDefChar = NULL;
4529 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4530 ok(strcmp(buffer, " ") == 0,
4531 "EM_GETTEXTEX returned incorrect string\n");
4533 /* Test number of lines reported after EM_REPLACESEL */
4534 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4535 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4538 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4539 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4540 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4541 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4542 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4543 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4545 /* Test the actual string */
4547 getText.codepage = CP_ACP;
4548 getText.flags = GT_DEFAULT;
4549 getText.lpDefaultChar = NULL;
4550 getText.lpUsedDefChar = NULL;
4551 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4552 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4553 "EM_GETTEXTEX returned incorrect string\n");
4555 /* Test number of lines reported after EM_REPLACESEL */
4556 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4557 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4559 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4560 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4561 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4562 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4563 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4564 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4565 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4567 /* Test the actual string */
4569 getText.codepage = CP_ACP;
4570 getText.flags = GT_DEFAULT;
4571 getText.lpDefaultChar = NULL;
4572 getText.lpUsedDefChar = NULL;
4573 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4574 ok(strcmp(buffer, " \r") == 0,
4575 "EM_GETTEXTEX returned incorrect string\n");
4577 /* Test number of lines reported after EM_REPLACESEL */
4578 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4579 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4582 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4583 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4584 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4585 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4586 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4587 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4589 /* Test the actual string */
4591 getText.codepage = CP_ACP;
4592 getText.flags = GT_DEFAULT;
4593 getText.lpDefaultChar = NULL;
4594 getText.lpUsedDefChar = NULL;
4595 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4596 ok(strcmp(buffer, " \r\r") == 0,
4597 "EM_GETTEXTEX returned incorrect string\n");
4599 /* Test number of lines reported after EM_REPLACESEL */
4600 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4601 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4603 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4604 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4605 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4606 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4607 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4608 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4609 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4611 /* Test the actual string */
4613 getText.codepage = CP_ACP;
4614 getText.flags = GT_DEFAULT;
4615 getText.lpDefaultChar = NULL;
4616 getText.lpUsedDefChar = NULL;
4617 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4618 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4619 "EM_GETTEXTEX returned incorrect string\n");
4621 /* Test number of lines reported after EM_REPLACESEL */
4622 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4623 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4625 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4626 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4627 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4628 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4629 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4630 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4631 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4633 /* Test the actual string */
4635 getText.codepage = CP_ACP;
4636 getText.flags = GT_DEFAULT;
4637 getText.lpDefaultChar = NULL;
4638 getText.lpUsedDefChar = NULL;
4639 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4640 ok(strcmp(buffer, "\r\r") == 0,
4641 "EM_GETTEXTEX returned incorrect string\n");
4643 /* Test number of lines reported after EM_REPLACESEL */
4644 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4645 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4648 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4649 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4650 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4651 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4652 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4653 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4655 /* Test the actual string */
4657 getText.codepage = CP_ACP;
4658 getText.flags = GT_DEFAULT;
4659 getText.lpDefaultChar = NULL;
4660 getText.lpUsedDefChar = NULL;
4661 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4662 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4663 "EM_GETTEXTEX returned incorrect string\n");
4665 /* Test number of lines reported after EM_REPLACESEL */
4666 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4667 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4670 /* This is needed to avoid interferring with keybd_event calls
4671 * on other tests that simulate keyboard events. */
4672 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4674 DestroyWindow(hwndRichEdit);
4677 static void test_WM_PASTE(void)
4680 char buffer[1024] = {0};
4681 const char* text1 = "testing paste\r";
4682 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4683 const char* text1_after = "testing paste\r\n";
4684 const char* text2 = "testing paste\r\rtesting paste";
4685 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4686 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4687 HWND hwndRichEdit = new_richedit(NULL);
4689 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4690 * to test the state of the modifiers (Ctrl/Alt/Shift).
4692 * Therefore Ctrl-<key> keystrokes need to be simulated with
4693 * keybd_event or by using SetKeyboardState to set the modifiers
4694 * and SendMessage to simulate the keystrokes.
4697 /* Sent keystrokes with keybd_event */
4698 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4699 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4700 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4701 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4702 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4704 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4705 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4707 SEND_CTRL_C(hwndRichEdit); /* Copy */
4708 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4709 SEND_CTRL_V(hwndRichEdit); /* Paste */
4710 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4711 /* Pasted text should be visible at this step */
4712 result = strcmp(text1_step1, buffer);
4714 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4716 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4717 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718 /* Text should be the same as before (except for \r -> \r\n conversion) */
4719 result = strcmp(text1_after, buffer);
4721 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4723 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4724 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4725 SEND_CTRL_C(hwndRichEdit); /* Copy */
4726 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4727 SEND_CTRL_V(hwndRichEdit); /* Paste */
4728 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4729 /* Pasted text should be visible at this step */
4730 result = strcmp(text3, buffer);
4732 "test paste: strcmp = %i\n", result);
4733 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4734 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4735 /* Text should be the same as before (except for \r -> \r\n conversion) */
4736 result = strcmp(text2_after, buffer);
4738 "test paste: strcmp = %i\n", result);
4739 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4740 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4741 /* Text should revert to post-paste state */
4742 result = strcmp(buffer,text3);
4744 "test paste: strcmp = %i\n", result);
4752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4753 /* Send WM_CHAR to simulates Ctrl-V */
4754 SendMessage(hwndRichEdit, WM_CHAR, 22,
4755 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4756 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4757 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4758 result = strcmp(buffer,"");
4760 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4762 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4763 * with SetKeyboard state. */
4765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4766 /* Simulates paste (Ctrl-V) */
4767 hold_key(VK_CONTROL);
4768 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4769 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4770 release_key(VK_CONTROL);
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 result = strcmp(buffer,"paste");
4774 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4777 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4778 /* Simulates copy (Ctrl-C) */
4779 hold_key(VK_CONTROL);
4780 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4781 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4782 release_key(VK_CONTROL);
4783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4784 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4785 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4786 result = strcmp(buffer,"testing");
4788 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4790 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4791 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4792 /* Simulates select all (Ctrl-A) */
4793 hold_key(VK_CONTROL);
4794 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4795 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4796 /* Simulates select cut (Ctrl-X) */
4797 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4798 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4799 release_key(VK_CONTROL);
4800 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4801 result = strcmp(buffer,"");
4803 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4804 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4805 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4806 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4807 result = strcmp(buffer,"cut\r\n");
4808 todo_wine ok(result == 0,
4809 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4810 /* Simulates undo (Ctrl-Z) */
4811 hold_key(VK_CONTROL);
4812 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4813 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4814 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4815 result = strcmp(buffer,"");
4817 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4818 /* Simulates redo (Ctrl-Y) */
4819 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4820 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4821 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4822 result = strcmp(buffer,"cut\r\n");
4823 todo_wine ok(result == 0,
4824 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4825 release_key(VK_CONTROL);
4827 DestroyWindow(hwndRichEdit);
4830 static void test_EM_FORMATRANGE(void)
4832 int r, i, tpp_x, tpp_y;
4834 HWND hwndRichEdit = new_richedit(NULL);
4836 BOOL skip_non_english;
4837 static const struct {
4838 const char *string; /* The string */
4839 int first; /* First 'pagebreak', 0 for don't care */
4840 int second; /* Second 'pagebreak', 0 for don't care */
4842 {"WINE wine", 0, 0},
4843 {"WINE wineWine", 0, 0},
4844 {"WINE\r\nwine\r\nwine", 5, 10},
4845 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4846 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4849 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4850 if (skip_non_english)
4851 skip("Skipping some tests on non-English platform\n");
4853 hdc = GetDC(hwndRichEdit);
4854 ok(hdc != NULL, "Could not get HDC\n");
4856 /* Calculate the twips per pixel */
4857 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4858 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4860 /* Test the simple case where all the text fits in the page rect. */
4861 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4862 fr.hdc = fr.hdcTarget = hdc;
4863 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4864 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4865 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4868 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4869 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4871 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4872 fr.rc.bottom = fr.rcPage.bottom;
4873 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4874 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4876 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4878 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4880 GETTEXTLENGTHEX gtl;
4884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4886 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4887 gtl.codepage = CP_ACP;
4888 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4890 /* Get some size information for the string */
4891 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4893 /* Define the box to be half the width needed and a bit larger than the height.
4894 * Changes to the width means we have at least 2 pages. Changes to the height
4895 * is done so we can check the changing of fr.rc.bottom.
4897 fr.hdc = fr.hdcTarget = hdc;
4898 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4899 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4900 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4902 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4904 ok(r == len, "Expected %d, got %d\n", len, r);
4907 /* We know that the page can't hold the full string. See how many characters
4908 * are on the first one
4912 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4914 if (! skip_non_english)
4915 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4917 if (fmtstrings[i].first)
4919 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4922 ok(r < len, "Expected < %d, got %d\n", len, r);
4924 /* Do another page */
4926 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4927 if (fmtstrings[i].second)
4929 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4931 else if (! skip_non_english)
4932 ok (r < len, "Expected < %d, got %d\n", len, r);
4934 /* There is at least on more page, but we don't care */
4936 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4938 ok(r == len, "Expected %d, got %d\n", len, r);
4942 ReleaseDC(NULL, hdc);
4943 DestroyWindow(hwndRichEdit);
4946 static int nCallbackCount = 0;
4948 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4951 const char text[] = {'t','e','s','t'};
4953 if (sizeof(text) <= cb)
4955 if ((int)dwCookie != nCallbackCount)
4961 memcpy (pbBuff, text, sizeof(text));
4962 *pcb = sizeof(text);
4969 return 1; /* indicates callback failed */
4972 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4977 const char** str = (const char**)dwCookie;
4978 int size = strlen(*str);
4984 memcpy(pbBuff, *str, *pcb);
4990 struct StringWithLength {
4995 /* This callback is used to handled the null characters in a string. */
4996 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5001 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5002 int size = str->length;
5008 memcpy(pbBuff, str->buffer, *pcb);
5009 str->buffer += *pcb;
5010 str->length -= *pcb;
5015 static void test_EM_STREAMIN(void)
5017 HWND hwndRichEdit = new_richedit(NULL);
5020 char buffer[1024] = {0};
5022 const char * streamText0 = "{\\rtf1 TestSomeText}";
5023 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5024 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5026 const char * streamText1 =
5027 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5028 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5031 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5032 const char * streamText2 =
5033 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5034 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5035 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5036 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5037 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5038 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5039 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5041 const char * streamText3 = "RichEdit1";
5043 struct StringWithLength cookieForStream4;
5044 const char * streamText4 =
5045 "This text just needs to be long enough to cause run to be split onto "
5046 "two separate lines and make sure the null terminating character is "
5047 "handled properly.\0";
5048 int length4 = strlen(streamText4) + 1;
5049 cookieForStream4.buffer = (char *)streamText4;
5050 cookieForStream4.length = length4;
5052 /* Minimal test without \par at the end */
5053 es.dwCookie = (DWORD_PTR)&streamText0;
5055 es.pfnCallback = test_EM_STREAMIN_esCallback;
5056 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5058 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5060 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5061 result = strcmp (buffer,"TestSomeText");
5063 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5064 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5066 /* Native richedit 2.0 ignores last \par */
5067 es.dwCookie = (DWORD_PTR)&streamText0a;
5069 es.pfnCallback = test_EM_STREAMIN_esCallback;
5070 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5072 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5074 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5075 result = strcmp (buffer,"TestSomeText");
5077 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5078 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5080 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5081 es.dwCookie = (DWORD_PTR)&streamText0b;
5083 es.pfnCallback = test_EM_STREAMIN_esCallback;
5084 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5086 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5088 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5089 result = strcmp (buffer,"TestSomeText\r\n");
5091 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5092 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5094 es.dwCookie = (DWORD_PTR)&streamText1;
5096 es.pfnCallback = test_EM_STREAMIN_esCallback;
5097 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5099 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5101 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5102 result = strcmp (buffer,"TestSomeText");
5104 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5105 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5107 es.dwCookie = (DWORD_PTR)&streamText2;
5109 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5111 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5113 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5114 ok (strlen(buffer) == 0,
5115 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5116 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5118 es.dwCookie = (DWORD_PTR)&streamText3;
5120 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5122 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5124 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5125 ok (strlen(buffer) == 0,
5126 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5127 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5129 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5131 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5132 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5134 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5135 ok (result == length4,
5136 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5137 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5139 DestroyWindow(hwndRichEdit);
5142 static void test_EM_StreamIn_Undo(void)
5144 /* The purpose of this test is to determine when a EM_StreamIn should be
5145 * undoable. This is important because WM_PASTE currently uses StreamIn and
5146 * pasting should always be undoable but streaming isn't always.
5149 * StreamIn plain text without SFF_SELECTION.
5150 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5151 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5152 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5153 * Feel free to add tests for other text modes or StreamIn things.
5157 HWND hwndRichEdit = new_richedit(NULL);
5160 char buffer[1024] = {0};
5161 const char randomtext[] = "Some text";
5163 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5165 /* StreamIn, no SFF_SELECTION */
5166 es.dwCookie = nCallbackCount;
5167 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5168 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5169 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5170 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5171 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5172 result = strcmp (buffer,"test");
5174 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5176 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5177 ok (result == FALSE,
5178 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5180 /* StreamIn, SFF_SELECTION, but nothing selected */
5181 es.dwCookie = nCallbackCount;
5182 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5183 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5184 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5185 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5186 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5187 result = strcmp (buffer,"testSome text");
5189 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5191 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5193 "EM_STREAMIN with SFF_SELECTION but no selection set "
5194 "should create an undo\n");
5196 /* StreamIn, SFF_SELECTION, with a selection */
5197 es.dwCookie = nCallbackCount;
5198 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5199 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5200 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5201 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5202 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5203 result = strcmp (buffer,"Sometesttext");
5205 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5207 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5209 "EM_STREAMIN with SFF_SELECTION and selection set "
5210 "should create an undo\n");
5212 DestroyWindow(hwndRichEdit);
5215 static BOOL is_em_settextex_supported(HWND hwnd)
5217 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5218 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5221 static void test_unicode_conversions(void)
5223 static const WCHAR tW[] = {'t',0};
5224 static const WCHAR teW[] = {'t','e',0};
5225 static const WCHAR textW[] = {'t','e','s','t',0};
5226 static const char textA[] = "test";
5230 int em_settextex_supported, ret;
5232 #define set_textA(hwnd, wm_set_text, txt) \
5234 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5235 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5236 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5237 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5238 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5240 #define expect_textA(hwnd, wm_get_text, txt) \
5242 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5243 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5244 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5245 memset(bufA, 0xAA, sizeof(bufA)); \
5246 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5247 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5248 ret = lstrcmpA(bufA, txt); \
5249 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5252 #define set_textW(hwnd, wm_set_text, txt) \
5254 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5255 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5256 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5257 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5258 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5260 #define expect_textW(hwnd, wm_get_text, txt) \
5262 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5263 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5264 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5265 memset(bufW, 0xAA, sizeof(bufW)); \
5266 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5267 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5268 ret = lstrcmpW(bufW, txt); \
5269 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5271 #define expect_empty(hwnd, wm_get_text) \
5273 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5274 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5275 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5276 memset(bufA, 0xAA, sizeof(bufA)); \
5277 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5278 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5279 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5282 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5283 0, 0, 200, 60, 0, 0, 0, 0);
5284 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5286 ret = IsWindowUnicode(hwnd);
5287 ok(ret, "RichEdit20W should be unicode under NT\n");
5289 /* EM_SETTEXTEX is supported starting from version 3.0 */
5290 em_settextex_supported = is_em_settextex_supported(hwnd);
5291 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5292 em_settextex_supported ? "" : "NOT ");
5294 expect_empty(hwnd, WM_GETTEXT);
5295 expect_empty(hwnd, EM_GETTEXTEX);
5297 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5298 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5299 expect_textA(hwnd, WM_GETTEXT, "t");
5300 expect_textA(hwnd, EM_GETTEXTEX, "t");
5301 expect_textW(hwnd, EM_GETTEXTEX, tW);
5303 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5304 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5305 expect_textA(hwnd, WM_GETTEXT, "te");
5306 expect_textA(hwnd, EM_GETTEXTEX, "te");
5307 expect_textW(hwnd, EM_GETTEXTEX, teW);
5309 set_textA(hwnd, WM_SETTEXT, NULL);
5310 expect_empty(hwnd, WM_GETTEXT);
5311 expect_empty(hwnd, EM_GETTEXTEX);
5313 set_textA(hwnd, WM_SETTEXT, textA);
5314 expect_textA(hwnd, WM_GETTEXT, textA);
5315 expect_textA(hwnd, EM_GETTEXTEX, textA);
5316 expect_textW(hwnd, EM_GETTEXTEX, textW);
5318 if (em_settextex_supported)
5320 set_textA(hwnd, EM_SETTEXTEX, textA);
5321 expect_textA(hwnd, WM_GETTEXT, textA);
5322 expect_textA(hwnd, EM_GETTEXTEX, textA);
5323 expect_textW(hwnd, EM_GETTEXTEX, textW);
5326 set_textW(hwnd, WM_SETTEXT, textW);
5327 expect_textW(hwnd, WM_GETTEXT, textW);
5328 expect_textA(hwnd, WM_GETTEXT, textA);
5329 expect_textW(hwnd, EM_GETTEXTEX, textW);
5330 expect_textA(hwnd, EM_GETTEXTEX, textA);
5332 if (em_settextex_supported)
5334 set_textW(hwnd, EM_SETTEXTEX, textW);
5335 expect_textW(hwnd, WM_GETTEXT, textW);
5336 expect_textA(hwnd, WM_GETTEXT, textA);
5337 expect_textW(hwnd, EM_GETTEXTEX, textW);
5338 expect_textA(hwnd, EM_GETTEXTEX, textA);
5340 DestroyWindow(hwnd);
5342 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5343 0, 0, 200, 60, 0, 0, 0, 0);
5344 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5346 ret = IsWindowUnicode(hwnd);
5347 ok(!ret, "RichEdit20A should NOT be unicode\n");
5349 set_textA(hwnd, WM_SETTEXT, textA);
5350 expect_textA(hwnd, WM_GETTEXT, textA);
5351 expect_textA(hwnd, EM_GETTEXTEX, textA);
5352 expect_textW(hwnd, EM_GETTEXTEX, textW);
5354 if (em_settextex_supported)
5356 set_textA(hwnd, EM_SETTEXTEX, textA);
5357 expect_textA(hwnd, WM_GETTEXT, textA);
5358 expect_textA(hwnd, EM_GETTEXTEX, textA);
5359 expect_textW(hwnd, EM_GETTEXTEX, textW);
5362 set_textW(hwnd, WM_SETTEXT, textW);
5363 expect_textW(hwnd, WM_GETTEXT, textW);
5364 expect_textA(hwnd, WM_GETTEXT, textA);
5365 expect_textW(hwnd, EM_GETTEXTEX, textW);
5366 expect_textA(hwnd, EM_GETTEXTEX, textA);
5368 if (em_settextex_supported)
5370 set_textW(hwnd, EM_SETTEXTEX, textW);
5371 expect_textW(hwnd, WM_GETTEXT, textW);
5372 expect_textA(hwnd, WM_GETTEXT, textA);
5373 expect_textW(hwnd, EM_GETTEXTEX, textW);
5374 expect_textA(hwnd, EM_GETTEXTEX, textA);
5376 DestroyWindow(hwnd);
5379 static void test_WM_CHAR(void)
5383 const char * char_list = "abc\rabc\r";
5384 const char * expected_content_single = "abcabc";
5385 const char * expected_content_multi = "abc\r\nabc\r\n";
5386 char buffer[64] = {0};
5389 /* single-line control must IGNORE carriage returns */
5390 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5391 0, 0, 200, 60, 0, 0, 0, 0);
5392 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5395 while (*p != '\0') {
5396 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5397 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5398 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5399 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5403 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5404 ret = strcmp(buffer, expected_content_single);
5405 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5407 DestroyWindow(hwnd);
5409 /* multi-line control inserts CR normally */
5410 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5411 0, 0, 200, 60, 0, 0, 0, 0);
5412 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5415 while (*p != '\0') {
5416 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5417 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5418 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5419 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5423 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5424 ret = strcmp(buffer, expected_content_multi);
5425 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5427 DestroyWindow(hwnd);
5430 static void test_EM_GETTEXTLENGTHEX(void)
5433 GETTEXTLENGTHEX gtl;
5435 const char * base_string = "base string";
5436 const char * test_string = "a\nb\n\n\r\n";
5437 const char * test_string_after = "a";
5438 const char * test_string_2 = "a\rtest\rstring";
5439 char buffer[64] = {0};
5442 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5443 0, 0, 200, 60, 0, 0, 0, 0);
5444 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5446 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5447 gtl.codepage = CP_ACP;
5448 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5449 ok(ret == 0, "ret %d\n",ret);
5451 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5452 gtl.codepage = CP_ACP;
5453 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5454 ok(ret == 0, "ret %d\n",ret);
5456 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5458 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5459 gtl.codepage = CP_ACP;
5460 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5461 ok(ret == strlen(base_string), "ret %d\n",ret);
5463 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5464 gtl.codepage = CP_ACP;
5465 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5466 ok(ret == strlen(base_string), "ret %d\n",ret);
5468 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5470 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5471 gtl.codepage = CP_ACP;
5472 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5473 ok(ret == 1, "ret %d\n",ret);
5475 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5476 gtl.codepage = CP_ACP;
5477 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5478 ok(ret == 1, "ret %d\n",ret);
5480 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5481 ret = strcmp(buffer, test_string_after);
5482 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5484 DestroyWindow(hwnd);
5487 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5488 0, 0, 200, 60, 0, 0, 0, 0);
5489 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5491 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5492 gtl.codepage = CP_ACP;
5493 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5494 ok(ret == 0, "ret %d\n",ret);
5496 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5497 gtl.codepage = CP_ACP;
5498 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5499 ok(ret == 0, "ret %d\n",ret);
5501 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5503 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5504 gtl.codepage = CP_ACP;
5505 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5506 ok(ret == strlen(base_string), "ret %d\n",ret);
5508 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5509 gtl.codepage = CP_ACP;
5510 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5511 ok(ret == strlen(base_string), "ret %d\n",ret);
5513 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5515 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5516 gtl.codepage = CP_ACP;
5517 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5518 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5520 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5521 gtl.codepage = CP_ACP;
5522 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5523 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5525 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5527 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5528 gtl.codepage = CP_ACP;
5529 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5530 ok(ret == 10, "ret %d\n",ret);
5532 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5533 gtl.codepage = CP_ACP;
5534 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5535 ok(ret == 6, "ret %d\n",ret);
5537 /* Unicode/NUMCHARS/NUMBYTES */
5538 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5540 gtl.flags = GTL_DEFAULT;
5541 gtl.codepage = 1200;
5542 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5543 ok(ret == lstrlen(test_string_2),
5544 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5546 gtl.flags = GTL_NUMCHARS;
5547 gtl.codepage = 1200;
5548 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5549 ok(ret == lstrlen(test_string_2),
5550 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5552 gtl.flags = GTL_NUMBYTES;
5553 gtl.codepage = 1200;
5554 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5555 ok(ret == lstrlen(test_string_2)*2,
5556 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5558 gtl.flags = GTL_PRECISE;
5559 gtl.codepage = 1200;
5560 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5561 ok(ret == lstrlen(test_string_2)*2,
5562 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5564 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5565 gtl.codepage = 1200;
5566 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5567 ok(ret == lstrlen(test_string_2),
5568 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5570 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5571 gtl.codepage = 1200;
5572 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5573 ok(ret == E_INVALIDARG,
5574 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5576 DestroyWindow(hwnd);
5580 /* globals that parent and child access when checking event masks & notifications */
5581 static HWND eventMaskEditHwnd = 0;
5582 static int queriedEventMask;
5583 static int watchForEventMask = 0;
5585 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5586 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5588 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5590 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5592 return DefWindowProcA(hwnd, message, wParam, lParam);
5595 /* test event masks in combination with WM_COMMAND */
5596 static void test_eventMask(void)
5601 const char text[] = "foo bar\n";
5604 /* register class to capture WM_COMMAND */
5606 cls.lpfnWndProc = ParentMsgCheckProcA;
5609 cls.hInstance = GetModuleHandleA(0);
5611 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5612 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5613 cls.lpszMenuName = NULL;
5614 cls.lpszClassName = "EventMaskParentClass";
5615 if(!RegisterClassA(&cls)) assert(0);
5617 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5618 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5619 ok (parent != 0, "Failed to create parent window\n");
5621 eventMaskEditHwnd = new_richedit(parent);
5622 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5624 eventMask = ENM_CHANGE | ENM_UPDATE;
5625 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5626 ok(ret == ENM_NONE, "wrong event mask\n");
5627 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5628 ok(ret == eventMask, "failed to set event mask\n");
5630 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5631 queriedEventMask = 0; /* initialize to something other than we expect */
5632 watchForEventMask = EN_CHANGE;
5633 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5634 ok(ret == TRUE, "failed to set text\n");
5635 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5636 notification in response to WM_SETTEXT */
5637 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5638 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5640 /* check to see if EN_CHANGE is sent when redraw is turned off */
5641 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5642 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5643 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5644 /* redraw is disabled by making the window invisible. */
5645 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5646 queriedEventMask = 0; /* initialize to something other than we expect */
5647 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5648 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5649 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5650 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5651 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5653 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5654 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5655 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5656 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5657 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5658 watchForEventMask = EN_UPDATE;
5659 queriedEventMask = 0; /* initialize to something other than we expect */
5660 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5661 ok(queriedEventMask == 0,
5662 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5663 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5664 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5665 queriedEventMask = 0; /* initialize to something other than we expect */
5666 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5667 ok(queriedEventMask == eventMask,
5668 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5671 DestroyWindow(parent);
5674 static int received_WM_NOTIFY = 0;
5675 static int modify_at_WM_NOTIFY = 0;
5676 static BOOL filter_on_WM_NOTIFY = FALSE;
5677 static HWND hwndRichedit_WM_NOTIFY;
5679 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5681 if(message == WM_NOTIFY)
5683 received_WM_NOTIFY = 1;
5684 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5685 if (filter_on_WM_NOTIFY) return TRUE;
5687 return DefWindowProcA(hwnd, message, wParam, lParam);
5690 static void test_WM_NOTIFY(void)
5695 int sel_start, sel_end;
5697 /* register class to capture WM_NOTIFY */
5699 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5702 cls.hInstance = GetModuleHandleA(0);
5704 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5705 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5706 cls.lpszMenuName = NULL;
5707 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5708 if(!RegisterClassA(&cls)) assert(0);
5710 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5711 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5712 ok (parent != 0, "Failed to create parent window\n");
5714 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5715 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5717 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5719 /* Notifications for selection change should only be sent when selection
5720 actually changes. EM_SETCHARFORMAT is one message that calls
5721 ME_CommitUndo, which should check whether message should be sent */
5722 received_WM_NOTIFY = 0;
5723 cf2.cbSize = sizeof(CHARFORMAT2);
5724 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5725 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5726 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5727 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5728 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5730 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5732 received_WM_NOTIFY = 0;
5733 modify_at_WM_NOTIFY = 0;
5734 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5735 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5736 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5738 received_WM_NOTIFY = 0;
5739 modify_at_WM_NOTIFY = 0;
5740 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5741 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5743 received_WM_NOTIFY = 0;
5744 modify_at_WM_NOTIFY = 0;
5745 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5746 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5747 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5749 /* Test for WM_NOTIFY messages with redraw disabled. */
5750 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5751 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5752 received_WM_NOTIFY = 0;
5753 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5754 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5755 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5757 /* Test filtering key events. */
5758 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5759 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5760 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5761 received_WM_NOTIFY = 0;
5762 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5763 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5764 ok(sel_start == 1 && sel_end == 1,
5765 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5766 filter_on_WM_NOTIFY = TRUE;
5767 received_WM_NOTIFY = 0;
5768 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5769 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5770 ok(sel_start == 1 && sel_end == 1,
5771 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5773 /* test with owner set to NULL */
5774 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5775 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5776 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5777 ok(sel_start == 1 && sel_end == 1,
5778 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5780 DestroyWindow(hwndRichedit_WM_NOTIFY);
5781 DestroyWindow(parent);
5784 static void test_undo_coalescing(void)
5788 char buffer[64] = {0};
5790 /* multi-line control inserts CR normally */
5791 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5792 0, 0, 200, 60, 0, 0, 0, 0);
5793 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5795 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5796 ok (result == FALSE, "Can undo after window creation.\n");
5797 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5798 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5799 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5800 ok (result == FALSE, "Can redo after window creation.\n");
5801 result = SendMessage(hwnd, EM_REDO, 0, 0);
5802 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5804 /* Test the effect of arrows keys during typing on undo transactions*/
5805 simulate_typing_characters(hwnd, "one two three");
5806 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5807 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5808 simulate_typing_characters(hwnd, " four five six");
5810 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5811 ok (result == FALSE, "Can redo before anything is undone.\n");
5812 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5813 ok (result == TRUE, "Cannot undo typed characters.\n");
5814 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5815 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5816 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5817 ok (result == TRUE, "Cannot redo after undo.\n");
5818 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5819 result = strcmp(buffer, "one two three");
5820 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5822 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5823 ok (result == TRUE, "Cannot undo typed characters.\n");
5824 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5825 ok (result == TRUE, "Failed to undo typed characters.\n");
5826 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5827 result = strcmp(buffer, "");
5828 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5830 /* Test the effect of focus changes during typing on undo transactions*/
5831 simulate_typing_characters(hwnd, "one two three");
5832 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5833 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5834 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5835 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5836 simulate_typing_characters(hwnd, " four five six");
5837 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5838 ok (result == TRUE, "Failed to undo typed characters.\n");
5839 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5840 result = strcmp(buffer, "one two three");
5841 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5843 /* Test the effect of the back key during typing on undo transactions */
5844 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5845 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5846 ok (result == TRUE, "Failed to clear the text.\n");
5847 simulate_typing_characters(hwnd, "one two threa");
5848 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5849 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5850 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5851 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5852 simulate_typing_characters(hwnd, "e four five six");
5853 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5854 ok (result == TRUE, "Failed to undo typed characters.\n");
5855 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5856 result = strcmp(buffer, "");
5857 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5859 /* Test the effect of the delete key during typing on undo transactions */
5860 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5861 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5862 ok(result == TRUE, "Failed to set the text.\n");
5863 SendMessage(hwnd, EM_SETSEL, 1, 1);
5864 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5865 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5866 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5867 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5868 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5869 ok (result == TRUE, "Failed to undo typed characters.\n");
5870 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5871 result = strcmp(buffer, "acd");
5872 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5873 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5874 ok (result == TRUE, "Failed to undo typed characters.\n");
5875 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5876 result = strcmp(buffer, "abcd");
5877 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5879 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5880 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5881 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5882 ok (result == TRUE, "Failed to clear the text.\n");
5883 simulate_typing_characters(hwnd, "one two three");
5884 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5885 ok (result == 0, "expected %d but got %d\n", 0, result);
5886 simulate_typing_characters(hwnd, " four five six");
5887 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5888 ok (result == TRUE, "Failed to undo typed characters.\n");
5889 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5890 result = strcmp(buffer, "one two three");
5891 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5892 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5893 ok (result == TRUE, "Failed to undo typed characters.\n");
5894 ok (result == TRUE, "Failed to undo typed characters.\n");
5895 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5896 result = strcmp(buffer, "");
5897 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5899 DestroyWindow(hwnd);
5902 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5906 /* MSDN lied, length is actually the number of bytes. */
5907 length = bytes / sizeof(WCHAR);
5910 case WB_ISDELIMITER:
5911 return text[pos] == 'X';
5913 case WB_MOVEWORDLEFT:
5914 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5916 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5919 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5923 case WB_MOVEWORDRIGHT:
5924 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5926 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5929 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5933 ok(FALSE, "Unexpected code %d\n", code);
5939 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5940 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5942 static void test_word_movement(void)
5946 int sel_start, sel_end;
5947 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5949 /* multi-line control inserts CR normally */
5950 hwnd = new_richedit(NULL);
5952 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5953 ok (result == TRUE, "Failed to clear the text.\n");
5954 SendMessage(hwnd, EM_SETSEL, 0, 0);
5955 /* |one two three */
5957 SEND_CTRL_RIGHT(hwnd);
5958 /* one |two three */
5959 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5960 ok(sel_start == sel_end, "Selection should be empty\n");
5961 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5963 SEND_CTRL_RIGHT(hwnd);
5964 /* one two |three */
5965 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5966 ok(sel_start == sel_end, "Selection should be empty\n");
5967 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5969 SEND_CTRL_LEFT(hwnd);
5970 /* one |two three */
5971 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5972 ok(sel_start == sel_end, "Selection should be empty\n");
5973 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5975 SEND_CTRL_LEFT(hwnd);
5976 /* |one two three */
5977 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5978 ok(sel_start == sel_end, "Selection should be empty\n");
5979 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5981 SendMessage(hwnd, EM_SETSEL, 8, 8);
5982 /* one two | three */
5983 SEND_CTRL_RIGHT(hwnd);
5984 /* one two |three */
5985 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5986 ok(sel_start == sel_end, "Selection should be empty\n");
5987 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5989 SendMessage(hwnd, EM_SETSEL, 11, 11);
5990 /* one two th|ree */
5991 SEND_CTRL_LEFT(hwnd);
5992 /* one two |three */
5993 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5994 ok(sel_start == sel_end, "Selection should be empty\n");
5995 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5997 /* Test with a custom word break procedure that uses X as the delimiter. */
5998 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5999 ok (result == TRUE, "Failed to clear the text.\n");
6000 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6001 /* |one twoXthree */
6002 SEND_CTRL_RIGHT(hwnd);
6003 /* one twoX|three */
6004 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6005 ok(sel_start == sel_end, "Selection should be empty\n");
6006 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6008 DestroyWindow(hwnd);
6010 /* Make sure the behaviour is the same with a unicode richedit window,
6011 * and using unicode functions. */
6013 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6014 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6015 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6017 /* Test with a custom word break procedure that uses X as the delimiter. */
6018 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6019 ok (result == TRUE, "Failed to clear the text.\n");
6020 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6021 /* |one twoXthree */
6022 SEND_CTRL_RIGHT(hwnd);
6023 /* one twoX|three */
6024 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6025 ok(sel_start == sel_end, "Selection should be empty\n");
6026 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6028 DestroyWindow(hwnd);
6031 static void test_EM_CHARFROMPOS(void)
6040 /* multi-line control inserts CR normally */
6041 hwnd = new_richedit(NULL);
6042 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6043 (LPARAM)"one two three four five six seven\reight");
6044 ok(result == 1, "Expected 1, got %d\n", result);
6045 GetClientRect(hwnd, &rcClient);
6047 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6048 ok(result == 34, "expected character index of 34 but got %d\n", result);
6050 /* Test with points outside the bounds of the richedit control. */
6053 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6054 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6058 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6059 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6063 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6064 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6068 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6069 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6072 point.y = rcClient.bottom + 1;
6073 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6074 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6077 point.y = rcClient.bottom;
6078 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6079 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6081 DestroyWindow(hwnd);
6084 static void test_word_wrap(void)
6087 POINTL point = {0, 60}; /* This point must be below the first line */
6088 const char *text = "Must be long enough to test line wrapping";
6089 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6090 int res, pos, lines;
6092 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6093 * when specified on window creation and set later. */
6094 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6095 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6096 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6097 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6098 ok(res, "WM_SETTEXT failed.\n");
6099 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6100 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6101 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6102 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6104 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6105 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6106 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6107 DestroyWindow(hwnd);
6109 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6110 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6111 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6113 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6114 ok(res, "WM_SETTEXT failed.\n");
6115 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6116 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6117 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6118 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6120 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6121 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6122 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6123 DestroyWindow(hwnd);
6125 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6126 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6127 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6128 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6129 ok(res, "WM_SETTEXT failed.\n");
6130 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6131 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6133 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6134 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6135 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6136 DestroyWindow(hwnd);
6138 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6139 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6140 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6141 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6142 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6143 ok(res, "WM_SETTEXT failed.\n");
6144 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6145 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6147 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6148 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6149 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6151 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6152 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6153 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6154 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6155 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6157 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6158 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6159 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6160 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6161 DestroyWindow(hwnd);
6163 /* Test to see if wrapping happens with redraw disabled. */
6164 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6165 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6166 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6167 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6168 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6169 ok(res, "EM_REPLACESEL failed.\n");
6170 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6171 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6172 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6173 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6174 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6176 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6177 DestroyWindow(hwnd);
6180 static void test_autoscroll(void)
6182 HWND hwnd = new_richedit(NULL);
6183 int lines, ret, redraw;
6186 for (redraw = 0; redraw <= 1; redraw++) {
6187 trace("testing with WM_SETREDRAW=%d\n", redraw);
6188 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6189 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6190 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6191 ok(lines == 8, "%d lines instead of 8\n", lines);
6192 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6193 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6194 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6195 ret = GetWindowLong(hwnd, GWL_STYLE);
6196 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6198 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6199 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6200 ok(lines == 1, "%d lines instead of 1\n", lines);
6201 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6202 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6203 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6204 ret = GetWindowLong(hwnd, GWL_STYLE);
6205 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6208 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6209 DestroyWindow(hwnd);
6211 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6212 * auto vertical/horizontal scrolling options. */
6213 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6214 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6215 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6216 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6217 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6218 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6219 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6220 ret = GetWindowLong(hwnd, GWL_STYLE);
6221 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6222 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6223 DestroyWindow(hwnd);
6225 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6226 WS_POPUP|ES_MULTILINE,
6227 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6228 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6229 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6230 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6231 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6232 ret = GetWindowLong(hwnd, GWL_STYLE);
6233 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6234 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6235 DestroyWindow(hwnd);
6239 static void test_format_rect(void)
6242 RECT rc, expected, clientRect;
6246 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6247 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6248 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6249 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6251 GetClientRect(hwnd, &clientRect);
6253 expected = clientRect;
6255 expected.right -= 1;
6256 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6257 ok(rc.top == expected.top && rc.left == expected.left &&
6258 rc.bottom == expected.bottom && rc.right == expected.right,
6259 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6260 rc.top, rc.left, rc.bottom, rc.right,
6261 expected.top, expected.left, expected.bottom, expected.right);
6263 for (n = -3; n <= 3; n++)
6270 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6273 expected.top = max(0, rc.top);
6274 expected.left = max(0, rc.left);
6275 expected.bottom = min(clientRect.bottom, rc.bottom);
6276 expected.right = min(clientRect.right, rc.right);
6277 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6278 ok(rc.top == expected.top && rc.left == expected.left &&
6279 rc.bottom == expected.bottom && rc.right == expected.right,
6280 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6281 n, rc.top, rc.left, rc.bottom, rc.right,
6282 expected.top, expected.left, expected.bottom, expected.right);
6286 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6287 expected = clientRect;
6288 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6289 ok(rc.top == expected.top && rc.left == expected.left &&
6290 rc.bottom == expected.bottom && rc.right == expected.right,
6291 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6292 rc.top, rc.left, rc.bottom, rc.right,
6293 expected.top, expected.left, expected.bottom, expected.right);
6295 /* Adding the selectionbar adds the selectionbar width to the left side. */
6296 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6297 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6298 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6299 expected.left += 8; /* selection bar width */
6300 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6301 ok(rc.top == expected.top && rc.left == expected.left &&
6302 rc.bottom == expected.bottom && rc.right == expected.right,
6303 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6304 rc.top, rc.left, rc.bottom, rc.right,
6305 expected.top, expected.left, expected.bottom, expected.right);
6308 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6309 expected = clientRect;
6310 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6311 ok(rc.top == expected.top && rc.left == expected.left &&
6312 rc.bottom == expected.bottom && rc.right == expected.right,
6313 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6314 rc.top, rc.left, rc.bottom, rc.right,
6315 expected.top, expected.left, expected.bottom, expected.right);
6317 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6318 * even if the left side is already 0. */
6319 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6320 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6321 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6322 expected.left -= 8; /* selection bar width */
6323 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6324 ok(rc.top == expected.top && rc.left == expected.left &&
6325 rc.bottom == expected.bottom && rc.right == expected.right,
6326 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6327 rc.top, rc.left, rc.bottom, rc.right,
6328 expected.top, expected.left, expected.bottom, expected.right);
6330 /* Set the absolute value of the formatting rectangle. */
6332 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6333 expected = clientRect;
6334 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6335 ok(rc.top == expected.top && rc.left == expected.left &&
6336 rc.bottom == expected.bottom && rc.right == expected.right,
6337 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6338 n, rc.top, rc.left, rc.bottom, rc.right,
6339 expected.top, expected.left, expected.bottom, expected.right);
6341 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6342 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6343 * tests show that this isn't true. */
6346 rc.bottom = clientRect.bottom - 15;
6347 rc.right = clientRect.right - 15;
6349 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6350 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6351 ok(rc.top == expected.top && rc.left == expected.left &&
6352 rc.bottom == expected.bottom && rc.right == expected.right,
6353 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6354 rc.top, rc.left, rc.bottom, rc.right,
6355 expected.top, expected.left, expected.bottom, expected.right);
6357 /* For some reason it does not limit the values to the client rect with
6358 * a WPARAM value of 1. */
6361 rc.bottom = clientRect.bottom + 15;
6362 rc.right = clientRect.right + 15;
6364 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6365 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6366 ok(rc.top == expected.top && rc.left == expected.left &&
6367 rc.bottom == expected.bottom && rc.right == expected.right,
6368 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6369 rc.top, rc.left, rc.bottom, rc.right,
6370 expected.top, expected.left, expected.bottom, expected.right);
6372 DestroyWindow(hwnd);
6374 /* The extended window style affects the formatting rectangle. */
6375 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6376 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6377 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6378 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6380 GetClientRect(hwnd, &clientRect);
6382 expected = clientRect;
6385 expected.right -= 1;
6386 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6387 ok(rc.top == expected.top && rc.left == expected.left &&
6388 rc.bottom == expected.bottom && rc.right == expected.right,
6389 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6390 rc.top, rc.left, rc.bottom, rc.right,
6391 expected.top, expected.left, expected.bottom, expected.right);
6401 expected.right += 1;
6402 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6403 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6404 ok(rc.top == expected.top && rc.left == expected.left &&
6405 rc.bottom == expected.bottom && rc.right == expected.right,
6406 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6407 rc.top, rc.left, rc.bottom, rc.right,
6408 expected.top, expected.left, expected.bottom, expected.right);
6410 DestroyWindow(hwnd);
6413 static void test_WM_GETDLGCODE(void)
6419 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6421 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6422 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6423 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6424 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6426 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6427 expected = expected | DLGC_WANTMESSAGE;
6428 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6430 DestroyWindow(hwnd);
6432 msg.message = WM_KEYDOWN;
6433 msg.wParam = VK_RETURN;
6434 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6437 msg.time = GetTickCount();
6439 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6440 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6441 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6442 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6444 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6445 expected = expected | DLGC_WANTMESSAGE;
6446 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6448 DestroyWindow(hwnd);
6450 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6451 ES_MULTILINE|WS_POPUP,
6452 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6453 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6455 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6456 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6457 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6459 DestroyWindow(hwnd);
6461 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6462 ES_WANTRETURN|WS_POPUP,
6463 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6464 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6466 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6467 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6468 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6470 DestroyWindow(hwnd);
6472 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6474 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6475 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6477 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6478 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6479 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6481 DestroyWindow(hwnd);
6483 msg.wParam = VK_TAB;
6484 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6486 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6487 ES_MULTILINE|WS_POPUP,
6488 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6489 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6491 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6492 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6493 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6495 DestroyWindow(hwnd);
6497 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6499 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6500 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6502 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6503 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6504 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6506 DestroyWindow(hwnd);
6508 hold_key(VK_CONTROL);
6510 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6511 ES_MULTILINE|WS_POPUP,
6512 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6513 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6515 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6516 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6517 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6519 DestroyWindow(hwnd);
6521 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6523 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6524 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6526 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6527 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6528 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6530 DestroyWindow(hwnd);
6532 release_key(VK_CONTROL);
6535 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6537 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6538 ES_MULTILINE|WS_POPUP,
6539 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6540 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6542 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6543 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6544 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6546 DestroyWindow(hwnd);
6548 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6550 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6551 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6553 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6554 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6555 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6557 DestroyWindow(hwnd);
6559 msg.message = WM_CHAR;
6561 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6562 ES_MULTILINE|WS_POPUP,
6563 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6564 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6566 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6567 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6568 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6570 DestroyWindow(hwnd);
6572 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6574 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6575 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6577 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6578 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6579 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6581 DestroyWindow(hwnd);
6584 static void test_zoom(void)
6590 int numerator, denominator;
6592 hwnd = new_richedit(NULL);
6593 GetClientRect(hwnd, &rc);
6594 pt.x = (rc.right - rc.left) / 2;
6595 pt.y = (rc.bottom - rc.top) / 2;
6596 ClientToScreen(hwnd, &pt);
6598 /* Test initial zoom value */
6599 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6600 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6601 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6602 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6604 /* test scroll wheel */
6605 hold_key(VK_CONTROL);
6606 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6607 MAKELPARAM(pt.x, pt.y));
6608 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6609 release_key(VK_CONTROL);
6611 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6612 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6613 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6614 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6616 /* Test how much the mouse wheel can zoom in and out. */
6617 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6618 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6620 hold_key(VK_CONTROL);
6621 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6622 MAKELPARAM(pt.x, pt.y));
6623 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6624 release_key(VK_CONTROL);
6626 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6627 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6628 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6629 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6631 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6632 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6634 hold_key(VK_CONTROL);
6635 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6636 MAKELPARAM(pt.x, pt.y));
6637 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6638 release_key(VK_CONTROL);
6640 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6641 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6642 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6643 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6645 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6646 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6648 hold_key(VK_CONTROL);
6649 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6650 MAKELPARAM(pt.x, pt.y));
6651 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6652 release_key(VK_CONTROL);
6654 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6655 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6656 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6657 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6659 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6660 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6662 hold_key(VK_CONTROL);
6663 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6664 MAKELPARAM(pt.x, pt.y));
6665 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6666 release_key(VK_CONTROL);
6668 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6669 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6670 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6671 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6673 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6674 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6675 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6677 hold_key(VK_CONTROL);
6678 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6679 MAKELPARAM(pt.x, pt.y));
6680 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6681 release_key(VK_CONTROL);
6683 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6684 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6685 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6686 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6688 /* Test bounds checking on EM_SETZOOM */
6689 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6690 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6692 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6693 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6695 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6696 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6698 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6699 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6700 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6701 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6703 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6704 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6706 /* See if negative numbers are accepted. */
6707 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6708 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6710 /* See if negative numbers are accepted. */
6711 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6712 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6714 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6715 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6716 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6717 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6719 /* Reset the zoom value */
6720 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6721 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6723 DestroyWindow(hwnd);
6726 struct dialog_mode_messages
6728 int wm_getdefid, wm_close, wm_nextdlgctl;
6731 static struct dialog_mode_messages dm_messages;
6733 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6734 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6735 "got %d\n", wmclose, dm_messages.wm_close); \
6736 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6737 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6738 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6739 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6741 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6746 dm_messages.wm_getdefid++;
6747 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6749 dm_messages.wm_nextdlgctl++;
6752 dm_messages.wm_close++;
6756 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6759 static void test_dialogmode(void)
6761 HWND hwRichEdit, hwParent, hwButton;
6767 cls.lpfnWndProc = dialog_mode_wnd_proc;
6770 cls.hInstance = GetModuleHandleA(0);
6772 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6773 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6774 cls.lpszMenuName = NULL;
6775 cls.lpszClassName = "DialogModeParentClass";
6776 if(!RegisterClassA(&cls)) assert(0);
6778 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6779 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6781 /* Test richedit(ES_MULTILINE) */
6783 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6785 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6786 ok(0 == r, "expected 0, got %d\n", r);
6787 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6788 ok(2 == lcount, "expected 2, got %d\n", lcount);
6790 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6791 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6793 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6794 ok(0 == r, "expected 0, got %d\n", r);
6795 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6796 ok(3 == lcount, "expected 3, got %d\n", lcount);
6798 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6799 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6800 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6801 ok(0 == r, "expected 0, got %d\n", r);
6802 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6803 ok(3 == lcount, "expected 3, got %d\n", lcount);
6805 DestroyWindow(hwRichEdit);
6807 /* Test standalone richedit(ES_MULTILINE) */
6809 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6811 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6812 ok(0 == r, "expected 0, got %d\n", r);
6813 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6814 ok(2 == lcount, "expected 2, got %d\n", lcount);
6816 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6817 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6819 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6820 ok(0 == r, "expected 0, got %d\n", r);
6821 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6822 ok(2 == lcount, "expected 2, got %d\n", lcount);
6824 DestroyWindow(hwRichEdit);
6826 /* Check a destination for messages */
6828 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6830 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6831 SetParent( hwRichEdit, NULL);
6833 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6834 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6836 memset(&dm_messages, 0, sizeof(dm_messages));
6837 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6838 ok(0 == r, "expected 0, got %d\n", r);
6839 test_dm_messages(0, 1, 0);
6841 memset(&dm_messages, 0, sizeof(dm_messages));
6842 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6843 ok(0 == r, "expected 0, got %d\n", r);
6844 test_dm_messages(0, 0, 1);
6846 DestroyWindow(hwRichEdit);
6848 /* Check messages from richedit(ES_MULTILINE) */
6850 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6852 memset(&dm_messages, 0, sizeof(dm_messages));
6853 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6854 ok(0 == r, "expected 0, got %d\n", r);
6855 test_dm_messages(0, 0, 0);
6857 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6858 ok(2 == lcount, "expected 2, got %d\n", lcount);
6860 memset(&dm_messages, 0, sizeof(dm_messages));
6861 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6862 ok(0 == r, "expected 0, got %d\n", r);
6863 test_dm_messages(0, 0, 0);
6865 memset(&dm_messages, 0, sizeof(dm_messages));
6866 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6867 ok(0 == r, "expected 0, got %d\n", r);
6868 test_dm_messages(0, 0, 0);
6870 memset(&dm_messages, 0, sizeof(dm_messages));
6871 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6872 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6873 test_dm_messages(0, 0, 0);
6875 memset(&dm_messages, 0, sizeof(dm_messages));
6876 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6877 ok(0 == r, "expected 0, got %d\n", r);
6878 test_dm_messages(0, 1, 0);
6880 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6881 ok(2 == lcount, "expected 2, got %d\n", lcount);
6883 memset(&dm_messages, 0, sizeof(dm_messages));
6884 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6885 ok(0 == r, "expected 0, got %d\n", r);
6886 test_dm_messages(0, 0, 0);
6888 memset(&dm_messages, 0, sizeof(dm_messages));
6889 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6890 ok(0 == r, "expected 0, got %d\n", r);
6891 test_dm_messages(0, 0, 1);
6893 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6894 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6895 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6897 memset(&dm_messages, 0, sizeof(dm_messages));
6898 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6899 ok(0 == r, "expected 0, got %d\n", r);
6900 test_dm_messages(0, 1, 1);
6902 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6903 ok(2 == lcount, "expected 2, got %d\n", lcount);
6905 DestroyWindow(hwButton);
6906 DestroyWindow(hwRichEdit);
6908 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6910 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6912 memset(&dm_messages, 0, sizeof(dm_messages));
6913 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6914 ok(0 == r, "expected 0, got %d\n", r);
6915 test_dm_messages(0, 0, 0);
6917 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6918 ok(2 == lcount, "expected 2, got %d\n", lcount);
6920 memset(&dm_messages, 0, sizeof(dm_messages));
6921 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6922 ok(0 == r, "expected 0, got %d\n", r);
6923 test_dm_messages(0, 0, 0);
6925 memset(&dm_messages, 0, sizeof(dm_messages));
6926 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6927 ok(0 == r, "expected 0, got %d\n", r);
6928 test_dm_messages(0, 0, 0);
6930 memset(&dm_messages, 0, sizeof(dm_messages));
6931 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6932 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6933 test_dm_messages(0, 0, 0);
6935 memset(&dm_messages, 0, sizeof(dm_messages));
6936 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6937 ok(0 == r, "expected 0, got %d\n", r);
6938 test_dm_messages(0, 0, 0);
6940 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6941 ok(3 == lcount, "expected 3, got %d\n", lcount);
6943 memset(&dm_messages, 0, sizeof(dm_messages));
6944 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6945 ok(0 == r, "expected 0, got %d\n", r);
6946 test_dm_messages(0, 0, 0);
6948 memset(&dm_messages, 0, sizeof(dm_messages));
6949 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6950 ok(0 == r, "expected 0, got %d\n", r);
6951 test_dm_messages(0, 0, 1);
6953 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6954 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6955 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6957 memset(&dm_messages, 0, sizeof(dm_messages));
6958 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6959 ok(0 == r, "expected 0, got %d\n", r);
6960 test_dm_messages(0, 0, 0);
6962 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6963 ok(4 == lcount, "expected 4, got %d\n", lcount);
6965 DestroyWindow(hwButton);
6966 DestroyWindow(hwRichEdit);
6968 /* Check messages from richedit(0) */
6970 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
6972 memset(&dm_messages, 0, sizeof(dm_messages));
6973 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6974 ok(0 == r, "expected 0, got %d\n", r);
6975 test_dm_messages(0, 0, 0);
6977 memset(&dm_messages, 0, sizeof(dm_messages));
6978 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6979 ok(0 == r, "expected 0, got %d\n", r);
6980 test_dm_messages(0, 0, 0);
6982 memset(&dm_messages, 0, sizeof(dm_messages));
6983 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6984 ok(0 == r, "expected 0, got %d\n", r);
6985 test_dm_messages(0, 0, 0);
6987 memset(&dm_messages, 0, sizeof(dm_messages));
6988 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6989 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
6990 test_dm_messages(0, 0, 0);
6992 memset(&dm_messages, 0, sizeof(dm_messages));
6993 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6994 ok(0 == r, "expected 0, got %d\n", r);
6995 test_dm_messages(0, 1, 0);
6997 memset(&dm_messages, 0, sizeof(dm_messages));
6998 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6999 ok(0 == r, "expected 0, got %d\n", r);
7000 test_dm_messages(0, 0, 0);
7002 memset(&dm_messages, 0, sizeof(dm_messages));
7003 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7004 ok(0 == r, "expected 0, got %d\n", r);
7005 test_dm_messages(0, 0, 1);
7007 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7008 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7009 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7011 memset(&dm_messages, 0, sizeof(dm_messages));
7012 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7013 ok(0 == r, "expected 0, got %d\n", r);
7014 test_dm_messages(0, 1, 1);
7016 DestroyWindow(hwRichEdit);
7018 /* Check messages from richedit(ES_WANTRETURN) */
7020 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7022 memset(&dm_messages, 0, sizeof(dm_messages));
7023 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7024 ok(0 == r, "expected 0, got %d\n", r);
7025 test_dm_messages(0, 0, 0);
7027 memset(&dm_messages, 0, sizeof(dm_messages));
7028 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7029 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7030 test_dm_messages(0, 0, 0);
7032 memset(&dm_messages, 0, sizeof(dm_messages));
7033 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7034 ok(0 == r, "expected 0, got %d\n", r);
7035 test_dm_messages(0, 0, 0);
7037 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7038 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7039 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7041 memset(&dm_messages, 0, sizeof(dm_messages));
7042 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7043 ok(0 == r, "expected 0, got %d\n", r);
7044 test_dm_messages(0, 0, 0);
7046 DestroyWindow(hwRichEdit);
7047 DestroyWindow(hwParent);
7050 static void test_EM_FINDWORDBREAK_W(void)
7052 static const struct {
7054 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7055 } delimiter_tests[] = {
7056 {0x0a, FALSE}, /* newline */
7057 {0x0b, FALSE}, /* vertical tab */
7058 {0x0c, FALSE}, /* form feed */
7059 {0x0d, FALSE}, /* carriage return */
7060 {0x20, TRUE}, /* space */
7061 {0x61, FALSE}, /* capital letter a */
7062 {0xa0, FALSE}, /* no-break space */
7063 {0x2000, FALSE}, /* en quad */
7064 {0x3000, FALSE}, /* Ideographic space */
7065 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7066 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7067 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7068 {0xac00, FALSE}, /* Hangul character GA*/
7069 {0xd7af, FALSE}, /* End of Hangul character chart */
7070 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7071 {0xff20, FALSE}, /* fullwidth commercial @ */
7072 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7075 HWND hwndRichEdit = new_richeditW(NULL);
7076 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7077 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7082 wbuf[0] = delimiter_tests[i].c;
7084 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7085 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7086 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7088 ok(result == delimiter_tests[i].isdelimiter,
7089 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7090 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7092 ok(result == delimiter_tests[i].isdelimiter,
7093 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7094 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7096 DestroyWindow(hwndRichEdit);
7099 static void test_EM_FINDWORDBREAK_A(void)
7101 static const struct {
7103 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7104 } delimiter_tests[] = {
7105 {0x0a, FALSE}, /* newline */
7106 {0x0b, FALSE}, /* vertical tab */
7107 {0x0c, FALSE}, /* form feed */
7108 {0x0d, FALSE}, /* carriage return */
7109 {0x20, TRUE}, /* space */
7110 {0x61, FALSE}, /* capital letter a */
7113 HWND hwndRichEdit = new_richedit(NULL);
7115 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7116 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7120 buf[0] = delimiter_tests[i].c;
7122 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7123 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7126 ok(result == delimiter_tests[i].isdelimiter,
7127 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7128 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7130 ok(result == delimiter_tests[i].isdelimiter,
7131 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7132 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7134 DestroyWindow(hwndRichEdit);
7137 START_TEST( editor )
7140 /* Must explicitly LoadLibrary(). The test has no references to functions in
7141 * RICHED20.DLL, so the linker doesn't actually link to it. */
7142 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7143 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7148 test_EM_POSFROMCHAR();
7149 test_EM_SCROLLCARET();
7151 test_scrollbar_visibility();
7153 test_EM_LINELENGTH();
7154 test_EM_SETCHARFORMAT();
7155 test_EM_SETTEXTMODE();
7156 test_TM_PLAINTEXT();
7157 test_EM_SETOPTIONS();
7159 test_EM_GETTEXTRANGE();
7160 test_EM_GETSELTEXT();
7161 test_EM_SETUNDOLIMIT();
7163 test_EM_SETTEXTEX();
7164 test_EM_LIMITTEXT();
7165 test_EM_EXLIMITTEXT();
7166 test_EM_GETLIMITTEXT();
7168 test_EM_GETMODIFY();
7172 test_EM_STREAMOUT();
7173 test_EM_STREAMOUT_FONTTBL();
7174 test_EM_StreamIn_Undo();
7175 test_EM_FORMATRANGE();
7176 test_unicode_conversions();
7177 test_EM_GETTEXTLENGTHEX();
7178 test_EM_REPLACESEL(1);
7179 test_EM_REPLACESEL(0);
7181 test_EM_AUTOURLDETECT();
7183 test_undo_coalescing();
7184 test_word_movement();
7185 test_EM_CHARFROMPOS();
7186 test_SETPARAFORMAT();
7190 test_WM_GETDLGCODE();
7193 test_EM_FINDWORDBREAK_W();
7194 test_EM_FINDWORDBREAK_A();
7196 /* Set the environment variable WINETEST_RICHED20 to keep windows
7197 * responsive and open for 30 seconds. This is useful for debugging.
7199 if (getenv( "WINETEST_RICHED20" )) {
7200 keep_responsive(30);
7203 OleFlushClipboard();
7204 ret = FreeLibrary(hmoduleRichEdit);
7205 ok(ret, "error: %d\n", (int) GetLastError());