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 int is_win9x = 0;
51 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
53 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
54 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
55 hmoduleRichEdit, NULL);
56 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
60 static HWND new_richedit(HWND parent) {
61 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
64 /* Keeps the window reponsive for the deley_time in seconds.
65 * This is useful for debugging a test to see what is happening. */
66 static void keep_responsive(time_t delay_time)
71 /* The message pump uses PeekMessage() to empty the queue and then
72 * sleeps for 50ms before retrying the queue. */
73 end = time(NULL) + delay_time;
74 while (time(NULL) < end) {
75 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
76 TranslateMessage(&msg);
77 DispatchMessage(&msg);
84 static void processPendingMessages(void)
87 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
88 TranslateMessage(&msg);
89 DispatchMessage(&msg);
93 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
95 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
96 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
98 keybd_event(mod_vk, mod_scan_code, 0, 0);
99 keybd_event(vk, scan_code, 0, 0);
100 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
101 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
102 processPendingMessages();
105 static void simulate_typing_characters(HWND hwnd, const char* szChars)
109 while (*szChars != '\0') {
110 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
111 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
112 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
113 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
118 static BOOL hold_key(int vk)
123 result = GetKeyboardState(key_state);
124 ok(result, "GetKeyboardState failed.\n");
125 if (!result) return FALSE;
126 key_state[vk] |= 0x80;
127 result = SetKeyboardState(key_state);
128 ok(result, "SetKeyboardState failed.\n");
132 static BOOL release_key(int vk)
137 result = GetKeyboardState(key_state);
138 ok(result, "GetKeyboardState failed.\n");
139 if (!result) return FALSE;
140 key_state[vk] &= ~0x80;
141 result = SetKeyboardState(key_state);
142 ok(result, "SetKeyboardState failed.\n");
146 static const char haystack[] = "WINEWine wineWine wine WineWine";
158 struct find_s find_tests[] = {
159 /* Find in empty text */
160 {0, -1, "foo", FR_DOWN, -1},
161 {0, -1, "foo", 0, -1},
162 {0, -1, "", FR_DOWN, -1},
163 {20, 5, "foo", FR_DOWN, -1},
164 {5, 20, "foo", FR_DOWN, -1}
167 struct find_s find_tests2[] = {
169 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
170 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
172 /* Subsequent finds */
173 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
174 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
175 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
176 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
179 {19, 20, "Wine", FR_MATCHCASE, 13},
180 {10, 20, "Wine", FR_MATCHCASE, 4},
181 {20, 10, "Wine", FR_MATCHCASE, 13},
183 /* Case-insensitive */
184 {1, 31, "wInE", FR_DOWN, 4},
185 {1, 31, "Wine", FR_DOWN, 4},
187 /* High-to-low ranges */
188 {20, 5, "Wine", FR_DOWN, -1},
189 {2, 1, "Wine", FR_DOWN, -1},
190 {30, 29, "Wine", FR_DOWN, -1},
191 {20, 5, "Wine", 0, 13},
194 {5, 10, "", FR_DOWN, -1},
195 {10, 5, "", FR_DOWN, -1},
196 {0, -1, "", FR_DOWN, -1},
199 /* Whole-word search */
200 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
202 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
203 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
204 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
205 {11, -1, "winewine", FR_WHOLEWORD, 0},
206 {31, -1, "winewine", FR_WHOLEWORD, 23},
209 {5, 200, "XXX", FR_DOWN, -1},
210 {-20, 20, "Wine", FR_DOWN, -1},
211 {-20, 20, "Wine", FR_DOWN, -1},
212 {-15, -20, "Wine", FR_DOWN, -1},
213 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
215 /* Check the case noted in bug 4479 where matches at end aren't recognized */
216 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
217 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
218 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
219 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
222 /* The backwards case of bug 4479; bounds look right
223 * Fails because backward find is wrong */
224 {19, 20, "WINE", FR_MATCHCASE, 0},
225 {0, 20, "WINE", FR_MATCHCASE, -1},
227 {0, -1, "wineWine wine", 0, -1},
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
233 memset(&ft, 0, sizeof(ft));
234 ft.chrg.cpMin = f->start;
235 ft.chrg.cpMax = f->end;
236 ft.lpstrText = f->needle;
237 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
238 ok(findloc == f->expected_loc,
239 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
240 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
243 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
247 int expected_end_loc;
249 memset(&ft, 0, sizeof(ft));
250 ft.chrg.cpMin = f->start;
251 ft.chrg.cpMax = f->end;
252 ft.lpstrText = f->needle;
253 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
254 ok(findloc == f->expected_loc,
255 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
256 name, id, f->needle, f->start, f->end, f->flags, findloc);
257 ok(ft.chrgText.cpMin == f->expected_loc,
258 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
259 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
260 expected_end_loc = ((f->expected_loc == -1) ? -1
261 : f->expected_loc + strlen(f->needle));
262 ok(ft.chrgText.cpMax == expected_end_loc,
263 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
264 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
267 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
272 for (i = 0; i < num_tests; i++) {
273 check_EM_FINDTEXT(hwnd, name, &find[i], i);
274 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
278 static void test_EM_FINDTEXT(void)
280 HWND hwndRichEdit = new_richedit(NULL);
283 /* Empty rich edit control */
284 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
285 sizeof(find_tests)/sizeof(struct find_s));
287 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
290 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
291 sizeof(find_tests2)/sizeof(struct find_s));
293 /* Setting a format on an arbitrary range should have no effect in search
294 results. This tests correct offset reporting across runs. */
295 cf2.cbSize = sizeof(CHARFORMAT2);
296 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
297 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
298 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
299 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
300 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
302 /* Haystack text, again */
303 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
304 sizeof(find_tests2)/sizeof(struct find_s));
306 /* Yet another range */
307 cf2.dwMask = CFM_BOLD | cf2.dwMask;
308 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
309 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
310 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
312 /* Haystack text, again */
313 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
314 sizeof(find_tests2)/sizeof(struct find_s));
316 DestroyWindow(hwndRichEdit);
319 static const struct getline_s {
324 {0, 10, "foo bar\r"},
329 /* Buffer smaller than line length */
335 static void test_EM_GETLINE(void)
338 HWND hwndRichEdit = new_richedit(NULL);
339 static const int nBuf = 1024;
340 char dest[1024], origdest[1024];
341 const char text[] = "foo bar\n"
345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
347 memset(origdest, 0xBB, nBuf);
348 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
351 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
352 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
353 memset(dest, 0xBB, nBuf);
354 *(WORD *) dest = gl[i].buffer_len;
356 /* EM_GETLINE appends a "\r\0" to the end of the line
357 * nCopied counts up to and including the '\r' */
358 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
359 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
361 /* two special cases since a parameter is passed via dest */
362 if (gl[i].buffer_len == 0)
363 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
365 else if (gl[i].buffer_len == 1)
366 ok(dest[0] == gl[i].text[0] && !dest[1] &&
367 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
370 /* Prepare hex strings of buffers to dump on failure. */
371 char expectedbuf[1024];
372 char resultbuf[1024];
375 for (j = 0; j < 32; j++)
376 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
377 expectedbuf[0] = '\0';
378 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
379 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
380 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
381 sprintf(expectedbuf+strlen(expectedbuf), "??");
382 for (; j < 32; j++) /* Bytes after declared buffer size */
383 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
385 /* Test the part of the buffer that is expected to be written according
386 * to the MSDN documentation fo EM_GETLINE, which does not state that
387 * a NULL terminating character will be added unless no text is copied.
389 * Windows 95, 98 & NT do not append a NULL terminating character, but
390 * Windows 2000 and up do append a NULL terminating character if there
391 * is space in the buffer. The test will ignore this difference. */
392 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
393 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
394 i, expected_bytes_written, expectedbuf, resultbuf);
395 /* Test the part of the buffer after the declared length to make sure
396 * there are no buffer overruns. */
397 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
398 nBuf - gl[i].buffer_len),
399 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
400 i, expected_bytes_written, expectedbuf, resultbuf);
404 DestroyWindow(hwndRichEdit);
407 static void test_EM_LINELENGTH(void)
409 HWND hwndRichEdit = new_richedit(NULL);
415 int offset_test[10][2] = {
430 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
432 for (i = 0; i < 10; i++) {
433 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
434 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
435 offset_test[i][0], result, offset_test[i][1]);
438 DestroyWindow(hwndRichEdit);
441 static int get_scroll_pos_y(HWND hwnd)
444 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
445 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
449 static void move_cursor(HWND hwnd, LONG charindex)
452 cr.cpMax = charindex;
453 cr.cpMin = charindex;
454 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
457 static void line_scroll(HWND hwnd, int amount)
459 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
462 static void test_EM_SCROLLCARET(void)
465 const char text[] = "aa\n"
466 "this is a long line of text that should be longer than the "
474 /* The richedit window height needs to be large enough vertically to fit in
475 * more than two lines of text, so the new_richedit function can't be used
476 * since a height of 60 was not large enough on some systems.
478 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
479 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
480 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
481 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
483 /* Can't verify this */
484 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
488 /* Caret above visible window */
489 line_scroll(hwndRichEdit, 3);
490 prevY = get_scroll_pos_y(hwndRichEdit);
491 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
492 curY = get_scroll_pos_y(hwndRichEdit);
493 ok(prevY != curY, "%d == %d\n", prevY, curY);
495 /* Caret below visible window */
496 move_cursor(hwndRichEdit, sizeof(text) - 1);
497 line_scroll(hwndRichEdit, -3);
498 prevY = get_scroll_pos_y(hwndRichEdit);
499 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
500 curY = get_scroll_pos_y(hwndRichEdit);
501 ok(prevY != curY, "%d == %d\n", prevY, curY);
503 /* Caret in visible window */
504 move_cursor(hwndRichEdit, sizeof(text) - 2);
505 prevY = get_scroll_pos_y(hwndRichEdit);
506 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
507 curY = get_scroll_pos_y(hwndRichEdit);
508 ok(prevY == curY, "%d != %d\n", prevY, curY);
510 /* Caret still in visible window */
511 line_scroll(hwndRichEdit, -1);
512 prevY = get_scroll_pos_y(hwndRichEdit);
513 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
514 curY = get_scroll_pos_y(hwndRichEdit);
515 ok(prevY == curY, "%d != %d\n", prevY, curY);
517 DestroyWindow(hwndRichEdit);
520 static void test_EM_POSFROMCHAR(void)
522 HWND hwndRichEdit = new_richedit(NULL);
525 unsigned int height = 0;
528 static const char text[] = "aa\n"
529 "this is a long line of text that should be longer than the "
538 /* Fill the control to lines to ensure that most of them are offscreen */
539 for (i = 0; i < 50; i++)
541 /* Do not modify the string; it is exactly 16 characters long. */
542 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
543 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
547 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
548 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
549 Richedit 3.0 accepts either of the above API conventions.
552 /* Testing Richedit 2.0 API format */
554 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
555 Since all lines are identical and drawn with the same font,
556 they should have the same height... right?
558 for (i = 0; i < 50; i++)
560 /* All the lines are 16 characters long */
561 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
564 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
565 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
566 xpos = LOWORD(result);
570 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
571 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
572 height = HIWORD(result);
576 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
577 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
581 /* Testing position at end of text */
582 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
583 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
584 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
586 /* Testing position way past end of text */
587 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
588 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
589 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
591 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
592 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
593 for (i = 0; i < 50; i++)
595 /* All the lines are 16 characters long */
596 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
597 ok((signed short)(HIWORD(result)) == (i - 1) * height,
598 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
599 (signed short)(HIWORD(result)), (i - 1) * height);
600 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
603 /* Testing position at end of text */
604 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
605 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
606 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
608 /* Testing position way past end of text */
609 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
610 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
611 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
613 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
614 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
615 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
617 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
618 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
619 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
620 xpos = LOWORD(result);
622 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
623 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
624 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
625 ok((signed short)(LOWORD(result)) < xpos,
626 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
627 (signed short)(LOWORD(result)), xpos);
628 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
630 /* Test around end of text that doesn't end in a newline. */
631 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
632 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
633 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
634 ok(pt.x > 1, "pt.x = %d\n", pt.x);
636 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
637 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
638 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
640 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
641 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
642 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
644 /* Try a negative position. */
645 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
646 ok(pt.x == 1, "pt.x = %d\n", pt.x);
648 DestroyWindow(hwndRichEdit);
651 static void test_EM_SETCHARFORMAT(void)
653 HWND hwndRichEdit = new_richedit(NULL);
656 int tested_effects[] = {
670 /* Invalid flags, CHARFORMAT2 structure blanked out */
671 memset(&cf2, 0, sizeof(cf2));
672 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
674 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
676 /* A valid flag, CHARFORMAT2 structure blanked out */
677 memset(&cf2, 0, sizeof(cf2));
678 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
680 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
682 /* A valid flag, CHARFORMAT2 structure blanked out */
683 memset(&cf2, 0, sizeof(cf2));
684 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
686 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
688 /* A valid flag, CHARFORMAT2 structure blanked out */
689 memset(&cf2, 0, sizeof(cf2));
690 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
692 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
694 /* A valid flag, CHARFORMAT2 structure blanked out */
695 memset(&cf2, 0, sizeof(cf2));
696 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
698 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
700 /* Invalid flags, CHARFORMAT2 structure minimally filled */
701 memset(&cf2, 0, sizeof(cf2));
702 cf2.cbSize = sizeof(CHARFORMAT2);
703 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
705 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
706 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
707 ok(rc == FALSE, "Should not be able to undo here.\n");
708 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
710 /* A valid flag, CHARFORMAT2 structure minimally filled */
711 memset(&cf2, 0, sizeof(cf2));
712 cf2.cbSize = sizeof(CHARFORMAT2);
713 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
715 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
716 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
717 ok(rc == FALSE, "Should not be able to undo here.\n");
718 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
720 /* A valid flag, CHARFORMAT2 structure minimally filled */
721 memset(&cf2, 0, sizeof(cf2));
722 cf2.cbSize = sizeof(CHARFORMAT2);
723 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
725 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
726 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
727 ok(rc == FALSE, "Should not be able to undo here.\n");
728 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
730 /* A valid flag, CHARFORMAT2 structure minimally filled */
731 memset(&cf2, 0, sizeof(cf2));
732 cf2.cbSize = sizeof(CHARFORMAT2);
733 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
735 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
736 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
737 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
738 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
740 /* A valid flag, CHARFORMAT2 structure minimally filled */
741 memset(&cf2, 0, sizeof(cf2));
742 cf2.cbSize = sizeof(CHARFORMAT2);
743 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
745 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
746 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
747 ok(rc == TRUE, "Should not be able to undo here.\n");
748 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
750 cf2.cbSize = sizeof(CHARFORMAT2);
751 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
754 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
755 cf2.cbSize = sizeof(CHARFORMAT2);
756 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
758 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
759 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
761 /* wParam==0 is default char format, does not set modify */
762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
763 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
764 ok(rc == 0, "Text marked as modified, expected not modified!\n");
765 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
766 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
767 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
768 ok(rc == 0, "Text marked as modified, expected not modified!\n");
770 /* wParam==SCF_SELECTION sets modify if nonempty selection */
771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
772 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
773 ok(rc == 0, "Text marked as modified, expected not modified!\n");
774 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
775 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
776 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
777 ok(rc == 0, "Text marked as modified, expected not modified!\n");
779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
780 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
781 ok(rc == 0, "Text marked as modified, expected not modified!\n");
782 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
783 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
784 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
785 ok(rc == 0, "Text marked as modified, expected not modified!\n");
786 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
787 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
788 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
789 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
790 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
792 /* wParam==SCF_ALL sets modify regardless of whether text is present */
793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
794 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
795 ok(rc == 0, "Text marked as modified, expected not modified!\n");
796 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
797 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
798 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
799 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
801 DestroyWindow(hwndRichEdit);
803 /* EM_GETCHARFORMAT tests */
804 for (i = 0; tested_effects[i]; i++)
806 hwndRichEdit = new_richedit(NULL);
807 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
809 /* Need to set a TrueType font to get consistent CFM_BOLD results */
810 memset(&cf2, 0, sizeof(CHARFORMAT2));
811 cf2.cbSize = sizeof(CHARFORMAT2);
812 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
814 strcpy(cf2.szFaceName, "Courier New");
815 cf2.wWeight = FW_DONTCARE;
816 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
818 memset(&cf2, 0, sizeof(CHARFORMAT2));
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
821 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
822 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
823 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
825 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
826 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
827 ok((cf2.dwEffects & tested_effects[i]) == 0,
828 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
830 memset(&cf2, 0, sizeof(CHARFORMAT2));
831 cf2.cbSize = sizeof(CHARFORMAT2);
832 cf2.dwMask = tested_effects[i];
833 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
834 cf2.dwMask = CFM_SUPERSCRIPT;
835 cf2.dwEffects = tested_effects[i];
836 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
837 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
839 memset(&cf2, 0, sizeof(CHARFORMAT2));
840 cf2.cbSize = sizeof(CHARFORMAT2);
841 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
842 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
843 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
844 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
846 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
847 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
848 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
849 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
851 memset(&cf2, 0, sizeof(CHARFORMAT2));
852 cf2.cbSize = sizeof(CHARFORMAT2);
853 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
854 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
855 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
856 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
858 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
859 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
860 ok((cf2.dwEffects & tested_effects[i]) == 0,
861 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
863 memset(&cf2, 0, sizeof(CHARFORMAT2));
864 cf2.cbSize = sizeof(CHARFORMAT2);
865 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
866 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
867 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
868 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
870 (cf2.dwMask & tested_effects[i]) == 0),
871 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
873 DestroyWindow(hwndRichEdit);
876 for (i = 0; tested_effects[i]; i++)
878 hwndRichEdit = new_richedit(NULL);
879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
881 /* Need to set a TrueType font to get consistent CFM_BOLD results */
882 memset(&cf2, 0, sizeof(CHARFORMAT2));
883 cf2.cbSize = sizeof(CHARFORMAT2);
884 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
886 strcpy(cf2.szFaceName, "Courier New");
887 cf2.wWeight = FW_DONTCARE;
888 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
890 memset(&cf2, 0, sizeof(CHARFORMAT2));
891 cf2.cbSize = sizeof(CHARFORMAT2);
892 cf2.dwMask = tested_effects[i];
893 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
894 cf2.dwMask = CFM_SUPERSCRIPT;
895 cf2.dwEffects = tested_effects[i];
896 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
897 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
899 memset(&cf2, 0, sizeof(CHARFORMAT2));
900 cf2.cbSize = sizeof(CHARFORMAT2);
901 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
902 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
903 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
904 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
906 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
907 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
908 ok((cf2.dwEffects & tested_effects[i]) == 0,
909 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
911 memset(&cf2, 0, sizeof(CHARFORMAT2));
912 cf2.cbSize = sizeof(CHARFORMAT2);
913 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
914 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
915 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
916 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
918 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
919 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
920 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
921 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
923 memset(&cf2, 0, sizeof(CHARFORMAT2));
924 cf2.cbSize = sizeof(CHARFORMAT2);
925 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
926 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
927 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
928 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
930 (cf2.dwMask & tested_effects[i]) == 0),
931 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
932 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
933 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
935 DestroyWindow(hwndRichEdit);
938 /* Effects applied on an empty selection should take effect when selection is
939 replaced with text */
940 hwndRichEdit = new_richedit(NULL);
941 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
942 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
944 memset(&cf2, 0, sizeof(CHARFORMAT2));
945 cf2.cbSize = sizeof(CHARFORMAT2);
946 cf2.dwMask = CFM_BOLD;
947 cf2.dwEffects = CFE_BOLD;
948 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
950 /* Selection is now nonempty */
951 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
953 memset(&cf2, 0, sizeof(CHARFORMAT2));
954 cf2.cbSize = sizeof(CHARFORMAT2);
955 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
956 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
958 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
959 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
960 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
961 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
964 /* Set two effects on an empty selection */
965 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
966 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
968 memset(&cf2, 0, sizeof(CHARFORMAT2));
969 cf2.cbSize = sizeof(CHARFORMAT2);
970 cf2.dwMask = CFM_BOLD;
971 cf2.dwEffects = CFE_BOLD;
972 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
973 cf2.dwMask = CFM_ITALIC;
974 cf2.dwEffects = CFE_ITALIC;
975 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
977 /* Selection is now nonempty */
978 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
980 memset(&cf2, 0, sizeof(CHARFORMAT2));
981 cf2.cbSize = sizeof(CHARFORMAT2);
982 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
983 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
985 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
986 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
987 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
988 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
990 /* Setting the (empty) selection to exactly the same place as before should
991 NOT clear the insertion style! */
992 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
993 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
995 memset(&cf2, 0, sizeof(CHARFORMAT2));
996 cf2.cbSize = sizeof(CHARFORMAT2);
997 cf2.dwMask = CFM_BOLD;
998 cf2.dwEffects = CFE_BOLD;
999 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1001 /* Empty selection in same place, insert style should NOT be forgotten here. */
1002 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1004 /* Selection is now nonempty */
1005 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1007 memset(&cf2, 0, sizeof(CHARFORMAT2));
1008 cf2.cbSize = sizeof(CHARFORMAT2);
1009 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1010 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1012 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1013 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1014 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1015 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1017 /* Ditto with EM_EXSETSEL */
1018 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1019 cr.cpMin = 2; cr.cpMax = 2;
1020 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1022 memset(&cf2, 0, sizeof(CHARFORMAT2));
1023 cf2.cbSize = sizeof(CHARFORMAT2);
1024 cf2.dwMask = CFM_BOLD;
1025 cf2.dwEffects = CFE_BOLD;
1026 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1028 /* Empty selection in same place, insert style should NOT be forgotten here. */
1029 cr.cpMin = 2; cr.cpMax = 2;
1030 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1032 /* Selection is now nonempty */
1033 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1035 memset(&cf2, 0, sizeof(CHARFORMAT2));
1036 cf2.cbSize = sizeof(CHARFORMAT2);
1037 cr.cpMin = 2; cr.cpMax = 6;
1038 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1039 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1041 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1042 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1043 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1044 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1046 DestroyWindow(hwndRichEdit);
1049 static void test_EM_SETTEXTMODE(void)
1051 HWND hwndRichEdit = new_richedit(NULL);
1052 CHARFORMAT2 cf2, cf2test;
1056 /*Attempt to use mutually exclusive modes*/
1057 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1058 ok(rc == E_INVALIDARG,
1059 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1061 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1062 /*Insert text into the control*/
1064 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1066 /*Attempt to change the control to plain text mode*/
1067 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1068 ok(rc == E_UNEXPECTED,
1069 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1071 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1072 If rich text is pasted, it should have the same formatting as the rest
1073 of the text in the control*/
1075 /*Italicize the text
1076 *NOTE: If the default text was already italicized, the test will simply
1077 reverse; in other words, it will copy a regular "wine" into a plain
1078 text window that uses an italicized format*/
1079 cf2.cbSize = sizeof(CHARFORMAT2);
1080 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1083 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1084 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1086 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1087 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1089 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1090 however, SCF_ALL has been implemented*/
1091 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1092 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1094 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1095 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1097 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1099 /*Select the string "wine"*/
1102 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1104 /*Copy the italicized "wine" to the clipboard*/
1105 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1107 /*Reset the formatting to default*/
1108 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1109 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1110 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1112 /*Clear the text in the control*/
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1115 /*Switch to Plain Text Mode*/
1116 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1117 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1119 /*Input "wine" again in normal format*/
1120 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1122 /*Paste the italicized "wine" into the control*/
1123 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1125 /*Select a character from the first "wine" string*/
1128 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1130 /*Retrieve its formatting*/
1131 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1134 /*Select a character from the second "wine" string*/
1137 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1139 /*Retrieve its formatting*/
1140 cf2test.cbSize = sizeof(CHARFORMAT2);
1141 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1144 /*Compare the two formattings*/
1145 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1146 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1147 cf2.dwEffects, cf2test.dwEffects);
1148 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1149 printing "wine" in the current format(normal)
1150 pasting "wine" from the clipboard(italicized)
1151 comparing the two formats(should differ)*/
1153 /*Attempt to switch with text in control*/
1154 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1155 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1160 /*Switch into Rich Text mode*/
1161 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1162 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1164 /*Print "wine" in normal formatting into the control*/
1165 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1167 /*Paste italicized "wine" into the control*/
1168 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1170 /*Select text from the first "wine" string*/
1173 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1175 /*Retrieve its formatting*/
1176 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1179 /*Select text from the second "wine" string*/
1182 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1184 /*Retrieve its formatting*/
1185 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1188 /*Test that the two formattings are not the same*/
1189 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1190 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1191 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1193 DestroyWindow(hwndRichEdit);
1196 static void test_SETPARAFORMAT(void)
1198 HWND hwndRichEdit = new_richedit(NULL);
1201 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1202 fmt.cbSize = sizeof(PARAFORMAT2);
1203 fmt.dwMask = PFM_ALIGNMENT;
1204 fmt.wAlignment = PFA_LEFT;
1206 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1207 ok(ret != 0, "expected non-zero got %d\n", ret);
1209 fmt.cbSize = sizeof(PARAFORMAT2);
1211 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1212 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1213 * between richedit different native builds of riched20.dll
1214 * used on different Windows versions. */
1215 ret &= ~PFM_TABLEROWDELIMITER;
1216 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1218 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1219 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1221 DestroyWindow(hwndRichEdit);
1224 static void test_TM_PLAINTEXT(void)
1226 /*Tests plain text properties*/
1228 HWND hwndRichEdit = new_richedit(NULL);
1229 CHARFORMAT2 cf2, cf2test;
1233 /*Switch to plain text mode*/
1235 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1236 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1238 /*Fill control with text*/
1240 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1242 /*Select some text and bold it*/
1246 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1247 cf2.cbSize = sizeof(CHARFORMAT2);
1248 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1250 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1251 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1253 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1254 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1256 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1257 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1259 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1260 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1262 /*Get the formatting of those characters*/
1264 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1266 /*Get the formatting of some other characters*/
1267 cf2test.cbSize = sizeof(CHARFORMAT2);
1270 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1271 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1273 /*Test that they are the same as plain text allows only one formatting*/
1275 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1276 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1277 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1279 /*Fill the control with a "wine" string, which when inserted will be bold*/
1281 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1283 /*Copy the bolded "wine" string*/
1287 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1288 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1290 /*Swap back to rich text*/
1292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1293 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1295 /*Set the default formatting to bold italics*/
1297 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1298 cf2.dwMask |= CFM_ITALIC;
1299 cf2.dwEffects ^= CFE_ITALIC;
1300 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1301 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1303 /*Set the text in the control to "wine", which will be bold and italicized*/
1305 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1307 /*Paste the plain text "wine" string, which should take the insert
1308 formatting, which at the moment is bold italics*/
1310 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1312 /*Select the first "wine" string and retrieve its formatting*/
1316 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1317 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1319 /*Select the second "wine" string and retrieve its formatting*/
1323 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1324 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1326 /*Compare the two formattings. They should be the same.*/
1328 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1329 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1330 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1331 DestroyWindow(hwndRichEdit);
1334 static void test_WM_GETTEXT(void)
1336 HWND hwndRichEdit = new_richedit(NULL);
1337 static const char text[] = "Hello. My name is RichEdit!";
1338 static const char text2[] = "Hello. My name is RichEdit!\r";
1339 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1340 char buffer[1024] = {0};
1343 /* Baseline test with normal-sized buffer */
1344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1345 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1346 ok(result == lstrlen(buffer),
1347 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1348 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1349 result = strcmp(buffer,text);
1351 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1353 /* Test for returned value of WM_GETTEXTLENGTH */
1354 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1355 ok(result == lstrlen(text),
1356 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1357 result, lstrlen(text));
1359 /* Test for behavior in overflow case */
1360 memset(buffer, 0, 1024);
1361 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1363 result == lstrlenA(text) - 1, /* XP, win2k3 */
1364 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1365 result = strcmp(buffer,text);
1367 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1369 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1371 /* Baseline test with normal-sized buffer and carriage return */
1372 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1373 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1374 ok(result == lstrlen(buffer),
1375 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1376 result = strcmp(buffer,text2_after);
1378 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1380 /* Test for returned value of WM_GETTEXTLENGTH */
1381 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1382 ok(result == lstrlen(text2_after),
1383 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1384 result, lstrlen(text2_after));
1386 /* Test for behavior of CRLF conversion in case of overflow */
1387 memset(buffer, 0, 1024);
1388 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1390 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1391 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1392 result = strcmp(buffer,text2);
1394 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1396 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1398 DestroyWindow(hwndRichEdit);
1401 static void test_EM_GETTEXTRANGE(void)
1403 HWND hwndRichEdit = new_richedit(NULL);
1404 const char * text1 = "foo bar\r\nfoo bar";
1405 const char * text2 = "foo bar\rfoo bar";
1406 const char * expect = "bar\rfoo";
1407 char buffer[1024] = {0};
1409 TEXTRANGEA textRange;
1411 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1413 textRange.lpstrText = buffer;
1414 textRange.chrg.cpMin = 4;
1415 textRange.chrg.cpMax = 11;
1416 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1417 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1418 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1420 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1422 textRange.lpstrText = buffer;
1423 textRange.chrg.cpMin = 4;
1424 textRange.chrg.cpMax = 11;
1425 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1426 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1427 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1429 /* cpMax of text length is used instead of -1 in this case */
1430 textRange.lpstrText = buffer;
1431 textRange.chrg.cpMin = 0;
1432 textRange.chrg.cpMax = -1;
1433 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1434 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1435 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1437 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1438 textRange.lpstrText = buffer;
1439 textRange.chrg.cpMin = -1;
1440 textRange.chrg.cpMax = 1;
1441 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1442 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1443 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1445 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1446 textRange.lpstrText = buffer;
1447 textRange.chrg.cpMin = 1;
1448 textRange.chrg.cpMax = -1;
1449 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1450 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1451 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1453 /* no end character is copied if cpMax - cpMin < 0 */
1454 textRange.lpstrText = buffer;
1455 textRange.chrg.cpMin = 5;
1456 textRange.chrg.cpMax = 5;
1457 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1458 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1459 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1461 /* cpMax of text length is used if cpMax > text length*/
1462 textRange.lpstrText = buffer;
1463 textRange.chrg.cpMin = 0;
1464 textRange.chrg.cpMax = 1000;
1465 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1466 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1467 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1469 DestroyWindow(hwndRichEdit);
1472 static void test_EM_GETSELTEXT(void)
1474 HWND hwndRichEdit = new_richedit(NULL);
1475 const char * text1 = "foo bar\r\nfoo bar";
1476 const char * text2 = "foo bar\rfoo bar";
1477 const char * expect = "bar\rfoo";
1478 char buffer[1024] = {0};
1481 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1483 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1484 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1485 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1486 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1490 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1491 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1492 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1493 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1495 DestroyWindow(hwndRichEdit);
1498 /* FIXME: need to test unimplemented options and robustly test wparam */
1499 static void test_EM_SETOPTIONS(void)
1502 static const char text[] = "Hello. My name is RichEdit!";
1503 char buffer[1024] = {0};
1504 DWORD dwStyle, options, oldOptions;
1505 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1506 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1507 ES_SELECTIONBAR|ES_VERTICAL;
1509 /* Test initial options. */
1510 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1511 0, 0, 200, 60, NULL, NULL,
1512 hmoduleRichEdit, NULL);
1513 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1514 RICHEDIT_CLASS, (int) GetLastError());
1515 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1516 ok(options == 0, "Incorrect initial options %x\n", options);
1517 DestroyWindow(hwndRichEdit);
1519 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1520 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1521 0, 0, 200, 60, NULL, NULL,
1522 hmoduleRichEdit, NULL);
1523 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1524 RICHEDIT_CLASS, (int) GetLastError());
1525 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1526 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1527 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1528 "Incorrect initial options %x\n", options);
1530 /* NEGATIVE TESTING - NO OPTIONS SET */
1531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1532 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1534 /* testing no readonly by sending 'a' to the control*/
1535 SetFocus(hwndRichEdit);
1536 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1537 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1539 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1542 /* READONLY - sending 'a' to the control */
1543 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1544 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1545 SetFocus(hwndRichEdit);
1546 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1547 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1548 ok(buffer[0]==text[0],
1549 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1551 /* EM_SETOPTIONS changes the window style, but changing the
1552 * window style does not change the options. */
1553 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1554 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1555 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1556 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1557 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1558 /* Confirm that the text is still read only. */
1559 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1560 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1561 ok(buffer[0]==text[0],
1562 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1564 oldOptions = options;
1565 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1566 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1567 ok(options == oldOptions,
1568 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1570 DestroyWindow(hwndRichEdit);
1573 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1575 CHARFORMAT2W text_format;
1576 text_format.cbSize = sizeof(text_format);
1577 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1578 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1579 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1582 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1584 int link_present = 0;
1586 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1588 { /* control text is url; should get CFE_LINK */
1589 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1593 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1597 static HWND new_static_wnd(HWND parent) {
1598 return new_window("Static", 0, parent);
1601 static void test_EM_AUTOURLDETECT(void)
1603 /* DO NOT change the properties of the first two elements. To shorten the
1604 tests, all tests after WM_SETTEXT test just the first two elements -
1605 one non-URL and one URL */
1611 {"http://www.winehq.org", 1},
1612 {"http//winehq.org", 0},
1613 {"ww.winehq.org", 0},
1614 {"www.winehq.org", 1},
1615 {"ftp://192.168.1.1", 1},
1616 {"ftp//192.168.1.1", 0},
1617 {"mailto:your@email.com", 1},
1618 {"prospero:prosperoserver", 1},
1620 {"news:newserver", 1},
1621 {"wais:waisserver", 1}
1626 HWND hwndRichEdit, parent;
1628 /* All of the following should cause the URL to be detected */
1629 const char * templates_delim[] = {
1630 "This is some text with X on it",
1631 "This is some text with (X) on it",
1632 "This is some text with X\r on it",
1633 "This is some text with ---X--- on it",
1634 "This is some text with \"X\" on it",
1635 "This is some text with 'X' on it",
1636 "This is some text with 'X' on it",
1637 "This is some text with :X: on it",
1639 "This text ends with X",
1641 "This is some text with X) on it",
1642 "This is some text with X--- on it",
1643 "This is some text with X\" on it",
1644 "This is some text with X' on it",
1645 "This is some text with X: on it",
1647 "This is some text with (X on it",
1648 "This is some text with \rX on it",
1649 "This is some text with ---X on it",
1650 "This is some text with \"X on it",
1651 "This is some text with 'X on it",
1652 "This is some text with :X on it",
1654 /* None of these should cause the URL to be detected */
1655 const char * templates_non_delim[] = {
1656 "This is some text with |X| on it",
1657 "This is some text with *X* on it",
1658 "This is some text with /X/ on it",
1659 "This is some text with +X+ on it",
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@ 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",
1668 "This is some text with %X on it",
1669 "This is some text with #X on it",
1670 "This is some text with @X on it",
1671 "This is some text with \\X on it",
1673 /* All of these cause the URL detection to be extended by one more byte,
1674 thus demonstrating that the tested character is considered as part
1676 const char * templates_xten_delim[] = {
1677 "This is some text with X| on it",
1678 "This is some text with X* 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",
1683 "This is some text with X@ on it",
1684 "This is some text with X\\ on it",
1688 parent = new_static_wnd(NULL);
1689 hwndRichEdit = new_richedit(parent);
1690 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1691 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1692 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1693 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1694 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1695 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1696 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1697 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1698 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1699 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1700 /* for each url, check the text to see if CFE_LINK effect is present */
1701 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1703 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1704 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1705 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1707 /* Link detection should happen immediately upon WM_SETTEXT */
1708 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1709 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1710 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1712 DestroyWindow(hwndRichEdit);
1714 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1715 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1716 hwndRichEdit = new_richedit(parent);
1718 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1723 at_pos = strchr(templates_delim[j], 'X');
1724 at_offset = at_pos - templates_delim[j];
1725 strncpy(buffer, templates_delim[j], at_offset);
1726 buffer[at_offset] = '\0';
1727 strcat(buffer, urls[i].text);
1728 strcat(buffer, templates_delim[j] + at_offset + 1);
1729 end_offset = at_offset + strlen(urls[i].text);
1731 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1732 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1734 /* This assumes no templates start with the URL itself, and that they
1735 have at least two characters before the URL text */
1736 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1737 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1738 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1739 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1740 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1741 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1745 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1746 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1747 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1748 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1752 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1757 if (buffer[end_offset] != '\0')
1759 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1760 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1761 if (buffer[end_offset +1] != '\0')
1763 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1769 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1774 at_pos = strchr(templates_non_delim[j], 'X');
1775 at_offset = at_pos - templates_non_delim[j];
1776 strncpy(buffer, templates_non_delim[j], at_offset);
1777 buffer[at_offset] = '\0';
1778 strcat(buffer, urls[i].text);
1779 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1780 end_offset = at_offset + strlen(urls[i].text);
1782 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1785 /* This assumes no templates start with the URL itself, and that they
1786 have at least two characters before the URL text */
1787 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1788 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1791 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1792 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1796 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1797 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1798 if (buffer[end_offset] != '\0')
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1802 if (buffer[end_offset +1] != '\0')
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1810 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1815 at_pos = strchr(templates_xten_delim[j], 'X');
1816 at_offset = at_pos - templates_xten_delim[j];
1817 strncpy(buffer, templates_xten_delim[j], at_offset);
1818 buffer[at_offset] = '\0';
1819 strcat(buffer, urls[i].text);
1820 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1821 end_offset = at_offset + strlen(urls[i].text);
1823 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1824 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1826 /* This assumes no templates start with the URL itself, and that they
1827 have at least two characters before the URL text */
1828 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1829 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1832 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1833 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1837 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1839 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1841 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1842 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1846 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1847 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1848 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1849 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1850 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1851 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1857 if (buffer[end_offset +2] != '\0')
1859 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1860 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1865 DestroyWindow(hwndRichEdit);
1866 hwndRichEdit = NULL;
1869 /* Test detection of URLs within normal text - WM_CHAR case. */
1870 /* Test only the first two URL examples for brevity */
1871 for (i = 0; i < 2; i++) {
1872 hwndRichEdit = new_richedit(parent);
1874 /* Also for brevity, test only the first three delimiters */
1875 for (j = 0; j < 3; j++) {
1881 at_pos = strchr(templates_delim[j], 'X');
1882 at_offset = at_pos - templates_delim[j];
1883 end_offset = at_offset + strlen(urls[i].text);
1885 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1887 for (u = 0; templates_delim[j][u]; u++) {
1888 if (templates_delim[j][u] == '\r') {
1889 simulate_typing_characters(hwndRichEdit, "\r");
1890 } else if (templates_delim[j][u] != 'X') {
1891 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1893 for (v = 0; urls[i].text[v]; v++) {
1894 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1898 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1900 /* This assumes no templates start with the URL itself, and that they
1901 have at least two characters before the URL text */
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1904 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1905 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1911 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1912 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1913 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1914 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1918 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1919 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1920 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1921 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1923 if (buffer[end_offset] != '\0')
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1927 if (buffer[end_offset +1] != '\0')
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1934 /* The following will insert a paragraph break after the first character
1935 of the URL candidate, thus breaking the URL. It is expected that the
1936 CFE_LINK attribute should break across both pieces of the URL */
1937 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1938 simulate_typing_characters(hwndRichEdit, "\r");
1939 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_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 /* end_offset moved because of paragraph break */
1951 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1952 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1953 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1954 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1956 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1957 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1958 if (buffer[end_offset +2] != '\0')
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1965 /* The following will remove the just-inserted paragraph break, thus
1966 restoring the URL */
1967 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1968 simulate_typing_characters(hwndRichEdit, "\b");
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);
1980 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1981 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1982 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1983 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1989 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1990 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1992 if (buffer[end_offset] != '\0')
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1996 if (buffer[end_offset +1] != '\0')
1998 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1999 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2003 DestroyWindow(hwndRichEdit);
2004 hwndRichEdit = NULL;
2007 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2008 /* Test just the first two URL examples for brevity */
2009 for (i = 0; i < 2; i++) {
2012 hwndRichEdit = new_richedit(parent);
2014 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2016 1) Set entire text, a la WM_SETTEXT
2017 2) Set a selection of the text to the URL
2018 3) Set a portion of the text at a time, which eventually results in
2020 All of them should give equivalent results
2023 /* Set entire text in one go, like WM_SETTEXT */
2024 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2029 st.codepage = CP_ACP;
2030 st.flags = ST_DEFAULT;
2032 at_pos = strchr(templates_delim[j], 'X');
2033 at_offset = at_pos - templates_delim[j];
2034 strncpy(buffer, templates_delim[j], at_offset);
2035 buffer[at_offset] = '\0';
2036 strcat(buffer, urls[i].text);
2037 strcat(buffer, templates_delim[j] + at_offset + 1);
2038 end_offset = at_offset + strlen(urls[i].text);
2040 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2041 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2043 /* This assumes no templates start with the URL itself, and that they
2044 have at least two characters before the URL text */
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2047 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2048 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2049 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2050 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2054 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2055 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2056 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2057 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2063 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2064 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2066 if (buffer[end_offset] != '\0')
2068 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2069 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2070 if (buffer[end_offset +1] != '\0')
2072 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2073 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2078 /* Set selection with X to the URL */
2079 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2084 at_pos = strchr(templates_delim[j], 'X');
2085 at_offset = at_pos - templates_delim[j];
2086 end_offset = at_offset + strlen(urls[i].text);
2088 st.codepage = CP_ACP;
2089 st.flags = ST_DEFAULT;
2090 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2091 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2092 st.flags = ST_SELECTION;
2093 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2094 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2095 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2097 /* This assumes no templates start with the URL itself, and that they
2098 have at least two characters before the URL text */
2099 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2100 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2101 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2102 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2103 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2104 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2108 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2109 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2110 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2111 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2115 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2116 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2117 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2118 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2120 if (buffer[end_offset] != '\0')
2122 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2123 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2124 if (buffer[end_offset +1] != '\0')
2126 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2127 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2132 /* Set selection with X to the first character of the URL, then the rest */
2133 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2138 at_pos = strchr(templates_delim[j], 'X');
2139 at_offset = at_pos - templates_delim[j];
2140 end_offset = at_offset + strlen(urls[i].text);
2142 strcpy(buffer, "YY");
2143 buffer[0] = urls[i].text[0];
2145 st.codepage = CP_ACP;
2146 st.flags = ST_DEFAULT;
2147 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2148 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2149 st.flags = ST_SELECTION;
2150 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2151 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2152 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2153 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2154 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2156 /* This assumes no templates start with the URL itself, and that they
2157 have at least two characters before the URL text */
2158 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2159 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2162 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2163 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2167 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2168 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2169 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2170 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2174 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2175 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2179 if (buffer[end_offset] != '\0')
2181 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2182 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2183 if (buffer[end_offset +1] != '\0')
2185 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2186 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2191 DestroyWindow(hwndRichEdit);
2192 hwndRichEdit = NULL;
2195 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2196 /* Test just the first two URL examples for brevity */
2197 for (i = 0; i < 2; i++) {
2198 hwndRichEdit = new_richedit(parent);
2200 /* Set selection with X to the URL */
2201 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2206 at_pos = strchr(templates_delim[j], 'X');
2207 at_offset = at_pos - templates_delim[j];
2208 end_offset = at_offset + strlen(urls[i].text);
2210 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2211 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2212 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2213 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2214 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2216 /* This assumes no templates start with the URL itself, and that they
2217 have at least two characters before the URL text */
2218 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2219 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2220 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2221 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2222 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2223 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2227 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2228 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2229 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2230 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2234 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2235 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2236 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2237 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2239 if (buffer[end_offset] != '\0')
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2243 if (buffer[end_offset +1] != '\0')
2245 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2246 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2251 /* Set selection with X to the first character of the URL, then the rest */
2252 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2257 at_pos = strchr(templates_delim[j], 'X');
2258 at_offset = at_pos - templates_delim[j];
2259 end_offset = at_offset + strlen(urls[i].text);
2261 strcpy(buffer, "YY");
2262 buffer[0] = urls[i].text[0];
2264 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2266 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2267 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2268 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2269 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2270 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2272 /* This assumes no templates start with the URL itself, and that they
2273 have at least two characters before the URL text */
2274 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2275 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2276 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2277 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2278 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2279 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2283 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2284 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2285 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2286 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2290 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2291 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2292 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2293 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2295 if (buffer[end_offset] != '\0')
2297 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2298 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2299 if (buffer[end_offset +1] != '\0')
2301 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2302 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2307 DestroyWindow(hwndRichEdit);
2308 hwndRichEdit = NULL;
2311 DestroyWindow(parent);
2314 static void test_EM_SCROLL(void)
2317 int r; /* return value */
2318 int expr; /* expected return value */
2319 HWND hwndRichEdit = new_richedit(NULL);
2320 int y_before, y_after; /* units of lines of text */
2322 /* test a richedit box containing a single line of text */
2323 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2325 for (i = 0; i < 4; i++) {
2326 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2328 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2329 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2330 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2331 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2332 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2333 "(i == %d)\n", y_after, i);
2337 * test a richedit box that will scroll. There are two general
2338 * cases: the case without any long lines and the case with a long
2341 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2343 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2345 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2346 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2347 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2348 "LONG LINE \nb\nc\nd\ne");
2349 for (j = 0; j < 12; j++) /* reset scroll position to top */
2350 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2352 /* get first visible line */
2353 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2354 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2356 /* get new current first visible line */
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2359 ok(((r & 0xffffff00) == 0x00010000) &&
2360 ((r & 0x000000ff) != 0x00000000),
2361 "EM_SCROLL page down didn't scroll by a small positive number of "
2362 "lines (r == 0x%08x)\n", r);
2363 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2364 "(line %d scrolled to line %d\n", y_before, y_after);
2368 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2369 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2370 ok(((r & 0xffffff00) == 0x0001ff00),
2371 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2372 "(r == 0x%08x)\n", r);
2373 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2374 "%d scrolled to line %d\n", y_before, y_after);
2378 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2380 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2382 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2383 "(r == 0x%08x)\n", r);
2384 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2385 "1 line (%d scrolled to %d)\n", y_before, y_after);
2389 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2391 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2393 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2394 "(r == 0x%08x)\n", r);
2395 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2396 "line (%d scrolled to %d)\n", y_before, y_after);
2400 r = SendMessage(hwndRichEdit, EM_SCROLL,
2401 SB_LINEUP, 0); /* lineup beyond top */
2403 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2406 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2407 ok(y_before == y_after,
2408 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2412 r = SendMessage(hwndRichEdit, EM_SCROLL,
2413 SB_PAGEUP, 0);/*page up beyond top */
2415 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2418 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2419 ok(y_before == y_after,
2420 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2422 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2423 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2424 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2425 r = SendMessage(hwndRichEdit, EM_SCROLL,
2426 SB_PAGEDOWN, 0); /* page down beyond bot */
2427 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2430 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2431 ok(y_before == y_after,
2432 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2435 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2436 SendMessage(hwndRichEdit, EM_SCROLL,
2437 SB_LINEDOWN, 0); /* line down beyond bot */
2438 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2441 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2442 ok(y_before == y_after,
2443 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2446 DestroyWindow(hwndRichEdit);
2449 unsigned int recursionLevel = 0;
2450 unsigned int WM_SIZE_recursionLevel = 0;
2451 BOOL bailedOutOfRecursion = FALSE;
2452 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2454 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2458 if (bailedOutOfRecursion) return 0;
2459 if (recursionLevel >= 32) {
2460 bailedOutOfRecursion = TRUE;
2467 WM_SIZE_recursionLevel++;
2468 r = richeditProc(hwnd, message, wParam, lParam);
2469 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2470 ShowScrollBar(hwnd, SB_VERT, TRUE);
2471 WM_SIZE_recursionLevel--;
2474 r = richeditProc(hwnd, message, wParam, lParam);
2481 static void test_scrollbar_visibility(void)
2484 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2489 /* These tests show that richedit should temporarily refrain from automatically
2490 hiding or showing its scrollbars (vertical at least) when an explicit request
2491 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2492 Some applications depend on forced showing (when otherwise richedit would
2493 hide the vertical scrollbar) and are thrown on an endless recursive loop
2494 if richedit auto-hides the scrollbar again. Apparently they never heard of
2495 the ES_DISABLENOSCROLL style... */
2497 hwndRichEdit = new_richedit(NULL);
2499 /* Test default scrollbar visibility behavior */
2500 memset(&si, 0, sizeof(si));
2501 si.cbSize = sizeof(si);
2502 si.fMask = SIF_PAGE | SIF_RANGE;
2503 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2504 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2505 "Vertical scrollbar is visible, should be invisible.\n");
2506 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2507 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2508 si.nPage, si.nMin, si.nMax);
2510 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2511 memset(&si, 0, sizeof(si));
2512 si.cbSize = sizeof(si);
2513 si.fMask = SIF_PAGE | SIF_RANGE;
2514 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2515 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2516 "Vertical scrollbar is visible, should be invisible.\n");
2517 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2518 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2519 si.nPage, si.nMin, si.nMax);
2521 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2522 memset(&si, 0, sizeof(si));
2523 si.cbSize = sizeof(si);
2524 si.fMask = SIF_PAGE | SIF_RANGE;
2525 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2526 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2527 "Vertical scrollbar is invisible, should be visible.\n");
2528 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2529 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2530 si.nPage, si.nMin, si.nMax);
2532 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2533 even though it hides the scrollbar */
2534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2535 memset(&si, 0, sizeof(si));
2536 si.cbSize = sizeof(si);
2537 si.fMask = SIF_PAGE | SIF_RANGE;
2538 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2539 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2540 "Vertical scrollbar is visible, should be invisible.\n");
2541 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2542 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2543 si.nPage, si.nMin, si.nMax);
2545 /* Setting non-scrolling text again does *not* reset scrollbar range */
2546 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2547 memset(&si, 0, sizeof(si));
2548 si.cbSize = sizeof(si);
2549 si.fMask = SIF_PAGE | SIF_RANGE;
2550 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2551 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2552 "Vertical scrollbar is visible, should be invisible.\n");
2553 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2554 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2555 si.nPage, si.nMin, si.nMax);
2557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2558 memset(&si, 0, sizeof(si));
2559 si.cbSize = sizeof(si);
2560 si.fMask = SIF_PAGE | SIF_RANGE;
2561 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2562 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2563 "Vertical scrollbar is visible, should be invisible.\n");
2564 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2565 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2566 si.nPage, si.nMin, si.nMax);
2568 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2569 memset(&si, 0, sizeof(si));
2570 si.cbSize = sizeof(si);
2571 si.fMask = SIF_PAGE | SIF_RANGE;
2572 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2573 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2574 "Vertical scrollbar is visible, should be invisible.\n");
2575 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2576 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2577 si.nPage, si.nMin, si.nMax);
2579 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2580 memset(&si, 0, sizeof(si));
2581 si.cbSize = sizeof(si);
2582 si.fMask = SIF_PAGE | SIF_RANGE;
2583 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2584 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2585 "Vertical scrollbar is visible, should be invisible.\n");
2586 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2587 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2588 si.nPage, si.nMin, si.nMax);
2590 DestroyWindow(hwndRichEdit);
2592 /* Test again, with ES_DISABLENOSCROLL style */
2593 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2595 /* Test default scrollbar visibility behavior */
2596 memset(&si, 0, sizeof(si));
2597 si.cbSize = sizeof(si);
2598 si.fMask = SIF_PAGE | SIF_RANGE;
2599 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2600 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2601 "Vertical scrollbar is invisible, should be visible.\n");
2602 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2603 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2604 si.nPage, si.nMin, si.nMax);
2606 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2607 memset(&si, 0, sizeof(si));
2608 si.cbSize = sizeof(si);
2609 si.fMask = SIF_PAGE | SIF_RANGE;
2610 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2611 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2612 "Vertical scrollbar is invisible, should be visible.\n");
2613 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2614 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2615 si.nPage, si.nMin, si.nMax);
2617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2618 memset(&si, 0, sizeof(si));
2619 si.cbSize = sizeof(si);
2620 si.fMask = SIF_PAGE | SIF_RANGE;
2621 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2622 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2623 "Vertical scrollbar is invisible, should be visible.\n");
2624 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2625 "reported page/range is %d (%d..%d)\n",
2626 si.nPage, si.nMin, si.nMax);
2628 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2629 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2630 memset(&si, 0, sizeof(si));
2631 si.cbSize = sizeof(si);
2632 si.fMask = SIF_PAGE | SIF_RANGE;
2633 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2634 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2635 "Vertical scrollbar is invisible, should be visible.\n");
2636 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2637 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2638 si.nPage, si.nMin, si.nMax);
2640 /* Setting non-scrolling text again does *not* reset scrollbar range */
2641 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2642 memset(&si, 0, sizeof(si));
2643 si.cbSize = sizeof(si);
2644 si.fMask = SIF_PAGE | SIF_RANGE;
2645 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2646 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2647 "Vertical scrollbar is invisible, should be visible.\n");
2648 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2649 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2650 si.nPage, si.nMin, si.nMax);
2652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2653 memset(&si, 0, sizeof(si));
2654 si.cbSize = sizeof(si);
2655 si.fMask = SIF_PAGE | SIF_RANGE;
2656 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2657 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2658 "Vertical scrollbar is invisible, should be visible.\n");
2659 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2660 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2661 si.nPage, si.nMin, si.nMax);
2663 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2664 memset(&si, 0, sizeof(si));
2665 si.cbSize = sizeof(si);
2666 si.fMask = SIF_PAGE | SIF_RANGE;
2667 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2668 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2669 "Vertical scrollbar is invisible, should be visible.\n");
2670 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2671 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2672 si.nPage, si.nMin, si.nMax);
2674 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2675 memset(&si, 0, sizeof(si));
2676 si.cbSize = sizeof(si);
2677 si.fMask = SIF_PAGE | SIF_RANGE;
2678 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2679 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2680 "Vertical scrollbar is invisible, should be visible.\n");
2681 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2682 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2683 si.nPage, si.nMin, si.nMax);
2685 DestroyWindow(hwndRichEdit);
2687 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2688 hwndRichEdit = new_richedit(NULL);
2690 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2691 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2692 memset(&si, 0, sizeof(si));
2693 si.cbSize = sizeof(si);
2694 si.fMask = SIF_PAGE | SIF_RANGE;
2695 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2697 "Vertical scrollbar is invisible, should be visible.\n");
2699 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2700 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2701 si.nPage, si.nMin, si.nMax);
2704 /* Ditto, see above */
2705 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2706 memset(&si, 0, sizeof(si));
2707 si.cbSize = sizeof(si);
2708 si.fMask = SIF_PAGE | SIF_RANGE;
2709 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2710 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2711 "Vertical scrollbar is invisible, should be visible.\n");
2713 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2714 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2715 si.nPage, si.nMin, si.nMax);
2718 /* Ditto, see above */
2719 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2720 memset(&si, 0, sizeof(si));
2721 si.cbSize = sizeof(si);
2722 si.fMask = SIF_PAGE | SIF_RANGE;
2723 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2724 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2725 "Vertical scrollbar is invisible, should be visible.\n");
2727 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2728 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2729 si.nPage, si.nMin, si.nMax);
2732 /* Ditto, see above */
2733 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2734 memset(&si, 0, sizeof(si));
2735 si.cbSize = sizeof(si);
2736 si.fMask = SIF_PAGE | SIF_RANGE;
2737 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2738 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2739 "Vertical scrollbar is invisible, should be visible.\n");
2741 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2742 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2743 si.nPage, si.nMin, si.nMax);
2746 /* Ditto, see above */
2747 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2748 memset(&si, 0, sizeof(si));
2749 si.cbSize = sizeof(si);
2750 si.fMask = SIF_PAGE | SIF_RANGE;
2751 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2752 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2753 "Vertical scrollbar is invisible, should be visible.\n");
2755 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2756 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2757 si.nPage, si.nMin, si.nMax);
2760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2762 memset(&si, 0, sizeof(si));
2763 si.cbSize = sizeof(si);
2764 si.fMask = SIF_PAGE | SIF_RANGE;
2765 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2766 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2767 "Vertical scrollbar is visible, should be invisible.\n");
2768 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2769 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2770 si.nPage, si.nMin, si.nMax);
2772 DestroyWindow(hwndRichEdit);
2774 hwndRichEdit = new_richedit(NULL);
2776 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2777 memset(&si, 0, sizeof(si));
2778 si.cbSize = sizeof(si);
2779 si.fMask = SIF_PAGE | SIF_RANGE;
2780 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2781 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2782 "Vertical scrollbar is visible, should be invisible.\n");
2783 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2784 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2785 si.nPage, si.nMin, si.nMax);
2787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2788 memset(&si, 0, sizeof(si));
2789 si.cbSize = sizeof(si);
2790 si.fMask = SIF_PAGE | SIF_RANGE;
2791 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2792 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2793 "Vertical scrollbar is visible, should be invisible.\n");
2794 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2795 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2796 si.nPage, si.nMin, si.nMax);
2798 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2799 memset(&si, 0, sizeof(si));
2800 si.cbSize = sizeof(si);
2801 si.fMask = SIF_PAGE | SIF_RANGE;
2802 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2803 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2804 "Vertical scrollbar is visible, should be invisible.\n");
2805 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2806 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2807 si.nPage, si.nMin, si.nMax);
2809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2810 memset(&si, 0, sizeof(si));
2811 si.cbSize = sizeof(si);
2812 si.fMask = SIF_PAGE | SIF_RANGE;
2813 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2814 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2815 "Vertical scrollbar is visible, should be invisible.\n");
2816 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2817 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2818 si.nPage, si.nMin, si.nMax);
2820 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2821 memset(&si, 0, sizeof(si));
2822 si.cbSize = sizeof(si);
2823 si.fMask = SIF_PAGE | SIF_RANGE;
2824 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2825 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2826 "Vertical scrollbar is invisible, should be visible.\n");
2827 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2828 "reported page/range is %d (%d..%d)\n",
2829 si.nPage, si.nMin, si.nMax);
2831 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2832 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2833 memset(&si, 0, sizeof(si));
2834 si.cbSize = sizeof(si);
2835 si.fMask = SIF_PAGE | SIF_RANGE;
2836 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2837 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2838 "Vertical scrollbar is visible, should be invisible.\n");
2839 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2840 "reported page/range is %d (%d..%d)\n",
2841 si.nPage, si.nMin, si.nMax);
2843 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2844 memset(&si, 0, sizeof(si));
2845 si.cbSize = sizeof(si);
2846 si.fMask = SIF_PAGE | SIF_RANGE;
2847 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2848 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2849 "Vertical scrollbar is visible, should be invisible.\n");
2850 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2851 "reported page/range is %d (%d..%d)\n",
2852 si.nPage, si.nMin, si.nMax);
2854 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2855 EM_SCROLL will make visible any forcefully invisible scrollbar */
2856 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2857 memset(&si, 0, sizeof(si));
2858 si.cbSize = sizeof(si);
2859 si.fMask = SIF_PAGE | SIF_RANGE;
2860 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2861 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2862 "Vertical scrollbar is invisible, should be visible.\n");
2863 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2864 "reported page/range is %d (%d..%d)\n",
2865 si.nPage, si.nMin, si.nMax);
2867 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2868 memset(&si, 0, sizeof(si));
2869 si.cbSize = sizeof(si);
2870 si.fMask = SIF_PAGE | SIF_RANGE;
2871 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2872 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2873 "Vertical scrollbar is visible, should be invisible.\n");
2874 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2875 "reported page/range is %d (%d..%d)\n",
2876 si.nPage, si.nMin, si.nMax);
2878 /* Again, EM_SCROLL, with SB_LINEUP */
2879 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2885 "Vertical scrollbar is invisible, should be visible.\n");
2886 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2887 "reported page/range is %d (%d..%d)\n",
2888 si.nPage, si.nMin, si.nMax);
2890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2891 memset(&si, 0, sizeof(si));
2892 si.cbSize = sizeof(si);
2893 si.fMask = SIF_PAGE | SIF_RANGE;
2894 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2895 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2896 "Vertical scrollbar is visible, should be invisible.\n");
2897 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2898 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2899 si.nPage, si.nMin, si.nMax);
2901 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2902 memset(&si, 0, sizeof(si));
2903 si.cbSize = sizeof(si);
2904 si.fMask = SIF_PAGE | SIF_RANGE;
2905 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2906 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2907 "Vertical scrollbar is invisible, should be visible.\n");
2908 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2909 "reported page/range is %d (%d..%d)\n",
2910 si.nPage, si.nMin, si.nMax);
2912 DestroyWindow(hwndRichEdit);
2915 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2916 hwndRichEdit = new_richedit(NULL);
2918 #define ENABLE_WS_VSCROLL(hwnd) \
2919 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2920 #define DISABLE_WS_VSCROLL(hwnd) \
2921 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2923 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2924 ENABLE_WS_VSCROLL(hwndRichEdit);
2925 memset(&si, 0, sizeof(si));
2926 si.cbSize = sizeof(si);
2927 si.fMask = SIF_PAGE | SIF_RANGE;
2928 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2929 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2930 "Vertical scrollbar is invisible, should be visible.\n");
2931 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2932 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2933 si.nPage, si.nMin, si.nMax);
2935 /* Ditto, see above */
2936 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2937 memset(&si, 0, sizeof(si));
2938 si.cbSize = sizeof(si);
2939 si.fMask = SIF_PAGE | SIF_RANGE;
2940 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2942 "Vertical scrollbar is invisible, should be visible.\n");
2943 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2944 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2945 si.nPage, si.nMin, si.nMax);
2947 /* Ditto, see above */
2948 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2949 memset(&si, 0, sizeof(si));
2950 si.cbSize = sizeof(si);
2951 si.fMask = SIF_PAGE | SIF_RANGE;
2952 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2953 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2954 "Vertical scrollbar is invisible, should be visible.\n");
2955 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2956 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2957 si.nPage, si.nMin, si.nMax);
2959 /* Ditto, see above */
2960 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2961 memset(&si, 0, sizeof(si));
2962 si.cbSize = sizeof(si);
2963 si.fMask = SIF_PAGE | SIF_RANGE;
2964 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2965 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2966 "Vertical scrollbar is invisible, should be visible.\n");
2967 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2968 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2969 si.nPage, si.nMin, si.nMax);
2971 /* Ditto, see above */
2972 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2973 memset(&si, 0, sizeof(si));
2974 si.cbSize = sizeof(si);
2975 si.fMask = SIF_PAGE | SIF_RANGE;
2976 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2977 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2978 "Vertical scrollbar is invisible, should be visible.\n");
2979 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2980 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2981 si.nPage, si.nMin, si.nMax);
2983 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2984 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2985 memset(&si, 0, sizeof(si));
2986 si.cbSize = sizeof(si);
2987 si.fMask = SIF_PAGE | SIF_RANGE;
2988 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2989 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2990 "Vertical scrollbar is visible, should be invisible.\n");
2991 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2992 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2993 si.nPage, si.nMin, si.nMax);
2995 DestroyWindow(hwndRichEdit);
2997 hwndRichEdit = new_richedit(NULL);
2999 DISABLE_WS_VSCROLL(hwndRichEdit);
3000 memset(&si, 0, sizeof(si));
3001 si.cbSize = sizeof(si);
3002 si.fMask = SIF_PAGE | SIF_RANGE;
3003 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3004 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3005 "Vertical scrollbar is visible, should be invisible.\n");
3006 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3007 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3008 si.nPage, si.nMin, si.nMax);
3010 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3011 memset(&si, 0, sizeof(si));
3012 si.cbSize = sizeof(si);
3013 si.fMask = SIF_PAGE | SIF_RANGE;
3014 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3016 "Vertical scrollbar is visible, should be invisible.\n");
3017 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3018 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3019 si.nPage, si.nMin, si.nMax);
3021 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3022 memset(&si, 0, sizeof(si));
3023 si.cbSize = sizeof(si);
3024 si.fMask = SIF_PAGE | SIF_RANGE;
3025 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3026 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3027 "Vertical scrollbar is visible, should be invisible.\n");
3028 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3029 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3030 si.nPage, si.nMin, si.nMax);
3032 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3033 memset(&si, 0, sizeof(si));
3034 si.cbSize = sizeof(si);
3035 si.fMask = SIF_PAGE | SIF_RANGE;
3036 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3037 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3038 "Vertical scrollbar is visible, should be invisible.\n");
3039 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3040 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3041 si.nPage, si.nMin, si.nMax);
3043 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3044 memset(&si, 0, sizeof(si));
3045 si.cbSize = sizeof(si);
3046 si.fMask = SIF_PAGE | SIF_RANGE;
3047 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3048 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3049 "Vertical scrollbar is invisible, should be visible.\n");
3050 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3051 "reported page/range is %d (%d..%d)\n",
3052 si.nPage, si.nMin, si.nMax);
3054 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3055 DISABLE_WS_VSCROLL(hwndRichEdit);
3056 memset(&si, 0, sizeof(si));
3057 si.cbSize = sizeof(si);
3058 si.fMask = SIF_PAGE | SIF_RANGE;
3059 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3060 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3061 "Vertical scrollbar is visible, should be invisible.\n");
3062 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3063 "reported page/range is %d (%d..%d)\n",
3064 si.nPage, si.nMin, si.nMax);
3066 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3067 memset(&si, 0, sizeof(si));
3068 si.cbSize = sizeof(si);
3069 si.fMask = SIF_PAGE | SIF_RANGE;
3070 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3071 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3072 "Vertical scrollbar is visible, should be invisible.\n");
3073 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3074 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3075 si.nPage, si.nMin, si.nMax);
3077 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3078 memset(&si, 0, sizeof(si));
3079 si.cbSize = sizeof(si);
3080 si.fMask = SIF_PAGE | SIF_RANGE;
3081 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3082 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3083 "Vertical scrollbar is invisible, should be visible.\n");
3084 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3085 "reported page/range is %d (%d..%d)\n",
3086 si.nPage, si.nMin, si.nMax);
3088 DISABLE_WS_VSCROLL(hwndRichEdit);
3089 memset(&si, 0, sizeof(si));
3090 si.cbSize = sizeof(si);
3091 si.fMask = SIF_PAGE | SIF_RANGE;
3092 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3093 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3094 "Vertical scrollbar is visible, should be invisible.\n");
3095 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3096 "reported page/range is %d (%d..%d)\n",
3097 si.nPage, si.nMin, si.nMax);
3099 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3100 EM_SCROLL will make visible any forcefully invisible scrollbar */
3101 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3102 memset(&si, 0, sizeof(si));
3103 si.cbSize = sizeof(si);
3104 si.fMask = SIF_PAGE | SIF_RANGE;
3105 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3106 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3107 "Vertical scrollbar is invisible, should be visible.\n");
3108 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3109 "reported page/range is %d (%d..%d)\n",
3110 si.nPage, si.nMin, si.nMax);
3112 DISABLE_WS_VSCROLL(hwndRichEdit);
3113 memset(&si, 0, sizeof(si));
3114 si.cbSize = sizeof(si);
3115 si.fMask = SIF_PAGE | SIF_RANGE;
3116 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3117 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3118 "Vertical scrollbar is visible, should be invisible.\n");
3119 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3120 "reported page/range is %d (%d..%d)\n",
3121 si.nPage, si.nMin, si.nMax);
3123 /* Again, EM_SCROLL, with SB_LINEUP */
3124 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3125 memset(&si, 0, sizeof(si));
3126 si.cbSize = sizeof(si);
3127 si.fMask = SIF_PAGE | SIF_RANGE;
3128 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3129 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3130 "Vertical scrollbar is invisible, should be visible.\n");
3131 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3132 "reported page/range is %d (%d..%d)\n",
3133 si.nPage, si.nMin, si.nMax);
3135 DestroyWindow(hwndRichEdit);
3137 /* This window proc models what is going on with Corman Lisp 3.0.
3138 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3139 force the scrollbar into visibility. Recursion should NOT happen
3140 as a result of this action.
3142 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3144 richeditProc = cls.lpfnWndProc;
3145 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3146 cls.lpszClassName = "RicheditStupidOverride";
3147 if(!RegisterClassA(&cls)) assert(0);
3150 WM_SIZE_recursionLevel = 0;
3151 bailedOutOfRecursion = FALSE;
3152 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3153 ok(!bailedOutOfRecursion,
3154 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3157 WM_SIZE_recursionLevel = 0;
3158 bailedOutOfRecursion = FALSE;
3159 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3160 ok(!bailedOutOfRecursion,
3161 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3163 /* Unblock window in order to process WM_DESTROY */
3165 bailedOutOfRecursion = FALSE;
3166 WM_SIZE_recursionLevel = 0;
3167 DestroyWindow(hwndRichEdit);
3171 static void test_EM_SETUNDOLIMIT(void)
3173 /* cases we test for:
3174 * default behaviour - limiting at 100 undo's
3175 * undo disabled - setting a limit of 0
3176 * undo limited - undo limit set to some to some number, like 2
3177 * bad input - sending a negative number should default to 100 undo's */
3179 HWND hwndRichEdit = new_richedit(NULL);
3184 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3187 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3188 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3189 also, multiple pastes don't combine like WM_CHAR would */
3190 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3192 /* first case - check the default */
3193 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3194 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3195 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3196 for (i=0; i<100; i++) /* Undo 100 of them */
3197 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3198 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3199 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3201 /* second case - cannot undo */
3202 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3203 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3204 SendMessage(hwndRichEdit,
3205 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3206 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3207 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3209 /* third case - set it to an arbitrary number */
3210 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3211 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3212 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3213 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3214 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3215 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3216 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3217 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3218 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3219 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3220 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3221 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3222 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3223 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3225 /* fourth case - setting negative numbers should default to 100 undos */
3226 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3227 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3229 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3231 DestroyWindow(hwndRichEdit);
3234 static void test_ES_PASSWORD(void)
3236 /* This isn't hugely testable, so we're just going to run it through its paces */
3238 HWND hwndRichEdit = new_richedit(NULL);
3241 /* First, check the default of a regular control */
3242 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3244 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3246 /* Now, set it to something normal */
3247 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3248 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3250 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3252 /* Now, set it to something odd */
3253 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3254 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3256 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3257 DestroyWindow(hwndRichEdit);
3260 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3265 char** str = (char**)dwCookie;
3268 memcpy(*str, pbBuff, *pcb);
3274 static void test_WM_SETTEXT(void)
3276 HWND hwndRichEdit = new_richedit(NULL);
3277 const char * TestItem1 = "TestSomeText";
3278 const char * TestItem2 = "TestSomeText\r";
3279 const char * TestItem2_after = "TestSomeText\r\n";
3280 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3281 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3282 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3283 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3284 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3285 const char * TestItem5_after = "TestSomeText TestSomeText";
3286 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3287 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3288 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3289 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3291 const char rtftextA[] = "{\\rtf sometext}";
3292 const char urtftextA[] = "{\\urtf sometext}";
3293 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3294 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3295 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3297 char buf[1024] = {0};
3298 WCHAR bufW[1024] = {0};
3301 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3302 any solitary \r to be converted to \r\n on return. Properly paired
3303 \r\n are not affected. It also shows that the special sequence \r\r\n
3304 gets converted to a single space.
3307 #define TEST_SETTEXT(a, b) \
3308 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3309 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3310 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3311 ok (result == lstrlen(buf), \
3312 "WM_GETTEXT returned %ld instead of expected %u\n", \
3313 result, lstrlen(buf)); \
3314 result = strcmp(b, buf); \
3316 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3318 TEST_SETTEXT(TestItem1, TestItem1)
3319 TEST_SETTEXT(TestItem2, TestItem2_after)
3320 TEST_SETTEXT(TestItem3, TestItem3_after)
3321 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3322 TEST_SETTEXT(TestItem4, TestItem4_after)
3323 TEST_SETTEXT(TestItem5, TestItem5_after)
3324 TEST_SETTEXT(TestItem6, TestItem6_after)
3325 TEST_SETTEXT(TestItem7, TestItem7_after)
3327 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3328 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3329 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3330 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3331 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3332 DestroyWindow(hwndRichEdit);
3335 #define TEST_SETTEXTW(a, b) \
3336 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3337 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3338 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3339 ok (result == lstrlenW(bufW), \
3340 "WM_GETTEXT returned %ld instead of expected %u\n", \
3341 result, lstrlenW(bufW)); \
3342 result = lstrcmpW(b, bufW); \
3343 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3347 skip("Cannot perform unicode tests\n");
3350 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3351 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3352 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3353 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3354 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3355 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3356 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3357 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3358 DestroyWindow(hwndRichEdit);
3359 #undef TEST_SETTEXTW
3362 static void test_EM_STREAMOUT(void)
3364 HWND hwndRichEdit = new_richedit(NULL);
3367 char buf[1024] = {0};
3370 const char * TestItem1 = "TestSomeText";
3371 const char * TestItem2 = "TestSomeText\r";
3372 const char * TestItem3 = "TestSomeText\r\n";
3374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3376 es.dwCookie = (DWORD_PTR)&p;
3378 es.pfnCallback = test_WM_SETTEXT_esCallback;
3379 memset(buf, 0, sizeof(buf));
3380 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3382 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3383 ok(strcmp(buf, TestItem1) == 0,
3384 "streamed text different, got %s\n", buf);
3386 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3388 es.dwCookie = (DWORD_PTR)&p;
3390 es.pfnCallback = test_WM_SETTEXT_esCallback;
3391 memset(buf, 0, sizeof(buf));
3392 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3394 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3395 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3396 ok(strcmp(buf, TestItem3) == 0,
3397 "streamed text different from, got %s\n", buf);
3398 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3400 es.dwCookie = (DWORD_PTR)&p;
3402 es.pfnCallback = test_WM_SETTEXT_esCallback;
3403 memset(buf, 0, sizeof(buf));
3404 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3406 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3407 ok(strcmp(buf, TestItem3) == 0,
3408 "streamed text different, got %s\n", buf);
3410 DestroyWindow(hwndRichEdit);
3413 static void test_EM_STREAMOUT_FONTTBL(void)
3415 HWND hwndRichEdit = new_richedit(NULL);
3417 char buf[1024] = {0};
3422 const char * TestItem = "TestSomeText";
3424 /* fills in the richedit control with some text */
3425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3427 /* streams out the text in rtf format */
3429 es.dwCookie = (DWORD_PTR)&p;
3431 es.pfnCallback = test_WM_SETTEXT_esCallback;
3432 memset(buf, 0, sizeof(buf));
3433 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3435 /* scans for \fonttbl, error if not found */
3436 fontTbl = strstr(buf, "\\fonttbl");
3437 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3440 /* scans for terminating closing bracket */
3442 while(*fontTbl && brackCount)
3446 else if(*fontTbl == '}')
3450 /* checks whether closing bracket is ok */
3451 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3454 /* char before closing fonttbl block should be a closed bracket */
3456 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3458 /* char after fonttbl block should be a crlf */
3460 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3463 DestroyWindow(hwndRichEdit);
3467 static void test_EM_SETTEXTEX(void)
3469 HWND hwndRichEdit, parent;
3471 int sel_start, sel_end;
3474 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3476 'T', 'e', 'x', 't', 0};
3477 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3483 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3484 '\r','t','S','o','m','e','T','e','x','t',0};
3485 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3489 const char * TestItem2_after = "TestSomeText\r\n";
3490 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3493 '\r','\n','\r','\n', 0};
3494 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3498 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3502 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3505 '\r','\r','\n','\r',
3507 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3511 #define MAX_BUF_LEN 1024
3512 WCHAR buf[MAX_BUF_LEN];
3513 char bufACP[MAX_BUF_LEN];
3520 /* Test the scroll position with and without a parent window.
3522 * For some reason the scroll position is 0 after EM_SETTEXTEX
3523 * with the ST_SELECTION flag only when the control has a parent
3524 * window, even though the selection is at the end. */
3526 cls.lpfnWndProc = DefWindowProcA;
3529 cls.hInstance = GetModuleHandleA(0);
3531 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3532 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3533 cls.lpszMenuName = NULL;
3534 cls.lpszClassName = "ParentTestClass";
3535 if(!RegisterClassA(&cls)) assert(0);
3537 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3538 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3539 ok (parent != 0, "Failed to create parent window\n");
3541 hwndRichEdit = CreateWindowEx(0,
3542 RICHEDIT_CLASS, NULL,
3543 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3544 0, 0, 200, 60, parent, NULL,
3545 hmoduleRichEdit, NULL);
3547 setText.codepage = CP_ACP;
3548 setText.flags = ST_SELECTION;
3549 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3550 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3551 si.cbSize = sizeof(si);
3553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3554 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3555 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3556 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3557 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3559 DestroyWindow(parent);
3561 /* Test without a parent window */
3562 hwndRichEdit = new_richedit(NULL);
3563 setText.codepage = CP_ACP;
3564 setText.flags = ST_SELECTION;
3565 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3566 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3567 si.cbSize = sizeof(si);
3569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3570 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3571 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3572 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3573 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3575 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3576 * but this time it is because the selection is at the beginning. */
3577 setText.codepage = CP_ACP;
3578 setText.flags = ST_DEFAULT;
3579 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3580 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3581 si.cbSize = sizeof(si);
3583 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3584 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3585 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3586 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3587 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3589 setText.codepage = 1200; /* no constant for unicode */
3590 getText.codepage = 1200; /* no constant for unicode */
3591 getText.cb = MAX_BUF_LEN;
3592 getText.flags = GT_DEFAULT;
3593 getText.lpDefaultChar = NULL;
3594 getText.lpUsedDefChar = NULL;
3597 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3598 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3599 ok(lstrcmpW(buf, TestItem1) == 0,
3600 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3602 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3603 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3605 setText.codepage = 1200; /* no constant for unicode */
3606 getText.codepage = 1200; /* no constant for unicode */
3607 getText.cb = MAX_BUF_LEN;
3608 getText.flags = GT_DEFAULT;
3609 getText.lpDefaultChar = NULL;
3610 getText.lpUsedDefChar = NULL;
3612 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3613 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3614 ok(lstrcmpW(buf, TestItem2) == 0,
3615 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3617 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3618 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3619 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3620 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3622 /* Baseline test for just-enough buffer space for string */
3623 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3624 getText.codepage = 1200; /* no constant for unicode */
3625 getText.flags = GT_DEFAULT;
3626 getText.lpDefaultChar = NULL;
3627 getText.lpUsedDefChar = NULL;
3628 memset(buf, 0, MAX_BUF_LEN);
3629 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3630 ok(lstrcmpW(buf, TestItem2) == 0,
3631 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3633 /* When there is enough space for one character, but not both, of the CRLF
3634 pair at the end of the string, the CR is not copied at all. That is,
3635 the caller must not see CRLF pairs truncated to CR at the end of the
3638 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3639 getText.codepage = 1200; /* no constant for unicode */
3640 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3641 getText.lpDefaultChar = NULL;
3642 getText.lpUsedDefChar = NULL;
3643 memset(buf, 0, MAX_BUF_LEN);
3644 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3645 ok(lstrcmpW(buf, TestItem1) == 0,
3646 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3649 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3650 setText.codepage = 1200; /* no constant for unicode */
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.cb = MAX_BUF_LEN;
3653 getText.flags = GT_DEFAULT;
3654 getText.lpDefaultChar = NULL;
3655 getText.lpUsedDefChar = NULL;
3657 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3658 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3659 ok(lstrcmpW(buf, TestItem3_after) == 0,
3660 "EM_SETTEXTEX did not convert properly\n");
3662 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3663 setText.codepage = 1200; /* no constant for unicode */
3664 getText.codepage = 1200; /* no constant for unicode */
3665 getText.cb = MAX_BUF_LEN;
3666 getText.flags = GT_DEFAULT;
3667 getText.lpDefaultChar = NULL;
3668 getText.lpUsedDefChar = NULL;
3670 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3671 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3672 ok(lstrcmpW(buf, TestItem3_after) == 0,
3673 "EM_SETTEXTEX did not convert properly\n");
3675 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3676 setText.codepage = 1200; /* no constant for unicode */
3677 getText.codepage = 1200; /* no constant for unicode */
3678 getText.cb = MAX_BUF_LEN;
3679 getText.flags = GT_DEFAULT;
3680 getText.lpDefaultChar = NULL;
3681 getText.lpUsedDefChar = NULL;
3683 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3684 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3685 ok(lstrcmpW(buf, TestItem4_after) == 0,
3686 "EM_SETTEXTEX did not convert properly\n");
3688 /* !ST_SELECTION && Unicode && !\rtf */
3689 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3693 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3694 ok(lstrlenW(buf) == 0,
3695 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3697 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3699 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3700 /* select some text */
3703 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3704 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3705 setText.flags = ST_SELECTION;
3706 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3708 "EM_SETTEXTEX with NULL lParam to replace selection"
3709 " with no text should return 0. Got %i\n",
3712 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3714 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3715 /* select some text */
3718 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3719 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3720 setText.flags = ST_SELECTION;
3721 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3722 (WPARAM)&setText, (LPARAM) TestItem1);
3724 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3725 ok(result == lstrlenW(TestItem1),
3726 "EM_SETTEXTEX with NULL lParam to replace selection"
3727 " with no text should return 0. Got %i\n",
3729 ok(lstrlenW(buf) == 22,
3730 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3733 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3734 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3736 es.dwCookie = (DWORD_PTR)&p;
3738 es.pfnCallback = test_WM_SETTEXT_esCallback;
3739 memset(buf, 0, sizeof(buf));
3740 SendMessage(hwndRichEdit, EM_STREAMOUT,
3741 (WPARAM)(SF_RTF), (LPARAM)&es);
3742 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3744 /* !ST_SELECTION && !Unicode && \rtf */
3745 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3746 getText.codepage = 1200; /* no constant for unicode */
3747 getText.cb = MAX_BUF_LEN;
3748 getText.flags = GT_DEFAULT;
3749 getText.lpDefaultChar = NULL;
3750 getText.lpUsedDefChar = NULL;
3753 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3754 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3755 ok(lstrcmpW(buf, TestItem1) == 0,
3756 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3758 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3759 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3760 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3761 getText.codepage = CP_ACP;
3762 getText.cb = MAX_BUF_LEN;
3763 getText.flags = GT_DEFAULT;
3764 getText.lpDefaultChar = NULL;
3765 getText.lpUsedDefChar = NULL;
3767 setText.flags = ST_SELECTION;
3768 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3769 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3770 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3771 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3772 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3774 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3775 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3777 es.dwCookie = (DWORD_PTR)&p;
3779 es.pfnCallback = test_WM_SETTEXT_esCallback;
3780 memset(buf, 0, sizeof(buf));
3781 SendMessage(hwndRichEdit, EM_STREAMOUT,
3782 (WPARAM)(SF_RTF), (LPARAM)&es);
3783 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3785 /* select some text */
3788 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3790 /* ST_SELECTION && !Unicode && \rtf */
3791 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3792 getText.codepage = 1200; /* no constant for unicode */
3793 getText.cb = MAX_BUF_LEN;
3794 getText.flags = GT_DEFAULT;
3795 getText.lpDefaultChar = NULL;
3796 getText.lpUsedDefChar = NULL;
3798 setText.flags = ST_SELECTION;
3799 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3800 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3801 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3803 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3804 setText.codepage = 1200; /* no constant for unicode */
3805 getText.codepage = CP_ACP;
3806 getText.cb = MAX_BUF_LEN;
3809 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3810 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3812 /* select some text */
3815 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3817 /* ST_SELECTION && !Unicode && !\rtf */
3818 setText.codepage = CP_ACP;
3819 getText.codepage = 1200; /* no constant for unicode */
3820 getText.cb = MAX_BUF_LEN;
3821 getText.flags = GT_DEFAULT;
3822 getText.lpDefaultChar = NULL;
3823 getText.lpUsedDefChar = NULL;
3825 setText.flags = ST_SELECTION;
3826 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3827 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3828 ok(lstrcmpW(buf, TestItem1alt) == 0,
3829 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3830 " using ST_SELECTION and non-Unicode\n");
3832 /* Test setting text using rich text format */
3834 setText.codepage = CP_ACP;
3835 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3836 getText.codepage = CP_ACP;
3837 getText.cb = MAX_BUF_LEN;
3838 getText.flags = GT_DEFAULT;
3839 getText.lpDefaultChar = NULL;
3840 getText.lpUsedDefChar = NULL;
3841 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3842 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3845 setText.codepage = CP_ACP;
3846 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3847 getText.codepage = CP_ACP;
3848 getText.cb = MAX_BUF_LEN;
3849 getText.flags = GT_DEFAULT;
3850 getText.lpDefaultChar = NULL;
3851 getText.lpUsedDefChar = NULL;
3852 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3853 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3855 DestroyWindow(hwndRichEdit);
3858 static void test_EM_LIMITTEXT(void)
3862 HWND hwndRichEdit = new_richedit(NULL);
3864 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3865 * about setting the length to -1 for multiline edit controls doesn't happen.
3868 /* Don't check default gettextlimit case. That's done in other tests */
3870 /* Set textlimit to 100 */
3871 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3872 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3874 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3876 /* Set textlimit to 0 */
3877 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3878 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3880 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3882 /* Set textlimit to -1 */
3883 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3884 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3886 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3888 /* Set textlimit to -2 */
3889 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3890 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3892 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3894 DestroyWindow (hwndRichEdit);
3898 static void test_EM_EXLIMITTEXT(void)
3900 int i, selBegin, selEnd, len1, len2;
3902 char text[1024 + 1];
3903 char buffer[1024 + 1];
3904 int textlimit = 0; /* multiple of 100 */
3905 HWND hwndRichEdit = new_richedit(NULL);
3907 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3908 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3911 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3912 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3914 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3917 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3918 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3920 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3922 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3923 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3924 /* default for WParam = 0 */
3925 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3927 textlimit = sizeof(text)-1;
3928 memset(text, 'W', textlimit);
3929 text[sizeof(text)-1] = 0;
3930 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3931 /* maxed out text */
3932 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3934 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3935 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3936 len1 = selEnd - selBegin;
3938 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3939 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3940 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3941 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3942 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3943 len2 = selEnd - selBegin;
3946 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3949 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3950 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3951 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3952 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3953 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3954 len1 = selEnd - selBegin;
3957 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3960 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3961 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3962 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3963 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3964 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3965 len2 = selEnd - selBegin;
3968 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3971 /* set text up to the limit, select all the text, then add a char */
3973 memset(text, 'W', textlimit);
3974 text[textlimit] = 0;
3975 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3976 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3977 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3978 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3979 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3980 result = strcmp(buffer, "A");
3981 ok(0 == result, "got string = \"%s\"\n", buffer);
3983 /* WM_SETTEXT not limited */
3985 memset(text, 'W', textlimit);
3986 text[textlimit] = 0;
3987 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3988 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3989 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3991 ok(10 == i, "expected 10 chars\n");
3992 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3993 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3995 /* try inserting more text at end */
3996 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3997 ok(0 == i, "WM_CHAR wasn't processed\n");
3998 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4000 ok(10 == i, "expected 10 chars, got %i\n", i);
4001 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4002 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4004 /* try inserting text at beginning */
4005 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4006 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4007 ok(0 == i, "WM_CHAR wasn't processed\n");
4008 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4010 ok(10 == i, "expected 10 chars, got %i\n", i);
4011 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4012 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4014 /* WM_CHAR is limited */
4016 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4017 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4018 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4019 ok(0 == i, "WM_CHAR wasn't processed\n");
4020 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4021 ok(0 == i, "WM_CHAR wasn't processed\n");
4022 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4024 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4026 DestroyWindow(hwndRichEdit);
4029 static void test_EM_GETLIMITTEXT(void)
4032 HWND hwndRichEdit = new_richedit(NULL);
4034 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4035 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4037 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4038 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4039 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4041 DestroyWindow(hwndRichEdit);
4044 static void test_WM_SETFONT(void)
4046 /* There is no invalid input or error conditions for this function.
4047 * NULL wParam and lParam just fall back to their default values
4048 * It should be noted that even if you use a gibberish name for your fonts
4049 * here, it will still work because the name is stored. They will display as
4050 * System, but will report their name to be whatever they were created as */
4052 HWND hwndRichEdit = new_richedit(NULL);
4053 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4054 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4055 FF_DONTCARE, "Marlett");
4056 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4057 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4058 FF_DONTCARE, "MS Sans Serif");
4059 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4060 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4061 FF_DONTCARE, "Courier");
4062 LOGFONTA sentLogFont;
4063 CHARFORMAT2A returnedCF2A;
4065 returnedCF2A.cbSize = sizeof(returnedCF2A);
4067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4068 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4069 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4071 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4072 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4073 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4074 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4076 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4077 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4078 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4079 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4080 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4081 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4083 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4084 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4085 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4086 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4087 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4088 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4090 /* This last test is special since we send in NULL. We clear the variables
4091 * and just compare to "System" instead of the sent in font name. */
4092 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4093 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4094 returnedCF2A.cbSize = sizeof(returnedCF2A);
4096 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4097 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4098 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4099 ok (!strcmp("System",returnedCF2A.szFaceName),
4100 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4102 DestroyWindow(hwndRichEdit);
4106 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4111 const char** str = (const char**)dwCookie;
4112 int size = strlen(*str);
4113 if(size > 3) /* let's make it piecemeal for fun */
4120 memcpy(pbBuff, *str, *pcb);
4126 static void test_EM_GETMODIFY(void)
4128 HWND hwndRichEdit = new_richedit(NULL);
4131 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4133 'T', 'e', 'x', 't', 0};
4134 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4136 'O', 't', 'h', 'e', 'r',
4137 'T', 'e', 'x', 't', 0};
4138 const char* streamText = "hello world";
4143 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4144 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4145 FF_DONTCARE, "Courier");
4147 setText.codepage = 1200; /* no constant for unicode */
4148 setText.flags = ST_KEEPUNDO;
4151 /* modify flag shouldn't be set when richedit is first created */
4152 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4154 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4156 /* setting modify flag should actually set it */
4157 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4158 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4160 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4162 /* clearing modify flag should actually clear it */
4163 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4164 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4166 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4168 /* setting font doesn't change modify flag */
4169 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4170 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4171 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4173 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4175 /* setting text should set modify flag */
4176 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4177 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4178 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4180 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4182 /* undo previous text doesn't reset modify flag */
4183 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4184 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4186 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4188 /* set text with no flag to keep undo stack should not set modify flag */
4189 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4191 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4192 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4194 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4196 /* WM_SETTEXT doesn't modify */
4197 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4198 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4199 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4201 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4203 /* clear the text */
4204 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4205 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4206 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4208 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4211 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4212 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4213 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4214 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4215 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4217 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4219 /* copy/paste text 1 */
4220 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4221 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4222 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4223 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4224 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4226 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4228 /* copy/paste text 2 */
4229 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4230 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4231 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4232 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4233 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4234 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4236 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4239 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4240 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4241 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4242 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4244 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4247 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4248 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4249 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4250 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4252 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4254 /* set char format */
4255 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4256 cf2.cbSize = sizeof(CHARFORMAT2);
4257 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4258 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4259 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4260 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4261 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4262 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4263 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4265 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4267 /* set para format */
4268 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4269 pf2.cbSize = sizeof(PARAFORMAT2);
4270 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4272 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4273 pf2.wAlignment = PFA_RIGHT;
4274 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4275 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4277 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4280 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 es.dwCookie = (DWORD_PTR)&streamText;
4283 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4284 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4285 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4287 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4289 DestroyWindow(hwndRichEdit);
4295 LRESULT expected_retval;
4296 int expected_getsel_start;
4297 int expected_getsel_end;
4298 int _getsel_todo_wine;
4301 const struct exsetsel_s exsetsel_tests[] = {
4303 {5, 10, 10, 5, 10, 0},
4304 {15, 17, 17, 15, 17, 0},
4305 /* test cpMax > strlen() */
4306 {0, 100, 18, 0, 18, 1},
4307 /* test cpMin == cpMax */
4309 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4310 {-1, 0, 5, 5, 5, 0},
4311 {-1, 17, 5, 5, 5, 0},
4312 {-1, 18, 5, 5, 5, 0},
4313 /* test cpMin < 0 && cpMax < 0 */
4314 {-1, -1, 17, 17, 17, 0},
4315 {-4, -5, 17, 17, 17, 0},
4316 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4317 {0, -1, 18, 0, 18, 1},
4318 {17, -5, 18, 17, 18, 1},
4319 {18, -3, 17, 17, 17, 0},
4320 /* test if cpMin > cpMax */
4321 {15, 19, 18, 15, 18, 1},
4322 {19, 15, 18, 15, 18, 1}
4325 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4330 cr.cpMin = setsel->min;
4331 cr.cpMax = setsel->max;
4332 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4334 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4336 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4338 if (setsel->_getsel_todo_wine) {
4340 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);
4343 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);
4347 static void test_EM_EXSETSEL(void)
4349 HWND hwndRichEdit = new_richedit(NULL);
4351 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4353 /* sending some text to the window */
4354 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4355 /* 01234567890123456*/
4358 for (i = 0; i < num_tests; i++) {
4359 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4362 DestroyWindow(hwndRichEdit);
4365 static void test_EM_REPLACESEL(int redraw)
4367 HWND hwndRichEdit = new_richedit(NULL);
4368 char buffer[1024] = {0};
4373 /* sending some text to the window */
4374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4375 /* 01234567890123456*/
4378 /* FIXME add more tests */
4379 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4380 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4381 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4382 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4383 r = strcmp(buffer, "testing");
4384 ok(0 == r, "expected %d, got %d\n", 0, r);
4386 DestroyWindow(hwndRichEdit);
4388 hwndRichEdit = new_richedit(NULL);
4390 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4391 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4393 /* Test behavior with carriage returns and newlines */
4394 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4395 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4396 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4397 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4398 r = strcmp(buffer, "RichEdit1");
4399 ok(0 == r, "expected %d, got %d\n", 0, r);
4401 getText.codepage = CP_ACP;
4402 getText.flags = GT_DEFAULT;
4403 getText.lpDefaultChar = NULL;
4404 getText.lpUsedDefChar = NULL;
4405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4406 ok(strcmp(buffer, "RichEdit1") == 0,
4407 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4409 /* Test number of lines reported after EM_REPLACESEL */
4410 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4411 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4414 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4415 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4416 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4417 r = strcmp(buffer, "RichEdit1\r\n");
4418 ok(0 == r, "expected %d, got %d\n", 0, r);
4420 getText.codepage = CP_ACP;
4421 getText.flags = GT_DEFAULT;
4422 getText.lpDefaultChar = NULL;
4423 getText.lpUsedDefChar = NULL;
4424 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4425 ok(strcmp(buffer, "RichEdit1\r") == 0,
4426 "EM_GETTEXTEX returned incorrect string\n");
4428 /* Test number of lines reported after EM_REPLACESEL */
4429 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4430 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4432 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4433 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4434 returns the number of characters *inserted* into the control (after
4435 required conversions), but WinXP's riched20 returns the number of
4436 characters interpreted from the original lParam. Wine's builtin riched20
4437 implements the WinXP behavior.
4439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4440 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4441 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4442 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4444 /* Test number of lines reported after EM_REPLACESEL */
4445 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4446 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4448 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4449 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4450 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4451 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4453 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4454 r = strcmp(buffer, "RichEdit1\r\n");
4455 ok(0 == r, "expected %d, got %d\n", 0, r);
4457 getText.codepage = CP_ACP;
4458 getText.flags = GT_DEFAULT;
4459 getText.lpDefaultChar = NULL;
4460 getText.lpUsedDefChar = NULL;
4461 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4462 ok(strcmp(buffer, "RichEdit1\r") == 0,
4463 "EM_GETTEXTEX returned incorrect string\n");
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 /* The following tests show that richedit should handle the special \r\r\n
4471 sequence by turning it into a single space on insertion. However,
4472 EM_REPLACESEL on WinXP returns the number of characters in the original
4476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4477 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4478 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4479 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4480 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4481 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4482 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4484 /* Test the actual string */
4486 getText.codepage = CP_ACP;
4487 getText.flags = GT_DEFAULT;
4488 getText.lpDefaultChar = NULL;
4489 getText.lpUsedDefChar = NULL;
4490 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4491 ok(strcmp(buffer, "\r\r") == 0,
4492 "EM_GETTEXTEX returned incorrect string\n");
4494 /* Test number of lines reported after EM_REPLACESEL */
4495 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4496 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4498 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4499 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4500 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4501 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4502 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4503 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4504 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4505 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4507 /* Test the actual string */
4509 getText.codepage = CP_ACP;
4510 getText.flags = GT_DEFAULT;
4511 getText.lpDefaultChar = NULL;
4512 getText.lpUsedDefChar = NULL;
4513 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4514 ok(strcmp(buffer, " ") == 0,
4515 "EM_GETTEXTEX returned incorrect string\n");
4517 /* Test number of lines reported after EM_REPLACESEL */
4518 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4519 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4521 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4522 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4523 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4524 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4525 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4526 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4527 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4528 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4530 /* Test the actual string */
4532 getText.codepage = CP_ACP;
4533 getText.flags = GT_DEFAULT;
4534 getText.lpDefaultChar = NULL;
4535 getText.lpUsedDefChar = NULL;
4536 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4537 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4538 "EM_GETTEXTEX returned incorrect string\n");
4540 /* Test number of lines reported after EM_REPLACESEL */
4541 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4542 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4544 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4545 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4546 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4547 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4548 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4549 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4550 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4551 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4553 /* Test the actual string */
4555 getText.codepage = CP_ACP;
4556 getText.flags = GT_DEFAULT;
4557 getText.lpDefaultChar = NULL;
4558 getText.lpUsedDefChar = NULL;
4559 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4560 ok(strcmp(buffer, " \r") == 0,
4561 "EM_GETTEXTEX returned incorrect string\n");
4563 /* Test number of lines reported after EM_REPLACESEL */
4564 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4565 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4567 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4568 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4569 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4570 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4571 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4572 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4573 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4574 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4576 /* Test the actual string */
4578 getText.codepage = CP_ACP;
4579 getText.flags = GT_DEFAULT;
4580 getText.lpDefaultChar = NULL;
4581 getText.lpUsedDefChar = NULL;
4582 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4583 ok(strcmp(buffer, " \r\r") == 0,
4584 "EM_GETTEXTEX returned incorrect string\n");
4586 /* Test number of lines reported after EM_REPLACESEL */
4587 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4588 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4591 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4592 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4593 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4594 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4595 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4596 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4597 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4599 /* Test the actual string */
4601 getText.codepage = CP_ACP;
4602 getText.flags = GT_DEFAULT;
4603 getText.lpDefaultChar = NULL;
4604 getText.lpUsedDefChar = NULL;
4605 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4606 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4607 "EM_GETTEXTEX returned incorrect string\n");
4609 /* Test number of lines reported after EM_REPLACESEL */
4610 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4611 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4614 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4615 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4616 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4617 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4618 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4619 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4621 /* Test the actual string */
4623 getText.codepage = CP_ACP;
4624 getText.flags = GT_DEFAULT;
4625 getText.lpDefaultChar = NULL;
4626 getText.lpUsedDefChar = NULL;
4627 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4628 ok(strcmp(buffer, "\r\r") == 0,
4629 "EM_GETTEXTEX returned incorrect string\n");
4631 /* Test number of lines reported after EM_REPLACESEL */
4632 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4633 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4636 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4637 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4638 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4639 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4640 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4641 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4642 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4644 /* Test the actual string */
4646 getText.codepage = CP_ACP;
4647 getText.flags = GT_DEFAULT;
4648 getText.lpDefaultChar = NULL;
4649 getText.lpUsedDefChar = NULL;
4650 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4651 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4652 "EM_GETTEXTEX returned incorrect string\n");
4654 /* Test number of lines reported after EM_REPLACESEL */
4655 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4656 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4659 /* This is needed to avoid interferring with keybd_event calls
4660 * on other tests that simulate keyboard events. */
4661 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4663 DestroyWindow(hwndRichEdit);
4666 static void test_WM_PASTE(void)
4669 char buffer[1024] = {0};
4670 const char* text1 = "testing paste\r";
4671 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4672 const char* text1_after = "testing paste\r\n";
4673 const char* text2 = "testing paste\r\rtesting paste";
4674 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4675 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4676 HWND hwndRichEdit = new_richedit(NULL);
4678 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4679 * to test the state of the modifiers (Ctrl/Alt/Shift).
4681 * Therefore Ctrl-<key> keystrokes need to be simulated with
4682 * keybd_event or by using SetKeyboardState to set the modifiers
4683 * and SendMessage to simulate the keystrokes.
4686 /* Sent keystrokes with keybd_event */
4687 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4688 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4689 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4690 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4691 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4694 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4696 SEND_CTRL_C(hwndRichEdit); /* Copy */
4697 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4698 SEND_CTRL_V(hwndRichEdit); /* Paste */
4699 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4700 /* Pasted text should be visible at this step */
4701 result = strcmp(text1_step1, buffer);
4703 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4705 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4706 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4707 /* Text should be the same as before (except for \r -> \r\n conversion) */
4708 result = strcmp(text1_after, buffer);
4710 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4712 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4713 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4714 SEND_CTRL_C(hwndRichEdit); /* Copy */
4715 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4716 SEND_CTRL_V(hwndRichEdit); /* Paste */
4717 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718 /* Pasted text should be visible at this step */
4719 result = strcmp(text3, buffer);
4721 "test paste: strcmp = %i\n", result);
4722 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4723 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4724 /* Text should be the same as before (except for \r -> \r\n conversion) */
4725 result = strcmp(text2_after, buffer);
4727 "test paste: strcmp = %i\n", result);
4728 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4729 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4730 /* Text should revert to post-paste state */
4731 result = strcmp(buffer,text3);
4733 "test paste: strcmp = %i\n", result);
4741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4742 /* Send WM_CHAR to simulates Ctrl-V */
4743 SendMessage(hwndRichEdit, WM_CHAR, 22,
4744 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4745 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4746 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4747 result = strcmp(buffer,"");
4749 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4751 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4752 * with SetKeyboard state. */
4754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4755 /* Simulates paste (Ctrl-V) */
4756 hold_key(VK_CONTROL);
4757 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4758 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4759 release_key(VK_CONTROL);
4760 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4761 result = strcmp(buffer,"paste");
4763 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4766 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4767 /* Simulates copy (Ctrl-C) */
4768 hold_key(VK_CONTROL);
4769 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4770 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4771 release_key(VK_CONTROL);
4772 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4773 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4774 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4775 result = strcmp(buffer,"testing");
4777 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4779 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4781 /* Simulates select all (Ctrl-A) */
4782 hold_key(VK_CONTROL);
4783 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4784 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4785 /* Simulates select cut (Ctrl-X) */
4786 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4787 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4788 release_key(VK_CONTROL);
4789 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4790 result = strcmp(buffer,"");
4792 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4794 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4795 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4796 result = strcmp(buffer,"cut\r\n");
4797 todo_wine ok(result == 0,
4798 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4799 /* Simulates undo (Ctrl-Z) */
4800 hold_key(VK_CONTROL);
4801 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4802 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4803 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4804 result = strcmp(buffer,"");
4806 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4807 /* Simulates redo (Ctrl-Y) */
4808 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4809 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4810 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4811 result = strcmp(buffer,"cut\r\n");
4812 todo_wine ok(result == 0,
4813 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4814 release_key(VK_CONTROL);
4816 DestroyWindow(hwndRichEdit);
4819 static void test_EM_FORMATRANGE(void)
4821 int r, i, tpp_x, tpp_y;
4823 HWND hwndRichEdit = new_richedit(NULL);
4825 static const struct {
4826 const char *string; /* The string */
4827 int first; /* First 'pagebreak', 0 for don't care */
4828 int second; /* Second 'pagebreak', 0 for don't care */
4830 {"WINE wine", 0, 0},
4831 {"WINE wineWine", 0, 0},
4832 {"WINE\r\nwine\r\nwine", 5, 10},
4833 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4834 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4837 hdc = GetDC(hwndRichEdit);
4838 ok(hdc != NULL, "Could not get HDC\n");
4840 /* Calculate the twips per pixel */
4841 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4842 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4844 /* Test the simple case where all the text fits in the page rect. */
4845 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4846 fr.hdc = fr.hdcTarget = hdc;
4847 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4848 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4849 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4852 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4853 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4856 fr.rc.bottom = fr.rcPage.bottom;
4857 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4858 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4860 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4862 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4864 GETTEXTLENGTHEX gtl;
4868 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4870 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4871 gtl.codepage = CP_ACP;
4872 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4874 /* Get some size information for the string */
4875 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4877 /* Define the box to be half the width needed and a bit larger than the height.
4878 * Changes to the width means we have at least 2 pages. Changes to the height
4879 * is done so we can check the changing of fr.rc.bottom.
4881 fr.hdc = fr.hdcTarget = hdc;
4882 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4883 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4884 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4886 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4888 ok(r == len, "Expected %d, got %d\n", len, r);
4891 /* We know that the page can't hold the full string. See how many characters
4892 * are on the first one
4896 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4898 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4900 if (fmtstrings[i].first)
4902 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4905 ok(r < len, "Expected < %d, got %d\n", len, r);
4907 /* Do another page */
4909 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4910 if (fmtstrings[i].second)
4912 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4915 ok (r < len, "Expected < %d, got %d\n", len, r);
4917 /* There is at least on more page, but we don't care */
4919 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4921 ok(r == len, "Expected %d, got %d\n", len, r);
4925 ReleaseDC(NULL, hdc);
4926 DestroyWindow(hwndRichEdit);
4929 static int nCallbackCount = 0;
4931 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4934 const char text[] = {'t','e','s','t'};
4936 if (sizeof(text) <= cb)
4938 if ((int)dwCookie != nCallbackCount)
4944 memcpy (pbBuff, text, sizeof(text));
4945 *pcb = sizeof(text);
4952 return 1; /* indicates callback failed */
4955 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4960 const char** str = (const char**)dwCookie;
4961 int size = strlen(*str);
4967 memcpy(pbBuff, *str, *pcb);
4973 struct StringWithLength {
4978 /* This callback is used to handled the null characters in a string. */
4979 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4984 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4985 int size = str->length;
4991 memcpy(pbBuff, str->buffer, *pcb);
4992 str->buffer += *pcb;
4993 str->length -= *pcb;
4998 static void test_EM_STREAMIN(void)
5000 HWND hwndRichEdit = new_richedit(NULL);
5003 char buffer[1024] = {0};
5005 const char * streamText0 = "{\\rtf1 TestSomeText}";
5006 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5007 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5009 const char * streamText1 =
5010 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5011 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5014 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5015 const char * streamText2 =
5016 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5017 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5018 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5019 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5020 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5021 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5022 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5024 const char * streamText3 = "RichEdit1";
5026 struct StringWithLength cookieForStream4;
5027 const char * streamText4 =
5028 "This text just needs to be long enough to cause run to be split onto "
5029 "two separate lines and make sure the null terminating character is "
5030 "handled properly.\0";
5031 int length4 = strlen(streamText4) + 1;
5032 cookieForStream4.buffer = (char *)streamText4;
5033 cookieForStream4.length = length4;
5035 /* Minimal test without \par at the end */
5036 es.dwCookie = (DWORD_PTR)&streamText0;
5038 es.pfnCallback = test_EM_STREAMIN_esCallback;
5039 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5041 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5043 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5044 result = strcmp (buffer,"TestSomeText");
5046 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5047 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5049 /* Native richedit 2.0 ignores last \par */
5050 es.dwCookie = (DWORD_PTR)&streamText0a;
5052 es.pfnCallback = test_EM_STREAMIN_esCallback;
5053 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5055 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5057 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5058 result = strcmp (buffer,"TestSomeText");
5060 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5061 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5063 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5064 es.dwCookie = (DWORD_PTR)&streamText0b;
5066 es.pfnCallback = test_EM_STREAMIN_esCallback;
5067 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5069 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5071 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5072 result = strcmp (buffer,"TestSomeText\r\n");
5074 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5075 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5077 es.dwCookie = (DWORD_PTR)&streamText1;
5079 es.pfnCallback = test_EM_STREAMIN_esCallback;
5080 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5082 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5084 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5085 result = strcmp (buffer,"TestSomeText");
5087 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5088 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5090 es.dwCookie = (DWORD_PTR)&streamText2;
5092 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5094 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5096 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5097 ok (strlen(buffer) == 0,
5098 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5099 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5101 es.dwCookie = (DWORD_PTR)&streamText3;
5103 SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5105 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5107 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5108 ok (strlen(buffer) == 0,
5109 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5110 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5112 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5114 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5115 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5117 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5118 ok (result == length4,
5119 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5120 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5122 DestroyWindow(hwndRichEdit);
5125 static void test_EM_StreamIn_Undo(void)
5127 /* The purpose of this test is to determine when a EM_StreamIn should be
5128 * undoable. This is important because WM_PASTE currently uses StreamIn and
5129 * pasting should always be undoable but streaming isn't always.
5132 * StreamIn plain text without SFF_SELECTION.
5133 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5134 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5135 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5136 * Feel free to add tests for other text modes or StreamIn things.
5140 HWND hwndRichEdit = new_richedit(NULL);
5143 char buffer[1024] = {0};
5144 const char randomtext[] = "Some text";
5146 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5148 /* StreamIn, no SFF_SELECTION */
5149 es.dwCookie = nCallbackCount;
5150 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5152 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5153 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5154 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5155 result = strcmp (buffer,"test");
5157 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5159 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5160 ok (result == FALSE,
5161 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5163 /* StreamIn, SFF_SELECTION, but nothing selected */
5164 es.dwCookie = nCallbackCount;
5165 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5167 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5168 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5169 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5170 result = strcmp (buffer,"testSome text");
5172 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5174 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5176 "EM_STREAMIN with SFF_SELECTION but no selection set "
5177 "should create an undo\n");
5179 /* StreamIn, SFF_SELECTION, with a selection */
5180 es.dwCookie = nCallbackCount;
5181 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5183 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5184 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5185 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5186 result = strcmp (buffer,"Sometesttext");
5188 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5190 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5192 "EM_STREAMIN with SFF_SELECTION and selection set "
5193 "should create an undo\n");
5195 DestroyWindow(hwndRichEdit);
5198 static BOOL is_em_settextex_supported(HWND hwnd)
5200 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5201 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5204 static void test_unicode_conversions(void)
5206 static const WCHAR tW[] = {'t',0};
5207 static const WCHAR teW[] = {'t','e',0};
5208 static const WCHAR textW[] = {'t','e','s','t',0};
5209 static const char textA[] = "test";
5213 int em_settextex_supported, ret;
5215 #define set_textA(hwnd, wm_set_text, txt) \
5217 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5218 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5219 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5220 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5221 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5223 #define expect_textA(hwnd, wm_get_text, txt) \
5225 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5226 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5227 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5228 memset(bufA, 0xAA, sizeof(bufA)); \
5229 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5230 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5231 ret = lstrcmpA(bufA, txt); \
5232 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5235 #define set_textW(hwnd, wm_set_text, txt) \
5237 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5238 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5239 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5240 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5241 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5243 #define expect_textW(hwnd, wm_get_text, txt) \
5245 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5246 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5247 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5248 memset(bufW, 0xAA, sizeof(bufW)); \
5251 assert(wm_get_text == EM_GETTEXTEX); \
5252 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5253 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5257 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5258 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5260 ret = lstrcmpW(bufW, txt); \
5261 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5263 #define expect_empty(hwnd, wm_get_text) \
5265 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5266 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5267 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5268 memset(bufA, 0xAA, sizeof(bufA)); \
5269 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5270 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5271 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5274 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5275 0, 0, 200, 60, 0, 0, 0, 0);
5276 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5278 ret = IsWindowUnicode(hwnd);
5280 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5282 ok(ret, "RichEdit20W should be unicode under NT\n");
5284 /* EM_SETTEXTEX is supported starting from version 3.0 */
5285 em_settextex_supported = is_em_settextex_supported(hwnd);
5286 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5287 em_settextex_supported ? "" : "NOT ");
5289 expect_empty(hwnd, WM_GETTEXT);
5290 expect_empty(hwnd, EM_GETTEXTEX);
5292 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5293 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5294 expect_textA(hwnd, WM_GETTEXT, "t");
5295 expect_textA(hwnd, EM_GETTEXTEX, "t");
5296 expect_textW(hwnd, EM_GETTEXTEX, tW);
5298 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5299 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5300 expect_textA(hwnd, WM_GETTEXT, "te");
5301 expect_textA(hwnd, EM_GETTEXTEX, "te");
5302 expect_textW(hwnd, EM_GETTEXTEX, teW);
5304 set_textA(hwnd, WM_SETTEXT, NULL);
5305 expect_empty(hwnd, WM_GETTEXT);
5306 expect_empty(hwnd, EM_GETTEXTEX);
5309 set_textA(hwnd, WM_SETTEXT, textW);
5311 set_textA(hwnd, WM_SETTEXT, textA);
5312 expect_textA(hwnd, WM_GETTEXT, textA);
5313 expect_textA(hwnd, EM_GETTEXTEX, textA);
5314 expect_textW(hwnd, EM_GETTEXTEX, textW);
5316 if (em_settextex_supported)
5318 set_textA(hwnd, EM_SETTEXTEX, textA);
5319 expect_textA(hwnd, WM_GETTEXT, textA);
5320 expect_textA(hwnd, EM_GETTEXTEX, textA);
5321 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);
5341 DestroyWindow(hwnd);
5343 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5344 0, 0, 200, 60, 0, 0, 0, 0);
5345 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5347 ret = IsWindowUnicode(hwnd);
5348 ok(!ret, "RichEdit20A should NOT be unicode\n");
5350 set_textA(hwnd, WM_SETTEXT, textA);
5351 expect_textA(hwnd, WM_GETTEXT, textA);
5352 expect_textA(hwnd, EM_GETTEXTEX, textA);
5353 expect_textW(hwnd, EM_GETTEXTEX, textW);
5355 if (em_settextex_supported)
5357 set_textA(hwnd, EM_SETTEXTEX, textA);
5358 expect_textA(hwnd, WM_GETTEXT, textA);
5359 expect_textA(hwnd, EM_GETTEXTEX, textA);
5360 expect_textW(hwnd, EM_GETTEXTEX, textW);
5365 set_textW(hwnd, WM_SETTEXT, textW);
5366 expect_textW(hwnd, WM_GETTEXT, textW);
5367 expect_textA(hwnd, WM_GETTEXT, textA);
5368 expect_textW(hwnd, EM_GETTEXTEX, textW);
5369 expect_textA(hwnd, EM_GETTEXTEX, textA);
5371 if (em_settextex_supported)
5373 set_textW(hwnd, EM_SETTEXTEX, textW);
5374 expect_textW(hwnd, WM_GETTEXT, textW);
5375 expect_textA(hwnd, WM_GETTEXT, textA);
5376 expect_textW(hwnd, EM_GETTEXTEX, textW);
5377 expect_textA(hwnd, EM_GETTEXTEX, textA);
5380 DestroyWindow(hwnd);
5383 static void test_WM_CHAR(void)
5387 const char * char_list = "abc\rabc\r";
5388 const char * expected_content_single = "abcabc";
5389 const char * expected_content_multi = "abc\r\nabc\r\n";
5390 char buffer[64] = {0};
5393 /* single-line control must IGNORE carriage returns */
5394 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5395 0, 0, 200, 60, 0, 0, 0, 0);
5396 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5399 while (*p != '\0') {
5400 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5401 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5402 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5403 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5407 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5408 ret = strcmp(buffer, expected_content_single);
5409 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5411 DestroyWindow(hwnd);
5413 /* multi-line control inserts CR normally */
5414 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5415 0, 0, 200, 60, 0, 0, 0, 0);
5416 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5419 while (*p != '\0') {
5420 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5421 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5422 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5423 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5427 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5428 ret = strcmp(buffer, expected_content_multi);
5429 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5431 DestroyWindow(hwnd);
5434 static void test_EM_GETTEXTLENGTHEX(void)
5437 GETTEXTLENGTHEX gtl;
5439 const char * base_string = "base string";
5440 const char * test_string = "a\nb\n\n\r\n";
5441 const char * test_string_after = "a";
5442 const char * test_string_2 = "a\rtest\rstring";
5443 char buffer[64] = {0};
5447 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5448 0, 0, 200, 60, 0, 0, 0, 0);
5450 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5451 0, 0, 200, 60, 0, 0, 0, 0);
5452 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5454 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5455 gtl.codepage = CP_ACP;
5456 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5457 ok(ret == 0, "ret %d\n",ret);
5459 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5460 gtl.codepage = CP_ACP;
5461 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5462 ok(ret == 0, "ret %d\n",ret);
5464 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5466 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5467 gtl.codepage = CP_ACP;
5468 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5469 ok(ret == strlen(base_string), "ret %d\n",ret);
5471 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5472 gtl.codepage = CP_ACP;
5473 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5474 ok(ret == strlen(base_string), "ret %d\n",ret);
5476 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5478 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5479 gtl.codepage = CP_ACP;
5480 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5481 ok(ret == 1, "ret %d\n",ret);
5483 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5484 gtl.codepage = CP_ACP;
5485 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5486 ok(ret == 1, "ret %d\n",ret);
5488 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5489 ret = strcmp(buffer, test_string_after);
5490 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5492 DestroyWindow(hwnd);
5496 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5497 0, 0, 200, 60, 0, 0, 0, 0);
5499 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5500 0, 0, 200, 60, 0, 0, 0, 0);
5501 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
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 == 0, "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 == 0, "ret %d\n",ret);
5513 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
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(base_string), "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(base_string), "ret %d\n",ret);
5525 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
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 == strlen(test_string_2) + 2, "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 == strlen(test_string_2), "ret %d\n",ret);
5537 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5539 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5540 gtl.codepage = CP_ACP;
5541 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5542 ok(ret == 10, "ret %d\n",ret);
5544 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5545 gtl.codepage = CP_ACP;
5546 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5547 ok(ret == 6, "ret %d\n",ret);
5549 /* Unicode/NUMCHARS/NUMBYTES */
5550 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5552 gtl.flags = GTL_DEFAULT;
5553 gtl.codepage = 1200;
5554 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5555 ok(ret == lstrlen(test_string_2),
5556 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5558 gtl.flags = GTL_NUMCHARS;
5559 gtl.codepage = 1200;
5560 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5561 ok(ret == lstrlen(test_string_2),
5562 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5564 gtl.flags = GTL_NUMBYTES;
5565 gtl.codepage = 1200;
5566 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5567 ok(ret == lstrlen(test_string_2)*2,
5568 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5570 gtl.flags = GTL_PRECISE;
5571 gtl.codepage = 1200;
5572 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5573 ok(ret == lstrlen(test_string_2)*2,
5574 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5576 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5577 gtl.codepage = 1200;
5578 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5579 ok(ret == lstrlen(test_string_2),
5580 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5582 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5583 gtl.codepage = 1200;
5584 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5585 ok(ret == E_INVALIDARG,
5586 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5588 DestroyWindow(hwnd);
5592 /* globals that parent and child access when checking event masks & notifications */
5593 static HWND eventMaskEditHwnd = 0;
5594 static int queriedEventMask;
5595 static int watchForEventMask = 0;
5597 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5598 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5600 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5602 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5604 return DefWindowProcA(hwnd, message, wParam, lParam);
5607 /* test event masks in combination with WM_COMMAND */
5608 static void test_eventMask(void)
5613 const char text[] = "foo bar\n";
5616 /* register class to capture WM_COMMAND */
5618 cls.lpfnWndProc = ParentMsgCheckProcA;
5621 cls.hInstance = GetModuleHandleA(0);
5623 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5624 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5625 cls.lpszMenuName = NULL;
5626 cls.lpszClassName = "EventMaskParentClass";
5627 if(!RegisterClassA(&cls)) assert(0);
5629 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5630 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5631 ok (parent != 0, "Failed to create parent window\n");
5633 eventMaskEditHwnd = new_richedit(parent);
5634 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5636 eventMask = ENM_CHANGE | ENM_UPDATE;
5637 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5638 ok(ret == ENM_NONE, "wrong event mask\n");
5639 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5640 ok(ret == eventMask, "failed to set event mask\n");
5642 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5643 queriedEventMask = 0; /* initialize to something other than we expect */
5644 watchForEventMask = EN_CHANGE;
5645 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5646 ok(ret == TRUE, "failed to set text\n");
5647 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5648 notification in response to WM_SETTEXT */
5649 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5650 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5652 /* check to see if EN_CHANGE is sent when redraw is turned off */
5653 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5654 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5655 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5656 /* redraw is disabled by making the window invisible. */
5657 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5658 queriedEventMask = 0; /* initialize to something other than we expect */
5659 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5660 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5661 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5662 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5663 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5665 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5666 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5667 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5668 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5669 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5670 watchForEventMask = EN_UPDATE;
5671 queriedEventMask = 0; /* initialize to something other than we expect */
5672 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5673 ok(queriedEventMask == 0,
5674 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5675 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5676 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5677 queriedEventMask = 0; /* initialize to something other than we expect */
5678 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5679 ok(queriedEventMask == eventMask,
5680 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5683 DestroyWindow(parent);
5686 static int received_WM_NOTIFY = 0;
5687 static int modify_at_WM_NOTIFY = 0;
5688 static BOOL filter_on_WM_NOTIFY = FALSE;
5689 static HWND hwndRichedit_WM_NOTIFY;
5691 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5693 if(message == WM_NOTIFY)
5695 received_WM_NOTIFY = 1;
5696 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5697 if (filter_on_WM_NOTIFY) return TRUE;
5699 return DefWindowProcA(hwnd, message, wParam, lParam);
5702 static void test_WM_NOTIFY(void)
5707 int sel_start, sel_end;
5709 /* register class to capture WM_NOTIFY */
5711 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5714 cls.hInstance = GetModuleHandleA(0);
5716 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5717 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5718 cls.lpszMenuName = NULL;
5719 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5720 if(!RegisterClassA(&cls)) assert(0);
5722 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5723 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5724 ok (parent != 0, "Failed to create parent window\n");
5726 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5727 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5729 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5731 /* Notifications for selection change should only be sent when selection
5732 actually changes. EM_SETCHARFORMAT is one message that calls
5733 ME_CommitUndo, which should check whether message should be sent */
5734 received_WM_NOTIFY = 0;
5735 cf2.cbSize = sizeof(CHARFORMAT2);
5736 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5737 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5738 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5739 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5740 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5742 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5744 received_WM_NOTIFY = 0;
5745 modify_at_WM_NOTIFY = 0;
5746 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5747 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5748 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5750 received_WM_NOTIFY = 0;
5751 modify_at_WM_NOTIFY = 0;
5752 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5753 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5755 received_WM_NOTIFY = 0;
5756 modify_at_WM_NOTIFY = 0;
5757 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5758 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5759 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5761 /* Test for WM_NOTIFY messages with redraw disabled. */
5762 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5763 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5764 received_WM_NOTIFY = 0;
5765 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5766 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5767 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5769 /* Test filtering key events. */
5770 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5771 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5772 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5773 received_WM_NOTIFY = 0;
5774 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5775 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5776 ok(sel_start == 1 && sel_end == 1,
5777 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5778 filter_on_WM_NOTIFY = TRUE;
5779 received_WM_NOTIFY = 0;
5780 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5781 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5782 ok(sel_start == 1 && sel_end == 1,
5783 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5785 /* test with owner set to NULL */
5786 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5787 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5788 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5789 ok(sel_start == 1 && sel_end == 1,
5790 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5792 DestroyWindow(hwndRichedit_WM_NOTIFY);
5793 DestroyWindow(parent);
5796 static void test_undo_coalescing(void)
5800 char buffer[64] = {0};
5802 /* multi-line control inserts CR normally */
5804 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5805 0, 0, 200, 60, 0, 0, 0, 0);
5807 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5808 0, 0, 200, 60, 0, 0, 0, 0);
5809 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5811 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5812 ok (result == FALSE, "Can undo after window creation.\n");
5813 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5814 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5815 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5816 ok (result == FALSE, "Can redo after window creation.\n");
5817 result = SendMessage(hwnd, EM_REDO, 0, 0);
5818 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5820 /* Test the effect of arrows keys during typing on undo transactions*/
5821 simulate_typing_characters(hwnd, "one two three");
5822 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5823 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5824 simulate_typing_characters(hwnd, " four five six");
5826 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5827 ok (result == FALSE, "Can redo before anything is undone.\n");
5828 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5829 ok (result == TRUE, "Cannot undo typed characters.\n");
5830 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5831 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5832 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5833 ok (result == TRUE, "Cannot redo after undo.\n");
5834 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5835 result = strcmp(buffer, "one two three");
5836 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5838 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5839 ok (result == TRUE, "Cannot undo typed characters.\n");
5840 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5841 ok (result == TRUE, "Failed to undo typed characters.\n");
5842 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5843 result = strcmp(buffer, "");
5844 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5846 /* Test the effect of focus changes during typing on undo transactions*/
5847 simulate_typing_characters(hwnd, "one two three");
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_KILLFOCUS, 0, 0);
5851 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5852 simulate_typing_characters(hwnd, " 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, "one two three");
5857 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5859 /* Test the effect of the back key during typing on undo transactions */
5860 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5861 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5862 ok (result == TRUE, "Failed to clear the text.\n");
5863 simulate_typing_characters(hwnd, "one two threa");
5864 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5865 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5866 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5867 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5868 simulate_typing_characters(hwnd, "e four five six");
5869 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5870 ok (result == TRUE, "Failed to undo typed characters.\n");
5871 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5872 result = strcmp(buffer, "");
5873 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5875 /* Test the effect of the delete key during typing on undo transactions */
5876 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5877 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5878 ok(result == TRUE, "Failed to set the text.\n");
5879 SendMessage(hwnd, EM_SETSEL, 1, 1);
5880 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5881 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5882 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5883 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5884 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5885 ok (result == TRUE, "Failed to undo typed characters.\n");
5886 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5887 result = strcmp(buffer, "acd");
5888 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5889 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5890 ok (result == TRUE, "Failed to undo typed characters.\n");
5891 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5892 result = strcmp(buffer, "abcd");
5893 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5895 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5896 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5897 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5898 ok (result == TRUE, "Failed to clear the text.\n");
5899 simulate_typing_characters(hwnd, "one two three");
5900 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5901 ok (result == 0, "expected %d but got %d\n", 0, result);
5902 simulate_typing_characters(hwnd, " four five six");
5903 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5904 ok (result == TRUE, "Failed to undo typed characters.\n");
5905 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5906 result = strcmp(buffer, "one two three");
5907 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5908 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5909 ok (result == TRUE, "Failed to undo typed characters.\n");
5910 ok (result == TRUE, "Failed to undo typed characters.\n");
5911 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5912 result = strcmp(buffer, "");
5913 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5915 DestroyWindow(hwnd);
5918 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5922 /* MSDN lied, length is actually the number of bytes. */
5923 length = bytes / sizeof(WCHAR);
5926 case WB_ISDELIMITER:
5927 return text[pos] == 'X';
5929 case WB_MOVEWORDLEFT:
5930 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5932 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5935 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5939 case WB_MOVEWORDRIGHT:
5940 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5942 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5945 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5949 ok(FALSE, "Unexpected code %d\n", code);
5955 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5956 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5958 static void test_word_movement(void)
5962 int sel_start, sel_end;
5963 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5965 /* multi-line control inserts CR normally */
5966 hwnd = new_richedit(NULL);
5968 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5969 ok (result == TRUE, "Failed to clear the text.\n");
5970 SendMessage(hwnd, EM_SETSEL, 0, 0);
5971 /* |one two three */
5973 SEND_CTRL_RIGHT(hwnd);
5974 /* one |two three */
5975 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5976 ok(sel_start == sel_end, "Selection should be empty\n");
5977 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5979 SEND_CTRL_RIGHT(hwnd);
5980 /* one two |three */
5981 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5982 ok(sel_start == sel_end, "Selection should be empty\n");
5983 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5985 SEND_CTRL_LEFT(hwnd);
5986 /* one |two three */
5987 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5988 ok(sel_start == sel_end, "Selection should be empty\n");
5989 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
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 == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5997 SendMessage(hwnd, EM_SETSEL, 8, 8);
5998 /* one two | three */
5999 SEND_CTRL_RIGHT(hwnd);
6000 /* one two |three */
6001 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6002 ok(sel_start == sel_end, "Selection should be empty\n");
6003 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6005 SendMessage(hwnd, EM_SETSEL, 11, 11);
6006 /* one two th|ree */
6007 SEND_CTRL_LEFT(hwnd);
6008 /* one two |three */
6009 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6010 ok(sel_start == sel_end, "Selection should be empty\n");
6011 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6013 /* Test with a custom word break procedure that uses X as the delimiter. */
6014 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6015 ok (result == TRUE, "Failed to clear the text.\n");
6016 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6017 /* |one twoXthree */
6018 SEND_CTRL_RIGHT(hwnd);
6019 /* one twoX|three */
6020 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6021 ok(sel_start == sel_end, "Selection should be empty\n");
6022 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6024 DestroyWindow(hwnd);
6026 /* Make sure the behaviour is the same with a unicode richedit window,
6027 * and using unicode functions. */
6030 skip("Cannot test with unicode richedit window\n");
6034 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6035 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6036 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6038 /* Test with a custom word break procedure that uses X as the delimiter. */
6039 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6040 ok (result == TRUE, "Failed to clear the text.\n");
6041 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6042 /* |one twoXthree */
6043 SEND_CTRL_RIGHT(hwnd);
6044 /* one twoX|three */
6045 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6046 ok(sel_start == sel_end, "Selection should be empty\n");
6047 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6049 DestroyWindow(hwnd);
6052 static void test_EM_CHARFROMPOS(void)
6061 /* multi-line control inserts CR normally */
6062 hwnd = new_richedit(NULL);
6063 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6064 (LPARAM)"one two three four five six seven\reight");
6066 GetClientRect(hwnd, &rcClient);
6068 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6069 ok(result == 34, "expected character index of 34 but got %d\n", result);
6071 /* Test with points outside the bounds of the richedit control. */
6074 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6075 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6079 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6080 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6084 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6085 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6089 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6090 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6093 point.y = rcClient.bottom + 1;
6094 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6095 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6098 point.y = rcClient.bottom;
6099 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6100 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6102 DestroyWindow(hwnd);
6105 static void test_word_wrap(void)
6108 POINTL point = {0, 60}; /* This point must be below the first line */
6109 const char *text = "Must be long enough to test line wrapping";
6110 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6111 int res, pos, lines;
6113 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6114 * when specified on window creation and set later. */
6115 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6116 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6117 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6118 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6119 ok(res, "WM_SETTEXT failed.\n");
6120 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6121 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6122 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6123 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6125 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6126 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6127 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6128 DestroyWindow(hwnd);
6130 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6131 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6132 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6134 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6135 ok(res, "WM_SETTEXT failed.\n");
6136 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6137 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6138 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6139 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6141 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6142 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6143 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6144 DestroyWindow(hwnd);
6146 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6147 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6148 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6149 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6150 ok(res, "WM_SETTEXT failed.\n");
6151 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6152 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6154 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6155 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6156 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6157 DestroyWindow(hwnd);
6159 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6160 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6161 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6162 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6163 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6164 ok(res, "WM_SETTEXT failed.\n");
6165 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6166 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6168 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6169 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6170 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6172 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6173 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6174 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6175 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6176 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6178 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6179 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6180 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6181 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6182 DestroyWindow(hwnd);
6184 /* Test to see if wrapping happens with redraw disabled. */
6185 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6186 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6187 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6188 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6189 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6190 ok(res, "EM_REPLACESEL failed.\n");
6191 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6192 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6193 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6194 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6195 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6197 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6198 DestroyWindow(hwnd);
6201 static void test_autoscroll(void)
6203 HWND hwnd = new_richedit(NULL);
6204 int lines, ret, redraw;
6207 for (redraw = 0; redraw <= 1; redraw++) {
6208 trace("testing with WM_SETREDRAW=%d\n", redraw);
6209 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6210 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6211 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6212 ok(lines == 8, "%d lines instead of 8\n", lines);
6213 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6214 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6215 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6216 ret = GetWindowLong(hwnd, GWL_STYLE);
6217 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6219 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6220 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6221 ok(lines == 1, "%d lines instead of 1\n", lines);
6222 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6223 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6224 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6225 ret = GetWindowLong(hwnd, GWL_STYLE);
6226 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6229 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6230 DestroyWindow(hwnd);
6232 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6233 * auto vertical/horizontal scrolling options. */
6234 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6235 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6236 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6237 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6238 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6239 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6240 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6241 ret = GetWindowLong(hwnd, GWL_STYLE);
6242 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6243 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6244 DestroyWindow(hwnd);
6246 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6247 WS_POPUP|ES_MULTILINE,
6248 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6249 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6250 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6251 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6252 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6253 ret = GetWindowLong(hwnd, GWL_STYLE);
6254 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6255 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6256 DestroyWindow(hwnd);
6260 static void test_format_rect(void)
6263 RECT rc, expected, clientRect;
6267 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6268 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6269 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6270 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6272 GetClientRect(hwnd, &clientRect);
6274 expected = clientRect;
6276 expected.right -= 1;
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 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6281 rc.top, rc.left, rc.bottom, rc.right,
6282 expected.top, expected.left, expected.bottom, expected.right);
6284 for (n = -3; n <= 3; n++)
6291 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6294 expected.top = max(0, rc.top);
6295 expected.left = max(0, rc.left);
6296 expected.bottom = min(clientRect.bottom, rc.bottom);
6297 expected.right = min(clientRect.right, rc.right);
6298 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6299 ok(rc.top == expected.top && rc.left == expected.left &&
6300 rc.bottom == expected.bottom && rc.right == expected.right,
6301 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6302 n, rc.top, rc.left, rc.bottom, rc.right,
6303 expected.top, expected.left, expected.bottom, expected.right);
6307 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6308 expected = clientRect;
6309 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6310 ok(rc.top == expected.top && rc.left == expected.left &&
6311 rc.bottom == expected.bottom && rc.right == expected.right,
6312 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6313 rc.top, rc.left, rc.bottom, rc.right,
6314 expected.top, expected.left, expected.bottom, expected.right);
6316 /* Adding the selectionbar adds the selectionbar width to the left side. */
6317 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6318 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6319 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6320 expected.left += 8; /* selection bar width */
6321 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6322 ok(rc.top == expected.top && rc.left == expected.left &&
6323 rc.bottom == expected.bottom && rc.right == expected.right,
6324 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6325 rc.top, rc.left, rc.bottom, rc.right,
6326 expected.top, expected.left, expected.bottom, expected.right);
6329 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6330 expected = clientRect;
6331 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6332 ok(rc.top == expected.top && rc.left == expected.left &&
6333 rc.bottom == expected.bottom && rc.right == expected.right,
6334 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6335 rc.top, rc.left, rc.bottom, rc.right,
6336 expected.top, expected.left, expected.bottom, expected.right);
6338 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6339 * even if the left side is already 0. */
6340 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6341 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6342 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6343 expected.left -= 8; /* selection bar width */
6344 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6345 ok(rc.top == expected.top && rc.left == expected.left &&
6346 rc.bottom == expected.bottom && rc.right == expected.right,
6347 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6348 rc.top, rc.left, rc.bottom, rc.right,
6349 expected.top, expected.left, expected.bottom, expected.right);
6351 /* Set the absolute value of the formatting rectangle. */
6353 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6354 expected = clientRect;
6355 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6356 ok(rc.top == expected.top && rc.left == expected.left &&
6357 rc.bottom == expected.bottom && rc.right == expected.right,
6358 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6359 n, rc.top, rc.left, rc.bottom, rc.right,
6360 expected.top, expected.left, expected.bottom, expected.right);
6362 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6363 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6364 * tests show that this isn't true. */
6367 rc.bottom = clientRect.bottom - 15;
6368 rc.right = clientRect.right - 15;
6370 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6371 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6372 ok(rc.top == expected.top && rc.left == expected.left &&
6373 rc.bottom == expected.bottom && rc.right == expected.right,
6374 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6375 rc.top, rc.left, rc.bottom, rc.right,
6376 expected.top, expected.left, expected.bottom, expected.right);
6378 /* For some reason it does not limit the values to the client rect with
6379 * a WPARAM value of 1. */
6382 rc.bottom = clientRect.bottom + 15;
6383 rc.right = clientRect.right + 15;
6385 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
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);
6393 DestroyWindow(hwnd);
6395 /* The extended window style affects the formatting rectangle. */
6396 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6397 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6398 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6399 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6401 GetClientRect(hwnd, &clientRect);
6403 expected = clientRect;
6406 expected.right -= 1;
6407 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6408 ok(rc.top == expected.top && rc.left == expected.left &&
6409 rc.bottom == expected.bottom && rc.right == expected.right,
6410 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6411 rc.top, rc.left, rc.bottom, rc.right,
6412 expected.top, expected.left, expected.bottom, expected.right);
6422 expected.right += 1;
6423 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6424 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6425 ok(rc.top == expected.top && rc.left == expected.left &&
6426 rc.bottom == expected.bottom && rc.right == expected.right,
6427 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6428 rc.top, rc.left, rc.bottom, rc.right,
6429 expected.top, expected.left, expected.bottom, expected.right);
6431 DestroyWindow(hwnd);
6434 static void test_WM_GETDLGCODE(void)
6440 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6442 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6443 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6444 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6445 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6447 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6448 expected = expected | DLGC_WANTMESSAGE;
6449 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6451 DestroyWindow(hwnd);
6453 msg.message = WM_KEYDOWN;
6454 msg.wParam = VK_RETURN;
6455 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6458 msg.time = GetTickCount();
6460 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6461 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6462 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6463 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6465 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6466 expected = expected | DLGC_WANTMESSAGE;
6467 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6469 DestroyWindow(hwnd);
6471 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6472 ES_MULTILINE|WS_POPUP,
6473 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6474 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6476 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6477 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6478 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6480 DestroyWindow(hwnd);
6482 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6483 ES_WANTRETURN|WS_POPUP,
6484 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6485 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6487 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6488 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6489 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6491 DestroyWindow(hwnd);
6493 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6495 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6496 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6498 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6499 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6500 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6502 DestroyWindow(hwnd);
6504 msg.wParam = VK_TAB;
6505 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6507 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6508 ES_MULTILINE|WS_POPUP,
6509 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6510 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6512 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6513 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6514 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6516 DestroyWindow(hwnd);
6518 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6520 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6521 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6523 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6524 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6525 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6527 DestroyWindow(hwnd);
6529 hold_key(VK_CONTROL);
6531 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6532 ES_MULTILINE|WS_POPUP,
6533 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6534 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6536 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6537 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6538 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6540 DestroyWindow(hwnd);
6542 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6544 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6545 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6547 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6548 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6549 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6551 DestroyWindow(hwnd);
6553 release_key(VK_CONTROL);
6556 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6558 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6559 ES_MULTILINE|WS_POPUP,
6560 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6561 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6563 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6564 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6565 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6567 DestroyWindow(hwnd);
6569 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6571 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6572 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6574 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6575 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6576 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6578 DestroyWindow(hwnd);
6580 msg.message = WM_CHAR;
6582 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6583 ES_MULTILINE|WS_POPUP,
6584 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6585 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6587 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6588 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6589 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6591 DestroyWindow(hwnd);
6593 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6595 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6596 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6598 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6599 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6600 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6602 DestroyWindow(hwnd);
6605 static void test_zoom(void)
6611 int numerator, denominator;
6613 hwnd = new_richedit(NULL);
6614 GetClientRect(hwnd, &rc);
6615 pt.x = (rc.right - rc.left) / 2;
6616 pt.y = (rc.bottom - rc.top) / 2;
6617 ClientToScreen(hwnd, &pt);
6619 /* Test initial zoom value */
6620 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6621 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6622 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6623 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6625 /* test scroll wheel */
6626 hold_key(VK_CONTROL);
6627 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6628 MAKELPARAM(pt.x, pt.y));
6629 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6630 release_key(VK_CONTROL);
6632 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6633 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6634 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6635 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6637 /* Test how much the mouse wheel can zoom in and out. */
6638 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6639 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6641 hold_key(VK_CONTROL);
6642 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6643 MAKELPARAM(pt.x, pt.y));
6644 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6645 release_key(VK_CONTROL);
6647 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6648 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6649 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6650 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6652 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6653 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6655 hold_key(VK_CONTROL);
6656 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6657 MAKELPARAM(pt.x, pt.y));
6658 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6659 release_key(VK_CONTROL);
6661 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6662 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6663 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6664 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6666 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6667 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6669 hold_key(VK_CONTROL);
6670 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6671 MAKELPARAM(pt.x, pt.y));
6672 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6673 release_key(VK_CONTROL);
6675 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6676 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6677 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6678 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6680 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6681 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6683 hold_key(VK_CONTROL);
6684 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6685 MAKELPARAM(pt.x, pt.y));
6686 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6687 release_key(VK_CONTROL);
6689 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6690 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6691 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6692 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6694 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6695 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6696 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6698 hold_key(VK_CONTROL);
6699 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6700 MAKELPARAM(pt.x, pt.y));
6701 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6702 release_key(VK_CONTROL);
6704 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6705 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6706 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6707 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6709 /* Test bounds checking on EM_SETZOOM */
6710 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6711 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6713 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6714 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6716 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6717 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6719 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6720 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6721 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6722 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6724 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6725 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6727 /* See if negative numbers are accepted. */
6728 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6729 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6731 /* See if negative numbers are accepted. */
6732 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6733 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6735 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6736 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6737 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6738 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6740 /* Reset the zoom value */
6741 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6742 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6744 DestroyWindow(hwnd);
6747 struct dialog_mode_messages
6749 int wm_getdefid, wm_close, wm_nextdlgctl;
6752 static struct dialog_mode_messages dm_messages;
6754 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6755 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6756 "got %d\n", wmclose, dm_messages.wm_close); \
6757 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6758 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6759 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6760 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6762 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6767 dm_messages.wm_getdefid++;
6768 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6770 dm_messages.wm_nextdlgctl++;
6773 dm_messages.wm_close++;
6777 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6780 static void test_dialogmode(void)
6782 HWND hwRichEdit, hwParent, hwButton;
6788 cls.lpfnWndProc = dialog_mode_wnd_proc;
6791 cls.hInstance = GetModuleHandleA(0);
6793 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6794 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6795 cls.lpszMenuName = NULL;
6796 cls.lpszClassName = "DialogModeParentClass";
6797 if(!RegisterClassA(&cls)) assert(0);
6799 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6800 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6802 /* Test richedit(ES_MULTILINE) */
6804 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6806 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6807 ok(0 == r, "expected 0, got %d\n", r);
6808 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6809 ok(2 == lcount, "expected 2, got %d\n", lcount);
6811 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6812 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6814 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6815 ok(0 == r, "expected 0, got %d\n", r);
6816 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6817 ok(3 == lcount, "expected 3, got %d\n", lcount);
6819 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6820 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6821 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6822 ok(0 == r, "expected 0, got %d\n", r);
6823 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6824 ok(3 == lcount, "expected 3, got %d\n", lcount);
6826 DestroyWindow(hwRichEdit);
6828 /* Test standalone richedit(ES_MULTILINE) */
6830 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6832 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6833 ok(0 == r, "expected 0, got %d\n", r);
6834 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6835 ok(2 == lcount, "expected 2, got %d\n", lcount);
6837 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6838 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6840 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6841 ok(0 == r, "expected 0, got %d\n", r);
6842 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6843 ok(2 == lcount, "expected 2, got %d\n", lcount);
6845 DestroyWindow(hwRichEdit);
6847 /* Check a destination for messages */
6849 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6851 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6852 SetParent( hwRichEdit, NULL);
6854 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6855 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6857 memset(&dm_messages, 0, sizeof(dm_messages));
6858 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6859 ok(0 == r, "expected 0, got %d\n", r);
6860 test_dm_messages(0, 1, 0);
6862 memset(&dm_messages, 0, sizeof(dm_messages));
6863 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6864 ok(0 == r, "expected 0, got %d\n", r);
6865 test_dm_messages(0, 0, 1);
6867 DestroyWindow(hwRichEdit);
6869 /* Check messages from richedit(ES_MULTILINE) */
6871 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6873 memset(&dm_messages, 0, sizeof(dm_messages));
6874 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6875 ok(0 == r, "expected 0, got %d\n", r);
6876 test_dm_messages(0, 0, 0);
6878 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6879 ok(2 == lcount, "expected 2, got %d\n", lcount);
6881 memset(&dm_messages, 0, sizeof(dm_messages));
6882 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6883 ok(0 == r, "expected 0, got %d\n", r);
6884 test_dm_messages(0, 0, 0);
6886 memset(&dm_messages, 0, sizeof(dm_messages));
6887 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6888 ok(0 == r, "expected 0, got %d\n", r);
6889 test_dm_messages(0, 0, 0);
6891 memset(&dm_messages, 0, sizeof(dm_messages));
6892 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6893 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6894 test_dm_messages(0, 0, 0);
6896 memset(&dm_messages, 0, sizeof(dm_messages));
6897 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6898 ok(0 == r, "expected 0, got %d\n", r);
6899 test_dm_messages(0, 1, 0);
6901 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6902 ok(2 == lcount, "expected 2, got %d\n", lcount);
6904 memset(&dm_messages, 0, sizeof(dm_messages));
6905 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6906 ok(0 == r, "expected 0, got %d\n", r);
6907 test_dm_messages(0, 0, 0);
6909 memset(&dm_messages, 0, sizeof(dm_messages));
6910 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6911 ok(0 == r, "expected 0, got %d\n", r);
6912 test_dm_messages(0, 0, 1);
6914 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6915 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6916 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6918 memset(&dm_messages, 0, sizeof(dm_messages));
6919 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6920 ok(0 == r, "expected 0, got %d\n", r);
6921 test_dm_messages(0, 1, 1);
6923 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6924 ok(2 == lcount, "expected 2, got %d\n", lcount);
6926 DestroyWindow(hwButton);
6927 DestroyWindow(hwRichEdit);
6929 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
6931 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
6933 memset(&dm_messages, 0, sizeof(dm_messages));
6934 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6935 ok(0 == r, "expected 0, got %d\n", r);
6936 test_dm_messages(0, 0, 0);
6938 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6939 ok(2 == lcount, "expected 2, got %d\n", lcount);
6941 memset(&dm_messages, 0, sizeof(dm_messages));
6942 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6943 ok(0 == r, "expected 0, got %d\n", r);
6944 test_dm_messages(0, 0, 0);
6946 memset(&dm_messages, 0, sizeof(dm_messages));
6947 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6948 ok(0 == r, "expected 0, got %d\n", r);
6949 test_dm_messages(0, 0, 0);
6951 memset(&dm_messages, 0, sizeof(dm_messages));
6952 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6953 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6954 test_dm_messages(0, 0, 0);
6956 memset(&dm_messages, 0, sizeof(dm_messages));
6957 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6958 ok(0 == r, "expected 0, got %d\n", r);
6959 test_dm_messages(0, 0, 0);
6961 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6962 ok(3 == lcount, "expected 3, got %d\n", lcount);
6964 memset(&dm_messages, 0, sizeof(dm_messages));
6965 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6966 ok(0 == r, "expected 0, got %d\n", r);
6967 test_dm_messages(0, 0, 0);
6969 memset(&dm_messages, 0, sizeof(dm_messages));
6970 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6971 ok(0 == r, "expected 0, got %d\n", r);
6972 test_dm_messages(0, 0, 1);
6974 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
6975 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
6976 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
6978 memset(&dm_messages, 0, sizeof(dm_messages));
6979 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6980 ok(0 == r, "expected 0, got %d\n", r);
6981 test_dm_messages(0, 0, 0);
6983 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6984 ok(4 == lcount, "expected 4, got %d\n", lcount);
6986 DestroyWindow(hwButton);
6987 DestroyWindow(hwRichEdit);
6989 /* Check messages from richedit(0) */
6991 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
6993 memset(&dm_messages, 0, sizeof(dm_messages));
6994 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6995 ok(0 == r, "expected 0, got %d\n", r);
6996 test_dm_messages(0, 0, 0);
6998 memset(&dm_messages, 0, sizeof(dm_messages));
6999 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7000 ok(0 == r, "expected 0, got %d\n", r);
7001 test_dm_messages(0, 0, 0);
7003 memset(&dm_messages, 0, sizeof(dm_messages));
7004 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7005 ok(0 == r, "expected 0, got %d\n", r);
7006 test_dm_messages(0, 0, 0);
7008 memset(&dm_messages, 0, sizeof(dm_messages));
7009 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7010 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7011 test_dm_messages(0, 0, 0);
7013 memset(&dm_messages, 0, sizeof(dm_messages));
7014 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7015 ok(0 == r, "expected 0, got %d\n", r);
7016 test_dm_messages(0, 1, 0);
7018 memset(&dm_messages, 0, sizeof(dm_messages));
7019 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7020 ok(0 == r, "expected 0, got %d\n", r);
7021 test_dm_messages(0, 0, 0);
7023 memset(&dm_messages, 0, sizeof(dm_messages));
7024 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7025 ok(0 == r, "expected 0, got %d\n", r);
7026 test_dm_messages(0, 0, 1);
7028 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7029 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7030 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
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, 1, 1);
7037 DestroyWindow(hwRichEdit);
7039 /* Check messages from richedit(ES_WANTRETURN) */
7041 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7043 memset(&dm_messages, 0, sizeof(dm_messages));
7044 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7045 ok(0 == r, "expected 0, got %d\n", r);
7046 test_dm_messages(0, 0, 0);
7048 memset(&dm_messages, 0, sizeof(dm_messages));
7049 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7050 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7051 test_dm_messages(0, 0, 0);
7053 memset(&dm_messages, 0, sizeof(dm_messages));
7054 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7055 ok(0 == r, "expected 0, got %d\n", r);
7056 test_dm_messages(0, 0, 0);
7058 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7059 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7060 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7062 memset(&dm_messages, 0, sizeof(dm_messages));
7063 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7064 ok(0 == r, "expected 0, got %d\n", r);
7065 test_dm_messages(0, 0, 0);
7067 DestroyWindow(hwRichEdit);
7068 DestroyWindow(hwParent);
7071 START_TEST( editor )
7073 /* Must explicitly LoadLibrary(). The test has no references to functions in
7074 * RICHED20.DLL, so the linker doesn't actually link to it. */
7075 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7076 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7078 is_win9x = GetVersion() & 0x80000000;
7083 test_EM_POSFROMCHAR();
7084 test_EM_SCROLLCARET();
7086 test_scrollbar_visibility();
7088 test_EM_LINELENGTH();
7089 test_EM_SETCHARFORMAT();
7090 test_EM_SETTEXTMODE();
7091 test_TM_PLAINTEXT();
7092 test_EM_SETOPTIONS();
7094 test_EM_GETTEXTRANGE();
7095 test_EM_GETSELTEXT();
7096 test_EM_SETUNDOLIMIT();
7098 test_EM_SETTEXTEX();
7099 test_EM_LIMITTEXT();
7100 test_EM_EXLIMITTEXT();
7101 test_EM_GETLIMITTEXT();
7103 test_EM_GETMODIFY();
7107 test_EM_STREAMOUT();
7108 test_EM_STREAMOUT_FONTTBL();
7109 test_EM_StreamIn_Undo();
7110 test_EM_FORMATRANGE();
7111 test_unicode_conversions();
7112 test_EM_GETTEXTLENGTHEX();
7113 test_EM_REPLACESEL(1);
7114 test_EM_REPLACESEL(0);
7116 test_EM_AUTOURLDETECT();
7118 test_undo_coalescing();
7119 test_word_movement();
7120 test_EM_CHARFROMPOS();
7121 test_SETPARAFORMAT();
7125 test_WM_GETDLGCODE();
7129 /* Set the environment variable WINETEST_RICHED20 to keep windows
7130 * responsive and open for 30 seconds. This is useful for debugging.
7132 if (getenv( "WINETEST_RICHED20" )) {
7133 keep_responsive(30);
7136 OleFlushClipboard();
7137 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());