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 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
38 #define ok_w3(format, szString1, szString2, szString3) \
39 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
41 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
42 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
43 format, string1, string2, string3);
45 static HMODULE hmoduleRichEdit;
47 static int is_win9x = 0;
49 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
51 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
58 static HWND new_richedit(HWND parent) {
59 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
62 /* Keeps the window reponsive for the deley_time in seconds.
63 * This is useful for debugging a test to see what is happening. */
64 static void keep_responsive(time_t delay_time)
69 /* The message pump uses PeekMessage() to empty the queue and then
70 * sleeps for 50ms before retrying the queue. */
71 end = time(NULL) + delay_time;
72 while (time(NULL) < end) {
73 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
74 TranslateMessage(&msg);
75 DispatchMessage(&msg);
82 static void processPendingMessages(void)
85 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
86 TranslateMessage(&msg);
87 DispatchMessage(&msg);
91 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
93 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
94 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
96 keybd_event(mod_vk, mod_scan_code, 0, 0);
97 keybd_event(vk, scan_code, 0, 0);
98 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
99 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
100 processPendingMessages();
103 static void simulate_typing_characters(HWND hwnd, const char* szChars)
107 while (*szChars != '\0') {
108 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
109 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
110 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
111 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
116 static BOOL hold_key(int vk)
121 result = GetKeyboardState(key_state);
122 ok(result, "GetKeyboardState failed.\n");
123 if (!result) return FALSE;
124 key_state[vk] |= 0x80;
125 result = SetKeyboardState(key_state);
126 ok(result, "SetKeyboardState failed.\n");
130 static BOOL release_key(int vk)
135 result = GetKeyboardState(key_state);
136 ok(result, "GetKeyboardState failed.\n");
137 if (!result) return FALSE;
138 key_state[vk] &= ~0x80;
139 result = SetKeyboardState(key_state);
140 ok(result, "SetKeyboardState failed.\n");
144 static const char haystack[] = "WINEWine wineWine wine WineWine";
156 struct find_s find_tests[] = {
157 /* Find in empty text */
158 {0, -1, "foo", FR_DOWN, -1},
159 {0, -1, "foo", 0, -1},
160 {0, -1, "", FR_DOWN, -1},
161 {20, 5, "foo", FR_DOWN, -1},
162 {5, 20, "foo", FR_DOWN, -1}
165 struct find_s find_tests2[] = {
167 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
168 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 /* Subsequent finds */
171 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
172 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
173 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
174 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
177 {19, 20, "Wine", FR_MATCHCASE, 13},
178 {10, 20, "Wine", FR_MATCHCASE, 4},
179 {20, 10, "Wine", FR_MATCHCASE, 13},
181 /* Case-insensitive */
182 {1, 31, "wInE", FR_DOWN, 4},
183 {1, 31, "Wine", FR_DOWN, 4},
185 /* High-to-low ranges */
186 {20, 5, "Wine", FR_DOWN, -1},
187 {2, 1, "Wine", FR_DOWN, -1},
188 {30, 29, "Wine", FR_DOWN, -1},
189 {20, 5, "Wine", 0, 13},
192 {5, 10, "", FR_DOWN, -1},
193 {10, 5, "", FR_DOWN, -1},
194 {0, -1, "", FR_DOWN, -1},
197 /* Whole-word search */
198 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
199 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
200 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
202 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
203 {11, -1, "winewine", FR_WHOLEWORD, 0},
204 {31, -1, "winewine", FR_WHOLEWORD, 23},
207 {5, 200, "XXX", FR_DOWN, -1},
208 {-20, 20, "Wine", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-15, -20, "Wine", FR_DOWN, -1},
211 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 /* Check the case noted in bug 4479 where matches at end aren't recognized */
214 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
215 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
216 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
218 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 /* The backwards case of bug 4479; bounds look right
221 * Fails because backward find is wrong */
222 {19, 20, "WINE", FR_MATCHCASE, 0},
223 {0, 20, "WINE", FR_MATCHCASE, -1},
225 {0, -1, "wineWine wine", 0, -1},
228 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
231 memset(&ft, 0, sizeof(ft));
232 ft.chrg.cpMin = f->start;
233 ft.chrg.cpMax = f->end;
234 ft.lpstrText = f->needle;
235 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
236 ok(findloc == f->expected_loc,
237 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
238 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
241 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
245 int expected_end_loc;
247 memset(&ft, 0, sizeof(ft));
248 ft.chrg.cpMin = f->start;
249 ft.chrg.cpMax = f->end;
250 ft.lpstrText = f->needle;
251 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
252 ok(findloc == f->expected_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, findloc);
255 ok(ft.chrgText.cpMin == f->expected_loc,
256 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
257 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
258 expected_end_loc = ((f->expected_loc == -1) ? -1
259 : f->expected_loc + strlen(f->needle));
260 ok(ft.chrgText.cpMax == expected_end_loc,
261 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
262 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
265 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
270 for (i = 0; i < num_tests; i++) {
271 check_EM_FINDTEXT(hwnd, name, &find[i], i);
272 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
276 static void test_EM_FINDTEXT(void)
278 HWND hwndRichEdit = new_richedit(NULL);
281 /* Empty rich edit control */
282 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
283 sizeof(find_tests)/sizeof(struct find_s));
285 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
288 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
289 sizeof(find_tests2)/sizeof(struct find_s));
291 /* Setting a format on an arbitrary range should have no effect in search
292 results. This tests correct offset reporting across runs. */
293 cf2.cbSize = sizeof(CHARFORMAT2);
294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
296 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
297 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
298 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
299 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
301 /* Haystack text, again */
302 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
303 sizeof(find_tests2)/sizeof(struct find_s));
305 /* Yet another range */
306 cf2.dwMask = CFM_BOLD | cf2.dwMask;
307 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
308 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
309 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
311 /* Haystack text, again */
312 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
313 sizeof(find_tests2)/sizeof(struct find_s));
315 DestroyWindow(hwndRichEdit);
318 static const struct getline_s {
323 {0, 10, "foo bar\r"},
328 /* Buffer smaller than line length */
334 static void test_EM_GETLINE(void)
337 HWND hwndRichEdit = new_richedit(NULL);
338 static const int nBuf = 1024;
339 char dest[1024], origdest[1024];
340 const char text[] = "foo bar\n"
344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
346 memset(origdest, 0xBB, nBuf);
347 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
350 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
351 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
352 memset(dest, 0xBB, nBuf);
353 *(WORD *) dest = gl[i].buffer_len;
355 /* EM_GETLINE appends a "\r\0" to the end of the line
356 * nCopied counts up to and including the '\r' */
357 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
358 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
360 /* two special cases since a parameter is passed via dest */
361 if (gl[i].buffer_len == 0)
362 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
364 else if (gl[i].buffer_len == 1)
365 ok(dest[0] == gl[i].text[0] && !dest[1] &&
366 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
369 /* Prepare hex strings of buffers to dump on failure. */
370 char expectedbuf[1024];
371 char resultbuf[1024];
374 for (j = 0; j < 32; j++)
375 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
376 expectedbuf[0] = '\0';
377 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
378 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
379 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
380 sprintf(expectedbuf+strlen(expectedbuf), "??");
381 for (; j < 32; j++) /* Bytes after declared buffer size */
382 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
384 /* Test the part of the buffer that is expected to be written according
385 * to the MSDN documentation fo EM_GETLINE, which does not state that
386 * a NULL terminating character will be added unless no text is copied.
388 * Windows 95, 98 & NT do not append a NULL terminating character, but
389 * Windows 2000 and up do append a NULL terminating character if there
390 * is space in the buffer. The test will ignore this difference. */
391 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
392 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
393 i, expected_bytes_written, expectedbuf, resultbuf);
394 /* Test the part of the buffer after the declared length to make sure
395 * there are no buffer overruns. */
396 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
397 nBuf - gl[i].buffer_len),
398 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
399 i, expected_bytes_written, expectedbuf, resultbuf);
403 DestroyWindow(hwndRichEdit);
406 static void test_EM_LINELENGTH(void)
408 HWND hwndRichEdit = new_richedit(NULL);
414 int offset_test[10][2] = {
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
431 for (i = 0; i < 10; i++) {
432 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
433 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
434 offset_test[i][0], result, offset_test[i][1]);
437 DestroyWindow(hwndRichEdit);
440 static int get_scroll_pos_y(HWND hwnd)
443 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
444 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
448 static void move_cursor(HWND hwnd, long charindex)
451 cr.cpMax = charindex;
452 cr.cpMin = charindex;
453 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
456 static void line_scroll(HWND hwnd, int amount)
458 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
461 static void test_EM_SCROLLCARET(void)
464 const char text[] = "aa\n"
465 "this is a long line of text that should be longer than the "
473 /* The richedit window height needs to be large enough vertically to fit in
474 * more than two lines of text, so the new_richedit function can't be used
475 * since a height of 60 was not large enough on some systems.
477 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
478 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
479 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
480 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
482 /* Can't verify this */
483 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
487 /* Caret above visible window */
488 line_scroll(hwndRichEdit, 3);
489 prevY = get_scroll_pos_y(hwndRichEdit);
490 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 curY = get_scroll_pos_y(hwndRichEdit);
492 ok(prevY != curY, "%d == %d\n", prevY, curY);
494 /* Caret below visible window */
495 move_cursor(hwndRichEdit, sizeof(text) - 1);
496 line_scroll(hwndRichEdit, -3);
497 prevY = get_scroll_pos_y(hwndRichEdit);
498 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
499 curY = get_scroll_pos_y(hwndRichEdit);
500 ok(prevY != curY, "%d == %d\n", prevY, curY);
502 /* Caret in visible window */
503 move_cursor(hwndRichEdit, sizeof(text) - 2);
504 prevY = get_scroll_pos_y(hwndRichEdit);
505 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
506 curY = get_scroll_pos_y(hwndRichEdit);
507 ok(prevY == curY, "%d != %d\n", prevY, curY);
509 /* Caret still in visible window */
510 line_scroll(hwndRichEdit, -1);
511 prevY = get_scroll_pos_y(hwndRichEdit);
512 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
513 curY = get_scroll_pos_y(hwndRichEdit);
514 ok(prevY == curY, "%d != %d\n", prevY, curY);
516 DestroyWindow(hwndRichEdit);
519 static void test_EM_POSFROMCHAR(void)
521 HWND hwndRichEdit = new_richedit(NULL);
524 unsigned int height = 0;
527 static const char text[] = "aa\n"
528 "this is a long line of text that should be longer than the "
537 /* Fill the control to lines to ensure that most of them are offscreen */
538 for (i = 0; i < 50; i++)
540 /* Do not modify the string; it is exactly 16 characters long. */
541 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
542 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
546 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
547 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
548 Richedit 3.0 accepts either of the above API conventions.
551 /* Testing Richedit 2.0 API format */
553 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
554 Since all lines are identical and drawn with the same font,
555 they should have the same height... right?
557 for (i = 0; i < 50; i++)
559 /* All the lines are 16 characters long */
560 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
563 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
564 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
565 xpos = LOWORD(result);
569 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
570 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 height = HIWORD(result);
575 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
576 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
580 /* Testing position at end of text */
581 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
582 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
583 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
585 /* Testing position way past end of text */
586 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
587 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
590 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
591 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
592 for (i = 0; i < 50; i++)
594 /* All the lines are 16 characters long */
595 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
596 ok((signed short)(HIWORD(result)) == (i - 1) * height,
597 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
598 (signed short)(HIWORD(result)), (i - 1) * height);
599 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
602 /* Testing position at end of text */
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
604 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
607 /* Testing position way past end of text */
608 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
609 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
610 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
612 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
614 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
616 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
617 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
618 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
619 xpos = LOWORD(result);
621 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
623 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
624 ok((signed short)(LOWORD(result)) < xpos,
625 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
626 (signed short)(LOWORD(result)), xpos);
627 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
629 /* Test around end of text that doesn't end in a newline. */
630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
631 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
632 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
633 ok(pt.x > 1, "pt.x = %d\n", pt.x);
635 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
636 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
637 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
639 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
640 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
641 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
643 /* Try a negative position. */
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
645 ok(pt.x == 1, "pt.x = %d\n", pt.x);
647 DestroyWindow(hwndRichEdit);
650 static void test_EM_SETCHARFORMAT(void)
652 HWND hwndRichEdit = new_richedit(NULL);
655 int tested_effects[] = {
669 /* Invalid flags, CHARFORMAT2 structure blanked out */
670 memset(&cf2, 0, sizeof(cf2));
671 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
673 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
675 /* A valid flag, CHARFORMAT2 structure blanked out */
676 memset(&cf2, 0, sizeof(cf2));
677 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
679 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
681 /* A valid flag, CHARFORMAT2 structure blanked out */
682 memset(&cf2, 0, sizeof(cf2));
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
685 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
687 /* A valid flag, CHARFORMAT2 structure blanked out */
688 memset(&cf2, 0, sizeof(cf2));
689 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
691 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
693 /* A valid flag, CHARFORMAT2 structure blanked out */
694 memset(&cf2, 0, sizeof(cf2));
695 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
697 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
699 /* Invalid flags, CHARFORMAT2 structure minimally filled */
700 memset(&cf2, 0, sizeof(cf2));
701 cf2.cbSize = sizeof(CHARFORMAT2);
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
704 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
705 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
706 ok(rc == FALSE, "Should not be able to undo here.\n");
707 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
709 /* A valid flag, CHARFORMAT2 structure minimally filled */
710 memset(&cf2, 0, sizeof(cf2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
714 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
715 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
716 ok(rc == FALSE, "Should not be able to undo here.\n");
717 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
719 /* A valid flag, CHARFORMAT2 structure minimally filled */
720 memset(&cf2, 0, sizeof(cf2));
721 cf2.cbSize = sizeof(CHARFORMAT2);
722 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
724 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
725 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
726 ok(rc == FALSE, "Should not be able to undo here.\n");
727 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
729 /* A valid flag, CHARFORMAT2 structure minimally filled */
730 memset(&cf2, 0, sizeof(cf2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
734 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
735 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
736 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
737 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
739 /* A valid flag, CHARFORMAT2 structure minimally filled */
740 memset(&cf2, 0, sizeof(cf2));
741 cf2.cbSize = sizeof(CHARFORMAT2);
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
744 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
745 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
746 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
747 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
753 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
757 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
758 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
760 /* wParam==0 is default char format, does not set modify */
761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
762 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
763 ok(rc == 0, "Text marked as modified, expected not modified!\n");
764 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
765 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
766 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
767 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 /* wParam==SCF_SELECTION sets modify if nonempty selection */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
779 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
780 ok(rc == 0, "Text marked as modified, expected not modified!\n");
781 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
784 ok(rc == 0, "Text marked as modified, expected not modified!\n");
785 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
786 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
791 /* wParam==SCF_ALL sets modify regardless of whether text is present */
792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
800 DestroyWindow(hwndRichEdit);
802 /* EM_GETCHARFORMAT tests */
803 for (i = 0; tested_effects[i]; i++)
805 hwndRichEdit = new_richedit(NULL);
806 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
808 /* Need to set a TrueType font to get consistent CFM_BOLD results */
809 memset(&cf2, 0, sizeof(CHARFORMAT2));
810 cf2.cbSize = sizeof(CHARFORMAT2);
811 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
813 strcpy(cf2.szFaceName, "Courier New");
814 cf2.wWeight = FW_DONTCARE;
815 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
817 memset(&cf2, 0, sizeof(CHARFORMAT2));
818 cf2.cbSize = sizeof(CHARFORMAT2);
819 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
821 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
822 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
824 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
826 ok((cf2.dwEffects & tested_effects[i]) == 0,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
829 memset(&cf2, 0, sizeof(CHARFORMAT2));
830 cf2.cbSize = sizeof(CHARFORMAT2);
831 cf2.dwMask = tested_effects[i];
832 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
833 cf2.dwMask = CFM_SUPERSCRIPT;
834 cf2.dwEffects = tested_effects[i];
835 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
836 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
843 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
845 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
846 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
847 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
848 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
850 memset(&cf2, 0, sizeof(CHARFORMAT2));
851 cf2.cbSize = sizeof(CHARFORMAT2);
852 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
853 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
854 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
855 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
857 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
858 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
859 ok((cf2.dwEffects & tested_effects[i]) == 0,
860 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
867 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
869 (cf2.dwMask & tested_effects[i]) == 0),
870 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
872 DestroyWindow(hwndRichEdit);
875 for (i = 0; tested_effects[i]; i++)
877 hwndRichEdit = new_richedit(NULL);
878 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
880 /* Need to set a TrueType font to get consistent CFM_BOLD results */
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
885 strcpy(cf2.szFaceName, "Courier New");
886 cf2.wWeight = FW_DONTCARE;
887 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
889 memset(&cf2, 0, sizeof(CHARFORMAT2));
890 cf2.cbSize = sizeof(CHARFORMAT2);
891 cf2.dwMask = tested_effects[i];
892 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
893 cf2.dwMask = CFM_SUPERSCRIPT;
894 cf2.dwEffects = tested_effects[i];
895 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
901 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
902 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
903 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
905 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
906 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
907 ok((cf2.dwEffects & tested_effects[i]) == 0,
908 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
910 memset(&cf2, 0, sizeof(CHARFORMAT2));
911 cf2.cbSize = sizeof(CHARFORMAT2);
912 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
913 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
914 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
915 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
917 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
918 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
919 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
920 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
925 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
926 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
927 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
929 (cf2.dwMask & tested_effects[i]) == 0),
930 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
932 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
934 DestroyWindow(hwndRichEdit);
937 /* Effects applied on an empty selection should take effect when selection is
938 replaced with text */
939 hwndRichEdit = new_richedit(NULL);
940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
941 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
943 memset(&cf2, 0, sizeof(CHARFORMAT2));
944 cf2.cbSize = sizeof(CHARFORMAT2);
945 cf2.dwMask = CFM_BOLD;
946 cf2.dwEffects = CFE_BOLD;
947 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
949 /* Selection is now nonempty */
950 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
955 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
958 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
959 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
960 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
963 /* Set two effects on an empty selection */
964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
967 memset(&cf2, 0, sizeof(CHARFORMAT2));
968 cf2.cbSize = sizeof(CHARFORMAT2);
969 cf2.dwMask = CFM_BOLD;
970 cf2.dwEffects = CFE_BOLD;
971 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 cf2.dwMask = CFM_ITALIC;
973 cf2.dwEffects = CFE_ITALIC;
974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
976 /* Selection is now nonempty */
977 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
979 memset(&cf2, 0, sizeof(CHARFORMAT2));
980 cf2.cbSize = sizeof(CHARFORMAT2);
981 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
982 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
985 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
986 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
987 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
989 /* Setting the (empty) selection to exactly the same place as before should
990 NOT clear the insertion style! */
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
992 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
994 memset(&cf2, 0, sizeof(CHARFORMAT2));
995 cf2.cbSize = sizeof(CHARFORMAT2);
996 cf2.dwMask = CFM_BOLD;
997 cf2.dwEffects = CFE_BOLD;
998 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1000 /* Empty selection in same place, insert style should NOT be forgotten here. */
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1003 /* Selection is now nonempty */
1004 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1006 memset(&cf2, 0, sizeof(CHARFORMAT2));
1007 cf2.cbSize = sizeof(CHARFORMAT2);
1008 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1009 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1011 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1012 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1013 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1014 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1016 /* Ditto with EM_EXSETSEL */
1017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1018 cr.cpMin = 2; cr.cpMax = 2;
1019 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1021 memset(&cf2, 0, sizeof(CHARFORMAT2));
1022 cf2.cbSize = sizeof(CHARFORMAT2);
1023 cf2.dwMask = CFM_BOLD;
1024 cf2.dwEffects = CFE_BOLD;
1025 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1027 /* Empty selection in same place, insert style should NOT be forgotten here. */
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1031 /* Selection is now nonempty */
1032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1034 memset(&cf2, 0, sizeof(CHARFORMAT2));
1035 cf2.cbSize = sizeof(CHARFORMAT2);
1036 cr.cpMin = 2; cr.cpMax = 6;
1037 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1038 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1040 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1041 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1042 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1043 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1045 DestroyWindow(hwndRichEdit);
1048 static void test_EM_SETTEXTMODE(void)
1050 HWND hwndRichEdit = new_richedit(NULL);
1051 CHARFORMAT2 cf2, cf2test;
1055 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1056 /*Insert text into the control*/
1058 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1060 /*Attempt to change the control to plain text mode*/
1061 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1062 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1064 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1065 If rich text is pasted, it should have the same formatting as the rest
1066 of the text in the control*/
1068 /*Italicize the text
1069 *NOTE: If the default text was already italicized, the test will simply
1070 reverse; in other words, it will copy a regular "wine" into a plain
1071 text window that uses an italicized format*/
1072 cf2.cbSize = sizeof(CHARFORMAT2);
1073 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1076 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1077 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1079 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1080 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1082 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1083 however, SCF_ALL has been implemented*/
1084 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1085 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1087 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1088 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1090 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1092 /*Select the string "wine"*/
1095 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1097 /*Copy the italicized "wine" to the clipboard*/
1098 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1100 /*Reset the formatting to default*/
1101 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1102 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1103 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1105 /*Clear the text in the control*/
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1108 /*Switch to Plain Text Mode*/
1109 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1110 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1112 /*Input "wine" again in normal format*/
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1115 /*Paste the italicized "wine" into the control*/
1116 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1118 /*Select a character from the first "wine" string*/
1121 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1123 /*Retrieve its formatting*/
1124 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1127 /*Select a character from the second "wine" string*/
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1132 /*Retrieve its formatting*/
1133 cf2test.cbSize = sizeof(CHARFORMAT2);
1134 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1137 /*Compare the two formattings*/
1138 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1139 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1140 cf2.dwEffects, cf2test.dwEffects);
1141 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1142 printing "wine" in the current format(normal)
1143 pasting "wine" from the clipboard(italicized)
1144 comparing the two formats(should differ)*/
1146 /*Attempt to switch with text in control*/
1147 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1148 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1153 /*Switch into Rich Text mode*/
1154 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1155 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1157 /*Print "wine" in normal formatting into the control*/
1158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1160 /*Paste italicized "wine" into the control*/
1161 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1163 /*Select text from the first "wine" string*/
1166 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1168 /*Retrieve its formatting*/
1169 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1172 /*Select text from the second "wine" string*/
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1181 /*Test that the two formattings are not the same*/
1182 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1183 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1184 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1186 DestroyWindow(hwndRichEdit);
1189 static void test_SETPARAFORMAT(void)
1191 HWND hwndRichEdit = new_richedit(NULL);
1194 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1195 fmt.cbSize = sizeof(PARAFORMAT2);
1196 fmt.dwMask = PFM_ALIGNMENT;
1197 fmt.wAlignment = PFA_LEFT;
1199 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1200 ok(ret != 0, "expected non-zero got %d\n", ret);
1202 fmt.cbSize = sizeof(PARAFORMAT2);
1204 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1205 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1206 * between richedit different native builds of riched20.dll
1207 * used on different Windows versions. */
1208 ret &= ~PFM_TABLEROWDELIMITER;
1209 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1211 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1212 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1214 DestroyWindow(hwndRichEdit);
1217 static void test_TM_PLAINTEXT(void)
1219 /*Tests plain text properties*/
1221 HWND hwndRichEdit = new_richedit(NULL);
1222 CHARFORMAT2 cf2, cf2test;
1226 /*Switch to plain text mode*/
1228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1231 /*Fill control with text*/
1233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1235 /*Select some text and bold it*/
1239 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1240 cf2.cbSize = sizeof(CHARFORMAT2);
1241 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1244 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1245 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1247 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1248 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1250 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1251 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1253 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1254 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1256 /*Get the formatting of those characters*/
1258 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1260 /*Get the formatting of some other characters*/
1261 cf2test.cbSize = sizeof(CHARFORMAT2);
1264 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1265 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1267 /*Test that they are the same as plain text allows only one formatting*/
1269 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1270 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1271 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1273 /*Fill the control with a "wine" string, which when inserted will be bold*/
1275 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1277 /*Copy the bolded "wine" string*/
1281 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1282 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1284 /*Swap back to rich text*/
1286 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1287 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1289 /*Set the default formatting to bold italics*/
1291 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1292 cf2.dwMask |= CFM_ITALIC;
1293 cf2.dwEffects ^= CFE_ITALIC;
1294 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1295 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1297 /*Set the text in the control to "wine", which will be bold and italicized*/
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1301 /*Paste the plain text "wine" string, which should take the insert
1302 formatting, which at the moment is bold italics*/
1304 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1306 /*Select the first "wine" string and retrieve its formatting*/
1310 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1311 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1313 /*Select the second "wine" string and retrieve its formatting*/
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1320 /*Compare the two formattings. They should be the same.*/
1322 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1323 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1324 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1325 DestroyWindow(hwndRichEdit);
1328 static void test_WM_GETTEXT(void)
1330 HWND hwndRichEdit = new_richedit(NULL);
1331 static const char text[] = "Hello. My name is RichEdit!";
1332 static const char text2[] = "Hello. My name is RichEdit!\r";
1333 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1334 char buffer[1024] = {0};
1337 /* Baseline test with normal-sized buffer */
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1339 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(result == lstrlen(buffer),
1341 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1342 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1343 result = strcmp(buffer,text);
1345 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1347 /* Test for returned value of WM_GETTEXTLENGTH */
1348 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1349 ok(result == lstrlen(text),
1350 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1351 result, lstrlen(text));
1353 /* Test for behavior in overflow case */
1354 memset(buffer, 0, 1024);
1355 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1357 result == lstrlenA(text) - 1, /* XP, win2k3 */
1358 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1359 result = strcmp(buffer,text);
1361 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1363 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1365 /* Baseline test with normal-sized buffer and carriage return */
1366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1367 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1368 ok(result == lstrlen(buffer),
1369 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1370 result = strcmp(buffer,text2_after);
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1374 /* Test for returned value of WM_GETTEXTLENGTH */
1375 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1376 ok(result == lstrlen(text2_after),
1377 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1378 result, lstrlen(text2_after));
1380 /* Test for behavior of CRLF conversion in case of overflow */
1381 memset(buffer, 0, 1024);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1384 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1385 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1386 result = strcmp(buffer,text2);
1388 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1390 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1392 DestroyWindow(hwndRichEdit);
1395 static void test_EM_GETTEXTRANGE(void)
1397 HWND hwndRichEdit = new_richedit(NULL);
1398 const char * text1 = "foo bar\r\nfoo bar";
1399 const char * text2 = "foo bar\rfoo bar";
1400 const char * expect = "bar\rfoo";
1401 char buffer[1024] = {0};
1403 TEXTRANGEA textRange;
1405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1407 textRange.lpstrText = buffer;
1408 textRange.chrg.cpMin = 4;
1409 textRange.chrg.cpMax = 11;
1410 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1411 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1412 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1423 DestroyWindow(hwndRichEdit);
1426 static void test_EM_GETSELTEXT(void)
1428 HWND hwndRichEdit = new_richedit(NULL);
1429 const char * text1 = "foo bar\r\nfoo bar";
1430 const char * text2 = "foo bar\rfoo bar";
1431 const char * expect = "bar\rfoo";
1432 char buffer[1024] = {0};
1435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1437 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1438 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1439 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1440 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1442 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1444 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1445 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1446 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1447 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1449 DestroyWindow(hwndRichEdit);
1452 /* FIXME: need to test unimplemented options and robustly test wparam */
1453 static void test_EM_SETOPTIONS(void)
1456 static const char text[] = "Hello. My name is RichEdit!";
1457 char buffer[1024] = {0};
1458 DWORD dwStyle, options, oldOptions;
1459 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1460 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1461 ES_SELECTIONBAR|ES_VERTICAL;
1463 /* Test initial options. */
1464 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1465 0, 0, 200, 60, NULL, NULL,
1466 hmoduleRichEdit, NULL);
1467 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1468 RICHEDIT_CLASS, (int) GetLastError());
1469 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1470 ok(options == 0, "Incorrect initial options %x\n", options);
1471 DestroyWindow(hwndRichEdit);
1473 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1474 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1475 0, 0, 200, 60, NULL, NULL,
1476 hmoduleRichEdit, NULL);
1477 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1478 RICHEDIT_CLASS, (int) GetLastError());
1479 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1480 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1481 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1482 "Incorrect initial options %x\n", options);
1484 /* NEGATIVE TESTING - NO OPTIONS SET */
1485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1486 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1488 /* testing no readonly by sending 'a' to the control*/
1489 SetFocus(hwndRichEdit);
1490 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1491 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1493 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1496 /* READONLY - sending 'a' to the control */
1497 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1498 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1499 SetFocus(hwndRichEdit);
1500 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1501 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1502 ok(buffer[0]==text[0],
1503 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1505 /* EM_SETOPTIONS changes the window style, but changing the
1506 * window style does not change the options. */
1507 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1508 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1509 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1510 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1511 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1512 /* Confirm that the text is still read only. */
1513 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1514 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1515 ok(buffer[0]==text[0],
1516 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1518 oldOptions = options;
1519 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1520 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1521 ok(options == oldOptions,
1522 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1524 DestroyWindow(hwndRichEdit);
1527 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1529 CHARFORMAT2W text_format;
1530 text_format.cbSize = sizeof(text_format);
1531 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1532 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1533 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1536 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1538 int link_present = 0;
1540 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1542 { /* control text is url; should get CFE_LINK */
1543 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1547 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1551 static HWND new_static_wnd(HWND parent) {
1552 return new_window("Static", 0, parent);
1555 static void test_EM_AUTOURLDETECT(void)
1557 /* DO NOT change the properties of the first two elements. To shorten the
1558 tests, all tests after WM_SETTEXT test just the first two elements -
1559 one non-URL and one URL */
1565 {"http://www.winehq.org", 1},
1566 {"http//winehq.org", 0},
1567 {"ww.winehq.org", 0},
1568 {"www.winehq.org", 1},
1569 {"ftp://192.168.1.1", 1},
1570 {"ftp//192.168.1.1", 0},
1571 {"mailto:your@email.com", 1},
1572 {"prospero:prosperoserver", 1},
1574 {"news:newserver", 1},
1575 {"wais:waisserver", 1}
1580 HWND hwndRichEdit, parent;
1582 /* All of the following should cause the URL to be detected */
1583 const char * templates_delim[] = {
1584 "This is some text with X on it",
1585 "This is some text with (X) on it",
1586 "This is some text with X\r on it",
1587 "This is some text with ---X--- on it",
1588 "This is some text with \"X\" on it",
1589 "This is some text with 'X' on it",
1590 "This is some text with 'X' on it",
1591 "This is some text with :X: on it",
1593 "This text ends with X",
1595 "This is some text with X) on it",
1596 "This is some text with X--- on it",
1597 "This is some text with X\" on it",
1598 "This is some text with X' on it",
1599 "This is some text with X: on it",
1601 "This is some text with (X on it",
1602 "This is some text with \rX on it",
1603 "This is some text with ---X on it",
1604 "This is some text with \"X on it",
1605 "This is some text with 'X on it",
1606 "This is some text with :X on it",
1608 /* None of these should cause the URL to be detected */
1609 const char * templates_non_delim[] = {
1610 "This is some text with |X| on it",
1611 "This is some text with *X* on it",
1612 "This is some text with /X/ on it",
1613 "This is some text with +X+ on it",
1614 "This is some text with %X% on it",
1615 "This is some text with #X# on it",
1616 "This is some text with @X@ on it",
1617 "This is some text with \\X\\ on it",
1618 "This is some text with |X on it",
1619 "This is some text with *X on it",
1620 "This is some text with /X on it",
1621 "This is some text with +X on it",
1622 "This is some text with %X on it",
1623 "This is some text with #X on it",
1624 "This is some text with @X on it",
1625 "This is some text with \\X on it",
1627 /* All of these cause the URL detection to be extended by one more byte,
1628 thus demonstrating that the tested character is considered as part
1630 const char * templates_xten_delim[] = {
1631 "This is some text with X| on it",
1632 "This is some text with X* 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",
1638 "This is some text with X\\ on it",
1642 parent = new_static_wnd(NULL);
1643 hwndRichEdit = new_richedit(parent);
1644 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1645 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1646 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1647 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1648 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1649 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1650 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1651 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1652 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1653 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1654 /* for each url, check the text to see if CFE_LINK effect is present */
1655 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1657 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1658 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1659 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1661 /* Link detection should happen immediately upon WM_SETTEXT */
1662 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1663 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1664 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1666 DestroyWindow(hwndRichEdit);
1668 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1669 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1670 hwndRichEdit = new_richedit(parent);
1672 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1677 at_pos = strchr(templates_delim[j], 'X');
1678 at_offset = at_pos - templates_delim[j];
1679 strncpy(buffer, templates_delim[j], at_offset);
1680 buffer[at_offset] = '\0';
1681 strcat(buffer, urls[i].text);
1682 strcat(buffer, templates_delim[j] + at_offset + 1);
1683 end_offset = at_offset + strlen(urls[i].text);
1685 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1688 /* This assumes no templates start with the URL itself, and that they
1689 have at least two characters before the URL text */
1690 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1691 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1692 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1693 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1694 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1695 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1699 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1700 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1701 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1702 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1711 if (buffer[end_offset] != '\0')
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1715 if (buffer[end_offset +1] != '\0')
1717 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1718 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1723 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1728 at_pos = strchr(templates_non_delim[j], 'X');
1729 at_offset = at_pos - templates_non_delim[j];
1730 strncpy(buffer, templates_non_delim[j], at_offset);
1731 buffer[at_offset] = '\0';
1732 strcat(buffer, urls[i].text);
1733 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1734 end_offset = at_offset + strlen(urls[i].text);
1736 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1737 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1739 /* This assumes no templates start with the URL itself, and that they
1740 have at least two characters before the URL text */
1741 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1742 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1750 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1751 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1752 if (buffer[end_offset] != '\0')
1754 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1755 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1756 if (buffer[end_offset +1] != '\0')
1758 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1759 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1764 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1769 at_pos = strchr(templates_xten_delim[j], 'X');
1770 at_offset = at_pos - templates_xten_delim[j];
1771 strncpy(buffer, templates_xten_delim[j], at_offset);
1772 buffer[at_offset] = '\0';
1773 strcat(buffer, urls[i].text);
1774 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1775 end_offset = at_offset + strlen(urls[i].text);
1777 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1780 /* This assumes no templates start with the URL itself, and that they
1781 have at least two characters before the URL text */
1782 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1783 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1784 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1785 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1786 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1787 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1791 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1792 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1793 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1794 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1795 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1796 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1800 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1801 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1802 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1803 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1804 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1805 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1807 if (buffer[end_offset +1] != '\0')
1809 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1810 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1811 if (buffer[end_offset +2] != '\0')
1813 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1814 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1819 DestroyWindow(hwndRichEdit);
1820 hwndRichEdit = NULL;
1823 /* Test detection of URLs within normal text - WM_CHAR case. */
1824 /* Test only the first two URL examples for brevity */
1825 for (i = 0; i < 2; i++) {
1826 hwndRichEdit = new_richedit(parent);
1828 /* Also for brevity, test only the first three delimiters */
1829 for (j = 0; j < 3; j++) {
1835 at_pos = strchr(templates_delim[j], 'X');
1836 at_offset = at_pos - templates_delim[j];
1837 end_offset = at_offset + strlen(urls[i].text);
1839 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1841 for (u = 0; templates_delim[j][u]; u++) {
1842 if (templates_delim[j][u] == '\r') {
1843 simulate_typing_characters(hwndRichEdit, "\r");
1844 } else if (templates_delim[j][u] != 'X') {
1845 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1847 for (v = 0; urls[i].text[v]; v++) {
1848 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1852 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1854 /* This assumes no templates start with the URL itself, and that they
1855 have at least two characters before the URL text */
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1865 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1866 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1867 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1868 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1872 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1873 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1874 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1875 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1877 if (buffer[end_offset] != '\0')
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1881 if (buffer[end_offset +1] != '\0')
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1888 /* The following will insert a paragraph break after the first character
1889 of the URL candidate, thus breaking the URL. It is expected that the
1890 CFE_LINK attribute should break across both pieces of the URL */
1891 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1892 simulate_typing_characters(hwndRichEdit, "\r");
1893 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1902 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1903 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1904 /* end_offset moved because of paragraph break */
1905 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1906 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1907 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1908 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1912 if (buffer[end_offset +2] != '\0')
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1919 /* The following will remove the just-inserted paragraph break, thus
1920 restoring the URL */
1921 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1922 simulate_typing_characters(hwndRichEdit, "\b");
1923 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1925 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1926 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1927 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1928 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1929 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1930 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1934 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1935 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1936 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1937 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1941 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1942 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1943 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1944 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1946 if (buffer[end_offset] != '\0')
1948 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1949 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1950 if (buffer[end_offset +1] != '\0')
1952 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1953 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1957 DestroyWindow(hwndRichEdit);
1958 hwndRichEdit = NULL;
1961 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1962 /* Test just the first two URL examples for brevity */
1963 for (i = 0; i < 2; i++) {
1966 hwndRichEdit = new_richedit(parent);
1968 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1970 1) Set entire text, a la WM_SETTEXT
1971 2) Set a selection of the text to the URL
1972 3) Set a portion of the text at a time, which eventually results in
1974 All of them should give equivalent results
1977 /* Set entire text in one go, like WM_SETTEXT */
1978 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1983 st.codepage = CP_ACP;
1984 st.flags = ST_DEFAULT;
1986 at_pos = strchr(templates_delim[j], 'X');
1987 at_offset = at_pos - templates_delim[j];
1988 strncpy(buffer, templates_delim[j], at_offset);
1989 buffer[at_offset] = '\0';
1990 strcat(buffer, urls[i].text);
1991 strcat(buffer, templates_delim[j] + at_offset + 1);
1992 end_offset = at_offset + strlen(urls[i].text);
1994 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1995 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1997 /* This assumes no templates start with the URL itself, and that they
1998 have at least two characters before the URL text */
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2001 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2002 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2003 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2004 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2008 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2009 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2010 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2011 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2018 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2020 if (buffer[end_offset] != '\0')
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2024 if (buffer[end_offset +1] != '\0')
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2032 /* Set selection with X to the URL */
2033 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2038 at_pos = strchr(templates_delim[j], 'X');
2039 at_offset = at_pos - templates_delim[j];
2040 end_offset = at_offset + strlen(urls[i].text);
2042 st.codepage = CP_ACP;
2043 st.flags = ST_DEFAULT;
2044 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2045 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2046 st.flags = ST_SELECTION;
2047 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2048 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2049 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2051 /* This assumes no templates start with the URL itself, and that they
2052 have at least two characters before the URL text */
2053 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2054 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2062 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2063 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2064 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2065 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2069 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2070 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2071 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2072 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2074 if (buffer[end_offset] != '\0')
2076 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2077 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2078 if (buffer[end_offset +1] != '\0')
2080 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2081 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2086 /* Set selection with X to the first character of the URL, then the rest */
2087 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2092 at_pos = strchr(templates_delim[j], 'X');
2093 at_offset = at_pos - templates_delim[j];
2094 end_offset = at_offset + strlen(urls[i].text);
2096 strcpy(buffer, "YY");
2097 buffer[0] = urls[i].text[0];
2099 st.codepage = CP_ACP;
2100 st.flags = ST_DEFAULT;
2101 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2102 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2103 st.flags = ST_SELECTION;
2104 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2105 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2106 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2107 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2108 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2110 /* This assumes no templates start with the URL itself, and that they
2111 have at least two characters before the URL text */
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2121 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2133 if (buffer[end_offset] != '\0')
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137 if (buffer[end_offset +1] != '\0')
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2145 DestroyWindow(hwndRichEdit);
2146 hwndRichEdit = NULL;
2149 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2150 /* Test just the first two URL examples for brevity */
2151 for (i = 0; i < 2; i++) {
2152 hwndRichEdit = new_richedit(parent);
2154 /* Set selection with X to the URL */
2155 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2160 at_pos = strchr(templates_delim[j], 'X');
2161 at_offset = at_pos - templates_delim[j];
2162 end_offset = at_offset + strlen(urls[i].text);
2164 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2165 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2166 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2167 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2168 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2170 /* This assumes no templates start with the URL itself, and that they
2171 have at least two characters before the URL text */
2172 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2173 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2174 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2175 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2176 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2177 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2181 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2182 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2183 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2184 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2188 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2189 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2190 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2191 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2193 if (buffer[end_offset] != '\0')
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2197 if (buffer[end_offset +1] != '\0')
2199 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2200 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2205 /* Set selection with X to the first character of the URL, then the rest */
2206 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2211 at_pos = strchr(templates_delim[j], 'X');
2212 at_offset = at_pos - templates_delim[j];
2213 end_offset = at_offset + strlen(urls[i].text);
2215 strcpy(buffer, "YY");
2216 buffer[0] = urls[i].text[0];
2218 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2219 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2220 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2221 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2222 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2223 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2224 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2226 /* This assumes no templates start with the URL itself, and that they
2227 have at least two characters before the URL text */
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2237 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2238 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2239 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2240 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2244 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2245 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2246 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2247 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2249 if (buffer[end_offset] != '\0')
2251 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2252 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2253 if (buffer[end_offset +1] != '\0')
2255 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2256 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2261 DestroyWindow(hwndRichEdit);
2262 hwndRichEdit = NULL;
2265 DestroyWindow(parent);
2268 static void test_EM_SCROLL(void)
2271 int r; /* return value */
2272 int expr; /* expected return value */
2273 HWND hwndRichEdit = new_richedit(NULL);
2274 int y_before, y_after; /* units of lines of text */
2276 /* test a richedit box containing a single line of text */
2277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2279 for (i = 0; i < 4; i++) {
2280 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2282 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2283 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2284 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2285 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2286 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2287 "(i == %d)\n", y_after, i);
2291 * test a richedit box that will scroll. There are two general
2292 * cases: the case without any long lines and the case with a long
2295 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2297 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2300 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2301 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2302 "LONG LINE \nb\nc\nd\ne");
2303 for (j = 0; j < 12; j++) /* reset scroll position to top */
2304 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2306 /* get first visible line */
2307 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2308 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2310 /* get new current first visible line */
2311 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2313 ok(((r & 0xffffff00) == 0x00010000) &&
2314 ((r & 0x000000ff) != 0x00000000),
2315 "EM_SCROLL page down didn't scroll by a small positive number of "
2316 "lines (r == 0x%08x)\n", r);
2317 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2318 "(line %d scrolled to line %d\n", y_before, y_after);
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(((r & 0xffffff00) == 0x0001ff00),
2325 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2326 "(r == 0x%08x)\n", r);
2327 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2328 "%d scrolled to line %d\n", y_before, y_after);
2332 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2334 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2336 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2337 "(r == 0x%08x)\n", r);
2338 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2339 "1 line (%d scrolled to %d)\n", y_before, y_after);
2343 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2345 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2347 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2348 "(r == 0x%08x)\n", r);
2349 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2350 "line (%d scrolled to %d)\n", y_before, y_after);
2354 r = SendMessage(hwndRichEdit, EM_SCROLL,
2355 SB_LINEUP, 0); /* lineup beyond top */
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2360 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2361 ok(y_before == y_after,
2362 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2366 r = SendMessage(hwndRichEdit, EM_SCROLL,
2367 SB_PAGEUP, 0);/*page up beyond top */
2369 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2372 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2373 ok(y_before == y_after,
2374 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2376 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2377 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2378 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2379 r = SendMessage(hwndRichEdit, EM_SCROLL,
2380 SB_PAGEDOWN, 0); /* page down beyond bot */
2381 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2384 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2385 ok(y_before == y_after,
2386 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2389 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2390 SendMessage(hwndRichEdit, EM_SCROLL,
2391 SB_LINEDOWN, 0); /* line down beyond bot */
2392 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2395 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2396 ok(y_before == y_after,
2397 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2400 DestroyWindow(hwndRichEdit);
2403 unsigned int recursionLevel = 0;
2404 unsigned int WM_SIZE_recursionLevel = 0;
2405 BOOL bailedOutOfRecursion = FALSE;
2406 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2408 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2412 if (bailedOutOfRecursion) return 0;
2413 if (recursionLevel >= 32) {
2414 bailedOutOfRecursion = TRUE;
2421 WM_SIZE_recursionLevel++;
2422 r = richeditProc(hwnd, message, wParam, lParam);
2423 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2424 ShowScrollBar(hwnd, SB_VERT, TRUE);
2425 WM_SIZE_recursionLevel--;
2428 r = richeditProc(hwnd, message, wParam, lParam);
2435 static void test_scrollbar_visibility(void)
2438 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2443 /* These tests show that richedit should temporarily refrain from automatically
2444 hiding or showing its scrollbars (vertical at least) when an explicit request
2445 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2446 Some applications depend on forced showing (when otherwise richedit would
2447 hide the vertical scrollbar) and are thrown on an endless recursive loop
2448 if richedit auto-hides the scrollbar again. Apparently they never heard of
2449 the ES_DISABLENOSCROLL style... */
2451 hwndRichEdit = new_richedit(NULL);
2453 /* Test default scrollbar visibility behavior */
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2461 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2462 si.nPage, si.nMin, si.nMax);
2464 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2465 memset(&si, 0, sizeof(si));
2466 si.cbSize = sizeof(si);
2467 si.fMask = SIF_PAGE | SIF_RANGE;
2468 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2469 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2470 "Vertical scrollbar is visible, should be invisible.\n");
2471 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2472 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2473 si.nPage, si.nMin, si.nMax);
2475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2476 memset(&si, 0, sizeof(si));
2477 si.cbSize = sizeof(si);
2478 si.fMask = SIF_PAGE | SIF_RANGE;
2479 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2480 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2481 "Vertical scrollbar is invisible, should be visible.\n");
2482 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2483 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2484 si.nPage, si.nMin, si.nMax);
2486 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2487 even though it hides the scrollbar */
2488 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2489 memset(&si, 0, sizeof(si));
2490 si.cbSize = sizeof(si);
2491 si.fMask = SIF_PAGE | SIF_RANGE;
2492 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2493 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2494 "Vertical scrollbar is visible, should be invisible.\n");
2495 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2496 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2497 si.nPage, si.nMin, si.nMax);
2499 /* Setting non-scrolling text again does *not* reset scrollbar range */
2500 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2501 memset(&si, 0, sizeof(si));
2502 si.cbSize = sizeof(si);
2503 si.fMask = SIF_PAGE | SIF_RANGE;
2504 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2505 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2506 "Vertical scrollbar is visible, should be invisible.\n");
2507 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2508 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2509 si.nPage, si.nMin, si.nMax);
2511 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2512 memset(&si, 0, sizeof(si));
2513 si.cbSize = sizeof(si);
2514 si.fMask = SIF_PAGE | SIF_RANGE;
2515 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2516 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2517 "Vertical scrollbar is visible, should be invisible.\n");
2518 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2519 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2520 si.nPage, si.nMin, si.nMax);
2522 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2523 memset(&si, 0, sizeof(si));
2524 si.cbSize = sizeof(si);
2525 si.fMask = SIF_PAGE | SIF_RANGE;
2526 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2527 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2528 "Vertical scrollbar is visible, should be invisible.\n");
2529 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2530 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2531 si.nPage, si.nMin, si.nMax);
2533 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2534 memset(&si, 0, sizeof(si));
2535 si.cbSize = sizeof(si);
2536 si.fMask = SIF_PAGE | SIF_RANGE;
2537 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2538 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2539 "Vertical scrollbar is visible, should be invisible.\n");
2540 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2541 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2542 si.nPage, si.nMin, si.nMax);
2544 DestroyWindow(hwndRichEdit);
2546 /* Test again, with ES_DISABLENOSCROLL style */
2547 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2549 /* Test default scrollbar visibility behavior */
2550 memset(&si, 0, sizeof(si));
2551 si.cbSize = sizeof(si);
2552 si.fMask = SIF_PAGE | SIF_RANGE;
2553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2554 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2555 "Vertical scrollbar is invisible, should be visible.\n");
2556 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2557 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2558 si.nPage, si.nMin, si.nMax);
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2568 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2569 si.nPage, si.nMin, si.nMax);
2571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2579 "reported page/range is %d (%d..%d)\n",
2580 si.nPage, si.nMin, si.nMax);
2582 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2583 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2584 memset(&si, 0, sizeof(si));
2585 si.cbSize = sizeof(si);
2586 si.fMask = SIF_PAGE | SIF_RANGE;
2587 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2588 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2589 "Vertical scrollbar is invisible, should be visible.\n");
2590 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2591 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2592 si.nPage, si.nMin, si.nMax);
2594 /* Setting non-scrolling text again does *not* reset scrollbar range */
2595 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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 nMax/nPage nonzero\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 nMax/nPage nonzero\n",
2615 si.nPage, si.nMin, si.nMax);
2617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
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) expected nMax/nPage nonzero\n",
2626 si.nPage, si.nMin, si.nMax);
2628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2629 memset(&si, 0, sizeof(si));
2630 si.cbSize = sizeof(si);
2631 si.fMask = SIF_PAGE | SIF_RANGE;
2632 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2633 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2634 "Vertical scrollbar is invisible, should be visible.\n");
2635 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2636 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2637 si.nPage, si.nMin, si.nMax);
2639 DestroyWindow(hwndRichEdit);
2641 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2642 hwndRichEdit = new_richedit(NULL);
2644 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2645 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2651 "Vertical scrollbar is invisible, should be visible.\n");
2653 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2654 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2655 si.nPage, si.nMin, si.nMax);
2658 /* Ditto, see above */
2659 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2660 memset(&si, 0, sizeof(si));
2661 si.cbSize = sizeof(si);
2662 si.fMask = SIF_PAGE | SIF_RANGE;
2663 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2664 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2665 "Vertical scrollbar is invisible, should be visible.\n");
2667 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2668 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2669 si.nPage, si.nMin, si.nMax);
2672 /* Ditto, see above */
2673 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2674 memset(&si, 0, sizeof(si));
2675 si.cbSize = sizeof(si);
2676 si.fMask = SIF_PAGE | SIF_RANGE;
2677 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2678 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2679 "Vertical scrollbar is invisible, should be visible.\n");
2681 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2682 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2683 si.nPage, si.nMin, si.nMax);
2686 /* Ditto, see above */
2687 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2688 memset(&si, 0, sizeof(si));
2689 si.cbSize = sizeof(si);
2690 si.fMask = SIF_PAGE | SIF_RANGE;
2691 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2692 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2693 "Vertical scrollbar is invisible, should be visible.\n");
2695 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2696 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2697 si.nPage, si.nMin, si.nMax);
2700 /* Ditto, see above */
2701 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2702 memset(&si, 0, sizeof(si));
2703 si.cbSize = sizeof(si);
2704 si.fMask = SIF_PAGE | SIF_RANGE;
2705 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2706 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2707 "Vertical scrollbar is invisible, should be visible.\n");
2709 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2710 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2711 si.nPage, si.nMin, si.nMax);
2714 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2715 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2716 memset(&si, 0, sizeof(si));
2717 si.cbSize = sizeof(si);
2718 si.fMask = SIF_PAGE | SIF_RANGE;
2719 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2720 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2721 "Vertical scrollbar is visible, should be invisible.\n");
2722 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2723 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2724 si.nPage, si.nMin, si.nMax);
2726 DestroyWindow(hwndRichEdit);
2728 hwndRichEdit = new_richedit(NULL);
2730 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2731 memset(&si, 0, sizeof(si));
2732 si.cbSize = sizeof(si);
2733 si.fMask = SIF_PAGE | SIF_RANGE;
2734 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2735 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2736 "Vertical scrollbar is visible, should be invisible.\n");
2737 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2738 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2739 si.nPage, si.nMin, si.nMax);
2741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2747 "Vertical scrollbar is visible, should be invisible.\n");
2748 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2749 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2750 si.nPage, si.nMin, si.nMax);
2752 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2753 memset(&si, 0, sizeof(si));
2754 si.cbSize = sizeof(si);
2755 si.fMask = SIF_PAGE | SIF_RANGE;
2756 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2757 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2758 "Vertical scrollbar is visible, should be invisible.\n");
2759 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2760 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2761 si.nPage, si.nMin, si.nMax);
2763 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2764 memset(&si, 0, sizeof(si));
2765 si.cbSize = sizeof(si);
2766 si.fMask = SIF_PAGE | SIF_RANGE;
2767 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2768 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2769 "Vertical scrollbar is visible, should be invisible.\n");
2770 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2771 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2772 si.nPage, si.nMin, si.nMax);
2774 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2775 memset(&si, 0, sizeof(si));
2776 si.cbSize = sizeof(si);
2777 si.fMask = SIF_PAGE | SIF_RANGE;
2778 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2779 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2780 "Vertical scrollbar is invisible, should be visible.\n");
2781 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2782 "reported page/range is %d (%d..%d)\n",
2783 si.nPage, si.nMin, si.nMax);
2785 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2786 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2787 memset(&si, 0, sizeof(si));
2788 si.cbSize = sizeof(si);
2789 si.fMask = SIF_PAGE | SIF_RANGE;
2790 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2791 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2792 "Vertical scrollbar is visible, should be invisible.\n");
2793 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2794 "reported page/range is %d (%d..%d)\n",
2795 si.nPage, si.nMin, si.nMax);
2797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2803 "Vertical scrollbar is visible, should be invisible.\n");
2804 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2805 "reported page/range is %d (%d..%d)\n",
2806 si.nPage, si.nMin, si.nMax);
2808 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2809 EM_SCROLL will make visible any forcefully invisible scrollbar */
2810 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2811 memset(&si, 0, sizeof(si));
2812 si.cbSize = sizeof(si);
2813 si.fMask = SIF_PAGE | SIF_RANGE;
2814 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2815 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2816 "Vertical scrollbar is invisible, should be visible.\n");
2817 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2818 "reported page/range is %d (%d..%d)\n",
2819 si.nPage, si.nMin, si.nMax);
2821 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2822 memset(&si, 0, sizeof(si));
2823 si.cbSize = sizeof(si);
2824 si.fMask = SIF_PAGE | SIF_RANGE;
2825 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2826 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2827 "Vertical scrollbar is visible, should be invisible.\n");
2828 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2829 "reported page/range is %d (%d..%d)\n",
2830 si.nPage, si.nMin, si.nMax);
2832 /* Again, EM_SCROLL, with SB_LINEUP */
2833 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2834 memset(&si, 0, sizeof(si));
2835 si.cbSize = sizeof(si);
2836 si.fMask = SIF_PAGE | SIF_RANGE;
2837 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2838 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2839 "Vertical scrollbar is invisible, should be visible.\n");
2840 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2841 "reported page/range is %d (%d..%d)\n",
2842 si.nPage, si.nMin, si.nMax);
2844 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2845 memset(&si, 0, sizeof(si));
2846 si.cbSize = sizeof(si);
2847 si.fMask = SIF_PAGE | SIF_RANGE;
2848 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2849 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2850 "Vertical scrollbar is visible, should be invisible.\n");
2851 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2852 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2853 si.nPage, si.nMin, si.nMax);
2855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2856 memset(&si, 0, sizeof(si));
2857 si.cbSize = sizeof(si);
2858 si.fMask = SIF_PAGE | SIF_RANGE;
2859 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2860 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2861 "Vertical scrollbar is invisible, should be visible.\n");
2862 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2863 "reported page/range is %d (%d..%d)\n",
2864 si.nPage, si.nMin, si.nMax);
2866 DestroyWindow(hwndRichEdit);
2869 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2870 hwndRichEdit = new_richedit(NULL);
2872 #define ENABLE_WS_VSCROLL(hwnd) \
2873 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2874 #define DISABLE_WS_VSCROLL(hwnd) \
2875 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2877 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2878 ENABLE_WS_VSCROLL(hwndRichEdit);
2879 memset(&si, 0, sizeof(si));
2880 si.cbSize = sizeof(si);
2881 si.fMask = SIF_PAGE | SIF_RANGE;
2882 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2883 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2884 "Vertical scrollbar is invisible, should be visible.\n");
2885 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2886 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2887 si.nPage, si.nMin, si.nMax);
2889 /* Ditto, see above */
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 invisible, should be visible.\n");
2897 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2898 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2899 si.nPage, si.nMin, si.nMax);
2901 /* Ditto, see above */
2902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2903 memset(&si, 0, sizeof(si));
2904 si.cbSize = sizeof(si);
2905 si.fMask = SIF_PAGE | SIF_RANGE;
2906 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2907 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2908 "Vertical scrollbar is invisible, should be visible.\n");
2909 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2910 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2911 si.nPage, si.nMin, si.nMax);
2913 /* Ditto, see above */
2914 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2915 memset(&si, 0, sizeof(si));
2916 si.cbSize = sizeof(si);
2917 si.fMask = SIF_PAGE | SIF_RANGE;
2918 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2919 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2920 "Vertical scrollbar is invisible, should be visible.\n");
2921 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2922 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2923 si.nPage, si.nMin, si.nMax);
2925 /* Ditto, see above */
2926 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2927 memset(&si, 0, sizeof(si));
2928 si.cbSize = sizeof(si);
2929 si.fMask = SIF_PAGE | SIF_RANGE;
2930 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2931 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2932 "Vertical scrollbar is invisible, should be visible.\n");
2933 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2934 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2935 si.nPage, si.nMin, si.nMax);
2937 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2938 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2939 memset(&si, 0, sizeof(si));
2940 si.cbSize = sizeof(si);
2941 si.fMask = SIF_PAGE | SIF_RANGE;
2942 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2943 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2944 "Vertical scrollbar is visible, should be invisible.\n");
2945 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2946 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2947 si.nPage, si.nMin, si.nMax);
2949 DestroyWindow(hwndRichEdit);
2951 hwndRichEdit = new_richedit(NULL);
2953 DISABLE_WS_VSCROLL(hwndRichEdit);
2954 memset(&si, 0, sizeof(si));
2955 si.cbSize = sizeof(si);
2956 si.fMask = SIF_PAGE | SIF_RANGE;
2957 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2958 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2959 "Vertical scrollbar is visible, should be invisible.\n");
2960 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2961 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2962 si.nPage, si.nMin, si.nMax);
2964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2965 memset(&si, 0, sizeof(si));
2966 si.cbSize = sizeof(si);
2967 si.fMask = SIF_PAGE | SIF_RANGE;
2968 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2969 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2970 "Vertical scrollbar is visible, should be invisible.\n");
2971 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2972 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2973 si.nPage, si.nMin, si.nMax);
2975 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2976 memset(&si, 0, sizeof(si));
2977 si.cbSize = sizeof(si);
2978 si.fMask = SIF_PAGE | SIF_RANGE;
2979 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2980 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2981 "Vertical scrollbar is visible, should be invisible.\n");
2982 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2983 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2984 si.nPage, si.nMin, si.nMax);
2986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2992 "Vertical scrollbar is visible, should be invisible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2997 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2998 memset(&si, 0, sizeof(si));
2999 si.cbSize = sizeof(si);
3000 si.fMask = SIF_PAGE | SIF_RANGE;
3001 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3002 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3003 "Vertical scrollbar is invisible, should be visible.\n");
3004 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3005 "reported page/range is %d (%d..%d)\n",
3006 si.nPage, si.nMin, si.nMax);
3008 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3009 DISABLE_WS_VSCROLL(hwndRichEdit);
3010 memset(&si, 0, sizeof(si));
3011 si.cbSize = sizeof(si);
3012 si.fMask = SIF_PAGE | SIF_RANGE;
3013 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3014 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3015 "Vertical scrollbar is visible, should be invisible.\n");
3016 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3017 "reported page/range is %d (%d..%d)\n",
3018 si.nPage, si.nMin, si.nMax);
3020 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3021 memset(&si, 0, sizeof(si));
3022 si.cbSize = sizeof(si);
3023 si.fMask = SIF_PAGE | SIF_RANGE;
3024 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3025 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3026 "Vertical scrollbar is visible, should be invisible.\n");
3027 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3028 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3029 si.nPage, si.nMin, si.nMax);
3031 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3032 memset(&si, 0, sizeof(si));
3033 si.cbSize = sizeof(si);
3034 si.fMask = SIF_PAGE | SIF_RANGE;
3035 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3036 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3037 "Vertical scrollbar is invisible, should be visible.\n");
3038 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3039 "reported page/range is %d (%d..%d)\n",
3040 si.nPage, si.nMin, si.nMax);
3042 DISABLE_WS_VSCROLL(hwndRichEdit);
3043 memset(&si, 0, sizeof(si));
3044 si.cbSize = sizeof(si);
3045 si.fMask = SIF_PAGE | SIF_RANGE;
3046 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3047 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3048 "Vertical scrollbar is visible, should be invisible.\n");
3049 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3050 "reported page/range is %d (%d..%d)\n",
3051 si.nPage, si.nMin, si.nMax);
3053 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3054 EM_SCROLL will make visible any forcefully invisible scrollbar */
3055 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
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 invisible, should be visible.\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 DISABLE_WS_VSCROLL(hwndRichEdit);
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)\n",
3075 si.nPage, si.nMin, si.nMax);
3077 /* Again, EM_SCROLL, with SB_LINEUP */
3078 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3079 memset(&si, 0, sizeof(si));
3080 si.cbSize = sizeof(si);
3081 si.fMask = SIF_PAGE | SIF_RANGE;
3082 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3083 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3084 "Vertical scrollbar is invisible, should be visible.\n");
3085 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3086 "reported page/range is %d (%d..%d)\n",
3087 si.nPage, si.nMin, si.nMax);
3089 DestroyWindow(hwndRichEdit);
3091 /* This window proc models what is going on with Corman Lisp 3.0.
3092 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3093 force the scrollbar into visibility. Recursion should NOT happen
3094 as a result of this action.
3096 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3098 richeditProc = cls.lpfnWndProc;
3099 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3100 cls.lpszClassName = "RicheditStupidOverride";
3101 if(!RegisterClassA(&cls)) assert(0);
3104 WM_SIZE_recursionLevel = 0;
3105 bailedOutOfRecursion = FALSE;
3106 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3107 ok(!bailedOutOfRecursion,
3108 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3111 WM_SIZE_recursionLevel = 0;
3112 bailedOutOfRecursion = FALSE;
3113 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3114 ok(!bailedOutOfRecursion,
3115 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3117 /* Unblock window in order to process WM_DESTROY */
3119 bailedOutOfRecursion = FALSE;
3120 WM_SIZE_recursionLevel = 0;
3121 DestroyWindow(hwndRichEdit);
3125 static void test_EM_SETUNDOLIMIT(void)
3127 /* cases we test for:
3128 * default behaviour - limiting at 100 undo's
3129 * undo disabled - setting a limit of 0
3130 * undo limited - undo limit set to some to some number, like 2
3131 * bad input - sending a negative number should default to 100 undo's */
3133 HWND hwndRichEdit = new_richedit(NULL);
3138 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3141 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3142 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3143 also, multiple pastes don't combine like WM_CHAR would */
3144 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3146 /* first case - check the default */
3147 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3148 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3149 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3150 for (i=0; i<100; i++) /* Undo 100 of them */
3151 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3152 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3153 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3155 /* second case - cannot undo */
3156 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3157 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3158 SendMessage(hwndRichEdit,
3159 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3160 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3161 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3163 /* third case - set it to an arbitrary number */
3164 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3165 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3166 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3167 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3168 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3169 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3170 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3171 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3172 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3173 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3174 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3175 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3176 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3177 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3179 /* fourth case - setting negative numbers should default to 100 undos */
3180 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3181 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3183 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3185 DestroyWindow(hwndRichEdit);
3188 static void test_ES_PASSWORD(void)
3190 /* This isn't hugely testable, so we're just going to run it through its paces */
3192 HWND hwndRichEdit = new_richedit(NULL);
3195 /* First, check the default of a regular control */
3196 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3198 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3200 /* Now, set it to something normal */
3201 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3202 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3204 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3206 /* Now, set it to something odd */
3207 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3208 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3210 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3211 DestroyWindow(hwndRichEdit);
3214 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3219 char** str = (char**)dwCookie;
3222 memcpy(*str, pbBuff, *pcb);
3228 static void test_WM_SETTEXT()
3230 HWND hwndRichEdit = new_richedit(NULL);
3231 const char * TestItem1 = "TestSomeText";
3232 const char * TestItem2 = "TestSomeText\r";
3233 const char * TestItem2_after = "TestSomeText\r\n";
3234 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3235 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3236 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3237 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3238 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3239 const char * TestItem5_after = "TestSomeText TestSomeText";
3240 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3241 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3242 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3243 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3245 char buf[1024] = {0};
3250 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3251 any solitary \r to be converted to \r\n on return. Properly paired
3252 \r\n are not affected. It also shows that the special sequence \r\r\n
3253 gets converted to a single space.
3256 #define TEST_SETTEXT(a, b) \
3257 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3258 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3259 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3260 ok (result == lstrlen(buf), \
3261 "WM_GETTEXT returned %ld instead of expected %u\n", \
3262 result, lstrlen(buf)); \
3263 result = strcmp(b, buf); \
3265 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3267 TEST_SETTEXT(TestItem1, TestItem1)
3268 TEST_SETTEXT(TestItem2, TestItem2_after)
3269 TEST_SETTEXT(TestItem3, TestItem3_after)
3270 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3271 TEST_SETTEXT(TestItem4, TestItem4_after)
3272 TEST_SETTEXT(TestItem5, TestItem5_after)
3273 TEST_SETTEXT(TestItem6, TestItem6_after)
3274 TEST_SETTEXT(TestItem7, TestItem7_after)
3276 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3277 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3279 es.dwCookie = (DWORD_PTR)&p;
3281 es.pfnCallback = test_WM_SETTEXT_esCallback;
3282 memset(buf, 0, sizeof(buf));
3283 SendMessage(hwndRichEdit, EM_STREAMOUT,
3284 (WPARAM)(SF_RTF), (LPARAM)&es);
3285 trace("EM_STREAMOUT produced: \n%s\n", buf);
3286 TEST_SETTEXT(buf, TestItem1)
3289 DestroyWindow(hwndRichEdit);
3292 static void test_EM_STREAMOUT(void)
3294 HWND hwndRichEdit = new_richedit(NULL);
3297 char buf[1024] = {0};
3300 const char * TestItem1 = "TestSomeText";
3301 const char * TestItem2 = "TestSomeText\r";
3302 const char * TestItem3 = "TestSomeText\r\n";
3304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3306 es.dwCookie = (DWORD_PTR)&p;
3308 es.pfnCallback = test_WM_SETTEXT_esCallback;
3309 memset(buf, 0, sizeof(buf));
3310 SendMessage(hwndRichEdit, EM_STREAMOUT,
3311 (WPARAM)(SF_TEXT), (LPARAM)&es);
3313 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3314 ok(strcmp(buf, TestItem1) == 0,
3315 "streamed text different, got %s\n", buf);
3317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3319 es.dwCookie = (DWORD_PTR)&p;
3321 es.pfnCallback = test_WM_SETTEXT_esCallback;
3322 memset(buf, 0, sizeof(buf));
3323 SendMessage(hwndRichEdit, EM_STREAMOUT,
3324 (WPARAM)(SF_TEXT), (LPARAM)&es);
3326 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3327 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3328 ok(strcmp(buf, TestItem3) == 0,
3329 "streamed text different from, got %s\n", buf);
3330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3332 es.dwCookie = (DWORD_PTR)&p;
3334 es.pfnCallback = test_WM_SETTEXT_esCallback;
3335 memset(buf, 0, sizeof(buf));
3336 SendMessage(hwndRichEdit, EM_STREAMOUT,
3337 (WPARAM)(SF_TEXT), (LPARAM)&es);
3339 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3340 ok(strcmp(buf, TestItem3) == 0,
3341 "streamed text different, got %s\n", buf);
3343 DestroyWindow(hwndRichEdit);
3346 static void test_EM_SETTEXTEX(void)
3348 HWND hwndRichEdit = new_richedit(NULL);
3351 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3353 'T', 'e', 'x', 't', 0};
3354 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3360 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3361 '\r','t','S','o','m','e','T','e','x','t',0};
3362 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3366 const char * TestItem2_after = "TestSomeText\r\n";
3367 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3370 '\r','\n','\r','\n', 0};
3371 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3375 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3379 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3382 '\r','\r','\n','\r',
3384 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3388 #define MAX_BUF_LEN 1024
3389 WCHAR buf[MAX_BUF_LEN];
3390 char bufACP[MAX_BUF_LEN];
3396 setText.codepage = 1200; /* no constant for unicode */
3397 getText.codepage = 1200; /* no constant for unicode */
3398 getText.cb = MAX_BUF_LEN;
3399 getText.flags = GT_DEFAULT;
3400 getText.lpDefaultChar = NULL;
3401 getText.lpUsedDefChar = NULL;
3404 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3406 ok(lstrcmpW(buf, TestItem1) == 0,
3407 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3409 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3410 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3412 setText.codepage = 1200; /* no constant for unicode */
3413 getText.codepage = 1200; /* no constant for unicode */
3414 getText.cb = MAX_BUF_LEN;
3415 getText.flags = GT_DEFAULT;
3416 getText.lpDefaultChar = NULL;
3417 getText.lpUsedDefChar = NULL;
3419 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3420 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3421 ok(lstrcmpW(buf, TestItem2) == 0,
3422 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3424 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3425 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3426 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3427 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3429 /* Baseline test for just-enough buffer space for string */
3430 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3431 getText.codepage = 1200; /* no constant for unicode */
3432 getText.flags = GT_DEFAULT;
3433 getText.lpDefaultChar = NULL;
3434 getText.lpUsedDefChar = NULL;
3435 memset(buf, 0, MAX_BUF_LEN);
3436 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3437 ok(lstrcmpW(buf, TestItem2) == 0,
3438 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3440 /* When there is enough space for one character, but not both, of the CRLF
3441 pair at the end of the string, the CR is not copied at all. That is,
3442 the caller must not see CRLF pairs truncated to CR at the end of the
3445 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3446 getText.codepage = 1200; /* no constant for unicode */
3447 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3448 getText.lpDefaultChar = NULL;
3449 getText.lpUsedDefChar = NULL;
3450 memset(buf, 0, MAX_BUF_LEN);
3451 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3452 ok(lstrcmpW(buf, TestItem1) == 0,
3453 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3456 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3457 setText.codepage = 1200; /* no constant for unicode */
3458 getText.codepage = 1200; /* no constant for unicode */
3459 getText.cb = MAX_BUF_LEN;
3460 getText.flags = GT_DEFAULT;
3461 getText.lpDefaultChar = NULL;
3462 getText.lpUsedDefChar = NULL;
3464 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3465 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3466 ok(lstrcmpW(buf, TestItem3_after) == 0,
3467 "EM_SETTEXTEX did not convert properly\n");
3469 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3470 setText.codepage = 1200; /* no constant for unicode */
3471 getText.codepage = 1200; /* no constant for unicode */
3472 getText.cb = MAX_BUF_LEN;
3473 getText.flags = GT_DEFAULT;
3474 getText.lpDefaultChar = NULL;
3475 getText.lpUsedDefChar = NULL;
3477 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3478 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3479 ok(lstrcmpW(buf, TestItem3_after) == 0,
3480 "EM_SETTEXTEX did not convert properly\n");
3482 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3483 setText.codepage = 1200; /* no constant for unicode */
3484 getText.codepage = 1200; /* no constant for unicode */
3485 getText.cb = MAX_BUF_LEN;
3486 getText.flags = GT_DEFAULT;
3487 getText.lpDefaultChar = NULL;
3488 getText.lpUsedDefChar = NULL;
3490 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3491 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3492 ok(lstrcmpW(buf, TestItem4_after) == 0,
3493 "EM_SETTEXTEX did not convert properly\n");
3495 /* !ST_SELECTION && Unicode && !\rtf */
3496 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3497 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3500 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3501 ok(lstrlenW(buf) == 0,
3502 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3504 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3506 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3507 /* select some text */
3510 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3511 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3512 setText.flags = ST_SELECTION;
3513 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3515 "EM_SETTEXTEX with NULL lParam to replace selection"
3516 " with no text should return 0. Got %i\n",
3519 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3521 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3522 /* select some text */
3525 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3526 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3527 setText.flags = ST_SELECTION;
3528 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3529 (WPARAM)&setText, (LPARAM) TestItem1);
3531 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3532 ok(result == lstrlenW(TestItem1),
3533 "EM_SETTEXTEX with NULL lParam to replace selection"
3534 " with no text should return 0. Got %i\n",
3536 ok(lstrlenW(buf) == 22,
3537 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3540 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3541 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3543 es.dwCookie = (DWORD_PTR)&p;
3545 es.pfnCallback = test_WM_SETTEXT_esCallback;
3546 memset(buf, 0, sizeof(buf));
3547 SendMessage(hwndRichEdit, EM_STREAMOUT,
3548 (WPARAM)(SF_RTF), (LPARAM)&es);
3549 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3551 /* !ST_SELECTION && !Unicode && \rtf */
3552 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3553 getText.codepage = 1200; /* no constant for unicode */
3554 getText.cb = MAX_BUF_LEN;
3555 getText.flags = GT_DEFAULT;
3556 getText.lpDefaultChar = NULL;
3557 getText.lpUsedDefChar = NULL;
3560 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3561 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3562 ok(lstrcmpW(buf, TestItem1) == 0,
3563 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3565 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3566 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3567 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3568 getText.codepage = CP_ACP;
3569 getText.cb = MAX_BUF_LEN;
3570 getText.flags = GT_DEFAULT;
3571 getText.lpDefaultChar = NULL;
3572 getText.lpUsedDefChar = NULL;
3574 setText.flags = ST_SELECTION;
3575 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3576 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3577 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3578 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3579 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3581 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3582 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3584 es.dwCookie = (DWORD_PTR)&p;
3586 es.pfnCallback = test_WM_SETTEXT_esCallback;
3587 memset(buf, 0, sizeof(buf));
3588 SendMessage(hwndRichEdit, EM_STREAMOUT,
3589 (WPARAM)(SF_RTF), (LPARAM)&es);
3590 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3592 /* select some text */
3595 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3597 /* ST_SELECTION && !Unicode && \rtf */
3598 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3599 getText.codepage = 1200; /* no constant for unicode */
3600 getText.cb = MAX_BUF_LEN;
3601 getText.flags = GT_DEFAULT;
3602 getText.lpDefaultChar = NULL;
3603 getText.lpUsedDefChar = NULL;
3605 setText.flags = ST_SELECTION;
3606 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3607 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3608 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3610 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3611 setText.codepage = 1200; /* no constant for unicode */
3612 getText.codepage = CP_ACP;
3613 getText.cb = MAX_BUF_LEN;
3616 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3617 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3619 /* select some text */
3622 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3624 /* ST_SELECTION && !Unicode && !\rtf */
3625 setText.codepage = CP_ACP;
3626 getText.codepage = 1200; /* no constant for unicode */
3627 getText.cb = MAX_BUF_LEN;
3628 getText.flags = GT_DEFAULT;
3629 getText.lpDefaultChar = NULL;
3630 getText.lpUsedDefChar = NULL;
3632 setText.flags = ST_SELECTION;
3633 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3634 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3635 ok(lstrcmpW(buf, TestItem1alt) == 0,
3636 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3637 " using ST_SELECTION and non-Unicode\n");
3639 /* Test setting text using rich text format */
3641 setText.codepage = CP_ACP;
3642 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3643 getText.codepage = CP_ACP;
3644 getText.cb = MAX_BUF_LEN;
3645 getText.flags = GT_DEFAULT;
3646 getText.lpDefaultChar = NULL;
3647 getText.lpUsedDefChar = NULL;
3648 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3649 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3652 setText.codepage = CP_ACP;
3653 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3654 getText.codepage = CP_ACP;
3655 getText.cb = MAX_BUF_LEN;
3656 getText.flags = GT_DEFAULT;
3657 getText.lpDefaultChar = NULL;
3658 getText.lpUsedDefChar = NULL;
3659 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3660 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3662 DestroyWindow(hwndRichEdit);
3665 static void test_EM_LIMITTEXT(void)
3669 HWND hwndRichEdit = new_richedit(NULL);
3671 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3672 * about setting the length to -1 for multiline edit controls doesn't happen.
3675 /* Don't check default gettextlimit case. That's done in other tests */
3677 /* Set textlimit to 100 */
3678 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3679 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3681 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3683 /* Set textlimit to 0 */
3684 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3685 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3687 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3689 /* Set textlimit to -1 */
3690 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3691 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3693 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3695 /* Set textlimit to -2 */
3696 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3697 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3699 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3701 DestroyWindow (hwndRichEdit);
3705 static void test_EM_EXLIMITTEXT(void)
3707 int i, selBegin, selEnd, len1, len2;
3709 char text[1024 + 1];
3710 char buffer[1024 + 1];
3711 int textlimit = 0; /* multiple of 100 */
3712 HWND hwndRichEdit = new_richedit(NULL);
3714 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3715 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3718 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3719 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3721 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3724 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3725 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3727 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3729 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3730 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3731 /* default for WParam = 0 */
3732 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3734 textlimit = sizeof(text)-1;
3735 memset(text, 'W', textlimit);
3736 text[sizeof(text)-1] = 0;
3737 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3738 /* maxed out text */
3739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3741 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3742 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3743 len1 = selEnd - selBegin;
3745 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3746 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3747 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3748 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3749 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3750 len2 = selEnd - selBegin;
3753 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3756 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3757 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3758 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3759 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3760 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3761 len1 = selEnd - selBegin;
3764 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3767 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3768 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3769 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3770 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3771 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3772 len2 = selEnd - selBegin;
3775 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3778 /* set text up to the limit, select all the text, then add a char */
3780 memset(text, 'W', textlimit);
3781 text[textlimit] = 0;
3782 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3784 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3785 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3786 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3787 result = strcmp(buffer, "A");
3788 ok(0 == result, "got string = \"%s\"\n", buffer);
3790 /* WM_SETTEXT not limited */
3792 memset(text, 'W', textlimit);
3793 text[textlimit] = 0;
3794 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3795 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3796 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3798 ok(10 == i, "expected 10 chars\n");
3799 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3800 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3802 /* try inserting more text at end */
3803 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3804 ok(0 == i, "WM_CHAR wasn't processed\n");
3805 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3807 ok(10 == i, "expected 10 chars, got %i\n", i);
3808 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3809 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3811 /* try inserting text at beginning */
3812 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3813 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3814 ok(0 == i, "WM_CHAR wasn't processed\n");
3815 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3817 ok(10 == i, "expected 10 chars, got %i\n", i);
3818 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3819 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3821 /* WM_CHAR is limited */
3823 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3824 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3825 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3826 ok(0 == i, "WM_CHAR wasn't processed\n");
3827 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3828 ok(0 == i, "WM_CHAR wasn't processed\n");
3829 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3831 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3833 DestroyWindow(hwndRichEdit);
3836 static void test_EM_GETLIMITTEXT(void)
3839 HWND hwndRichEdit = new_richedit(NULL);
3841 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3842 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3844 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3845 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3846 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3848 DestroyWindow(hwndRichEdit);
3851 static void test_WM_SETFONT(void)
3853 /* There is no invalid input or error conditions for this function.
3854 * NULL wParam and lParam just fall back to their default values
3855 * It should be noted that even if you use a gibberish name for your fonts
3856 * here, it will still work because the name is stored. They will display as
3857 * System, but will report their name to be whatever they were created as */
3859 HWND hwndRichEdit = new_richedit(NULL);
3860 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3861 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3862 FF_DONTCARE, "Marlett");
3863 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3864 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3865 FF_DONTCARE, "MS Sans Serif");
3866 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3867 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3868 FF_DONTCARE, "Courier");
3869 LOGFONTA sentLogFont;
3870 CHARFORMAT2A returnedCF2A;
3872 returnedCF2A.cbSize = sizeof(returnedCF2A);
3874 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3875 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3876 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3878 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3879 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3880 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3881 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3883 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3884 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3885 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3886 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3887 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3888 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3890 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
3891 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3892 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3893 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3894 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3895 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3897 /* This last test is special since we send in NULL. We clear the variables
3898 * and just compare to "System" instead of the sent in font name. */
3899 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3900 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3901 returnedCF2A.cbSize = sizeof(returnedCF2A);
3903 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
3904 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3905 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3906 ok (!strcmp("System",returnedCF2A.szFaceName),
3907 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3909 DestroyWindow(hwndRichEdit);
3913 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3918 const char** str = (const char**)dwCookie;
3919 int size = strlen(*str);
3920 if(size > 3) /* let's make it piecemeal for fun */
3927 memcpy(pbBuff, *str, *pcb);
3933 static void test_EM_GETMODIFY(void)
3935 HWND hwndRichEdit = new_richedit(NULL);
3938 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3940 'T', 'e', 'x', 't', 0};
3941 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3943 'O', 't', 'h', 'e', 'r',
3944 'T', 'e', 'x', 't', 0};
3945 const char* streamText = "hello world";
3950 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3951 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3952 FF_DONTCARE, "Courier");
3954 setText.codepage = 1200; /* no constant for unicode */
3955 setText.flags = ST_KEEPUNDO;
3958 /* modify flag shouldn't be set when richedit is first created */
3959 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3961 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3963 /* setting modify flag should actually set it */
3964 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3965 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3967 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3969 /* clearing modify flag should actually clear it */
3970 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3971 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3973 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3975 /* setting font doesn't change modify flag */
3976 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3977 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
3978 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3980 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3982 /* setting text should set modify flag */
3983 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3984 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3985 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3987 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3989 /* undo previous text doesn't reset modify flag */
3990 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3991 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3993 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3995 /* set text with no flag to keep undo stack should not set modify flag */
3996 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3998 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3999 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4001 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4003 /* WM_SETTEXT doesn't modify */
4004 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4005 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4006 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4008 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4010 /* clear the text */
4011 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4012 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4013 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4015 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4018 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4019 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4020 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4021 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4022 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4024 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4026 /* copy/paste text 1 */
4027 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4028 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4029 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4030 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4031 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4033 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4035 /* copy/paste text 2 */
4036 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4037 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4038 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4039 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4040 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4041 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4043 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4046 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4047 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4048 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4049 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4051 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4054 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4055 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4056 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4057 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4059 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4061 /* set char format */
4062 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4063 cf2.cbSize = sizeof(CHARFORMAT2);
4064 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4066 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4067 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4068 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4069 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4070 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4071 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4073 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4075 /* set para format */
4076 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4077 pf2.cbSize = sizeof(PARAFORMAT2);
4078 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4080 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4081 pf2.wAlignment = PFA_RIGHT;
4082 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4083 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4085 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4088 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4089 es.dwCookie = (DWORD_PTR)&streamText;
4091 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4092 SendMessage(hwndRichEdit, EM_STREAMIN,
4093 (WPARAM)(SF_TEXT), (LPARAM)&es);
4094 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4096 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4098 DestroyWindow(hwndRichEdit);
4104 long expected_retval;
4105 int expected_getsel_start;
4106 int expected_getsel_end;
4107 int _exsetsel_todo_wine;
4108 int _getsel_todo_wine;
4111 const struct exsetsel_s exsetsel_tests[] = {
4113 {5, 10, 10, 5, 10, 0, 0},
4114 {15, 17, 17, 15, 17, 0, 0},
4115 /* test cpMax > strlen() */
4116 {0, 100, 18, 0, 18, 0, 1},
4117 /* test cpMin == cpMax */
4118 {5, 5, 5, 5, 5, 0, 0},
4119 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4120 {-1, 0, 5, 5, 5, 0, 0},
4121 {-1, 17, 5, 5, 5, 0, 0},
4122 {-1, 18, 5, 5, 5, 0, 0},
4123 /* test cpMin < 0 && cpMax < 0 */
4124 {-1, -1, 17, 17, 17, 0, 0},
4125 {-4, -5, 17, 17, 17, 0, 0},
4126 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4127 {0, -1, 18, 0, 18, 0, 1},
4128 {17, -5, 18, 17, 18, 0, 1},
4129 {18, -3, 17, 17, 17, 0, 0},
4130 /* test if cpMin > cpMax */
4131 {15, 19, 18, 15, 18, 0, 1},
4132 {19, 15, 18, 15, 18, 0, 1}
4135 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4140 cr.cpMin = setsel->min;
4141 cr.cpMax = setsel->max;
4142 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4144 if (setsel->_exsetsel_todo_wine) {
4146 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4149 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4152 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4154 if (setsel->_getsel_todo_wine) {
4156 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);
4159 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);
4163 static void test_EM_EXSETSEL(void)
4165 HWND hwndRichEdit = new_richedit(NULL);
4167 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4169 /* sending some text to the window */
4170 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4171 /* 01234567890123456*/
4174 for (i = 0; i < num_tests; i++) {
4175 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4178 DestroyWindow(hwndRichEdit);
4181 static void test_EM_REPLACESEL(int redraw)
4183 HWND hwndRichEdit = new_richedit(NULL);
4184 char buffer[1024] = {0};
4189 /* sending some text to the window */
4190 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4191 /* 01234567890123456*/
4194 /* FIXME add more tests */
4195 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4196 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4197 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4198 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4199 r = strcmp(buffer, "testing");
4200 ok(0 == r, "expected %d, got %d\n", 0, r);
4202 DestroyWindow(hwndRichEdit);
4204 hwndRichEdit = new_richedit(NULL);
4206 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4207 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4209 /* Test behavior with carriage returns and newlines */
4210 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4211 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4212 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4213 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4214 r = strcmp(buffer, "RichEdit1");
4215 ok(0 == r, "expected %d, got %d\n", 0, r);
4217 getText.codepage = CP_ACP;
4218 getText.flags = GT_DEFAULT;
4219 getText.lpDefaultChar = NULL;
4220 getText.lpUsedDefChar = NULL;
4221 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4222 ok(strcmp(buffer, "RichEdit1") == 0,
4223 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4225 /* Test number of lines reported after EM_REPLACESEL */
4226 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4227 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4229 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4230 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4231 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4232 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4233 r = strcmp(buffer, "RichEdit1\r\n");
4234 ok(0 == r, "expected %d, got %d\n", 0, r);
4236 getText.codepage = CP_ACP;
4237 getText.flags = GT_DEFAULT;
4238 getText.lpDefaultChar = NULL;
4239 getText.lpUsedDefChar = NULL;
4240 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4241 ok(strcmp(buffer, "RichEdit1\r") == 0,
4242 "EM_GETTEXTEX returned incorrect string\n");
4244 /* Test number of lines reported after EM_REPLACESEL */
4245 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4246 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4248 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4249 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4250 returns the number of characters *inserted* into the control (after
4251 required conversions), but WinXP's riched20 returns the number of
4252 characters interpreted from the original lParam. Wine's builtin riched20
4253 implements the WinXP behavior.
4255 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4256 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4257 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4258 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4260 /* Test number of lines reported after EM_REPLACESEL */
4261 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4262 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4264 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4265 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4266 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4267 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4269 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4270 r = strcmp(buffer, "RichEdit1\r\n");
4271 ok(0 == r, "expected %d, got %d\n", 0, r);
4273 getText.codepage = CP_ACP;
4274 getText.flags = GT_DEFAULT;
4275 getText.lpDefaultChar = NULL;
4276 getText.lpUsedDefChar = NULL;
4277 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4278 ok(strcmp(buffer, "RichEdit1\r") == 0,
4279 "EM_GETTEXTEX returned incorrect string\n");
4281 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4282 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4283 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4284 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4286 /* The following tests show that richedit should handle the special \r\r\n
4287 sequence by turning it into a single space on insertion. However,
4288 EM_REPLACESEL on WinXP returns the number of characters in the original
4292 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4293 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4294 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4295 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4296 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4297 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4298 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4300 /* Test the actual string */
4302 getText.codepage = CP_ACP;
4303 getText.flags = GT_DEFAULT;
4304 getText.lpDefaultChar = NULL;
4305 getText.lpUsedDefChar = NULL;
4306 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4307 ok(strcmp(buffer, "\r\r") == 0,
4308 "EM_GETTEXTEX returned incorrect string\n");
4310 /* Test number of lines reported after EM_REPLACESEL */
4311 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4312 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4314 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4315 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4316 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4317 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4318 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4319 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4320 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4321 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4323 /* Test the actual string */
4325 getText.codepage = CP_ACP;
4326 getText.flags = GT_DEFAULT;
4327 getText.lpDefaultChar = NULL;
4328 getText.lpUsedDefChar = NULL;
4329 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4330 ok(strcmp(buffer, " ") == 0,
4331 "EM_GETTEXTEX returned incorrect string\n");
4333 /* Test number of lines reported after EM_REPLACESEL */
4334 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4335 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4338 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4339 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4340 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4341 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4342 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4343 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4344 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4346 /* Test the actual string */
4348 getText.codepage = CP_ACP;
4349 getText.flags = GT_DEFAULT;
4350 getText.lpDefaultChar = NULL;
4351 getText.lpUsedDefChar = NULL;
4352 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4353 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4354 "EM_GETTEXTEX returned incorrect string\n");
4356 /* Test number of lines reported after EM_REPLACESEL */
4357 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4358 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4360 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4361 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4362 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4363 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4364 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4365 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4366 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4367 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4369 /* Test the actual string */
4371 getText.codepage = CP_ACP;
4372 getText.flags = GT_DEFAULT;
4373 getText.lpDefaultChar = NULL;
4374 getText.lpUsedDefChar = NULL;
4375 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4376 ok(strcmp(buffer, " \r") == 0,
4377 "EM_GETTEXTEX returned incorrect string\n");
4379 /* Test number of lines reported after EM_REPLACESEL */
4380 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4381 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4383 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4384 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4385 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4386 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4387 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4388 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4389 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4390 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4392 /* Test the actual string */
4394 getText.codepage = CP_ACP;
4395 getText.flags = GT_DEFAULT;
4396 getText.lpDefaultChar = NULL;
4397 getText.lpUsedDefChar = NULL;
4398 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4399 ok(strcmp(buffer, " \r\r") == 0,
4400 "EM_GETTEXTEX returned incorrect string\n");
4402 /* Test number of lines reported after EM_REPLACESEL */
4403 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4404 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4406 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4407 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4408 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4409 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4410 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4411 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4412 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4413 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4415 /* Test the actual string */
4417 getText.codepage = CP_ACP;
4418 getText.flags = GT_DEFAULT;
4419 getText.lpDefaultChar = NULL;
4420 getText.lpUsedDefChar = NULL;
4421 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4422 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4423 "EM_GETTEXTEX returned incorrect string\n");
4425 /* Test number of lines reported after EM_REPLACESEL */
4426 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4427 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4430 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4431 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4432 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4433 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4434 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4435 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4437 /* Test the actual string */
4439 getText.codepage = CP_ACP;
4440 getText.flags = GT_DEFAULT;
4441 getText.lpDefaultChar = NULL;
4442 getText.lpUsedDefChar = NULL;
4443 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4444 ok(strcmp(buffer, "\r\r") == 0,
4445 "EM_GETTEXTEX returned incorrect string\n");
4447 /* Test number of lines reported after EM_REPLACESEL */
4448 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4449 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4451 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4452 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4453 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4454 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4455 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4456 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4457 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4458 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4460 /* Test the actual string */
4462 getText.codepage = CP_ACP;
4463 getText.flags = GT_DEFAULT;
4464 getText.lpDefaultChar = NULL;
4465 getText.lpUsedDefChar = NULL;
4466 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4467 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4468 "EM_GETTEXTEX returned incorrect string\n");
4470 /* Test number of lines reported after EM_REPLACESEL */
4471 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4472 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4475 /* This is needed to avoid interferring with keybd_event calls
4476 * on other tests that simulate keyboard events. */
4477 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4479 DestroyWindow(hwndRichEdit);
4482 static void test_WM_PASTE(void)
4485 char buffer[1024] = {0};
4486 const char* text1 = "testing paste\r";
4487 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4488 const char* text1_after = "testing paste\r\n";
4489 const char* text2 = "testing paste\r\rtesting paste";
4490 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4491 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4492 HWND hwndRichEdit = new_richedit(NULL);
4494 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4495 * to test the state of the modifiers (Ctrl/Alt/Shift).
4497 * Therefore Ctrl-<key> keystrokes need to be simulated with
4498 * keybd_event or by using SetKeyboardState to set the modifiers
4499 * and SendMessage to simulate the keystrokes.
4502 /* Sent keystrokes with keybd_event */
4503 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4504 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4505 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4506 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4507 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4509 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4510 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4512 SEND_CTRL_C(hwndRichEdit); /* Copy */
4513 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4514 SEND_CTRL_V(hwndRichEdit); /* Paste */
4515 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4516 /* Pasted text should be visible at this step */
4517 result = strcmp(text1_step1, buffer);
4519 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4521 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4522 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4523 /* Text should be the same as before (except for \r -> \r\n conversion) */
4524 result = strcmp(text1_after, buffer);
4526 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4529 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4530 SEND_CTRL_C(hwndRichEdit); /* Copy */
4531 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4532 SEND_CTRL_V(hwndRichEdit); /* Paste */
4533 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4534 /* Pasted text should be visible at this step */
4535 result = strcmp(text3, buffer);
4537 "test paste: strcmp = %i\n", result);
4538 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4539 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4540 /* Text should be the same as before (except for \r -> \r\n conversion) */
4541 result = strcmp(text2_after, buffer);
4543 "test paste: strcmp = %i\n", result);
4544 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4545 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4546 /* Text should revert to post-paste state */
4547 result = strcmp(buffer,text3);
4549 "test paste: strcmp = %i\n", result);
4557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4558 /* Send WM_CHAR to simulates Ctrl-V */
4559 SendMessage(hwndRichEdit, WM_CHAR, 22,
4560 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4561 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4562 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4563 result = strcmp(buffer,"");
4565 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4567 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4568 * with SetKeyboard state. */
4570 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4571 /* Simulates paste (Ctrl-V) */
4572 hold_key(VK_CONTROL);
4573 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4574 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4575 release_key(VK_CONTROL);
4576 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4577 result = strcmp(buffer,"paste");
4579 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4581 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4582 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4583 /* Simulates copy (Ctrl-C) */
4584 hold_key(VK_CONTROL);
4585 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4586 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4587 release_key(VK_CONTROL);
4588 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4589 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4590 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4591 result = strcmp(buffer,"testing");
4593 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4595 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4596 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4597 /* Simulates select all (Ctrl-A) */
4598 hold_key(VK_CONTROL);
4599 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4600 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4601 /* Simulates select cut (Ctrl-X) */
4602 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4603 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4604 release_key(VK_CONTROL);
4605 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4606 result = strcmp(buffer,"");
4608 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4609 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4610 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4611 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4612 result = strcmp(buffer,"cut\r\n");
4613 todo_wine ok(result == 0,
4614 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4615 /* Simulates undo (Ctrl-Z) */
4616 hold_key(VK_CONTROL);
4617 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4618 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4619 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4620 result = strcmp(buffer,"");
4622 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4623 /* Simulates redo (Ctrl-Y) */
4624 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4625 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4626 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4627 result = strcmp(buffer,"cut\r\n");
4628 todo_wine ok(result == 0,
4629 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4630 release_key(VK_CONTROL);
4632 DestroyWindow(hwndRichEdit);
4635 static void test_EM_FORMATRANGE(void)
4640 HWND hwndRichEdit = new_richedit(NULL);
4642 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4644 hdc = GetDC(hwndRichEdit);
4645 ok(hdc != NULL, "Could not get HDC\n");
4647 fr.hdc = fr.hdcTarget = hdc;
4648 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4649 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4650 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4654 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4656 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4659 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4661 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4667 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4669 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4672 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4674 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4677 DestroyWindow(hwndRichEdit);
4680 static int nCallbackCount = 0;
4682 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4685 const char text[] = {'t','e','s','t'};
4687 if (sizeof(text) <= cb)
4689 if ((int)dwCookie != nCallbackCount)
4695 memcpy (pbBuff, text, sizeof(text));
4696 *pcb = sizeof(text);
4703 return 1; /* indicates callback failed */
4706 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4711 const char** str = (const char**)dwCookie;
4712 int size = strlen(*str);
4718 memcpy(pbBuff, *str, *pcb);
4724 struct StringWithLength {
4729 /* This callback is used to handled the null characters in a string. */
4730 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4735 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4736 int size = str->length;
4742 memcpy(pbBuff, str->buffer, *pcb);
4743 str->buffer += *pcb;
4744 str->length -= *pcb;
4749 static void test_EM_STREAMIN(void)
4751 HWND hwndRichEdit = new_richedit(NULL);
4754 char buffer[1024] = {0};
4756 const char * streamText0 = "{\\rtf1 TestSomeText}";
4757 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4758 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4760 const char * streamText1 =
4761 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4762 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4765 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4766 const char * streamText2 =
4767 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4768 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4769 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4770 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4771 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4772 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4773 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4775 const char * streamText3 = "RichEdit1";
4777 struct StringWithLength cookieForStream4;
4778 const char * streamText4 =
4779 "This text just needs to be long enough to cause run to be split onto "
4780 "two separate lines and make sure the null terminating character is "
4781 "handled properly.\0";
4782 int length4 = strlen(streamText4) + 1;
4783 cookieForStream4.buffer = (char *)streamText4;
4784 cookieForStream4.length = length4;
4786 /* Minimal test without \par at the end */
4787 es.dwCookie = (DWORD_PTR)&streamText0;
4789 es.pfnCallback = test_EM_STREAMIN_esCallback;
4790 SendMessage(hwndRichEdit, EM_STREAMIN,
4791 (WPARAM)(SF_RTF), (LPARAM)&es);
4793 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4795 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4796 result = strcmp (buffer,"TestSomeText");
4798 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4799 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4801 /* Native richedit 2.0 ignores last \par */
4802 es.dwCookie = (DWORD_PTR)&streamText0a;
4804 es.pfnCallback = test_EM_STREAMIN_esCallback;
4805 SendMessage(hwndRichEdit, EM_STREAMIN,
4806 (WPARAM)(SF_RTF), (LPARAM)&es);
4808 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4810 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4811 result = strcmp (buffer,"TestSomeText");
4813 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4814 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4816 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4817 es.dwCookie = (DWORD_PTR)&streamText0b;
4819 es.pfnCallback = test_EM_STREAMIN_esCallback;
4820 SendMessage(hwndRichEdit, EM_STREAMIN,
4821 (WPARAM)(SF_RTF), (LPARAM)&es);
4823 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4825 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4826 result = strcmp (buffer,"TestSomeText\r\n");
4828 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4829 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4831 es.dwCookie = (DWORD_PTR)&streamText1;
4833 es.pfnCallback = test_EM_STREAMIN_esCallback;
4834 SendMessage(hwndRichEdit, EM_STREAMIN,
4835 (WPARAM)(SF_RTF), (LPARAM)&es);
4837 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4839 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4840 result = strcmp (buffer,"TestSomeText");
4842 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4843 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4845 es.dwCookie = (DWORD_PTR)&streamText2;
4847 SendMessage(hwndRichEdit, EM_STREAMIN,
4848 (WPARAM)(SF_RTF), (LPARAM)&es);
4850 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4852 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4853 ok (strlen(buffer) == 0,
4854 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4855 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4857 es.dwCookie = (DWORD_PTR)&streamText3;
4859 SendMessage(hwndRichEdit, EM_STREAMIN,
4860 (WPARAM)(SF_RTF), (LPARAM)&es);
4862 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4864 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4865 ok (strlen(buffer) == 0,
4866 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4867 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4869 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4871 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4872 SendMessage(hwndRichEdit, EM_STREAMIN,
4873 (WPARAM)(SF_TEXT), (LPARAM)&es);
4875 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4876 ok (result == length4,
4877 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4878 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4880 DestroyWindow(hwndRichEdit);
4883 static void test_EM_StreamIn_Undo(void)
4885 /* The purpose of this test is to determine when a EM_StreamIn should be
4886 * undoable. This is important because WM_PASTE currently uses StreamIn and
4887 * pasting should always be undoable but streaming isn't always.
4890 * StreamIn plain text without SFF_SELECTION.
4891 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4892 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4893 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4894 * Feel free to add tests for other text modes or StreamIn things.
4898 HWND hwndRichEdit = new_richedit(NULL);
4901 char buffer[1024] = {0};
4902 const char randomtext[] = "Some text";
4904 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4906 /* StreamIn, no SFF_SELECTION */
4907 es.dwCookie = nCallbackCount;
4908 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4910 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4911 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4912 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4913 result = strcmp (buffer,"test");
4915 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4917 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4918 ok (result == FALSE,
4919 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4921 /* StreamIn, SFF_SELECTION, but nothing selected */
4922 es.dwCookie = nCallbackCount;
4923 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4924 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4925 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4926 SendMessage(hwndRichEdit, EM_STREAMIN,
4927 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4928 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4929 result = strcmp (buffer,"testSome text");
4931 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4933 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4935 "EM_STREAMIN with SFF_SELECTION but no selection set "
4936 "should create an undo\n");
4938 /* StreamIn, SFF_SELECTION, with a selection */
4939 es.dwCookie = nCallbackCount;
4940 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4941 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4942 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4943 SendMessage(hwndRichEdit, EM_STREAMIN,
4944 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4945 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4946 result = strcmp (buffer,"Sometesttext");
4948 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4950 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4952 "EM_STREAMIN with SFF_SELECTION and selection set "
4953 "should create an undo\n");
4955 DestroyWindow(hwndRichEdit);
4958 static BOOL is_em_settextex_supported(HWND hwnd)
4960 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4961 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4964 static void test_unicode_conversions(void)
4966 static const WCHAR tW[] = {'t',0};
4967 static const WCHAR teW[] = {'t','e',0};
4968 static const WCHAR textW[] = {'t','e','s','t',0};
4969 static const char textA[] = "test";
4973 int em_settextex_supported, ret;
4975 #define set_textA(hwnd, wm_set_text, txt) \
4977 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4978 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4979 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4980 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4981 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4983 #define expect_textA(hwnd, wm_get_text, txt) \
4985 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4986 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4987 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4988 memset(bufA, 0xAA, sizeof(bufA)); \
4989 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4990 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4991 ret = lstrcmpA(bufA, txt); \
4992 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4995 #define set_textW(hwnd, wm_set_text, txt) \
4997 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4998 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4999 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5000 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5001 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5003 #define expect_textW(hwnd, wm_get_text, txt) \
5005 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5006 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5007 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5008 memset(bufW, 0xAA, sizeof(bufW)); \
5011 assert(wm_get_text == EM_GETTEXTEX); \
5012 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5013 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5017 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5018 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5020 ret = lstrcmpW(bufW, txt); \
5021 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5023 #define expect_empty(hwnd, wm_get_text) \
5025 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5026 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5027 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5028 memset(bufA, 0xAA, sizeof(bufA)); \
5029 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5030 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5031 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5034 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5035 0, 0, 200, 60, 0, 0, 0, 0);
5036 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5038 ret = IsWindowUnicode(hwnd);
5040 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5042 ok(ret, "RichEdit20W should be unicode under NT\n");
5044 /* EM_SETTEXTEX is supported starting from version 3.0 */
5045 em_settextex_supported = is_em_settextex_supported(hwnd);
5046 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5047 em_settextex_supported ? "" : "NOT ");
5049 expect_empty(hwnd, WM_GETTEXT);
5050 expect_empty(hwnd, EM_GETTEXTEX);
5052 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5053 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5054 expect_textA(hwnd, WM_GETTEXT, "t");
5055 expect_textA(hwnd, EM_GETTEXTEX, "t");
5056 expect_textW(hwnd, EM_GETTEXTEX, tW);
5058 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5059 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5060 expect_textA(hwnd, WM_GETTEXT, "te");
5061 expect_textA(hwnd, EM_GETTEXTEX, "te");
5062 expect_textW(hwnd, EM_GETTEXTEX, teW);
5064 set_textA(hwnd, WM_SETTEXT, NULL);
5065 expect_empty(hwnd, WM_GETTEXT);
5066 expect_empty(hwnd, EM_GETTEXTEX);
5069 set_textA(hwnd, WM_SETTEXT, textW);
5071 set_textA(hwnd, WM_SETTEXT, textA);
5072 expect_textA(hwnd, WM_GETTEXT, textA);
5073 expect_textA(hwnd, EM_GETTEXTEX, textA);
5074 expect_textW(hwnd, EM_GETTEXTEX, textW);
5076 if (em_settextex_supported)
5078 set_textA(hwnd, EM_SETTEXTEX, textA);
5079 expect_textA(hwnd, WM_GETTEXT, textA);
5080 expect_textA(hwnd, EM_GETTEXTEX, textA);
5081 expect_textW(hwnd, EM_GETTEXTEX, textW);
5086 set_textW(hwnd, WM_SETTEXT, textW);
5087 expect_textW(hwnd, WM_GETTEXT, textW);
5088 expect_textA(hwnd, WM_GETTEXT, textA);
5089 expect_textW(hwnd, EM_GETTEXTEX, textW);
5090 expect_textA(hwnd, EM_GETTEXTEX, textA);
5092 if (em_settextex_supported)
5094 set_textW(hwnd, EM_SETTEXTEX, textW);
5095 expect_textW(hwnd, WM_GETTEXT, textW);
5096 expect_textA(hwnd, WM_GETTEXT, textA);
5097 expect_textW(hwnd, EM_GETTEXTEX, textW);
5098 expect_textA(hwnd, EM_GETTEXTEX, textA);
5101 DestroyWindow(hwnd);
5103 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5104 0, 0, 200, 60, 0, 0, 0, 0);
5105 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5107 ret = IsWindowUnicode(hwnd);
5108 ok(!ret, "RichEdit20A should NOT be unicode\n");
5110 set_textA(hwnd, WM_SETTEXT, textA);
5111 expect_textA(hwnd, WM_GETTEXT, textA);
5112 expect_textA(hwnd, EM_GETTEXTEX, textA);
5113 expect_textW(hwnd, EM_GETTEXTEX, textW);
5115 if (em_settextex_supported)
5117 set_textA(hwnd, EM_SETTEXTEX, textA);
5118 expect_textA(hwnd, WM_GETTEXT, textA);
5119 expect_textA(hwnd, EM_GETTEXTEX, textA);
5120 expect_textW(hwnd, EM_GETTEXTEX, textW);
5125 set_textW(hwnd, WM_SETTEXT, textW);
5126 expect_textW(hwnd, WM_GETTEXT, textW);
5127 expect_textA(hwnd, WM_GETTEXT, textA);
5128 expect_textW(hwnd, EM_GETTEXTEX, textW);
5129 expect_textA(hwnd, EM_GETTEXTEX, textA);
5131 if (em_settextex_supported)
5133 set_textW(hwnd, EM_SETTEXTEX, textW);
5134 expect_textW(hwnd, WM_GETTEXT, textW);
5135 expect_textA(hwnd, WM_GETTEXT, textA);
5136 expect_textW(hwnd, EM_GETTEXTEX, textW);
5137 expect_textA(hwnd, EM_GETTEXTEX, textA);
5140 DestroyWindow(hwnd);
5143 static void test_WM_CHAR(void)
5147 const char * char_list = "abc\rabc\r";
5148 const char * expected_content_single = "abcabc";
5149 const char * expected_content_multi = "abc\r\nabc\r\n";
5150 char buffer[64] = {0};
5153 /* single-line control must IGNORE carriage returns */
5154 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5155 0, 0, 200, 60, 0, 0, 0, 0);
5156 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5159 while (*p != '\0') {
5160 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5161 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5162 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5163 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5167 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5168 ret = strcmp(buffer, expected_content_single);
5169 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5171 DestroyWindow(hwnd);
5173 /* multi-line control inserts CR normally */
5174 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5175 0, 0, 200, 60, 0, 0, 0, 0);
5176 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5179 while (*p != '\0') {
5180 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5181 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5182 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5183 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5187 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5188 ret = strcmp(buffer, expected_content_multi);
5189 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5191 DestroyWindow(hwnd);
5194 static void test_EM_GETTEXTLENGTHEX(void)
5197 GETTEXTLENGTHEX gtl;
5199 const char * base_string = "base string";
5200 const char * test_string = "a\nb\n\n\r\n";
5201 const char * test_string_after = "a";
5202 const char * test_string_2 = "a\rtest\rstring";
5203 char buffer[64] = {0};
5207 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5208 0, 0, 200, 60, 0, 0, 0, 0);
5210 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5211 0, 0, 200, 60, 0, 0, 0, 0);
5212 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5214 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5215 gtl.codepage = CP_ACP;
5216 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5217 ok(ret == 0, "ret %d\n",ret);
5219 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5220 gtl.codepage = CP_ACP;
5221 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5222 ok(ret == 0, "ret %d\n",ret);
5224 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5226 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5227 gtl.codepage = CP_ACP;
5228 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5229 ok(ret == strlen(base_string), "ret %d\n",ret);
5231 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5232 gtl.codepage = CP_ACP;
5233 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5234 ok(ret == strlen(base_string), "ret %d\n",ret);
5236 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5238 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5239 gtl.codepage = CP_ACP;
5240 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5241 ok(ret == 1, "ret %d\n",ret);
5243 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5244 gtl.codepage = CP_ACP;
5245 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5246 ok(ret == 1, "ret %d\n",ret);
5248 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5249 ret = strcmp(buffer, test_string_after);
5250 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5252 DestroyWindow(hwnd);
5256 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5257 0, 0, 200, 60, 0, 0, 0, 0);
5259 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5260 0, 0, 200, 60, 0, 0, 0, 0);
5261 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5263 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5264 gtl.codepage = CP_ACP;
5265 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5266 ok(ret == 0, "ret %d\n",ret);
5268 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5269 gtl.codepage = CP_ACP;
5270 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5271 ok(ret == 0, "ret %d\n",ret);
5273 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5275 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5276 gtl.codepage = CP_ACP;
5277 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5278 ok(ret == strlen(base_string), "ret %d\n",ret);
5280 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5281 gtl.codepage = CP_ACP;
5282 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5283 ok(ret == strlen(base_string), "ret %d\n",ret);
5285 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5287 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5288 gtl.codepage = CP_ACP;
5289 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5290 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5292 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5293 gtl.codepage = CP_ACP;
5294 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5295 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5297 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5299 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5300 gtl.codepage = CP_ACP;
5301 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5302 ok(ret == 10, "ret %d\n",ret);
5304 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5305 gtl.codepage = CP_ACP;
5306 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5307 ok(ret == 6, "ret %d\n",ret);
5309 DestroyWindow(hwnd);
5313 /* globals that parent and child access when checking event masks & notifications */
5314 static HWND eventMaskEditHwnd = 0;
5315 static int queriedEventMask;
5316 static int watchForEventMask = 0;
5318 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5319 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5321 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5323 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5325 return DefWindowProcA(hwnd, message, wParam, lParam);
5328 /* test event masks in combination with WM_COMMAND */
5329 static void test_eventMask(void)
5334 const char text[] = "foo bar\n";
5337 /* register class to capture WM_COMMAND */
5339 cls.lpfnWndProc = ParentMsgCheckProcA;
5342 cls.hInstance = GetModuleHandleA(0);
5344 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5345 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5346 cls.lpszMenuName = NULL;
5347 cls.lpszClassName = "EventMaskParentClass";
5348 if(!RegisterClassA(&cls)) assert(0);
5350 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5351 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5352 ok (parent != 0, "Failed to create parent window\n");
5354 eventMaskEditHwnd = new_richedit(parent);
5355 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5357 eventMask = ENM_CHANGE | ENM_UPDATE;
5358 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5359 ok(ret == ENM_NONE, "wrong event mask\n");
5360 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5361 ok(ret == eventMask, "failed to set event mask\n");
5363 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5364 queriedEventMask = 0; /* initialize to something other than we expect */
5365 watchForEventMask = EN_CHANGE;
5366 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5367 ok(ret == TRUE, "failed to set text\n");
5368 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5369 notification in response to WM_SETTEXT */
5370 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5371 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5373 /* check to see if EN_CHANGE is sent when redraw is turned off */
5374 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5375 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5376 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5377 /* redraw is disabled by making the window invisible. */
5378 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5379 queriedEventMask = 0; /* initialize to something other than we expect */
5380 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5381 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5382 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5383 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5384 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5386 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5387 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5388 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5389 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5390 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5391 watchForEventMask = EN_UPDATE;
5392 queriedEventMask = 0; /* initialize to something other than we expect */
5393 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5394 ok(queriedEventMask == 0,
5395 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5396 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5397 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5398 queriedEventMask = 0; /* initialize to something other than we expect */
5399 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5400 ok(queriedEventMask == eventMask,
5401 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5404 DestroyWindow(parent);
5407 static int received_WM_NOTIFY = 0;
5408 static int modify_at_WM_NOTIFY = 0;
5409 static HWND hwndRichedit_WM_NOTIFY;
5411 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5413 if(message == WM_NOTIFY)
5415 received_WM_NOTIFY = 1;
5416 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5418 return DefWindowProcA(hwnd, message, wParam, lParam);
5421 static void test_WM_NOTIFY(void)
5427 /* register class to capture WM_NOTIFY */
5429 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5432 cls.hInstance = GetModuleHandleA(0);
5434 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5435 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5436 cls.lpszMenuName = NULL;
5437 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5438 if(!RegisterClassA(&cls)) assert(0);
5440 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5441 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5442 ok (parent != 0, "Failed to create parent window\n");
5444 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5445 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5447 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5449 /* Notifications for selection change should only be sent when selection
5450 actually changes. EM_SETCHARFORMAT is one message that calls
5451 ME_CommitUndo, which should check whether message should be sent */
5452 received_WM_NOTIFY = 0;
5453 cf2.cbSize = sizeof(CHARFORMAT2);
5454 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5456 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5457 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5458 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5459 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5461 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5463 received_WM_NOTIFY = 0;
5464 modify_at_WM_NOTIFY = 0;
5465 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5466 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5467 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5469 received_WM_NOTIFY = 0;
5470 modify_at_WM_NOTIFY = 0;
5471 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5472 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5474 received_WM_NOTIFY = 0;
5475 modify_at_WM_NOTIFY = 0;
5476 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5477 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5478 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5480 /* Test for WM_NOTIFY messages with redraw disabled. */
5481 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5482 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5483 received_WM_NOTIFY = 0;
5484 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5485 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5486 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5488 DestroyWindow(hwndRichedit_WM_NOTIFY);
5489 DestroyWindow(parent);
5492 static void test_undo_coalescing(void)
5496 char buffer[64] = {0};
5498 /* multi-line control inserts CR normally */
5500 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5501 0, 0, 200, 60, 0, 0, 0, 0);
5503 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5504 0, 0, 200, 60, 0, 0, 0, 0);
5505 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5507 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5508 ok (result == FALSE, "Can undo after window creation.\n");
5509 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5510 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5511 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5512 ok (result == FALSE, "Can redo after window creation.\n");
5513 result = SendMessage(hwnd, EM_REDO, 0, 0);
5514 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5516 /* Test the effect of arrows keys during typing on undo transactions*/
5517 simulate_typing_characters(hwnd, "one two three");
5518 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5519 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5520 simulate_typing_characters(hwnd, " four five six");
5522 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5523 ok (result == FALSE, "Can redo before anything is undone.\n");
5524 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5525 ok (result == TRUE, "Cannot undo typed characters.\n");
5526 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5527 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5528 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5529 ok (result == TRUE, "Cannot redo after undo.\n");
5530 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5531 result = strcmp(buffer, "one two three");
5532 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5534 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5535 ok (result == TRUE, "Cannot undo typed characters.\n");
5536 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5537 ok (result == TRUE, "Failed to undo typed characters.\n");
5538 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5539 result = strcmp(buffer, "");
5540 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5542 /* Test the effect of focus changes during typing on undo transactions*/
5543 simulate_typing_characters(hwnd, "one two three");
5544 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5545 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5546 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5547 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5548 simulate_typing_characters(hwnd, " four five six");
5549 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5550 ok (result == TRUE, "Failed to undo typed characters.\n");
5551 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5552 result = strcmp(buffer, "one two three");
5553 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5555 /* Test the effect of the back key during typing on undo transactions */
5556 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5557 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5558 ok (result == TRUE, "Failed to clear the text.\n");
5559 simulate_typing_characters(hwnd, "one two threa");
5560 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5561 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5562 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5563 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5564 simulate_typing_characters(hwnd, "e four five six");
5565 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5566 ok (result == TRUE, "Failed to undo typed characters.\n");
5567 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5568 result = strcmp(buffer, "");
5569 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5571 /* Test the effect of the delete key during typing on undo transactions */
5572 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5573 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5574 ok(result == TRUE, "Failed to set the text.\n");
5575 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5576 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5577 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5578 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5579 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5580 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5581 ok (result == TRUE, "Failed to undo typed characters.\n");
5582 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5583 result = strcmp(buffer, "acd");
5584 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5585 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5586 ok (result == TRUE, "Failed to undo typed characters.\n");
5587 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5588 result = strcmp(buffer, "abcd");
5589 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5591 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5592 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5593 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5594 ok (result == TRUE, "Failed to clear the text.\n");
5595 simulate_typing_characters(hwnd, "one two three");
5596 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5597 ok (result == 0, "expected %d but got %d\n", 0, result);
5598 simulate_typing_characters(hwnd, " four five six");
5599 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5600 ok (result == TRUE, "Failed to undo typed characters.\n");
5601 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5602 result = strcmp(buffer, "one two three");
5603 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5604 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5605 ok (result == TRUE, "Failed to undo typed characters.\n");
5606 ok (result == TRUE, "Failed to undo typed characters.\n");
5607 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5608 result = strcmp(buffer, "");
5609 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5611 DestroyWindow(hwnd);
5614 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5618 /* MSDN lied, length is actually the number of bytes. */
5619 length = bytes / sizeof(WCHAR);
5622 case WB_ISDELIMITER:
5623 return text[pos] == 'X';
5625 case WB_MOVEWORDLEFT:
5626 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5628 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5631 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5635 case WB_MOVEWORDRIGHT:
5636 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5638 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5641 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5645 ok(FALSE, "Unexpected code %d\n", code);
5651 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5652 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5654 static void test_word_movement(void)
5658 int sel_start, sel_end;
5659 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5661 /* multi-line control inserts CR normally */
5662 hwnd = new_richedit(NULL);
5664 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5665 ok (result == TRUE, "Failed to clear the text.\n");
5666 SendMessage(hwnd, EM_SETSEL, 0, 0);
5667 /* |one two three */
5669 SEND_CTRL_RIGHT(hwnd);
5670 /* one |two three */
5671 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5672 ok(sel_start == sel_end, "Selection should be empty\n");
5673 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5675 SEND_CTRL_RIGHT(hwnd);
5676 /* one two |three */
5677 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5678 ok(sel_start == sel_end, "Selection should be empty\n");
5679 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5681 SEND_CTRL_LEFT(hwnd);
5682 /* one |two three */
5683 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5684 ok(sel_start == sel_end, "Selection should be empty\n");
5685 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5687 SEND_CTRL_LEFT(hwnd);
5688 /* |one two three */
5689 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5690 ok(sel_start == sel_end, "Selection should be empty\n");
5691 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5693 SendMessage(hwnd, EM_SETSEL, 8, 8);
5694 /* one two | three */
5695 SEND_CTRL_RIGHT(hwnd);
5696 /* one two |three */
5697 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5698 ok(sel_start == sel_end, "Selection should be empty\n");
5699 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5701 SendMessage(hwnd, EM_SETSEL, 11, 11);
5702 /* one two th|ree */
5703 SEND_CTRL_LEFT(hwnd);
5704 /* one two |three */
5705 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5706 ok(sel_start == sel_end, "Selection should be empty\n");
5707 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5709 /* Test with a custom word break procedure that uses X as the delimiter. */
5710 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5711 ok (result == TRUE, "Failed to clear the text.\n");
5712 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5713 /* |one twoXthree */
5714 SEND_CTRL_RIGHT(hwnd);
5715 /* one twoX|three */
5716 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5717 ok(sel_start == sel_end, "Selection should be empty\n");
5718 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5720 DestroyWindow(hwnd);
5722 /* Make sure the behaviour is the same with a unicode richedit window,
5723 * and using unicode functions. */
5726 skip("Cannot test with unicode richedit window\n");
5730 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5731 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5732 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5734 /* Test with a custom word break procedure that uses X as the delimiter. */
5735 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5736 ok (result == TRUE, "Failed to clear the text.\n");
5737 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5738 /* |one twoXthree */
5739 SEND_CTRL_RIGHT(hwnd);
5740 /* one twoX|three */
5741 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5742 ok(sel_start == sel_end, "Selection should be empty\n");
5743 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5745 DestroyWindow(hwnd);
5748 static void test_EM_CHARFROMPOS(void)
5757 /* multi-line control inserts CR normally */
5758 hwnd = new_richedit(NULL);
5759 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5760 (LPARAM)"one two three four five six seven\reight");
5762 GetClientRect(hwnd, &rcClient);
5764 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5765 ok(result == 34, "expected character index of 34 but got %d\n", result);
5767 /* Test with points outside the bounds of the richedit control. */
5770 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5771 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5775 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5776 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
5780 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5781 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5785 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5786 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
5789 point.y = rcClient.bottom + 1;
5790 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5791 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
5794 point.y = rcClient.bottom;
5795 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5796 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
5798 DestroyWindow(hwnd);
5801 static void test_word_wrap(void)
5804 POINTL point = {0, 60}; /* This point must be below the first line */
5805 const char *text = "Must be long enough to test line wrapping";
5806 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5807 int res, pos, lines;
5809 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5810 * when specified on window creation and set later. */
5811 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5812 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5813 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5814 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5815 ok(res, "WM_SETTEXT failed.\n");
5816 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5817 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5818 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5819 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5821 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5822 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5823 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5824 DestroyWindow(hwnd);
5826 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5827 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5828 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5830 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5831 ok(res, "WM_SETTEXT failed.\n");
5832 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5833 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5834 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5835 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5837 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5838 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5839 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5840 DestroyWindow(hwnd);
5842 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5843 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5844 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5845 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5846 ok(res, "WM_SETTEXT failed.\n");
5847 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5848 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5850 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5851 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5852 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5853 DestroyWindow(hwnd);
5855 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5856 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5857 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5858 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5859 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5860 ok(res, "WM_SETTEXT failed.\n");
5861 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5862 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5864 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5865 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5866 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5868 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5869 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5870 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5871 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5872 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5874 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5875 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5876 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5877 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5878 DestroyWindow(hwnd);
5880 /* Test to see if wrapping happens with redraw disabled. */
5881 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5882 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5883 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5884 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5885 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5886 ok(res, "EM_REPLACESEL failed.\n");
5887 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5888 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5889 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5890 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5891 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5893 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5894 DestroyWindow(hwnd);
5897 static void test_autoscroll(void)
5899 HWND hwnd = new_richedit(NULL);
5900 int lines, ret, redraw;
5903 for (redraw = 0; redraw <= 1; redraw++) {
5904 trace("testing with WM_SETREDRAW=%d\n", redraw);
5905 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5906 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5907 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5908 ok(lines == 8, "%d lines instead of 8\n", lines);
5909 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5910 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5911 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5912 ret = GetWindowLong(hwnd, GWL_STYLE);
5913 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5915 SendMessage(hwnd, WM_SETTEXT, 0, 0);
5916 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5917 ok(lines == 1, "%d lines instead of 1\n", lines);
5918 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5919 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5920 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5921 ret = GetWindowLong(hwnd, GWL_STYLE);
5922 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5925 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5926 DestroyWindow(hwnd);
5928 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
5929 * auto vertical/horizontal scrolling options. */
5930 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5931 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
5932 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5933 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5934 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
5935 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
5936 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
5937 ret = GetWindowLong(hwnd, GWL_STYLE);
5938 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
5939 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
5940 DestroyWindow(hwnd);
5942 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5943 WS_POPUP|ES_MULTILINE,
5944 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5945 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5946 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
5947 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
5948 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
5949 ret = GetWindowLong(hwnd, GWL_STYLE);
5950 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
5951 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
5952 DestroyWindow(hwnd);
5956 static void test_format_rect(void)
5959 RECT rc, expected, clientRect;
5963 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
5964 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5965 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5966 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
5968 GetClientRect(hwnd, &clientRect);
5970 expected = clientRect;
5972 expected.right -= 1;
5973 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5974 ok(rc.top == expected.top && rc.left == expected.left &&
5975 rc.bottom == expected.bottom && rc.right == expected.right,
5976 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5977 rc.top, rc.left, rc.bottom, rc.right,
5978 expected.top, expected.left, expected.bottom, expected.right);
5980 for (n = -3; n <= 3; n++)
5987 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
5990 expected.top = max(0, rc.top);
5991 expected.left = max(0, rc.left);
5992 expected.bottom = min(clientRect.bottom, rc.bottom);
5993 expected.right = min(clientRect.right, rc.right);
5994 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
5995 ok(rc.top == expected.top && rc.left == expected.left &&
5996 rc.bottom == expected.bottom && rc.right == expected.right,
5997 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
5998 n, rc.top, rc.left, rc.bottom, rc.right,
5999 expected.top, expected.left, expected.bottom, expected.right);
6003 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6004 expected = clientRect;
6005 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6006 ok(rc.top == expected.top && rc.left == expected.left &&
6007 rc.bottom == expected.bottom && rc.right == expected.right,
6008 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6009 rc.top, rc.left, rc.bottom, rc.right,
6010 expected.top, expected.left, expected.bottom, expected.right);
6012 /* Adding the selectionbar adds the selectionbar width to the left side. */
6013 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6014 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6015 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6016 expected.left += 8; /* selection bar width */
6017 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6018 ok(rc.top == expected.top && rc.left == expected.left &&
6019 rc.bottom == expected.bottom && rc.right == expected.right,
6020 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6021 rc.top, rc.left, rc.bottom, rc.right,
6022 expected.top, expected.left, expected.bottom, expected.right);
6025 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6026 expected = clientRect;
6027 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6028 ok(rc.top == expected.top && rc.left == expected.left &&
6029 rc.bottom == expected.bottom && rc.right == expected.right,
6030 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6031 rc.top, rc.left, rc.bottom, rc.right,
6032 expected.top, expected.left, expected.bottom, expected.right);
6034 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6035 * even if the left side is already 0. */
6036 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6037 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6038 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6039 expected.left -= 8; /* selection bar width */
6040 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6041 ok(rc.top == expected.top && rc.left == expected.left &&
6042 rc.bottom == expected.bottom && rc.right == expected.right,
6043 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6044 rc.top, rc.left, rc.bottom, rc.right,
6045 expected.top, expected.left, expected.bottom, expected.right);
6047 /* Set the absolute value of the formatting rectangle. */
6049 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6050 expected = clientRect;
6051 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6052 ok(rc.top == expected.top && rc.left == expected.left &&
6053 rc.bottom == expected.bottom && rc.right == expected.right,
6054 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6055 n, rc.top, rc.left, rc.bottom, rc.right,
6056 expected.top, expected.left, expected.bottom, expected.right);
6058 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6059 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6060 * tests show that this isn't true. */
6063 rc.bottom = clientRect.bottom - 15;
6064 rc.right = clientRect.right - 15;
6066 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6067 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6068 ok(rc.top == expected.top && rc.left == expected.left &&
6069 rc.bottom == expected.bottom && rc.right == expected.right,
6070 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6071 rc.top, rc.left, rc.bottom, rc.right,
6072 expected.top, expected.left, expected.bottom, expected.right);
6074 /* For some reason it does not limit the values to the client rect with
6075 * a WPARAM value of 1. */
6078 rc.bottom = clientRect.bottom + 15;
6079 rc.right = clientRect.right + 15;
6081 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6082 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6083 ok(rc.top == expected.top && rc.left == expected.left &&
6084 rc.bottom == expected.bottom && rc.right == expected.right,
6085 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6086 rc.top, rc.left, rc.bottom, rc.right,
6087 expected.top, expected.left, expected.bottom, expected.right);
6089 DestroyWindow(hwnd);
6091 /* The extended window style affects the formatting rectangle. */
6092 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6093 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6094 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6095 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6097 GetClientRect(hwnd, &clientRect);
6099 expected = clientRect;
6102 expected.right -= 1;
6103 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6104 ok(rc.top == expected.top && rc.left == expected.left &&
6105 rc.bottom == expected.bottom && rc.right == expected.right,
6106 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6107 rc.top, rc.left, rc.bottom, rc.right,
6108 expected.top, expected.left, expected.bottom, expected.right);
6118 expected.right += 1;
6119 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6120 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6121 ok(rc.top == expected.top && rc.left == expected.left &&
6122 rc.bottom == expected.bottom && rc.right == expected.right,
6123 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6124 rc.top, rc.left, rc.bottom, rc.right,
6125 expected.top, expected.left, expected.bottom, expected.right);
6127 DestroyWindow(hwnd);
6130 static void test_WM_GETDLGCODE(void)
6136 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6138 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6139 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6140 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6141 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6143 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6144 expected = expected | DLGC_WANTMESSAGE;
6145 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6147 DestroyWindow(hwnd);
6149 msg.message = WM_KEYDOWN;
6150 msg.wParam = VK_RETURN;
6151 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6154 msg.time = GetTickCount();
6156 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6157 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6158 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6159 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6161 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6162 expected = expected | DLGC_WANTMESSAGE;
6163 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6165 DestroyWindow(hwnd);
6167 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6168 ES_MULTILINE|WS_POPUP,
6169 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6170 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6172 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6173 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6174 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6176 DestroyWindow(hwnd);
6178 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6179 ES_WANTRETURN|WS_POPUP,
6180 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6181 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6183 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6184 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6185 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6187 DestroyWindow(hwnd);
6189 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6191 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6192 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6194 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6195 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6196 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6198 DestroyWindow(hwnd);
6200 msg.wParam = VK_TAB;
6201 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6203 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6204 ES_MULTILINE|WS_POPUP,
6205 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6206 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6208 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6209 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6210 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6212 DestroyWindow(hwnd);
6214 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6216 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6217 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6219 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6220 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6221 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6223 DestroyWindow(hwnd);
6225 hold_key(VK_CONTROL);
6227 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6228 ES_MULTILINE|WS_POPUP,
6229 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6230 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6232 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6233 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6234 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6236 DestroyWindow(hwnd);
6238 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6240 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6241 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6243 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6244 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6245 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6247 DestroyWindow(hwnd);
6249 release_key(VK_CONTROL);
6252 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6254 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6255 ES_MULTILINE|WS_POPUP,
6256 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6257 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6259 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6260 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6261 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6263 DestroyWindow(hwnd);
6265 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6267 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6268 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6270 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6271 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6272 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6274 DestroyWindow(hwnd);
6276 msg.message = WM_CHAR;
6278 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6279 ES_MULTILINE|WS_POPUP,
6280 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6281 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6283 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6284 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6285 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6287 DestroyWindow(hwnd);
6289 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6291 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6292 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6294 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6295 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6296 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6298 DestroyWindow(hwnd);
6301 static void test_zoom(void)
6307 int numerator, denominator;
6309 hwnd = new_richedit(NULL);
6310 GetClientRect(hwnd, &rc);
6311 pt.x = (rc.right - rc.left) / 2;
6312 pt.y = (rc.bottom - rc.top) / 2;
6313 ClientToScreen(hwnd, &pt);
6315 /* Test initial zoom value */
6316 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6317 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6318 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6319 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6321 /* test scroll wheel */
6322 hold_key(VK_CONTROL);
6323 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6324 MAKELPARAM(pt.x, pt.y));
6325 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6326 release_key(VK_CONTROL);
6328 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6329 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6330 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6331 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6333 /* Test how much the mouse wheel can zoom in and out. */
6334 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6335 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6337 hold_key(VK_CONTROL);
6338 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6339 MAKELPARAM(pt.x, pt.y));
6340 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6341 release_key(VK_CONTROL);
6343 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6344 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6345 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6346 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6348 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6349 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6351 hold_key(VK_CONTROL);
6352 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6353 MAKELPARAM(pt.x, pt.y));
6354 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6355 release_key(VK_CONTROL);
6357 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6358 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6359 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6360 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6362 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6363 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6365 hold_key(VK_CONTROL);
6366 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6367 MAKELPARAM(pt.x, pt.y));
6368 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6369 release_key(VK_CONTROL);
6371 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6372 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6373 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6374 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6376 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6377 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6379 hold_key(VK_CONTROL);
6380 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6381 MAKELPARAM(pt.x, pt.y));
6382 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6383 release_key(VK_CONTROL);
6385 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6386 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6387 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6388 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6390 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6391 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6392 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6394 hold_key(VK_CONTROL);
6395 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6396 MAKELPARAM(pt.x, pt.y));
6397 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6398 release_key(VK_CONTROL);
6400 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6401 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6402 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6403 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6405 /* Test bounds checking on EM_SETZOOM */
6406 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6407 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6409 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6410 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6412 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6413 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6415 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6416 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6417 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6418 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6420 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6421 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6423 /* See if negative numbers are accepted. */
6424 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6425 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6427 /* See if negative numbers are accepted. */
6428 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6429 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6431 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6432 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6433 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6434 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6436 /* Reset the zoom value */
6437 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6438 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6440 DestroyWindow(hwnd);
6443 START_TEST( editor )
6445 /* Must explicitly LoadLibrary(). The test has no references to functions in
6446 * RICHED20.DLL, so the linker doesn't actually link to it. */
6447 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6448 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6450 is_win9x = GetVersion() & 0x80000000;
6455 test_EM_POSFROMCHAR();
6456 test_EM_SCROLLCARET();
6458 test_scrollbar_visibility();
6460 test_EM_LINELENGTH();
6461 test_EM_SETCHARFORMAT();
6462 test_EM_SETTEXTMODE();
6463 test_TM_PLAINTEXT();
6464 test_EM_SETOPTIONS();
6466 test_EM_GETTEXTRANGE();
6467 test_EM_GETSELTEXT();
6468 test_EM_SETUNDOLIMIT();
6470 test_EM_SETTEXTEX();
6471 test_EM_LIMITTEXT();
6472 test_EM_EXLIMITTEXT();
6473 test_EM_GETLIMITTEXT();
6475 test_EM_GETMODIFY();
6479 test_EM_STREAMOUT();
6480 test_EM_StreamIn_Undo();
6481 test_EM_FORMATRANGE();
6482 test_unicode_conversions();
6483 test_EM_GETTEXTLENGTHEX();
6484 test_EM_REPLACESEL(1);
6485 test_EM_REPLACESEL(0);
6487 test_EM_AUTOURLDETECT();
6489 test_undo_coalescing();
6490 test_word_movement();
6491 test_EM_CHARFROMPOS();
6492 test_SETPARAFORMAT();
6496 test_WM_GETDLGCODE();
6499 /* Set the environment variable WINETEST_RICHED20 to keep windows
6500 * responsive and open for 30 seconds. This is useful for debugging.
6502 if (getenv( "WINETEST_RICHED20" )) {
6503 keep_responsive(30);
6506 OleFlushClipboard();
6507 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());