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 HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
49 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
50 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
51 hmoduleRichEdit, NULL);
52 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
56 static HWND new_richedit(HWND parent) {
57 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
60 /* Keeps the window reponsive for the deley_time in seconds.
61 * This is useful for debugging a test to see what is happening. */
62 void keep_responsive(time_t delay_time)
67 /* The message pump uses PeekMessage() to empty the queue and then
68 * sleeps for 50ms before retrying the queue. */
69 end = time(NULL) + delay_time;
70 while (time(NULL) < end) {
71 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
72 TranslateMessage(&msg);
73 DispatchMessage(&msg);
80 static void processPendingMessages(void)
83 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
84 TranslateMessage(&msg);
85 DispatchMessage(&msg);
89 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
91 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
92 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
94 keybd_event(mod_vk, mod_scan_code, 0, 0);
95 keybd_event(vk, scan_code, 0, 0);
96 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
97 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
98 processPendingMessages();
101 static void simulate_typing_characters(HWND hwnd, const char* szChars)
105 while (*szChars != '\0') {
106 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
107 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
108 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
109 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
114 static BOOL hold_key(int vk)
119 result = GetKeyboardState(key_state);
120 ok(result, "GetKeyboardState failed.\n");
121 if (!result) return FALSE;
122 key_state[vk] |= 0x80;
123 result = SetKeyboardState(key_state);
124 ok(result, "SetKeyboardState failed.\n");
128 static BOOL release_key(int vk)
133 result = GetKeyboardState(key_state);
134 ok(result, "GetKeyboardState failed.\n");
135 if (!result) return FALSE;
136 key_state[vk] &= ~0x80;
137 result = SetKeyboardState(key_state);
138 ok(result, "SetKeyboardState failed.\n");
142 static const char haystack[] = "WINEWine wineWine wine WineWine";
155 struct find_s find_tests[] = {
156 /* Find in empty text */
157 {0, -1, "foo", FR_DOWN, -1, 0},
158 {0, -1, "foo", 0, -1, 0},
159 {0, -1, "", FR_DOWN, -1, 0},
160 {20, 5, "foo", FR_DOWN, -1, 0},
161 {5, 20, "foo", FR_DOWN, -1, 0}
164 struct find_s find_tests2[] = {
166 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0},
167 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0},
169 /* Subsequent finds */
170 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0},
171 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0},
172 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
173 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
176 {19, 20, "Wine", FR_MATCHCASE, 13, 0},
177 {10, 20, "Wine", FR_MATCHCASE, 4, 0},
178 {20, 10, "Wine", FR_MATCHCASE, 13, 0},
180 /* Case-insensitive */
181 {1, 31, "wInE", FR_DOWN, 4, 0},
182 {1, 31, "Wine", FR_DOWN, 4, 0},
184 /* High-to-low ranges */
185 {20, 5, "Wine", FR_DOWN, -1, 0},
186 {2, 1, "Wine", FR_DOWN, -1, 0},
187 {30, 29, "Wine", FR_DOWN, -1, 0},
188 {20, 5, "Wine", 0, 13, 0},
191 {5, 10, "", FR_DOWN, -1, 0},
192 {10, 5, "", FR_DOWN, -1, 0},
193 {0, -1, "", FR_DOWN, -1, 0},
194 {10, 5, "", 0, -1, 0},
196 /* Whole-word search */
197 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
198 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0},
199 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0},
200 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0},
201 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0},
202 {11, -1, "winewine", FR_WHOLEWORD, 0, 0},
203 {31, -1, "winewine", FR_WHOLEWORD, 23, 0},
206 {5, 200, "XXX", FR_DOWN, -1, 0},
207 {-20, 20, "Wine", FR_DOWN, -1, 0},
208 {-20, 20, "Wine", FR_DOWN, -1, 0},
209 {-15, -20, "Wine", FR_DOWN, -1, 0},
210 {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0},
212 /* Check the case noted in bug 4479 where matches at end aren't recognized */
213 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0},
214 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
215 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0},
216 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
217 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0},
219 /* The backwards case of bug 4479; bounds look right
220 * Fails because backward find is wrong */
221 {19, 20, "WINE", FR_MATCHCASE, 0, 0},
222 {0, 20, "WINE", FR_MATCHCASE, -1, 0},
224 {0, -1, "wineWine wine", 0, -1, 0},
227 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
230 memset(&ft, 0, sizeof(ft));
231 ft.chrg.cpMin = f->start;
232 ft.chrg.cpMax = f->end;
233 ft.lpstrText = f->needle;
234 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
235 ok(findloc == f->expected_loc,
236 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
237 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
240 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
244 int expected_end_loc;
246 memset(&ft, 0, sizeof(ft));
247 ft.chrg.cpMin = f->start;
248 ft.chrg.cpMax = f->end;
249 ft.lpstrText = f->needle;
250 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
251 ok(findloc == f->expected_loc,
252 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
253 name, id, f->needle, f->start, f->end, f->flags, findloc);
254 ok(ft.chrgText.cpMin == f->expected_loc,
255 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
256 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
257 expected_end_loc = ((f->expected_loc == -1) ? -1
258 : f->expected_loc + strlen(f->needle));
259 ok(ft.chrgText.cpMax == expected_end_loc,
260 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
261 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
264 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
269 for (i = 0; i < num_tests; i++) {
270 if (find[i]._todo_wine) {
272 check_EM_FINDTEXT(hwnd, name, &find[i], i);
273 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
276 check_EM_FINDTEXT(hwnd, name, &find[i], i);
277 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
282 static void test_EM_FINDTEXT(void)
284 HWND hwndRichEdit = new_richedit(NULL);
287 /* Empty rich edit control */
288 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
289 sizeof(find_tests)/sizeof(struct find_s));
291 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
294 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
295 sizeof(find_tests2)/sizeof(struct find_s));
297 /* Setting a format on an arbitrary range should have no effect in search
298 results. This tests correct offset reporting across runs. */
299 cf2.cbSize = sizeof(CHARFORMAT2);
300 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
302 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
303 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
304 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
305 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
307 /* Haystack text, again */
308 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
309 sizeof(find_tests2)/sizeof(struct find_s));
311 /* Yet another range */
312 cf2.dwMask = CFM_BOLD | cf2.dwMask;
313 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
314 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
315 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
317 /* Haystack text, again */
318 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
319 sizeof(find_tests2)/sizeof(struct find_s));
321 DestroyWindow(hwndRichEdit);
324 static const struct getline_s {
329 {0, 10, "foo bar\r"},
334 /* Buffer smaller than line length */
340 static void test_EM_GETLINE(void)
343 HWND hwndRichEdit = new_richedit(NULL);
344 static const int nBuf = 1024;
345 char dest[1024], origdest[1024];
346 const char text[] = "foo bar\n"
350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
352 memset(origdest, 0xBB, nBuf);
353 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
356 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
357 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
358 memset(dest, 0xBB, nBuf);
359 *(WORD *) dest = gl[i].buffer_len;
361 /* EM_GETLINE appends a "\r\0" to the end of the line
362 * nCopied counts up to and including the '\r' */
363 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
364 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
366 /* two special cases since a parameter is passed via dest */
367 if (gl[i].buffer_len == 0)
368 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
370 else if (gl[i].buffer_len == 1)
371 ok(dest[0] == gl[i].text[0] && !dest[1] &&
372 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
375 /* Prepare hex strings of buffers to dump on failure. */
376 char expectedbuf[1024];
377 char resultbuf[1024];
380 for (j = 0; j < 32; j++)
381 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
382 expectedbuf[0] = '\0';
383 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
384 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
385 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
386 sprintf(expectedbuf+strlen(expectedbuf), "??");
387 for (; j < 32; j++) /* Bytes after declared buffer size */
388 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
390 /* Test the part of the buffer that is expected to be written according
391 * to the MSDN documentation fo EM_GETLINE, which does not state that
392 * a NULL terminating character will be added unless no text is copied.
394 * Windows 95, 98 & NT do not append a NULL terminating character, but
395 * Windows 2000 and up do append a NULL terminating character if there
396 * is space in the buffer. The test will ignore this difference. */
397 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
398 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
399 i, expected_bytes_written, expectedbuf, resultbuf);
400 /* Test the part of the buffer after the declared length to make sure
401 * there are no buffer overruns. */
402 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
403 nBuf - gl[i].buffer_len),
404 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
405 i, expected_bytes_written, expectedbuf, resultbuf);
409 DestroyWindow(hwndRichEdit);
412 static void test_EM_LINELENGTH(void)
414 HWND hwndRichEdit = new_richedit(NULL);
420 int offset_test[10][2] = {
435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
437 for (i = 0; i < 10; i++) {
438 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
439 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
440 offset_test[i][0], result, offset_test[i][1]);
443 DestroyWindow(hwndRichEdit);
446 static int get_scroll_pos_y(HWND hwnd)
449 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
450 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
454 static void move_cursor(HWND hwnd, long charindex)
457 cr.cpMax = charindex;
458 cr.cpMin = charindex;
459 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
462 static void line_scroll(HWND hwnd, int amount)
464 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
467 static void test_EM_SCROLLCARET(void)
470 const char text[] = "aa\n"
471 "this is a long line of text that should be longer than the "
479 /* The richedit window height needs to be large enough vertically to fit in
480 * more than two lines of text, so the new_richedit function can't be used
481 * since a height of 60 was not large enough on some systems.
483 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
484 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
485 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
486 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
488 /* Can't verify this */
489 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
493 /* Caret above visible window */
494 line_scroll(hwndRichEdit, 3);
495 prevY = get_scroll_pos_y(hwndRichEdit);
496 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
497 curY = get_scroll_pos_y(hwndRichEdit);
498 ok(prevY != curY, "%d == %d\n", prevY, curY);
500 /* Caret below visible window */
501 move_cursor(hwndRichEdit, sizeof(text) - 1);
502 line_scroll(hwndRichEdit, -3);
503 prevY = get_scroll_pos_y(hwndRichEdit);
504 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
505 curY = get_scroll_pos_y(hwndRichEdit);
506 ok(prevY != curY, "%d == %d\n", prevY, curY);
508 /* Caret in visible window */
509 move_cursor(hwndRichEdit, sizeof(text) - 2);
510 prevY = get_scroll_pos_y(hwndRichEdit);
511 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
512 curY = get_scroll_pos_y(hwndRichEdit);
513 ok(prevY == curY, "%d != %d\n", prevY, curY);
515 /* Caret still in visible window */
516 line_scroll(hwndRichEdit, -1);
517 prevY = get_scroll_pos_y(hwndRichEdit);
518 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
519 curY = get_scroll_pos_y(hwndRichEdit);
520 ok(prevY == curY, "%d != %d\n", prevY, curY);
522 DestroyWindow(hwndRichEdit);
525 static void test_EM_POSFROMCHAR(void)
527 HWND hwndRichEdit = new_richedit(NULL);
530 unsigned int height = 0;
533 static const char text[] = "aa\n"
534 "this is a long line of text that should be longer than the "
543 /* Fill the control to lines to ensure that most of them are offscreen */
544 for (i = 0; i < 50; i++)
546 /* Do not modify the string; it is exactly 16 characters long. */
547 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
548 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
552 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
553 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
554 Richedit 3.0 accepts either of the above API conventions.
557 /* Testing Richedit 2.0 API format */
559 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
560 Since all lines are identical and drawn with the same font,
561 they should have the same height... right?
563 for (i = 0; i < 50; i++)
565 /* All the lines are 16 characters long */
566 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
569 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
570 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 xpos = LOWORD(result);
575 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
576 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
577 height = HIWORD(result);
581 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
582 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
586 /* Testing position at end of text */
587 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
588 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
589 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
591 /* Testing position way past end of text */
592 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
593 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
594 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
596 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
597 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
598 for (i = 0; i < 50; i++)
600 /* All the lines are 16 characters long */
601 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
602 ok((signed short)(HIWORD(result)) == (i - 1) * height,
603 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
604 (signed short)(HIWORD(result)), (i - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
608 /* Testing position at end of text */
609 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
610 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
611 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
613 /* Testing position way past end of text */
614 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
615 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
616 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
618 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
619 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
620 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
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(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
625 xpos = LOWORD(result);
627 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
628 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
629 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
631 /* Fails on builtin because horizontal scrollbar is not being shown */
632 ok((signed short)(LOWORD(result)) < xpos,
633 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
634 (signed short)(LOWORD(result)), xpos);
636 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
638 /* Test around end of text that doesn't end in a newline. */
639 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
640 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
641 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
642 ok(pt.x > 1, "pt.x = %d\n", pt.x);
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
645 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
646 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
648 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
649 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
650 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
652 DestroyWindow(hwndRichEdit);
655 static void test_EM_SETCHARFORMAT(void)
657 HWND hwndRichEdit = new_richedit(NULL);
660 int tested_effects[] = {
674 /* Invalid flags, CHARFORMAT2 structure blanked out */
675 memset(&cf2, 0, sizeof(cf2));
676 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
678 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
680 /* A valid flag, CHARFORMAT2 structure blanked out */
681 memset(&cf2, 0, sizeof(cf2));
682 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
684 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
686 /* A valid flag, CHARFORMAT2 structure blanked out */
687 memset(&cf2, 0, sizeof(cf2));
688 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
690 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
692 /* A valid flag, CHARFORMAT2 structure blanked out */
693 memset(&cf2, 0, sizeof(cf2));
694 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
696 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
698 /* A valid flag, CHARFORMAT2 structure blanked out */
699 memset(&cf2, 0, sizeof(cf2));
700 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
702 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
704 /* Invalid flags, CHARFORMAT2 structure minimally filled */
705 memset(&cf2, 0, sizeof(cf2));
706 cf2.cbSize = sizeof(CHARFORMAT2);
707 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
709 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
710 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
711 ok(rc == FALSE, "Should not be able to undo here.\n");
712 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
714 /* A valid flag, CHARFORMAT2 structure minimally filled */
715 memset(&cf2, 0, sizeof(cf2));
716 cf2.cbSize = sizeof(CHARFORMAT2);
717 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
719 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
720 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
721 ok(rc == FALSE, "Should not be able to undo here.\n");
722 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
724 /* A valid flag, CHARFORMAT2 structure minimally filled */
725 memset(&cf2, 0, sizeof(cf2));
726 cf2.cbSize = sizeof(CHARFORMAT2);
727 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
729 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
730 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
731 ok(rc == FALSE, "Should not be able to undo here.\n");
732 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
734 /* A valid flag, CHARFORMAT2 structure minimally filled */
735 memset(&cf2, 0, sizeof(cf2));
736 cf2.cbSize = sizeof(CHARFORMAT2);
737 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
739 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
740 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
741 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
742 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
744 /* A valid flag, CHARFORMAT2 structure minimally filled */
745 memset(&cf2, 0, sizeof(cf2));
746 cf2.cbSize = sizeof(CHARFORMAT2);
747 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
749 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
750 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
751 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
752 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
758 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
759 cf2.cbSize = sizeof(CHARFORMAT2);
760 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
762 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
763 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
765 /* wParam==0 is default char format, does not set modify */
766 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
767 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
768 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
770 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
774 /* wParam==SCF_SELECTION sets modify if nonempty selection */
775 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
776 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
777 ok(rc == 0, "Text marked as modified, expected not modified!\n");
778 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
779 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
780 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
781 ok(rc == 0, "Text marked as modified, expected not modified!\n");
783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
784 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
785 ok(rc == 0, "Text marked as modified, expected not modified!\n");
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 == 0, "Text marked as modified, expected not modified!\n");
790 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
791 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
792 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
796 /* wParam==SCF_ALL sets modify regardless of whether text is present */
797 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
798 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
799 ok(rc == 0, "Text marked as modified, expected not modified!\n");
800 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
801 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
802 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
805 DestroyWindow(hwndRichEdit);
807 /* EM_GETCHARFORMAT tests */
808 for (i = 0; tested_effects[i]; i++)
810 hwndRichEdit = new_richedit(NULL);
811 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
813 /* Need to set a TrueType font to get consistent CFM_BOLD results */
814 memset(&cf2, 0, sizeof(CHARFORMAT2));
815 cf2.cbSize = sizeof(CHARFORMAT2);
816 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
818 strcpy(cf2.szFaceName, "Courier New");
819 cf2.wWeight = FW_DONTCARE;
820 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
822 memset(&cf2, 0, sizeof(CHARFORMAT2));
823 cf2.cbSize = sizeof(CHARFORMAT2);
824 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
825 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
826 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
827 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
829 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
830 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
831 ok((cf2.dwEffects & tested_effects[i]) == 0,
832 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
834 memset(&cf2, 0, sizeof(CHARFORMAT2));
835 cf2.cbSize = sizeof(CHARFORMAT2);
836 cf2.dwMask = tested_effects[i];
837 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
838 cf2.dwMask = CFM_SUPERSCRIPT;
839 cf2.dwEffects = tested_effects[i];
840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
843 memset(&cf2, 0, sizeof(CHARFORMAT2));
844 cf2.cbSize = sizeof(CHARFORMAT2);
845 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
846 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
847 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
848 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
850 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
851 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
852 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
853 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
855 memset(&cf2, 0, sizeof(CHARFORMAT2));
856 cf2.cbSize = sizeof(CHARFORMAT2);
857 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
858 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
859 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
860 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
862 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
863 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
864 ok((cf2.dwEffects & tested_effects[i]) == 0,
865 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
867 memset(&cf2, 0, sizeof(CHARFORMAT2));
868 cf2.cbSize = sizeof(CHARFORMAT2);
869 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
870 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
871 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
872 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
874 (cf2.dwMask & tested_effects[i]) == 0),
875 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
877 DestroyWindow(hwndRichEdit);
880 for (i = 0; tested_effects[i]; i++)
882 hwndRichEdit = new_richedit(NULL);
883 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
885 /* Need to set a TrueType font to get consistent CFM_BOLD results */
886 memset(&cf2, 0, sizeof(CHARFORMAT2));
887 cf2.cbSize = sizeof(CHARFORMAT2);
888 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
890 strcpy(cf2.szFaceName, "Courier New");
891 cf2.wWeight = FW_DONTCARE;
892 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
894 memset(&cf2, 0, sizeof(CHARFORMAT2));
895 cf2.cbSize = sizeof(CHARFORMAT2);
896 cf2.dwMask = tested_effects[i];
897 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
898 cf2.dwMask = CFM_SUPERSCRIPT;
899 cf2.dwEffects = tested_effects[i];
900 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
901 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
903 memset(&cf2, 0, sizeof(CHARFORMAT2));
904 cf2.cbSize = sizeof(CHARFORMAT2);
905 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
906 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
907 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
908 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
910 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
911 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
912 ok((cf2.dwEffects & tested_effects[i]) == 0,
913 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
915 memset(&cf2, 0, sizeof(CHARFORMAT2));
916 cf2.cbSize = sizeof(CHARFORMAT2);
917 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
918 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
919 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
920 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
922 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
923 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
924 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
925 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
927 memset(&cf2, 0, sizeof(CHARFORMAT2));
928 cf2.cbSize = sizeof(CHARFORMAT2);
929 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
930 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
931 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
932 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
934 (cf2.dwMask & tested_effects[i]) == 0),
935 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
936 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
937 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
939 DestroyWindow(hwndRichEdit);
942 /* Effects applied on an empty selection should take effect when selection is
943 replaced with text */
944 hwndRichEdit = new_richedit(NULL);
945 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
946 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
948 memset(&cf2, 0, sizeof(CHARFORMAT2));
949 cf2.cbSize = sizeof(CHARFORMAT2);
950 cf2.dwMask = CFM_BOLD;
951 cf2.dwEffects = CFE_BOLD;
952 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
954 /* Selection is now nonempty */
955 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
957 memset(&cf2, 0, sizeof(CHARFORMAT2));
958 cf2.cbSize = sizeof(CHARFORMAT2);
959 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
960 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
962 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
963 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
964 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
965 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
968 /* Set two effects on an empty selection */
969 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
970 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
972 memset(&cf2, 0, sizeof(CHARFORMAT2));
973 cf2.cbSize = sizeof(CHARFORMAT2);
974 cf2.dwMask = CFM_BOLD;
975 cf2.dwEffects = CFE_BOLD;
976 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
977 cf2.dwMask = CFM_ITALIC;
978 cf2.dwEffects = CFE_ITALIC;
979 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
981 /* Selection is now nonempty */
982 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
984 memset(&cf2, 0, sizeof(CHARFORMAT2));
985 cf2.cbSize = sizeof(CHARFORMAT2);
986 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
987 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
989 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
990 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
991 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
992 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
994 /* Setting the (empty) selection to exactly the same place as before should
995 NOT clear the insertion style! */
996 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
997 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
999 memset(&cf2, 0, sizeof(CHARFORMAT2));
1000 cf2.cbSize = sizeof(CHARFORMAT2);
1001 cf2.dwMask = CFM_BOLD;
1002 cf2.dwEffects = CFE_BOLD;
1003 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1005 /* Empty selection in same place, insert style should NOT be forgotten here. */
1006 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1008 /* Selection is now nonempty */
1009 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1011 memset(&cf2, 0, sizeof(CHARFORMAT2));
1012 cf2.cbSize = sizeof(CHARFORMAT2);
1013 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1014 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1016 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1017 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1018 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1019 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1021 /* Ditto with EM_EXSETSEL */
1022 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1023 cr.cpMin = 2; cr.cpMax = 2;
1024 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1026 memset(&cf2, 0, sizeof(CHARFORMAT2));
1027 cf2.cbSize = sizeof(CHARFORMAT2);
1028 cf2.dwMask = CFM_BOLD;
1029 cf2.dwEffects = CFE_BOLD;
1030 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1032 /* Empty selection in same place, insert style should NOT be forgotten here. */
1033 cr.cpMin = 2; cr.cpMax = 2;
1034 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1036 /* Selection is now nonempty */
1037 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1039 memset(&cf2, 0, sizeof(CHARFORMAT2));
1040 cf2.cbSize = sizeof(CHARFORMAT2);
1041 cr.cpMin = 2; cr.cpMax = 6;
1042 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1043 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1045 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1046 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1047 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1048 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1050 DestroyWindow(hwndRichEdit);
1053 static void test_EM_SETTEXTMODE(void)
1055 HWND hwndRichEdit = new_richedit(NULL);
1056 CHARFORMAT2 cf2, cf2test;
1060 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1061 /*Insert text into the control*/
1063 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1065 /*Attempt to change the control to plain text mode*/
1066 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1067 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1069 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1070 If rich text is pasted, it should have the same formatting as the rest
1071 of the text in the control*/
1073 /*Italicize the text
1074 *NOTE: If the default text was already italicized, the test will simply
1075 reverse; in other words, it will copy a regular "wine" into a plain
1076 text window that uses an italicized format*/
1077 cf2.cbSize = sizeof(CHARFORMAT2);
1078 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1081 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1082 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1084 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1085 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1087 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1088 however, SCF_ALL has been implemented*/
1089 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1090 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1092 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1093 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1095 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1097 /*Select the string "wine"*/
1100 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1102 /*Copy the italicized "wine" to the clipboard*/
1103 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1105 /*Reset the formatting to default*/
1106 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1107 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1108 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1110 /*Clear the text in the control*/
1111 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1113 /*Switch to Plain Text Mode*/
1114 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1115 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1117 /*Input "wine" again in normal format*/
1118 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1120 /*Paste the italicized "wine" into the control*/
1121 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1123 /*Select a character from the first "wine" string*/
1126 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1128 /*Retrieve its formatting*/
1129 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1132 /*Select a character from the second "wine" string*/
1135 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1137 /*Retrieve its formatting*/
1138 cf2test.cbSize = sizeof(CHARFORMAT2);
1139 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1142 /*Compare the two formattings*/
1143 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1144 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1145 cf2.dwEffects, cf2test.dwEffects);
1146 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1147 printing "wine" in the current format(normal)
1148 pasting "wine" from the clipboard(italicized)
1149 comparing the two formats(should differ)*/
1151 /*Attempt to switch with text in control*/
1152 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1153 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1156 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1158 /*Switch into Rich Text mode*/
1159 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1160 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1162 /*Print "wine" in normal formatting into the control*/
1163 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1165 /*Paste italicized "wine" into the control*/
1166 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1168 /*Select text from the first "wine" string*/
1171 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1173 /*Retrieve its formatting*/
1174 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1177 /*Select text from the second "wine" string*/
1180 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1182 /*Retrieve its formatting*/
1183 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1186 /*Test that the two formattings are not the same*/
1187 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1188 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1189 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1191 DestroyWindow(hwndRichEdit);
1194 static void test_SETPARAFORMAT(void)
1196 HWND hwndRichEdit = new_richedit(NULL);
1199 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1200 fmt.cbSize = sizeof(PARAFORMAT2);
1201 fmt.dwMask = PFM_ALIGNMENT;
1202 fmt.wAlignment = PFA_LEFT;
1204 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1205 ok(ret != 0, "expected non-zero got %d\n", ret);
1207 fmt.cbSize = sizeof(PARAFORMAT2);
1209 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1210 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1211 * between richedit different native builds of riched20.dll
1212 * used on different Windows versions. */
1213 ret &= ~PFM_TABLEROWDELIMITER;
1214 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1216 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1217 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1219 DestroyWindow(hwndRichEdit);
1222 static void test_TM_PLAINTEXT(void)
1224 /*Tests plain text properties*/
1226 HWND hwndRichEdit = new_richedit(NULL);
1227 CHARFORMAT2 cf2, cf2test;
1231 /*Switch to plain text mode*/
1233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1234 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1236 /*Fill control with text*/
1238 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1240 /*Select some text and bold it*/
1244 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1245 cf2.cbSize = sizeof(CHARFORMAT2);
1246 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1249 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1250 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1252 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1253 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1255 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1256 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1258 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1259 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1261 /*Get the formatting of those characters*/
1263 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1265 /*Get the formatting of some other characters*/
1266 cf2test.cbSize = sizeof(CHARFORMAT2);
1269 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1270 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1272 /*Test that they are the same as plain text allows only one formatting*/
1274 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1275 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1276 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1278 /*Fill the control with a "wine" string, which when inserted will be bold*/
1280 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1282 /*Copy the bolded "wine" string*/
1286 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1287 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1289 /*Swap back to rich text*/
1291 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1292 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1294 /*Set the default formatting to bold italics*/
1296 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1297 cf2.dwMask |= CFM_ITALIC;
1298 cf2.dwEffects ^= CFE_ITALIC;
1299 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1300 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1302 /*Set the text in the control to "wine", which will be bold and italicized*/
1304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1306 /*Paste the plain text "wine" string, which should take the insert
1307 formatting, which at the moment is bold italics*/
1309 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1311 /*Select the first "wine" string and retrieve its formatting*/
1315 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1316 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1318 /*Select the second "wine" string and retrieve its formatting*/
1322 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1323 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1325 /*Compare the two formattings. They should be the same.*/
1327 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1328 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1329 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1330 DestroyWindow(hwndRichEdit);
1333 static void test_WM_GETTEXT(void)
1335 HWND hwndRichEdit = new_richedit(NULL);
1336 static const char text[] = "Hello. My name is RichEdit!";
1337 static const char text2[] = "Hello. My name is RichEdit!\r";
1338 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1339 char buffer[1024] = {0};
1342 /* Baseline test with normal-sized buffer */
1343 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1344 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1345 ok(result == lstrlen(buffer),
1346 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1347 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1348 result = strcmp(buffer,text);
1350 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1352 /* Test for returned value of WM_GETTEXTLENGTH */
1353 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1354 ok(result == lstrlen(text),
1355 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1356 result, lstrlen(text));
1358 /* Test for behavior in overflow case */
1359 memset(buffer, 0, 1024);
1360 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1362 result == lstrlenA(text) - 1, /* XP, win2k3 */
1363 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1364 result = strcmp(buffer,text);
1366 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1368 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1370 /* Baseline test with normal-sized buffer and carriage return */
1371 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1372 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1373 ok(result == lstrlen(buffer),
1374 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1375 result = strcmp(buffer,text2_after);
1377 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1379 /* Test for returned value of WM_GETTEXTLENGTH */
1380 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1381 ok(result == lstrlen(text2_after),
1382 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1383 result, lstrlen(text2_after));
1385 /* Test for behavior of CRLF conversion in case of overflow */
1386 memset(buffer, 0, 1024);
1387 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1389 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1390 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1391 result = strcmp(buffer,text2);
1393 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1395 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1397 DestroyWindow(hwndRichEdit);
1400 static void test_EM_GETTEXTRANGE(void)
1402 HWND hwndRichEdit = new_richedit(NULL);
1403 const char * text1 = "foo bar\r\nfoo bar";
1404 const char * text2 = "foo bar\rfoo bar";
1405 const char * expect = "bar\rfoo";
1406 char buffer[1024] = {0};
1408 TEXTRANGEA textRange;
1410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1412 textRange.lpstrText = buffer;
1413 textRange.chrg.cpMin = 4;
1414 textRange.chrg.cpMax = 11;
1415 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1416 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1417 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1419 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1421 textRange.lpstrText = buffer;
1422 textRange.chrg.cpMin = 4;
1423 textRange.chrg.cpMax = 11;
1424 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1425 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1426 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1428 DestroyWindow(hwndRichEdit);
1431 static void test_EM_GETSELTEXT(void)
1433 HWND hwndRichEdit = new_richedit(NULL);
1434 const char * text1 = "foo bar\r\nfoo bar";
1435 const char * text2 = "foo bar\rfoo bar";
1436 const char * expect = "bar\rfoo";
1437 char buffer[1024] = {0};
1440 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1442 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1443 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1444 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1445 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1449 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1450 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1451 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1452 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1454 DestroyWindow(hwndRichEdit);
1457 /* FIXME: need to test unimplemented options and robustly test wparam */
1458 static void test_EM_SETOPTIONS(void)
1460 HWND hwndRichEdit = new_richedit(NULL);
1461 static const char text[] = "Hello. My name is RichEdit!";
1462 char buffer[1024] = {0};
1464 /* NEGATIVE TESTING - NO OPTIONS SET */
1465 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1466 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1468 /* testing no readonly by sending 'a' to the control*/
1469 SetFocus(hwndRichEdit);
1470 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1471 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1473 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1474 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1476 /* READONLY - sending 'a' to the control */
1477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1478 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1479 SetFocus(hwndRichEdit);
1480 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1481 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1482 ok(buffer[0]==text[0],
1483 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1485 DestroyWindow(hwndRichEdit);
1488 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1490 CHARFORMAT2W text_format;
1491 text_format.cbSize = sizeof(text_format);
1492 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1493 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1494 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1497 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1499 int link_present = 0;
1501 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1503 { /* control text is url; should get CFE_LINK */
1504 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1508 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1512 static HWND new_static_wnd(HWND parent) {
1513 return new_window("Static", 0, parent);
1516 static void test_EM_AUTOURLDETECT(void)
1518 /* DO NOT change the properties of the first two elements. To shorten the
1519 tests, all tests after WM_SETTEXT test just the first two elements -
1520 one non-URL and one URL */
1526 {"http://www.winehq.org", 1},
1527 {"http//winehq.org", 0},
1528 {"ww.winehq.org", 0},
1529 {"www.winehq.org", 1},
1530 {"ftp://192.168.1.1", 1},
1531 {"ftp//192.168.1.1", 0},
1532 {"mailto:your@email.com", 1},
1533 {"prospero:prosperoserver", 1},
1535 {"news:newserver", 1},
1536 {"wais:waisserver", 1}
1541 HWND hwndRichEdit, parent;
1543 /* All of the following should cause the URL to be detected */
1544 const char * templates_delim[] = {
1545 "This is some text with X on it",
1546 "This is some text with (X) on it",
1547 "This is some text with X\r on it",
1548 "This is some text with ---X--- on it",
1549 "This is some text with \"X\" on it",
1550 "This is some text with 'X' on it",
1551 "This is some text with 'X' on it",
1552 "This is some text with :X: on it",
1554 "This text ends with X",
1556 "This is some text with X) on it",
1557 "This is some text with X--- on it",
1558 "This is some text with X\" on it",
1559 "This is some text with X' on it",
1560 "This is some text with X: on it",
1562 "This is some text with (X on it",
1563 "This is some text with \rX on it",
1564 "This is some text with ---X on it",
1565 "This is some text with \"X on it",
1566 "This is some text with 'X on it",
1567 "This is some text with :X on it",
1569 /* None of these should cause the URL to be detected */
1570 const char * templates_non_delim[] = {
1571 "This is some text with |X| on it",
1572 "This is some text with *X* on it",
1573 "This is some text with /X/ on it",
1574 "This is some text with +X+ on it",
1575 "This is some text with %X% on it",
1576 "This is some text with #X# on it",
1577 "This is some text with @X@ on it",
1578 "This is some text with \\X\\ on it",
1579 "This is some text with |X on it",
1580 "This is some text with *X on it",
1581 "This is some text with /X on it",
1582 "This is some text with +X on it",
1583 "This is some text with %X on it",
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 on it",
1588 /* All of these cause the URL detection to be extended by one more byte,
1589 thus demonstrating that the tested character is considered as part
1591 const char * templates_xten_delim[] = {
1592 "This is some text with X| on it",
1593 "This is some text with X* on it",
1594 "This is some text with X/ on it",
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",
1603 parent = new_static_wnd(NULL);
1604 hwndRichEdit = new_richedit(parent);
1605 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1606 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1607 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1608 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1609 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1610 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1611 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1612 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1613 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1614 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1615 /* for each url, check the text to see if CFE_LINK effect is present */
1616 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1618 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1619 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1620 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1622 /* Link detection should happen immediately upon WM_SETTEXT */
1623 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1625 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1627 DestroyWindow(hwndRichEdit);
1629 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1630 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1631 hwndRichEdit = new_richedit(parent);
1633 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1638 at_pos = strchr(templates_delim[j], 'X');
1639 at_offset = at_pos - templates_delim[j];
1640 strncpy(buffer, templates_delim[j], at_offset);
1641 buffer[at_offset] = '\0';
1642 strcat(buffer, urls[i].text);
1643 strcat(buffer, templates_delim[j] + at_offset + 1);
1644 end_offset = at_offset + strlen(urls[i].text);
1646 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1647 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1649 /* This assumes no templates start with the URL itself, and that they
1650 have at least two characters before the URL text */
1651 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1652 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1653 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1654 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1655 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1656 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1660 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1661 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1662 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1663 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1667 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1668 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1669 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1670 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1672 if (buffer[end_offset] != '\0')
1674 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1675 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1676 if (buffer[end_offset +1] != '\0')
1678 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1679 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1684 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1689 at_pos = strchr(templates_non_delim[j], 'X');
1690 at_offset = at_pos - templates_non_delim[j];
1691 strncpy(buffer, templates_non_delim[j], at_offset);
1692 buffer[at_offset] = '\0';
1693 strcat(buffer, urls[i].text);
1694 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1695 end_offset = at_offset + strlen(urls[i].text);
1697 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1700 /* This assumes no templates start with the URL itself, and that they
1701 have at least two characters before the URL text */
1702 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1703 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1704 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1705 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1709 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1710 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1711 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1712 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1713 if (buffer[end_offset] != '\0')
1715 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1716 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1717 if (buffer[end_offset +1] != '\0')
1719 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1720 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1725 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1730 at_pos = strchr(templates_xten_delim[j], 'X');
1731 at_offset = at_pos - templates_xten_delim[j];
1732 strncpy(buffer, templates_xten_delim[j], at_offset);
1733 buffer[at_offset] = '\0';
1734 strcat(buffer, urls[i].text);
1735 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1736 end_offset = at_offset + strlen(urls[i].text);
1738 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1741 /* This assumes no templates start with the URL itself, and that they
1742 have at least two characters before the URL text */
1743 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1744 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1745 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1746 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1752 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1753 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1754 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1755 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1756 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1757 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1761 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1762 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1763 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1764 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1765 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1766 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1768 if (buffer[end_offset +1] != '\0')
1770 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1771 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1772 if (buffer[end_offset +2] != '\0')
1774 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1775 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1780 DestroyWindow(hwndRichEdit);
1781 hwndRichEdit = NULL;
1784 /* Test detection of URLs within normal text - WM_CHAR case. */
1785 /* Test only the first two URL examples for brevity */
1786 for (i = 0; i < 2; i++) {
1787 hwndRichEdit = new_richedit(parent);
1789 /* Also for brevity, test only the first three delimiters */
1790 for (j = 0; j < 3; j++) {
1796 at_pos = strchr(templates_delim[j], 'X');
1797 at_offset = at_pos - templates_delim[j];
1798 end_offset = at_offset + strlen(urls[i].text);
1800 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1801 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1802 for (u = 0; templates_delim[j][u]; u++) {
1803 if (templates_delim[j][u] == '\r') {
1804 simulate_typing_characters(hwndRichEdit, "\r");
1805 } else if (templates_delim[j][u] != 'X') {
1806 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1808 for (v = 0; urls[i].text[v]; v++) {
1809 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1813 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1815 /* This assumes no templates start with the URL itself, and that they
1816 have at least two characters before the URL text */
1817 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1818 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1826 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1827 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1828 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1829 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1833 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1834 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1835 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1836 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1838 if (buffer[end_offset] != '\0')
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1842 if (buffer[end_offset +1] != '\0')
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1849 /* The following will insert a paragraph break after the first character
1850 of the URL candidate, thus breaking the URL. It is expected that the
1851 CFE_LINK attribute should break across both pieces of the URL */
1852 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1853 simulate_typing_characters(hwndRichEdit, "\r");
1854 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
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);
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1865 /* end_offset moved because of paragraph break */
1866 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1867 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1868 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1869 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1871 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1872 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1873 if (buffer[end_offset +2] != '\0')
1875 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1876 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1880 /* The following will remove the just-inserted paragraph break, thus
1881 restoring the URL */
1882 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1883 simulate_typing_characters(hwndRichEdit, "\b");
1884 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1886 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1887 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1888 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1889 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1895 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1896 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1897 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1898 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_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 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1905 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1907 if (buffer[end_offset] != '\0')
1909 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1910 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1911 if (buffer[end_offset +1] != '\0')
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1918 DestroyWindow(hwndRichEdit);
1919 hwndRichEdit = NULL;
1922 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1923 /* Test just the first two URL examples for brevity */
1924 for (i = 0; i < 2; i++) {
1927 hwndRichEdit = new_richedit(parent);
1929 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1931 1) Set entire text, a la WM_SETTEXT
1932 2) Set a selection of the text to the URL
1933 3) Set a portion of the text at a time, which eventually results in
1935 All of them should give equivalent results
1938 /* Set entire text in one go, like WM_SETTEXT */
1939 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1944 st.codepage = CP_ACP;
1945 st.flags = ST_DEFAULT;
1947 at_pos = strchr(templates_delim[j], 'X');
1948 at_offset = at_pos - templates_delim[j];
1949 strncpy(buffer, templates_delim[j], at_offset);
1950 buffer[at_offset] = '\0';
1951 strcat(buffer, urls[i].text);
1952 strcat(buffer, templates_delim[j] + at_offset + 1);
1953 end_offset = at_offset + strlen(urls[i].text);
1955 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1956 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1958 /* This assumes no templates start with the URL itself, and that they
1959 have at least two characters before the URL text */
1960 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1961 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1962 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1963 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1969 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1970 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1971 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1972 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1976 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1977 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1978 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1979 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1981 if (buffer[end_offset] != '\0')
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1985 if (buffer[end_offset +1] != '\0')
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1993 /* Set selection with X to the URL */
1994 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1999 at_pos = strchr(templates_delim[j], 'X');
2000 at_offset = at_pos - templates_delim[j];
2001 end_offset = at_offset + strlen(urls[i].text);
2003 st.codepage = CP_ACP;
2004 st.flags = ST_DEFAULT;
2005 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2006 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2007 st.flags = ST_SELECTION;
2008 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2009 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2010 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2012 /* This assumes no templates start with the URL itself, and that they
2013 have at least two characters before the URL text */
2014 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2015 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2016 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2017 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2023 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2024 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2025 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2026 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2030 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2031 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2032 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2033 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2035 if (buffer[end_offset] != '\0')
2037 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2038 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2039 if (buffer[end_offset +1] != '\0')
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2047 /* Set selection with X to the first character of the URL, then the rest */
2048 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2053 at_pos = strchr(templates_delim[j], 'X');
2054 at_offset = at_pos - templates_delim[j];
2055 end_offset = at_offset + strlen(urls[i].text);
2057 strcpy(buffer, "YY");
2058 buffer[0] = urls[i].text[0];
2060 st.codepage = CP_ACP;
2061 st.flags = ST_DEFAULT;
2062 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2063 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2064 st.flags = ST_SELECTION;
2065 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2066 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2067 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2068 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2069 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2071 /* This assumes no templates start with the URL itself, and that they
2072 have at least two characters before the URL text */
2073 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2074 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2075 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2076 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2082 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2083 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2084 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2085 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2089 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2090 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2091 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2092 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2094 if (buffer[end_offset] != '\0')
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2098 if (buffer[end_offset +1] != '\0')
2100 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2101 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2106 DestroyWindow(hwndRichEdit);
2107 hwndRichEdit = NULL;
2110 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2111 /* Test just the first two URL examples for brevity */
2112 for (i = 0; i < 2; i++) {
2113 hwndRichEdit = new_richedit(parent);
2115 /* Set selection with X to the URL */
2116 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2121 at_pos = strchr(templates_delim[j], 'X');
2122 at_offset = at_pos - templates_delim[j];
2123 end_offset = at_offset + strlen(urls[i].text);
2125 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2126 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2127 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2128 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2129 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2131 /* This assumes no templates start with the URL itself, and that they
2132 have at least two characters before the URL text */
2133 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2134 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2137 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2138 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2142 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2143 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2144 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2145 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2149 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2150 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2151 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2152 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2154 if (buffer[end_offset] != '\0')
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2158 if (buffer[end_offset +1] != '\0')
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2166 /* Set selection with X to the first character of the URL, then the rest */
2167 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2172 at_pos = strchr(templates_delim[j], 'X');
2173 at_offset = at_pos - templates_delim[j];
2174 end_offset = at_offset + strlen(urls[i].text);
2176 strcpy(buffer, "YY");
2177 buffer[0] = urls[i].text[0];
2179 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2180 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2181 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2182 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2183 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2184 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2185 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2187 /* This assumes no templates start with the URL itself, and that they
2188 have at least two characters before the URL text */
2189 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2190 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2191 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2192 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2198 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2199 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2200 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2201 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2205 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2206 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2207 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2208 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2210 if (buffer[end_offset] != '\0')
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2214 if (buffer[end_offset +1] != '\0')
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2222 DestroyWindow(hwndRichEdit);
2223 hwndRichEdit = NULL;
2226 DestroyWindow(parent);
2229 static void test_EM_SCROLL(void)
2232 int r; /* return value */
2233 int expr; /* expected return value */
2234 HWND hwndRichEdit = new_richedit(NULL);
2235 int y_before, y_after; /* units of lines of text */
2237 /* test a richedit box containing a single line of text */
2238 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2240 for (i = 0; i < 4; i++) {
2241 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2243 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2244 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2245 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2246 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2247 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2248 "(i == %d)\n", y_after, i);
2252 * test a richedit box that will scroll. There are two general
2253 * cases: the case without any long lines and the case with a long
2256 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2258 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2260 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2261 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2262 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2263 "LONG LINE \nb\nc\nd\ne");
2264 for (j = 0; j < 12; j++) /* reset scroll position to top */
2265 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2267 /* get first visible line */
2268 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2269 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2271 /* get new current first visible line */
2272 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2274 ok(((r & 0xffffff00) == 0x00010000) &&
2275 ((r & 0x000000ff) != 0x00000000),
2276 "EM_SCROLL page down didn't scroll by a small positive number of "
2277 "lines (r == 0x%08x)\n", r);
2278 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2279 "(line %d scrolled to line %d\n", y_before, y_after);
2283 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2284 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2285 ok(((r & 0xffffff00) == 0x0001ff00),
2286 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2287 "(r == 0x%08x)\n", r);
2288 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2289 "%d scrolled to line %d\n", y_before, y_after);
2293 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2295 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2297 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2298 "(r == 0x%08x)\n", r);
2299 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2300 "1 line (%d scrolled to %d)\n", y_before, y_after);
2304 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2306 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2308 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2309 "(r == 0x%08x)\n", r);
2310 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2311 "line (%d scrolled to %d)\n", y_before, y_after);
2315 r = SendMessage(hwndRichEdit, EM_SCROLL,
2316 SB_LINEUP, 0); /* lineup beyond top */
2318 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2321 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2322 ok(y_before == y_after,
2323 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2327 r = SendMessage(hwndRichEdit, EM_SCROLL,
2328 SB_PAGEUP, 0);/*page up beyond top */
2330 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2333 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2334 ok(y_before == y_after,
2335 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2337 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2338 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2339 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2340 r = SendMessage(hwndRichEdit, EM_SCROLL,
2341 SB_PAGEDOWN, 0); /* page down beyond bot */
2342 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2345 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2346 ok(y_before == y_after,
2347 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2350 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2351 SendMessage(hwndRichEdit, EM_SCROLL,
2352 SB_LINEDOWN, 0); /* line down beyond bot */
2353 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2356 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2357 ok(y_before == y_after,
2358 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2361 DestroyWindow(hwndRichEdit);
2364 unsigned int recursionLevel = 0;
2365 unsigned int WM_SIZE_recursionLevel = 0;
2366 BOOL bailedOutOfRecursion = FALSE;
2367 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2369 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2373 if (bailedOutOfRecursion) return 0;
2374 if (recursionLevel >= 32) {
2375 bailedOutOfRecursion = TRUE;
2382 WM_SIZE_recursionLevel++;
2383 r = richeditProc(hwnd, message, wParam, lParam);
2384 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2385 ShowScrollBar(hwnd, SB_VERT, TRUE);
2386 WM_SIZE_recursionLevel--;
2389 r = richeditProc(hwnd, message, wParam, lParam);
2396 static void test_scrollbar_visibility(void)
2399 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2404 /* These tests show that richedit should temporarily refrain from automatically
2405 hiding or showing its scrollbars (vertical at least) when an explicit request
2406 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2407 Some applications depend on forced showing (when otherwise richedit would
2408 hide the vertical scrollbar) and are thrown on an endless recursive loop
2409 if richedit auto-hides the scrollbar again. Apparently they never heard of
2410 the ES_DISABLENOSCROLL style... */
2412 hwndRichEdit = new_richedit(NULL);
2414 /* Test default scrollbar visibility behavior */
2415 memset(&si, 0, sizeof(si));
2416 si.cbSize = sizeof(si);
2417 si.fMask = SIF_PAGE | SIF_RANGE;
2418 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2419 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2420 "Vertical scrollbar is visible, should be invisible.\n");
2421 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2422 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2423 si.nPage, si.nMin, si.nMax);
2425 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2426 memset(&si, 0, sizeof(si));
2427 si.cbSize = sizeof(si);
2428 si.fMask = SIF_PAGE | SIF_RANGE;
2429 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2430 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2431 "Vertical scrollbar is visible, should be invisible.\n");
2432 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2433 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2434 si.nPage, si.nMin, si.nMax);
2436 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2437 memset(&si, 0, sizeof(si));
2438 si.cbSize = sizeof(si);
2439 si.fMask = SIF_PAGE | SIF_RANGE;
2440 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2441 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2442 "Vertical scrollbar is invisible, should be visible.\n");
2443 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2444 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2445 si.nPage, si.nMin, si.nMax);
2447 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2448 even though it hides the scrollbar */
2449 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2450 memset(&si, 0, sizeof(si));
2451 si.cbSize = sizeof(si);
2452 si.fMask = SIF_PAGE | SIF_RANGE;
2453 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2454 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2455 "Vertical scrollbar is visible, should be invisible.\n");
2456 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2457 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2458 si.nPage, si.nMin, si.nMax);
2460 /* Setting non-scrolling text again does *not* reset scrollbar range */
2461 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2462 memset(&si, 0, sizeof(si));
2463 si.cbSize = sizeof(si);
2464 si.fMask = SIF_PAGE | SIF_RANGE;
2465 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2466 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2467 "Vertical scrollbar is visible, should be invisible.\n");
2468 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2469 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2470 si.nPage, si.nMin, si.nMax);
2472 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2473 memset(&si, 0, sizeof(si));
2474 si.cbSize = sizeof(si);
2475 si.fMask = SIF_PAGE | SIF_RANGE;
2476 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2477 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2478 "Vertical scrollbar is visible, should be invisible.\n");
2479 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2480 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2481 si.nPage, si.nMin, si.nMax);
2483 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2484 memset(&si, 0, sizeof(si));
2485 si.cbSize = sizeof(si);
2486 si.fMask = SIF_PAGE | SIF_RANGE;
2487 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2488 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2489 "Vertical scrollbar is visible, should be invisible.\n");
2490 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2491 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2492 si.nPage, si.nMin, si.nMax);
2494 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2495 memset(&si, 0, sizeof(si));
2496 si.cbSize = sizeof(si);
2497 si.fMask = SIF_PAGE | SIF_RANGE;
2498 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2499 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2500 "Vertical scrollbar is visible, should be invisible.\n");
2501 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2502 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2503 si.nPage, si.nMin, si.nMax);
2505 DestroyWindow(hwndRichEdit);
2507 /* Test again, with ES_DISABLENOSCROLL style */
2508 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2510 /* Test default scrollbar visibility behavior */
2511 memset(&si, 0, sizeof(si));
2512 si.cbSize = sizeof(si);
2513 si.fMask = SIF_PAGE | SIF_RANGE;
2514 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2515 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2516 "Vertical scrollbar is invisible, should be visible.\n");
2517 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2518 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2519 si.nPage, si.nMin, si.nMax);
2521 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2522 memset(&si, 0, sizeof(si));
2523 si.cbSize = sizeof(si);
2524 si.fMask = SIF_PAGE | SIF_RANGE;
2525 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2526 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2527 "Vertical scrollbar is invisible, should be visible.\n");
2528 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2529 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2530 si.nPage, si.nMin, si.nMax);
2532 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2533 memset(&si, 0, sizeof(si));
2534 si.cbSize = sizeof(si);
2535 si.fMask = SIF_PAGE | SIF_RANGE;
2536 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2537 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2538 "Vertical scrollbar is invisible, should be visible.\n");
2539 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2540 "reported page/range is %d (%d..%d)\n",
2541 si.nPage, si.nMin, si.nMax);
2543 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2544 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2545 memset(&si, 0, sizeof(si));
2546 si.cbSize = sizeof(si);
2547 si.fMask = SIF_PAGE | SIF_RANGE;
2548 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2549 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2550 "Vertical scrollbar is invisible, should be visible.\n");
2551 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2552 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2553 si.nPage, si.nMin, si.nMax);
2555 /* Setting non-scrolling text again does *not* reset scrollbar range */
2556 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2557 memset(&si, 0, sizeof(si));
2558 si.cbSize = sizeof(si);
2559 si.fMask = SIF_PAGE | SIF_RANGE;
2560 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2561 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2562 "Vertical scrollbar is invisible, should be visible.\n");
2563 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2564 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2565 si.nPage, si.nMin, si.nMax);
2567 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2568 memset(&si, 0, sizeof(si));
2569 si.cbSize = sizeof(si);
2570 si.fMask = SIF_PAGE | SIF_RANGE;
2571 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2572 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2573 "Vertical scrollbar is invisible, should be visible.\n");
2574 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2575 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2576 si.nPage, si.nMin, si.nMax);
2578 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2579 memset(&si, 0, sizeof(si));
2580 si.cbSize = sizeof(si);
2581 si.fMask = SIF_PAGE | SIF_RANGE;
2582 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2583 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2584 "Vertical scrollbar is invisible, should be visible.\n");
2585 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2586 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2587 si.nPage, si.nMin, si.nMax);
2589 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2590 memset(&si, 0, sizeof(si));
2591 si.cbSize = sizeof(si);
2592 si.fMask = SIF_PAGE | SIF_RANGE;
2593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595 "Vertical scrollbar is invisible, should be visible.\n");
2596 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2597 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2598 si.nPage, si.nMin, si.nMax);
2600 DestroyWindow(hwndRichEdit);
2602 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2603 hwndRichEdit = new_richedit(NULL);
2605 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2606 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
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");
2614 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2615 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2616 si.nPage, si.nMin, si.nMax);
2619 /* Ditto, see above */
2620 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2621 memset(&si, 0, sizeof(si));
2622 si.cbSize = sizeof(si);
2623 si.fMask = SIF_PAGE | SIF_RANGE;
2624 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2625 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2626 "Vertical scrollbar is invisible, should be visible.\n");
2628 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2629 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2630 si.nPage, si.nMin, si.nMax);
2633 /* Ditto, see above */
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2635 memset(&si, 0, sizeof(si));
2636 si.cbSize = sizeof(si);
2637 si.fMask = SIF_PAGE | SIF_RANGE;
2638 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2640 "Vertical scrollbar is invisible, should be visible.\n");
2642 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2643 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2644 si.nPage, si.nMin, si.nMax);
2647 /* Ditto, see above */
2648 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2649 memset(&si, 0, sizeof(si));
2650 si.cbSize = sizeof(si);
2651 si.fMask = SIF_PAGE | SIF_RANGE;
2652 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2653 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2654 "Vertical scrollbar is invisible, should be visible.\n");
2656 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2657 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2658 si.nPage, si.nMin, si.nMax);
2661 /* Ditto, see above */
2662 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2663 memset(&si, 0, sizeof(si));
2664 si.cbSize = sizeof(si);
2665 si.fMask = SIF_PAGE | SIF_RANGE;
2666 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2667 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2668 "Vertical scrollbar is invisible, should be visible.\n");
2670 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2671 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2672 si.nPage, si.nMin, si.nMax);
2675 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2676 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2677 memset(&si, 0, sizeof(si));
2678 si.cbSize = sizeof(si);
2679 si.fMask = SIF_PAGE | SIF_RANGE;
2680 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2681 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2682 "Vertical scrollbar is visible, should be invisible.\n");
2683 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2684 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2685 si.nPage, si.nMin, si.nMax);
2687 DestroyWindow(hwndRichEdit);
2689 hwndRichEdit = new_richedit(NULL);
2691 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2692 memset(&si, 0, sizeof(si));
2693 si.cbSize = sizeof(si);
2694 si.fMask = SIF_PAGE | SIF_RANGE;
2695 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2697 "Vertical scrollbar is visible, should be invisible.\n");
2698 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2699 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2700 si.nPage, si.nMin, si.nMax);
2702 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2703 memset(&si, 0, sizeof(si));
2704 si.cbSize = sizeof(si);
2705 si.fMask = SIF_PAGE | SIF_RANGE;
2706 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2707 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2708 "Vertical scrollbar is visible, should be invisible.\n");
2709 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2710 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2711 si.nPage, si.nMin, si.nMax);
2713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2719 "Vertical scrollbar is visible, should be invisible.\n");
2720 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2721 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2722 si.nPage, si.nMin, si.nMax);
2724 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2725 memset(&si, 0, sizeof(si));
2726 si.cbSize = sizeof(si);
2727 si.fMask = SIF_PAGE | SIF_RANGE;
2728 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2729 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2730 "Vertical scrollbar is visible, should be invisible.\n");
2731 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2732 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2733 si.nPage, si.nMin, si.nMax);
2735 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2736 memset(&si, 0, sizeof(si));
2737 si.cbSize = sizeof(si);
2738 si.fMask = SIF_PAGE | SIF_RANGE;
2739 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2740 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2741 "Vertical scrollbar is invisible, should be visible.\n");
2742 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2743 "reported page/range is %d (%d..%d)\n",
2744 si.nPage, si.nMin, si.nMax);
2746 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2747 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2748 memset(&si, 0, sizeof(si));
2749 si.cbSize = sizeof(si);
2750 si.fMask = SIF_PAGE | SIF_RANGE;
2751 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2752 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2753 "Vertical scrollbar is visible, should be invisible.\n");
2754 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2755 "reported page/range is %d (%d..%d)\n",
2756 si.nPage, si.nMin, si.nMax);
2758 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2759 memset(&si, 0, sizeof(si));
2760 si.cbSize = sizeof(si);
2761 si.fMask = SIF_PAGE | SIF_RANGE;
2762 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2763 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2764 "Vertical scrollbar is visible, should be invisible.\n");
2765 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2766 "reported page/range is %d (%d..%d)\n",
2767 si.nPage, si.nMin, si.nMax);
2769 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2770 EM_SCROLL will make visible any forcefully invisible scrollbar */
2771 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2772 memset(&si, 0, sizeof(si));
2773 si.cbSize = sizeof(si);
2774 si.fMask = SIF_PAGE | SIF_RANGE;
2775 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2776 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2777 "Vertical scrollbar is invisible, should be visible.\n");
2778 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2779 "reported page/range is %d (%d..%d)\n",
2780 si.nPage, si.nMin, si.nMax);
2782 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2783 memset(&si, 0, sizeof(si));
2784 si.cbSize = sizeof(si);
2785 si.fMask = SIF_PAGE | SIF_RANGE;
2786 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2787 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2788 "Vertical scrollbar is visible, should be invisible.\n");
2789 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2790 "reported page/range is %d (%d..%d)\n",
2791 si.nPage, si.nMin, si.nMax);
2793 /* Again, EM_SCROLL, with SB_LINEUP */
2794 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2795 memset(&si, 0, sizeof(si));
2796 si.cbSize = sizeof(si);
2797 si.fMask = SIF_PAGE | SIF_RANGE;
2798 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2799 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2800 "Vertical scrollbar is invisible, should be visible.\n");
2801 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2802 "reported page/range is %d (%d..%d)\n",
2803 si.nPage, si.nMin, si.nMax);
2805 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2806 memset(&si, 0, sizeof(si));
2807 si.cbSize = sizeof(si);
2808 si.fMask = SIF_PAGE | SIF_RANGE;
2809 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2810 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2811 "Vertical scrollbar is visible, should be invisible.\n");
2812 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2813 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2814 si.nPage, si.nMin, si.nMax);
2816 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2817 memset(&si, 0, sizeof(si));
2818 si.cbSize = sizeof(si);
2819 si.fMask = SIF_PAGE | SIF_RANGE;
2820 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2821 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2822 "Vertical scrollbar is invisible, should be visible.\n");
2823 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2824 "reported page/range is %d (%d..%d)\n",
2825 si.nPage, si.nMin, si.nMax);
2827 DestroyWindow(hwndRichEdit);
2830 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2831 hwndRichEdit = new_richedit(NULL);
2833 #define ENABLE_WS_VSCROLL(hwnd) \
2834 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2835 #define DISABLE_WS_VSCROLL(hwnd) \
2836 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2838 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2839 ENABLE_WS_VSCROLL(hwndRichEdit);
2840 memset(&si, 0, sizeof(si));
2841 si.cbSize = sizeof(si);
2842 si.fMask = SIF_PAGE | SIF_RANGE;
2843 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2844 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2845 "Vertical scrollbar is invisible, should be visible.\n");
2846 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2847 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2848 si.nPage, si.nMin, si.nMax);
2850 /* Ditto, see above */
2851 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2852 memset(&si, 0, sizeof(si));
2853 si.cbSize = sizeof(si);
2854 si.fMask = SIF_PAGE | SIF_RANGE;
2855 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2856 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2857 "Vertical scrollbar is invisible, should be visible.\n");
2858 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2859 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2860 si.nPage, si.nMin, si.nMax);
2862 /* Ditto, see above */
2863 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2864 memset(&si, 0, sizeof(si));
2865 si.cbSize = sizeof(si);
2866 si.fMask = SIF_PAGE | SIF_RANGE;
2867 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2868 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2869 "Vertical scrollbar is invisible, should be visible.\n");
2870 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2871 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2872 si.nPage, si.nMin, si.nMax);
2874 /* Ditto, see above */
2875 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2881 "Vertical scrollbar is invisible, should be visible.\n");
2882 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884 si.nPage, si.nMin, si.nMax);
2886 /* Ditto, see above */
2887 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2888 memset(&si, 0, sizeof(si));
2889 si.cbSize = sizeof(si);
2890 si.fMask = SIF_PAGE | SIF_RANGE;
2891 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2892 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2893 "Vertical scrollbar is invisible, should be visible.\n");
2894 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2895 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2896 si.nPage, si.nMin, si.nMax);
2898 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2899 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2900 memset(&si, 0, sizeof(si));
2901 si.cbSize = sizeof(si);
2902 si.fMask = SIF_PAGE | SIF_RANGE;
2903 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2904 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2905 "Vertical scrollbar is visible, should be invisible.\n");
2906 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2907 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2908 si.nPage, si.nMin, si.nMax);
2910 DestroyWindow(hwndRichEdit);
2912 hwndRichEdit = new_richedit(NULL);
2914 DISABLE_WS_VSCROLL(hwndRichEdit);
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 visible, should be invisible.\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 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2926 memset(&si, 0, sizeof(si));
2927 si.cbSize = sizeof(si);
2928 si.fMask = SIF_PAGE | SIF_RANGE;
2929 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2930 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2931 "Vertical scrollbar is visible, should be invisible.\n");
2932 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2933 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2934 si.nPage, si.nMin, si.nMax);
2936 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2937 memset(&si, 0, sizeof(si));
2938 si.cbSize = sizeof(si);
2939 si.fMask = SIF_PAGE | SIF_RANGE;
2940 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2941 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2942 "Vertical scrollbar is visible, should be invisible.\n");
2943 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2944 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2945 si.nPage, si.nMin, si.nMax);
2947 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2948 memset(&si, 0, sizeof(si));
2949 si.cbSize = sizeof(si);
2950 si.fMask = SIF_PAGE | SIF_RANGE;
2951 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2952 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2953 "Vertical scrollbar is visible, should be invisible.\n");
2954 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2955 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2956 si.nPage, si.nMin, si.nMax);
2958 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2959 memset(&si, 0, sizeof(si));
2960 si.cbSize = sizeof(si);
2961 si.fMask = SIF_PAGE | SIF_RANGE;
2962 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2963 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2964 "Vertical scrollbar is invisible, should be visible.\n");
2965 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2966 "reported page/range is %d (%d..%d)\n",
2967 si.nPage, si.nMin, si.nMax);
2969 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2970 DISABLE_WS_VSCROLL(hwndRichEdit);
2971 memset(&si, 0, sizeof(si));
2972 si.cbSize = sizeof(si);
2973 si.fMask = SIF_PAGE | SIF_RANGE;
2974 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2975 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2976 "Vertical scrollbar is visible, should be invisible.\n");
2977 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2978 "reported page/range is %d (%d..%d)\n",
2979 si.nPage, si.nMin, si.nMax);
2981 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2982 memset(&si, 0, sizeof(si));
2983 si.cbSize = sizeof(si);
2984 si.fMask = SIF_PAGE | SIF_RANGE;
2985 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2986 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2987 "Vertical scrollbar is visible, should be invisible.\n");
2988 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2989 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2990 si.nPage, si.nMin, si.nMax);
2992 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2993 memset(&si, 0, sizeof(si));
2994 si.cbSize = sizeof(si);
2995 si.fMask = SIF_PAGE | SIF_RANGE;
2996 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2997 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2998 "Vertical scrollbar is invisible, should be visible.\n");
2999 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3000 "reported page/range is %d (%d..%d)\n",
3001 si.nPage, si.nMin, si.nMax);
3003 DISABLE_WS_VSCROLL(hwndRichEdit);
3004 memset(&si, 0, sizeof(si));
3005 si.cbSize = sizeof(si);
3006 si.fMask = SIF_PAGE | SIF_RANGE;
3007 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3008 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3009 "Vertical scrollbar is visible, should be invisible.\n");
3010 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3011 "reported page/range is %d (%d..%d)\n",
3012 si.nPage, si.nMin, si.nMax);
3014 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3015 EM_SCROLL will make visible any forcefully invisible scrollbar */
3016 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3017 memset(&si, 0, sizeof(si));
3018 si.cbSize = sizeof(si);
3019 si.fMask = SIF_PAGE | SIF_RANGE;
3020 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3021 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3022 "Vertical scrollbar is invisible, should be visible.\n");
3023 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3024 "reported page/range is %d (%d..%d)\n",
3025 si.nPage, si.nMin, si.nMax);
3027 DISABLE_WS_VSCROLL(hwndRichEdit);
3028 memset(&si, 0, sizeof(si));
3029 si.cbSize = sizeof(si);
3030 si.fMask = SIF_PAGE | SIF_RANGE;
3031 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3032 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3033 "Vertical scrollbar is visible, should be invisible.\n");
3034 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3035 "reported page/range is %d (%d..%d)\n",
3036 si.nPage, si.nMin, si.nMax);
3038 /* Again, EM_SCROLL, with SB_LINEUP */
3039 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3040 memset(&si, 0, sizeof(si));
3041 si.cbSize = sizeof(si);
3042 si.fMask = SIF_PAGE | SIF_RANGE;
3043 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3044 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3045 "Vertical scrollbar is invisible, should be visible.\n");
3046 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3047 "reported page/range is %d (%d..%d)\n",
3048 si.nPage, si.nMin, si.nMax);
3050 DestroyWindow(hwndRichEdit);
3052 /* This window proc models what is going on with Corman Lisp 3.0.
3053 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3054 force the scrollbar into visibility. Recursion should NOT happen
3055 as a result of this action.
3057 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3059 richeditProc = cls.lpfnWndProc;
3060 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3061 cls.lpszClassName = "RicheditStupidOverride";
3062 if(!RegisterClassA(&cls)) assert(0);
3065 WM_SIZE_recursionLevel = 0;
3066 bailedOutOfRecursion = FALSE;
3067 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3068 ok(!bailedOutOfRecursion,
3069 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3072 WM_SIZE_recursionLevel = 0;
3073 bailedOutOfRecursion = FALSE;
3074 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3075 ok(!bailedOutOfRecursion,
3076 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3078 /* Unblock window in order to process WM_DESTROY */
3080 bailedOutOfRecursion = FALSE;
3081 WM_SIZE_recursionLevel = 0;
3082 DestroyWindow(hwndRichEdit);
3086 static void test_EM_SETUNDOLIMIT(void)
3088 /* cases we test for:
3089 * default behaviour - limiting at 100 undo's
3090 * undo disabled - setting a limit of 0
3091 * undo limited - undo limit set to some to some number, like 2
3092 * bad input - sending a negative number should default to 100 undo's */
3094 HWND hwndRichEdit = new_richedit(NULL);
3099 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3102 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3103 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3104 also, multiple pastes don't combine like WM_CHAR would */
3105 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3107 /* first case - check the default */
3108 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3109 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3110 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3111 for (i=0; i<100; i++) /* Undo 100 of them */
3112 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3113 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3114 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3116 /* second case - cannot undo */
3117 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3118 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3119 SendMessage(hwndRichEdit,
3120 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3121 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3122 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3124 /* third case - set it to an arbitrary number */
3125 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3126 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3127 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3128 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3129 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3130 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3131 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3132 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3133 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3134 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3135 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3136 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3137 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3138 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3140 /* fourth case - setting negative numbers should default to 100 undos */
3141 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3142 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3144 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3146 DestroyWindow(hwndRichEdit);
3149 static void test_ES_PASSWORD(void)
3151 /* This isn't hugely testable, so we're just going to run it through its paces */
3153 HWND hwndRichEdit = new_richedit(NULL);
3156 /* First, check the default of a regular control */
3157 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3159 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3161 /* Now, set it to something normal */
3162 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3163 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3165 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3167 /* Now, set it to something odd */
3168 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3169 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3171 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3172 DestroyWindow(hwndRichEdit);
3175 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3180 char** str = (char**)dwCookie;
3183 memcpy(*str, pbBuff, *pcb);
3189 static void test_WM_SETTEXT()
3191 HWND hwndRichEdit = new_richedit(NULL);
3192 const char * TestItem1 = "TestSomeText";
3193 const char * TestItem2 = "TestSomeText\r";
3194 const char * TestItem2_after = "TestSomeText\r\n";
3195 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3196 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3197 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3198 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3199 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3200 const char * TestItem5_after = "TestSomeText TestSomeText";
3201 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3202 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3203 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3204 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3206 char buf[1024] = {0};
3211 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3212 any solitary \r to be converted to \r\n on return. Properly paired
3213 \r\n are not affected. It also shows that the special sequence \r\r\n
3214 gets converted to a single space.
3217 #define TEST_SETTEXT(a, b) \
3218 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3219 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3220 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3221 ok (result == lstrlen(buf), \
3222 "WM_GETTEXT returned %ld instead of expected %u\n", \
3223 result, lstrlen(buf)); \
3224 result = strcmp(b, buf); \
3226 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3228 TEST_SETTEXT(TestItem1, TestItem1)
3229 TEST_SETTEXT(TestItem2, TestItem2_after)
3230 TEST_SETTEXT(TestItem3, TestItem3_after)
3231 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3232 TEST_SETTEXT(TestItem4, TestItem4_after)
3233 TEST_SETTEXT(TestItem5, TestItem5_after)
3234 TEST_SETTEXT(TestItem6, TestItem6_after)
3235 TEST_SETTEXT(TestItem7, TestItem7_after)
3237 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3238 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3240 es.dwCookie = (DWORD_PTR)&p;
3242 es.pfnCallback = test_WM_SETTEXT_esCallback;
3243 memset(buf, 0, sizeof(buf));
3244 SendMessage(hwndRichEdit, EM_STREAMOUT,
3245 (WPARAM)(SF_RTF), (LPARAM)&es);
3246 trace("EM_STREAMOUT produced: \n%s\n", buf);
3247 TEST_SETTEXT(buf, TestItem1)
3250 DestroyWindow(hwndRichEdit);
3253 static void test_EM_STREAMOUT(void)
3255 HWND hwndRichEdit = new_richedit(NULL);
3258 char buf[1024] = {0};
3261 const char * TestItem1 = "TestSomeText";
3262 const char * TestItem2 = "TestSomeText\r";
3263 const char * TestItem3 = "TestSomeText\r\n";
3265 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3267 es.dwCookie = (DWORD_PTR)&p;
3269 es.pfnCallback = test_WM_SETTEXT_esCallback;
3270 memset(buf, 0, sizeof(buf));
3271 SendMessage(hwndRichEdit, EM_STREAMOUT,
3272 (WPARAM)(SF_TEXT), (LPARAM)&es);
3274 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3275 ok(strcmp(buf, TestItem1) == 0,
3276 "streamed text different, got %s\n", buf);
3278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3280 es.dwCookie = (DWORD_PTR)&p;
3282 es.pfnCallback = test_WM_SETTEXT_esCallback;
3283 memset(buf, 0, sizeof(buf));
3284 SendMessage(hwndRichEdit, EM_STREAMOUT,
3285 (WPARAM)(SF_TEXT), (LPARAM)&es);
3287 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3288 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3289 ok(strcmp(buf, TestItem3) == 0,
3290 "streamed text different from, got %s\n", buf);
3291 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3293 es.dwCookie = (DWORD_PTR)&p;
3295 es.pfnCallback = test_WM_SETTEXT_esCallback;
3296 memset(buf, 0, sizeof(buf));
3297 SendMessage(hwndRichEdit, EM_STREAMOUT,
3298 (WPARAM)(SF_TEXT), (LPARAM)&es);
3300 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3301 ok(strcmp(buf, TestItem3) == 0,
3302 "streamed text different, got %s\n", buf);
3304 DestroyWindow(hwndRichEdit);
3307 static void test_EM_SETTEXTEX(void)
3309 HWND hwndRichEdit = new_richedit(NULL);
3312 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3314 'T', 'e', 'x', 't', 0};
3315 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3321 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3322 '\r','t','S','o','m','e','T','e','x','t',0};
3323 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3327 const char * TestItem2_after = "TestSomeText\r\n";
3328 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3331 '\r','\n','\r','\n', 0};
3332 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3336 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3340 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3343 '\r','\r','\n','\r',
3345 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3349 #define MAX_BUF_LEN 1024
3350 WCHAR buf[MAX_BUF_LEN];
3351 char bufACP[MAX_BUF_LEN];
3357 setText.codepage = 1200; /* no constant for unicode */
3358 getText.codepage = 1200; /* no constant for unicode */
3359 getText.cb = MAX_BUF_LEN;
3360 getText.flags = GT_DEFAULT;
3361 getText.lpDefaultChar = NULL;
3362 getText.lpUsedDefChar = NULL;
3365 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3366 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3367 ok(lstrcmpW(buf, TestItem1) == 0,
3368 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3370 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3371 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3373 setText.codepage = 1200; /* no constant for unicode */
3374 getText.codepage = 1200; /* no constant for unicode */
3375 getText.cb = MAX_BUF_LEN;
3376 getText.flags = GT_DEFAULT;
3377 getText.lpDefaultChar = NULL;
3378 getText.lpUsedDefChar = NULL;
3380 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3381 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3382 ok(lstrcmpW(buf, TestItem2) == 0,
3383 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3385 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3386 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3387 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3388 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3390 /* Baseline test for just-enough buffer space for string */
3391 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3392 getText.codepage = 1200; /* no constant for unicode */
3393 getText.flags = GT_DEFAULT;
3394 getText.lpDefaultChar = NULL;
3395 getText.lpUsedDefChar = NULL;
3396 memset(buf, 0, MAX_BUF_LEN);
3397 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3398 ok(lstrcmpW(buf, TestItem2) == 0,
3399 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3401 /* When there is enough space for one character, but not both, of the CRLF
3402 pair at the end of the string, the CR is not copied at all. That is,
3403 the caller must not see CRLF pairs truncated to CR at the end of the
3406 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3407 getText.codepage = 1200; /* no constant for unicode */
3408 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3409 getText.lpDefaultChar = NULL;
3410 getText.lpUsedDefChar = NULL;
3411 memset(buf, 0, MAX_BUF_LEN);
3412 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3413 ok(lstrcmpW(buf, TestItem1) == 0,
3414 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3417 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3418 setText.codepage = 1200; /* no constant for unicode */
3419 getText.codepage = 1200; /* no constant for unicode */
3420 getText.cb = MAX_BUF_LEN;
3421 getText.flags = GT_DEFAULT;
3422 getText.lpDefaultChar = NULL;
3423 getText.lpUsedDefChar = NULL;
3425 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3426 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3427 ok(lstrcmpW(buf, TestItem3_after) == 0,
3428 "EM_SETTEXTEX did not convert properly\n");
3430 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3431 setText.codepage = 1200; /* no constant for unicode */
3432 getText.codepage = 1200; /* no constant for unicode */
3433 getText.cb = MAX_BUF_LEN;
3434 getText.flags = GT_DEFAULT;
3435 getText.lpDefaultChar = NULL;
3436 getText.lpUsedDefChar = NULL;
3438 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3439 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3440 ok(lstrcmpW(buf, TestItem3_after) == 0,
3441 "EM_SETTEXTEX did not convert properly\n");
3443 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3444 setText.codepage = 1200; /* no constant for unicode */
3445 getText.codepage = 1200; /* no constant for unicode */
3446 getText.cb = MAX_BUF_LEN;
3447 getText.flags = GT_DEFAULT;
3448 getText.lpDefaultChar = NULL;
3449 getText.lpUsedDefChar = NULL;
3451 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3452 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3453 ok(lstrcmpW(buf, TestItem4_after) == 0,
3454 "EM_SETTEXTEX did not convert properly\n");
3456 /* !ST_SELECTION && Unicode && !\rtf */
3457 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3458 (WPARAM)&setText, (LPARAM) NULL);
3459 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3462 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3463 ok(lstrlenW(buf) == 0,
3464 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3466 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3468 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3469 /* select some text */
3472 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3473 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3474 setText.flags = ST_SELECTION;
3475 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3476 (WPARAM)&setText, (LPARAM) NULL);
3478 "EM_SETTEXTEX with NULL lParam to replace selection"
3479 " with no text should return 0. Got %i\n",
3482 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3484 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3485 /* select some text */
3488 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3489 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3490 setText.flags = ST_SELECTION;
3491 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3492 (WPARAM)&setText, (LPARAM) TestItem1);
3494 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3495 ok(result == lstrlenW(TestItem1),
3496 "EM_SETTEXTEX with NULL lParam to replace selection"
3497 " with no text should return 0. Got %i\n",
3499 ok(lstrlenW(buf) == 22,
3500 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3503 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3506 es.dwCookie = (DWORD_PTR)&p;
3508 es.pfnCallback = test_WM_SETTEXT_esCallback;
3509 memset(buf, 0, sizeof(buf));
3510 SendMessage(hwndRichEdit, EM_STREAMOUT,
3511 (WPARAM)(SF_RTF), (LPARAM)&es);
3512 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3514 /* !ST_SELECTION && !Unicode && \rtf */
3515 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3516 getText.codepage = 1200; /* no constant for unicode */
3517 getText.cb = MAX_BUF_LEN;
3518 getText.flags = GT_DEFAULT;
3519 getText.lpDefaultChar = NULL;
3520 getText.lpUsedDefChar = NULL;
3523 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3524 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3525 ok(lstrcmpW(buf, TestItem1) == 0,
3526 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3528 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3531 es.dwCookie = (DWORD_PTR)&p;
3533 es.pfnCallback = test_WM_SETTEXT_esCallback;
3534 memset(buf, 0, sizeof(buf));
3535 SendMessage(hwndRichEdit, EM_STREAMOUT,
3536 (WPARAM)(SF_RTF), (LPARAM)&es);
3537 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3539 /* select some text */
3542 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3544 /* ST_SELECTION && !Unicode && \rtf */
3545 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3546 getText.codepage = 1200; /* no constant for unicode */
3547 getText.cb = MAX_BUF_LEN;
3548 getText.flags = GT_DEFAULT;
3549 getText.lpDefaultChar = NULL;
3550 getText.lpUsedDefChar = NULL;
3552 setText.flags = ST_SELECTION;
3553 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3554 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3555 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3557 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3558 setText.codepage = 1200; /* no constant for unicode */
3559 getText.codepage = CP_ACP;
3560 getText.cb = MAX_BUF_LEN;
3563 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3564 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3566 /* select some text */
3569 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3571 /* ST_SELECTION && !Unicode && !\rtf */
3572 setText.codepage = CP_ACP;
3573 getText.codepage = 1200; /* no constant for unicode */
3574 getText.cb = MAX_BUF_LEN;
3575 getText.flags = GT_DEFAULT;
3576 getText.lpDefaultChar = NULL;
3577 getText.lpUsedDefChar = NULL;
3579 setText.flags = ST_SELECTION;
3580 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3581 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3582 ok(lstrcmpW(buf, TestItem1alt) == 0,
3583 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3584 " using ST_SELECTION and non-Unicode\n");
3586 /* Test setting text using rich text format */
3588 setText.codepage = CP_ACP;
3589 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3590 getText.codepage = CP_ACP;
3591 getText.cb = MAX_BUF_LEN;
3592 getText.flags = GT_DEFAULT;
3593 getText.lpDefaultChar = NULL;
3594 getText.lpUsedDefChar = NULL;
3595 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3596 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3599 setText.codepage = CP_ACP;
3600 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3601 getText.codepage = CP_ACP;
3602 getText.cb = MAX_BUF_LEN;
3603 getText.flags = GT_DEFAULT;
3604 getText.lpDefaultChar = NULL;
3605 getText.lpUsedDefChar = NULL;
3606 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3607 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3609 DestroyWindow(hwndRichEdit);
3612 static void test_EM_LIMITTEXT(void)
3616 HWND hwndRichEdit = new_richedit(NULL);
3618 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3619 * about setting the length to -1 for multiline edit controls doesn't happen.
3622 /* Don't check default gettextlimit case. That's done in other tests */
3624 /* Set textlimit to 100 */
3625 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3626 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3628 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3630 /* Set textlimit to 0 */
3631 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3632 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3634 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3636 /* Set textlimit to -1 */
3637 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3638 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3640 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3642 /* Set textlimit to -2 */
3643 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3644 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3646 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3648 DestroyWindow (hwndRichEdit);
3652 static void test_EM_EXLIMITTEXT(void)
3654 int i, selBegin, selEnd, len1, len2;
3656 char text[1024 + 1];
3657 char buffer[1024 + 1];
3658 int textlimit = 0; /* multiple of 100 */
3659 HWND hwndRichEdit = new_richedit(NULL);
3661 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3662 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3665 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3666 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3668 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3671 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3672 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3674 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3676 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3677 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3678 /* default for WParam = 0 */
3679 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3681 textlimit = sizeof(text)-1;
3682 memset(text, 'W', textlimit);
3683 text[sizeof(text)-1] = 0;
3684 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3685 /* maxed out text */
3686 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3688 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3689 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3690 len1 = selEnd - selBegin;
3692 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3693 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3694 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3695 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3696 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3697 len2 = selEnd - selBegin;
3700 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3703 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3704 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3705 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3706 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3707 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3708 len1 = selEnd - selBegin;
3711 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3714 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3715 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3716 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3717 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3718 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3719 len2 = selEnd - selBegin;
3722 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3725 /* set text up to the limit, select all the text, then add a char */
3727 memset(text, 'W', textlimit);
3728 text[textlimit] = 0;
3729 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3730 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3731 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3732 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3733 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3734 result = strcmp(buffer, "A");
3735 ok(0 == result, "got string = \"%s\"\n", buffer);
3737 /* WM_SETTEXT not limited */
3739 memset(text, 'W', textlimit);
3740 text[textlimit] = 0;
3741 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3742 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3743 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3745 ok(10 == i, "expected 10 chars\n");
3746 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3747 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3749 /* try inserting more text at end */
3750 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3751 ok(0 == i, "WM_CHAR wasn't processed\n");
3752 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3754 ok(10 == i, "expected 10 chars, got %i\n", i);
3755 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3756 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3758 /* try inserting text at beginning */
3759 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3760 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3761 ok(0 == i, "WM_CHAR wasn't processed\n");
3762 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3764 ok(10 == i, "expected 10 chars, got %i\n", i);
3765 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3766 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3768 /* WM_CHAR is limited */
3770 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3771 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3772 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3773 ok(0 == i, "WM_CHAR wasn't processed\n");
3774 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3775 ok(0 == i, "WM_CHAR wasn't processed\n");
3776 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3778 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3780 DestroyWindow(hwndRichEdit);
3783 static void test_EM_GETLIMITTEXT(void)
3786 HWND hwndRichEdit = new_richedit(NULL);
3788 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3789 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3791 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3792 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3793 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3795 DestroyWindow(hwndRichEdit);
3798 static void test_WM_SETFONT(void)
3800 /* There is no invalid input or error conditions for this function.
3801 * NULL wParam and lParam just fall back to their default values
3802 * It should be noted that even if you use a gibberish name for your fonts
3803 * here, it will still work because the name is stored. They will display as
3804 * System, but will report their name to be whatever they were created as */
3806 HWND hwndRichEdit = new_richedit(NULL);
3807 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3808 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3809 FF_DONTCARE, "Marlett");
3810 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3811 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3812 FF_DONTCARE, "MS Sans Serif");
3813 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3814 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3815 FF_DONTCARE, "Courier");
3816 LOGFONTA sentLogFont;
3817 CHARFORMAT2A returnedCF2A;
3819 returnedCF2A.cbSize = sizeof(returnedCF2A);
3821 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3822 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0));
3823 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3825 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3826 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3827 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3828 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3830 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0));
3831 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3832 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3833 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3834 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3835 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3837 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0));
3838 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3839 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3840 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3841 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3842 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3844 /* This last test is special since we send in NULL. We clear the variables
3845 * and just compare to "System" instead of the sent in font name. */
3846 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3847 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3848 returnedCF2A.cbSize = sizeof(returnedCF2A);
3850 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0));
3851 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3852 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3853 ok (!strcmp("System",returnedCF2A.szFaceName),
3854 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3856 DestroyWindow(hwndRichEdit);
3860 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3865 const char** str = (const char**)dwCookie;
3866 int size = strlen(*str);
3867 if(size > 3) /* let's make it piecemeal for fun */
3874 memcpy(pbBuff, *str, *pcb);
3880 static void test_EM_GETMODIFY(void)
3882 HWND hwndRichEdit = new_richedit(NULL);
3885 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3887 'T', 'e', 'x', 't', 0};
3888 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3890 'O', 't', 'h', 'e', 'r',
3891 'T', 'e', 'x', 't', 0};
3892 const char* streamText = "hello world";
3897 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3898 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3899 FF_DONTCARE, "Courier");
3901 setText.codepage = 1200; /* no constant for unicode */
3902 setText.flags = ST_KEEPUNDO;
3905 /* modify flag shouldn't be set when richedit is first created */
3906 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3908 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3910 /* setting modify flag should actually set it */
3911 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3912 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3914 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3916 /* clearing modify flag should actually clear it */
3917 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3918 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3920 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3922 /* setting font doesn't change modify flag */
3923 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3924 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0));
3925 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3927 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3929 /* setting text should set modify flag */
3930 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3931 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3932 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3934 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3936 /* undo previous text doesn't reset modify flag */
3937 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3938 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3940 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3942 /* set text with no flag to keep undo stack should not set modify flag */
3943 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3945 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3946 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3948 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3950 /* WM_SETTEXT doesn't modify */
3951 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3952 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3953 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3955 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3957 /* clear the text */
3958 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3959 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3960 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3962 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3965 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3966 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3967 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3968 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3969 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3971 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3973 /* copy/paste text 1 */
3974 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3975 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3976 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3977 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3978 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3980 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3982 /* copy/paste text 2 */
3983 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3984 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3985 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3986 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3987 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3988 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3990 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3993 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3994 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3995 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3996 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3998 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4001 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4002 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4003 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4004 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4006 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4008 /* set char format */
4009 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4010 cf2.cbSize = sizeof(CHARFORMAT2);
4011 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4013 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4014 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4015 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4016 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4017 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4018 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4020 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4022 /* set para format */
4023 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4024 pf2.cbSize = sizeof(PARAFORMAT2);
4025 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4027 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4028 pf2.wAlignment = PFA_RIGHT;
4029 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4030 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4032 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4035 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4036 es.dwCookie = (DWORD_PTR)&streamText;
4038 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4039 SendMessage(hwndRichEdit, EM_STREAMIN,
4040 (WPARAM)(SF_TEXT), (LPARAM)&es);
4041 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4043 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4045 DestroyWindow(hwndRichEdit);
4051 long expected_retval;
4052 int expected_getsel_start;
4053 int expected_getsel_end;
4054 int _exsetsel_todo_wine;
4055 int _getsel_todo_wine;
4058 const struct exsetsel_s exsetsel_tests[] = {
4060 {5, 10, 10, 5, 10, 0, 0},
4061 {15, 17, 17, 15, 17, 0, 0},
4062 /* test cpMax > strlen() */
4063 {0, 100, 18, 0, 18, 0, 1},
4064 /* test cpMin == cpMax */
4065 {5, 5, 5, 5, 5, 0, 0},
4066 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4067 {-1, 0, 5, 5, 5, 0, 0},
4068 {-1, 17, 5, 5, 5, 0, 0},
4069 {-1, 18, 5, 5, 5, 0, 0},
4070 /* test cpMin < 0 && cpMax < 0 */
4071 {-1, -1, 17, 17, 17, 0, 0},
4072 {-4, -5, 17, 17, 17, 0, 0},
4073 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4074 {0, -1, 18, 0, 18, 0, 1},
4075 {17, -5, 18, 17, 18, 0, 1},
4076 {18, -3, 17, 17, 17, 0, 0},
4077 /* test if cpMin > cpMax */
4078 {15, 19, 18, 15, 18, 0, 1},
4079 {19, 15, 18, 15, 18, 0, 1}
4082 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4087 cr.cpMin = setsel->min;
4088 cr.cpMax = setsel->max;
4089 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4091 if (setsel->_exsetsel_todo_wine) {
4093 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4096 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4099 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4101 if (setsel->_getsel_todo_wine) {
4103 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);
4106 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);
4110 static void test_EM_EXSETSEL(void)
4112 HWND hwndRichEdit = new_richedit(NULL);
4114 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4116 /* sending some text to the window */
4117 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4118 /* 01234567890123456*/
4121 for (i = 0; i < num_tests; i++) {
4122 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4125 DestroyWindow(hwndRichEdit);
4128 static void test_EM_REPLACESEL(int redraw)
4130 HWND hwndRichEdit = new_richedit(NULL);
4131 char buffer[1024] = {0};
4136 /* sending some text to the window */
4137 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4138 /* 01234567890123456*/
4141 /* FIXME add more tests */
4142 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4143 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL);
4144 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4145 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4146 r = strcmp(buffer, "testing");
4147 ok(0 == r, "expected %d, got %d\n", 0, r);
4149 DestroyWindow(hwndRichEdit);
4151 hwndRichEdit = new_richedit(NULL);
4153 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4154 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4156 /* Test behavior with carriage returns and newlines */
4157 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4158 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4159 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4160 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4161 r = strcmp(buffer, "RichEdit1");
4162 ok(0 == r, "expected %d, got %d\n", 0, r);
4164 getText.codepage = CP_ACP;
4165 getText.flags = GT_DEFAULT;
4166 getText.lpDefaultChar = NULL;
4167 getText.lpUsedDefChar = NULL;
4168 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4169 ok(strcmp(buffer, "RichEdit1") == 0,
4170 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4172 /* Test number of lines reported after EM_REPLACESEL */
4173 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4174 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4176 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4177 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4178 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4179 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4180 r = strcmp(buffer, "RichEdit1\r\n");
4181 ok(0 == r, "expected %d, got %d\n", 0, r);
4183 getText.codepage = CP_ACP;
4184 getText.flags = GT_DEFAULT;
4185 getText.lpDefaultChar = NULL;
4186 getText.lpUsedDefChar = NULL;
4187 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4188 ok(strcmp(buffer, "RichEdit1\r") == 0,
4189 "EM_GETTEXTEX returned incorrect string\n");
4191 /* Test number of lines reported after EM_REPLACESEL */
4192 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4193 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4195 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4196 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4197 returns the number of characters *inserted* into the control (after
4198 required conversions), but WinXP's riched20 returns the number of
4199 characters interpreted from the original lParam. Wine's builtin riched20
4200 implements the WinXP behavior.
4202 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4203 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4204 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4205 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4207 /* Test number of lines reported after EM_REPLACESEL */
4208 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4209 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4211 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4212 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4213 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4214 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4216 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4217 r = strcmp(buffer, "RichEdit1\r\n");
4218 ok(0 == r, "expected %d, got %d\n", 0, r);
4220 getText.codepage = CP_ACP;
4221 getText.flags = GT_DEFAULT;
4222 getText.lpDefaultChar = NULL;
4223 getText.lpUsedDefChar = NULL;
4224 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4225 ok(strcmp(buffer, "RichEdit1\r") == 0,
4226 "EM_GETTEXTEX returned incorrect string\n");
4228 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4229 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4230 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4231 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4233 /* The following tests show that richedit should handle the special \r\r\n
4234 sequence by turning it into a single space on insertion. However,
4235 EM_REPLACESEL on WinXP returns the number of characters in the original
4239 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4240 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4241 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4242 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4243 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4244 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4245 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4247 /* Test the actual string */
4249 getText.codepage = CP_ACP;
4250 getText.flags = GT_DEFAULT;
4251 getText.lpDefaultChar = NULL;
4252 getText.lpUsedDefChar = NULL;
4253 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4254 ok(strcmp(buffer, "\r\r") == 0,
4255 "EM_GETTEXTEX returned incorrect string\n");
4257 /* Test number of lines reported after EM_REPLACESEL */
4258 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4259 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4261 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4262 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4263 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4264 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4265 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4266 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4267 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4268 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4270 /* Test the actual string */
4272 getText.codepage = CP_ACP;
4273 getText.flags = GT_DEFAULT;
4274 getText.lpDefaultChar = NULL;
4275 getText.lpUsedDefChar = NULL;
4276 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4277 ok(strcmp(buffer, " ") == 0,
4278 "EM_GETTEXTEX returned incorrect string\n");
4280 /* Test number of lines reported after EM_REPLACESEL */
4281 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4282 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4284 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4285 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4286 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4287 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4288 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4289 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4290 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4291 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4293 /* Test the actual string */
4295 getText.codepage = CP_ACP;
4296 getText.flags = GT_DEFAULT;
4297 getText.lpDefaultChar = NULL;
4298 getText.lpUsedDefChar = NULL;
4299 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4300 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4301 "EM_GETTEXTEX returned incorrect string\n");
4303 /* Test number of lines reported after EM_REPLACESEL */
4304 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4305 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4307 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4308 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4309 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4310 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4311 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4312 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4313 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4314 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4316 /* Test the actual string */
4318 getText.codepage = CP_ACP;
4319 getText.flags = GT_DEFAULT;
4320 getText.lpDefaultChar = NULL;
4321 getText.lpUsedDefChar = NULL;
4322 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4323 ok(strcmp(buffer, " \r") == 0,
4324 "EM_GETTEXTEX returned incorrect string\n");
4326 /* Test number of lines reported after EM_REPLACESEL */
4327 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4328 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4330 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4331 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4332 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4333 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4334 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4335 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4336 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4337 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4339 /* Test the actual string */
4341 getText.codepage = CP_ACP;
4342 getText.flags = GT_DEFAULT;
4343 getText.lpDefaultChar = NULL;
4344 getText.lpUsedDefChar = NULL;
4345 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4346 ok(strcmp(buffer, " \r\r") == 0,
4347 "EM_GETTEXTEX returned incorrect string\n");
4349 /* Test number of lines reported after EM_REPLACESEL */
4350 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4351 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4353 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4354 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4355 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4356 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4357 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4358 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4359 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4360 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4362 /* Test the actual string */
4364 getText.codepage = CP_ACP;
4365 getText.flags = GT_DEFAULT;
4366 getText.lpDefaultChar = NULL;
4367 getText.lpUsedDefChar = NULL;
4368 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4369 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4370 "EM_GETTEXTEX returned incorrect string\n");
4372 /* Test number of lines reported after EM_REPLACESEL */
4373 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4374 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4376 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4377 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4378 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4379 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4380 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4381 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4382 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4384 /* Test the actual string */
4386 getText.codepage = CP_ACP;
4387 getText.flags = GT_DEFAULT;
4388 getText.lpDefaultChar = NULL;
4389 getText.lpUsedDefChar = NULL;
4390 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4391 ok(strcmp(buffer, "\r\r") == 0,
4392 "EM_GETTEXTEX returned incorrect string\n");
4394 /* Test number of lines reported after EM_REPLACESEL */
4395 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4396 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4398 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL);
4399 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4400 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4401 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4402 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4403 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4404 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4405 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4407 /* Test the actual string */
4409 getText.codepage = CP_ACP;
4410 getText.flags = GT_DEFAULT;
4411 getText.lpDefaultChar = NULL;
4412 getText.lpUsedDefChar = NULL;
4413 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4414 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4415 "EM_GETTEXTEX returned incorrect string\n");
4417 /* Test number of lines reported after EM_REPLACESEL */
4418 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4419 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4422 /* This is needed to avoid interferring with keybd_event calls
4423 * on other tests that simulate keyboard events. */
4424 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4426 DestroyWindow(hwndRichEdit);
4429 static void test_WM_PASTE(void)
4432 char buffer[1024] = {0};
4433 const char* text1 = "testing paste\r";
4434 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4435 const char* text1_after = "testing paste\r\n";
4436 const char* text2 = "testing paste\r\rtesting paste";
4437 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4438 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4439 HWND hwndRichEdit = new_richedit(NULL);
4441 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4442 * to test the state of the modifiers (Ctrl/Alt/Shift).
4444 * Therefore Ctrl-<key> keystrokes need to be simulated with
4445 * keybd_event or by using SetKeyboardState to set the modifiers
4446 * and SendMessage to simulate the keystrokes.
4449 /* Sent keystrokes with keybd_event */
4450 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4451 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4452 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4453 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4454 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4456 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4457 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4459 SEND_CTRL_C(hwndRichEdit); /* Copy */
4460 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4461 SEND_CTRL_V(hwndRichEdit); /* Paste */
4462 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4463 /* Pasted text should be visible at this step */
4464 result = strcmp(text1_step1, buffer);
4466 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4468 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4469 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4470 /* Text should be the same as before (except for \r -> \r\n conversion) */
4471 result = strcmp(text1_after, buffer);
4473 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4476 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4477 SEND_CTRL_C(hwndRichEdit); /* Copy */
4478 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4479 SEND_CTRL_V(hwndRichEdit); /* Paste */
4480 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4481 /* Pasted text should be visible at this step */
4482 result = strcmp(text3, buffer);
4484 "test paste: strcmp = %i\n", result);
4485 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4486 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4487 /* Text should be the same as before (except for \r -> \r\n conversion) */
4488 result = strcmp(text2_after, buffer);
4490 "test paste: strcmp = %i\n", result);
4491 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4492 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4493 /* Text should revert to post-paste state */
4494 result = strcmp(buffer,text3);
4496 "test paste: strcmp = %i\n", result);
4504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) NULL);
4505 /* Send WM_CHAR to simulates Ctrl-V */
4506 SendMessage(hwndRichEdit, WM_CHAR, 22,
4507 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4508 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4509 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4510 result = strcmp(buffer,"");
4512 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4514 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4515 * with SetKeyboard state. */
4517 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) NULL);
4518 /* Simulates paste (Ctrl-V) */
4519 hold_key(VK_CONTROL);
4520 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4521 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4522 release_key(VK_CONTROL);
4523 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4524 result = strcmp(buffer,"paste");
4526 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4529 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4530 /* Simulates copy (Ctrl-C) */
4531 hold_key(VK_CONTROL);
4532 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4533 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4534 release_key(VK_CONTROL);
4535 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) NULL);
4536 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4537 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4538 result = strcmp(buffer,"testing");
4540 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4542 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4543 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4544 /* Simulates select all (Ctrl-A) */
4545 hold_key(VK_CONTROL);
4546 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4547 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4548 /* Simulates select cut (Ctrl-X) */
4549 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4550 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4551 release_key(VK_CONTROL);
4552 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4553 result = strcmp(buffer,"");
4555 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4556 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) NULL);
4557 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4558 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4559 result = strcmp(buffer,"cut\r\n");
4560 todo_wine ok(result == 0,
4561 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4562 /* Simulates undo (Ctrl-Z) */
4563 hold_key(VK_CONTROL);
4564 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4565 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4566 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4567 result = strcmp(buffer,"");
4569 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4570 /* Simulates redo (Ctrl-Y) */
4571 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4572 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4573 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4574 result = strcmp(buffer,"cut\r\n");
4575 todo_wine ok(result == 0,
4576 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4577 release_key(VK_CONTROL);
4579 DestroyWindow(hwndRichEdit);
4582 static void test_EM_FORMATRANGE(void)
4587 HWND hwndRichEdit = new_richedit(NULL);
4589 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4591 hdc = GetDC(hwndRichEdit);
4592 ok(hdc != NULL, "Could not get HDC\n");
4594 fr.hdc = fr.hdcTarget = hdc;
4595 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4596 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4597 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4601 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4603 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4606 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4608 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4614 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4616 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4619 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL);
4621 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4624 DestroyWindow(hwndRichEdit);
4627 static int nCallbackCount = 0;
4629 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4632 const char text[] = {'t','e','s','t'};
4634 if (sizeof(text) <= cb)
4636 if ((int)dwCookie != nCallbackCount)
4642 memcpy (pbBuff, text, sizeof(text));
4643 *pcb = sizeof(text);
4650 return 1; /* indicates callback failed */
4653 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4658 const char** str = (const char**)dwCookie;
4659 int size = strlen(*str);
4665 memcpy(pbBuff, *str, *pcb);
4671 struct StringWithLength {
4676 /* This callback is used to handled the null characters in a string. */
4677 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4682 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4683 int size = str->length;
4689 memcpy(pbBuff, str->buffer, *pcb);
4690 str->buffer += *pcb;
4691 str->length -= *pcb;
4696 static void test_EM_STREAMIN(void)
4698 HWND hwndRichEdit = new_richedit(NULL);
4701 char buffer[1024] = {0};
4703 const char * streamText0 = "{\\rtf1 TestSomeText}";
4704 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4705 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4707 const char * streamText1 =
4708 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4709 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4712 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4713 const char * streamText2 =
4714 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4715 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4716 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4717 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4718 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4719 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4720 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4722 const char * streamText3 = "RichEdit1";
4724 struct StringWithLength cookieForStream4;
4725 const char * streamText4 =
4726 "This text just needs to be long enough to cause run to be split onto "
4727 "two separate lines and make sure the null terminating character is "
4728 "handled properly.\0";
4729 int length4 = strlen(streamText4) + 1;
4730 cookieForStream4.buffer = (char *)streamText4;
4731 cookieForStream4.length = length4;
4733 /* Minimal test without \par at the end */
4734 es.dwCookie = (DWORD_PTR)&streamText0;
4736 es.pfnCallback = test_EM_STREAMIN_esCallback;
4737 SendMessage(hwndRichEdit, EM_STREAMIN,
4738 (WPARAM)(SF_RTF), (LPARAM)&es);
4740 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4742 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4743 result = strcmp (buffer,"TestSomeText");
4745 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4746 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4748 /* Native richedit 2.0 ignores last \par */
4749 es.dwCookie = (DWORD_PTR)&streamText0a;
4751 es.pfnCallback = test_EM_STREAMIN_esCallback;
4752 SendMessage(hwndRichEdit, EM_STREAMIN,
4753 (WPARAM)(SF_RTF), (LPARAM)&es);
4755 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4757 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4758 result = strcmp (buffer,"TestSomeText");
4760 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4761 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4763 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4764 es.dwCookie = (DWORD_PTR)&streamText0b;
4766 es.pfnCallback = test_EM_STREAMIN_esCallback;
4767 SendMessage(hwndRichEdit, EM_STREAMIN,
4768 (WPARAM)(SF_RTF), (LPARAM)&es);
4770 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4773 result = strcmp (buffer,"TestSomeText\r\n");
4775 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4776 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4778 es.dwCookie = (DWORD_PTR)&streamText1;
4780 es.pfnCallback = test_EM_STREAMIN_esCallback;
4781 SendMessage(hwndRichEdit, EM_STREAMIN,
4782 (WPARAM)(SF_RTF), (LPARAM)&es);
4784 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4786 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4787 result = strcmp (buffer,"TestSomeText");
4789 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4790 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4792 es.dwCookie = (DWORD_PTR)&streamText2;
4794 SendMessage(hwndRichEdit, EM_STREAMIN,
4795 (WPARAM)(SF_RTF), (LPARAM)&es);
4797 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4799 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4800 ok (strlen(buffer) == 0,
4801 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4802 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4804 es.dwCookie = (DWORD_PTR)&streamText3;
4806 SendMessage(hwndRichEdit, EM_STREAMIN,
4807 (WPARAM)(SF_RTF), (LPARAM)&es);
4809 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4811 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4812 ok (strlen(buffer) == 0,
4813 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4814 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4816 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4818 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4819 SendMessage(hwndRichEdit, EM_STREAMIN,
4820 (WPARAM)(SF_TEXT), (LPARAM)&es);
4822 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4823 ok (result == length4,
4824 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4825 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4827 DestroyWindow(hwndRichEdit);
4830 static void test_EM_StreamIn_Undo(void)
4832 /* The purpose of this test is to determine when a EM_StreamIn should be
4833 * undoable. This is important because WM_PASTE currently uses StreamIn and
4834 * pasting should always be undoable but streaming isn't always.
4837 * StreamIn plain text without SFF_SELECTION.
4838 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4839 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4840 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4841 * Feel free to add tests for other text modes or StreamIn things.
4845 HWND hwndRichEdit = new_richedit(NULL);
4848 char buffer[1024] = {0};
4849 const char randomtext[] = "Some text";
4851 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4853 /* StreamIn, no SFF_SELECTION */
4854 es.dwCookie = nCallbackCount;
4855 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4856 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4857 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4858 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4859 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4860 result = strcmp (buffer,"test");
4862 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4864 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4865 ok (result == FALSE,
4866 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4868 /* StreamIn, SFF_SELECTION, but nothing selected */
4869 es.dwCookie = nCallbackCount;
4870 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4871 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4872 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4873 SendMessage(hwndRichEdit, EM_STREAMIN,
4874 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4875 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4876 result = strcmp (buffer,"testSome text");
4878 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4880 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4882 "EM_STREAMIN with SFF_SELECTION but no selection set "
4883 "should create an undo\n");
4885 /* StreamIn, SFF_SELECTION, with a selection */
4886 es.dwCookie = nCallbackCount;
4887 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4888 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4889 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4890 SendMessage(hwndRichEdit, EM_STREAMIN,
4891 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4892 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4893 result = strcmp (buffer,"Sometesttext");
4895 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4897 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4899 "EM_STREAMIN with SFF_SELECTION and selection set "
4900 "should create an undo\n");
4902 DestroyWindow(hwndRichEdit);
4905 static BOOL is_em_settextex_supported(HWND hwnd)
4907 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4908 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4911 static void test_unicode_conversions(void)
4913 static const WCHAR tW[] = {'t',0};
4914 static const WCHAR teW[] = {'t','e',0};
4915 static const WCHAR textW[] = {'t','e','s','t',0};
4916 static const char textA[] = "test";
4920 int is_win9x, em_settextex_supported, ret;
4922 is_win9x = GetVersion() & 0x80000000;
4924 #define set_textA(hwnd, wm_set_text, txt) \
4926 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4927 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4928 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4929 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4930 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4932 #define expect_textA(hwnd, wm_get_text, txt) \
4934 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4935 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4936 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4937 memset(bufA, 0xAA, sizeof(bufA)); \
4938 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4939 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4940 ret = lstrcmpA(bufA, txt); \
4941 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4944 #define set_textW(hwnd, wm_set_text, txt) \
4946 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4947 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4948 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4949 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4950 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4952 #define expect_textW(hwnd, wm_get_text, txt) \
4954 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4955 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4956 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4957 memset(bufW, 0xAA, sizeof(bufW)); \
4960 assert(wm_get_text == EM_GETTEXTEX); \
4961 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4962 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4966 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4967 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4969 ret = lstrcmpW(bufW, txt); \
4970 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4972 #define expect_empty(hwnd, wm_get_text) \
4974 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4975 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4976 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4977 memset(bufA, 0xAA, sizeof(bufA)); \
4978 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4979 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4980 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4983 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4984 0, 0, 200, 60, 0, 0, 0, 0);
4985 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4987 ret = IsWindowUnicode(hwnd);
4989 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4991 ok(ret, "RichEdit20W should be unicode under NT\n");
4993 /* EM_SETTEXTEX is supported starting from version 3.0 */
4994 em_settextex_supported = is_em_settextex_supported(hwnd);
4995 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4996 em_settextex_supported ? "" : "NOT ");
4998 expect_empty(hwnd, WM_GETTEXT);
4999 expect_empty(hwnd, EM_GETTEXTEX);
5001 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5002 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5003 expect_textA(hwnd, WM_GETTEXT, "t");
5004 expect_textA(hwnd, EM_GETTEXTEX, "t");
5005 expect_textW(hwnd, EM_GETTEXTEX, tW);
5007 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5008 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5009 expect_textA(hwnd, WM_GETTEXT, "te");
5010 expect_textA(hwnd, EM_GETTEXTEX, "te");
5011 expect_textW(hwnd, EM_GETTEXTEX, teW);
5013 set_textA(hwnd, WM_SETTEXT, NULL);
5014 expect_empty(hwnd, WM_GETTEXT);
5015 expect_empty(hwnd, EM_GETTEXTEX);
5018 set_textA(hwnd, WM_SETTEXT, textW);
5020 set_textA(hwnd, WM_SETTEXT, textA);
5021 expect_textA(hwnd, WM_GETTEXT, textA);
5022 expect_textA(hwnd, EM_GETTEXTEX, textA);
5023 expect_textW(hwnd, EM_GETTEXTEX, textW);
5025 if (em_settextex_supported)
5027 set_textA(hwnd, EM_SETTEXTEX, textA);
5028 expect_textA(hwnd, WM_GETTEXT, textA);
5029 expect_textA(hwnd, EM_GETTEXTEX, textA);
5030 expect_textW(hwnd, EM_GETTEXTEX, textW);
5035 set_textW(hwnd, WM_SETTEXT, textW);
5036 expect_textW(hwnd, WM_GETTEXT, textW);
5037 expect_textA(hwnd, WM_GETTEXT, textA);
5038 expect_textW(hwnd, EM_GETTEXTEX, textW);
5039 expect_textA(hwnd, EM_GETTEXTEX, textA);
5041 if (em_settextex_supported)
5043 set_textW(hwnd, EM_SETTEXTEX, textW);
5044 expect_textW(hwnd, WM_GETTEXT, textW);
5045 expect_textA(hwnd, WM_GETTEXT, textA);
5046 expect_textW(hwnd, EM_GETTEXTEX, textW);
5047 expect_textA(hwnd, EM_GETTEXTEX, textA);
5050 DestroyWindow(hwnd);
5052 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5053 0, 0, 200, 60, 0, 0, 0, 0);
5054 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5056 ret = IsWindowUnicode(hwnd);
5057 ok(!ret, "RichEdit20A should NOT be unicode\n");
5059 set_textA(hwnd, WM_SETTEXT, textA);
5060 expect_textA(hwnd, WM_GETTEXT, textA);
5061 expect_textA(hwnd, EM_GETTEXTEX, textA);
5062 expect_textW(hwnd, EM_GETTEXTEX, textW);
5064 if (em_settextex_supported)
5066 set_textA(hwnd, EM_SETTEXTEX, textA);
5067 expect_textA(hwnd, WM_GETTEXT, textA);
5068 expect_textA(hwnd, EM_GETTEXTEX, textA);
5069 expect_textW(hwnd, EM_GETTEXTEX, textW);
5074 set_textW(hwnd, WM_SETTEXT, textW);
5075 expect_textW(hwnd, WM_GETTEXT, textW);
5076 expect_textA(hwnd, WM_GETTEXT, textA);
5077 expect_textW(hwnd, EM_GETTEXTEX, textW);
5078 expect_textA(hwnd, EM_GETTEXTEX, textA);
5080 if (em_settextex_supported)
5082 set_textW(hwnd, EM_SETTEXTEX, textW);
5083 expect_textW(hwnd, WM_GETTEXT, textW);
5084 expect_textA(hwnd, WM_GETTEXT, textA);
5085 expect_textW(hwnd, EM_GETTEXTEX, textW);
5086 expect_textA(hwnd, EM_GETTEXTEX, textA);
5089 DestroyWindow(hwnd);
5092 static void test_WM_CHAR(void)
5096 const char * char_list = "abc\rabc\r";
5097 const char * expected_content_single = "abcabc";
5098 const char * expected_content_multi = "abc\r\nabc\r\n";
5099 char buffer[64] = {0};
5102 /* single-line control must IGNORE carriage returns */
5103 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5104 0, 0, 200, 60, 0, 0, 0, 0);
5105 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5108 while (*p != '\0') {
5109 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5110 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5111 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5112 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5116 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5117 ret = strcmp(buffer, expected_content_single);
5118 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5120 DestroyWindow(hwnd);
5122 /* multi-line control inserts CR normally */
5123 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5124 0, 0, 200, 60, 0, 0, 0, 0);
5125 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5128 while (*p != '\0') {
5129 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5130 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5131 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5132 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5136 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5137 ret = strcmp(buffer, expected_content_multi);
5138 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5140 DestroyWindow(hwnd);
5143 static void test_EM_GETTEXTLENGTHEX(void)
5146 GETTEXTLENGTHEX gtl;
5148 const char * base_string = "base string";
5149 const char * test_string = "a\nb\n\n\r\n";
5150 const char * test_string_after = "a";
5151 const char * test_string_2 = "a\rtest\rstring";
5152 char buffer[64] = {0};
5155 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5156 0, 0, 200, 60, 0, 0, 0, 0);
5157 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5159 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5160 gtl.codepage = CP_ACP;
5161 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5162 ok(ret == 0, "ret %d\n",ret);
5164 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5165 gtl.codepage = CP_ACP;
5166 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5167 ok(ret == 0, "ret %d\n",ret);
5169 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5171 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5172 gtl.codepage = CP_ACP;
5173 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5174 ok(ret == strlen(base_string), "ret %d\n",ret);
5176 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5177 gtl.codepage = CP_ACP;
5178 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5179 ok(ret == strlen(base_string), "ret %d\n",ret);
5181 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5183 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5184 gtl.codepage = CP_ACP;
5185 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5186 ok(ret == 1, "ret %d\n",ret);
5188 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5189 gtl.codepage = CP_ACP;
5190 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5191 ok(ret == 1, "ret %d\n",ret);
5193 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5194 ret = strcmp(buffer, test_string_after);
5195 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5197 DestroyWindow(hwnd);
5200 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5201 0, 0, 200, 60, 0, 0, 0, 0);
5202 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5204 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5205 gtl.codepage = CP_ACP;
5206 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5207 ok(ret == 0, "ret %d\n",ret);
5209 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5210 gtl.codepage = CP_ACP;
5211 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5212 ok(ret == 0, "ret %d\n",ret);
5214 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5216 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5217 gtl.codepage = CP_ACP;
5218 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5219 ok(ret == strlen(base_string), "ret %d\n",ret);
5221 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5222 gtl.codepage = CP_ACP;
5223 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5224 ok(ret == strlen(base_string), "ret %d\n",ret);
5226 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5228 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5229 gtl.codepage = CP_ACP;
5230 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5231 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5233 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5234 gtl.codepage = CP_ACP;
5235 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5236 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5238 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5240 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5241 gtl.codepage = CP_ACP;
5242 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5243 ok(ret == 10, "ret %d\n",ret);
5245 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5246 gtl.codepage = CP_ACP;
5247 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5248 ok(ret == 6, "ret %d\n",ret);
5250 DestroyWindow(hwnd);
5254 /* globals that parent and child access when checking event masks & notifications */
5255 static HWND eventMaskEditHwnd = 0;
5256 static int queriedEventMask;
5257 static int watchForEventMask = 0;
5259 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5260 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5262 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5264 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5266 return DefWindowProcA(hwnd, message, wParam, lParam);
5269 /* test event masks in combination with WM_COMMAND */
5270 static void test_eventMask(void)
5275 const char text[] = "foo bar\n";
5278 /* register class to capture WM_COMMAND */
5280 cls.lpfnWndProc = ParentMsgCheckProcA;
5283 cls.hInstance = GetModuleHandleA(0);
5285 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5286 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5287 cls.lpszMenuName = NULL;
5288 cls.lpszClassName = "EventMaskParentClass";
5289 if(!RegisterClassA(&cls)) assert(0);
5291 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5292 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5293 ok (parent != 0, "Failed to create parent window\n");
5295 eventMaskEditHwnd = new_richedit(parent);
5296 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5298 eventMask = ENM_CHANGE | ENM_UPDATE;
5299 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5300 ok(ret == ENM_NONE, "wrong event mask\n");
5301 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5302 ok(ret == eventMask, "failed to set event mask\n");
5304 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5305 queriedEventMask = 0; /* initialize to something other than we expect */
5306 watchForEventMask = EN_CHANGE;
5307 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5308 ok(ret == TRUE, "failed to set text\n");
5309 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5310 notification in response to WM_SETTEXT */
5311 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5312 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5314 /* check to see if EN_CHANGE is sent when redraw is turned off */
5315 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5316 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5317 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5318 /* redraw is disabled by making the window invisible. */
5319 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5320 queriedEventMask = 0; /* initialize to something other than we expect */
5321 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5322 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5323 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5324 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5325 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5327 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5328 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5329 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5330 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5331 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5332 watchForEventMask = EN_UPDATE;
5333 queriedEventMask = 0; /* initialize to something other than we expect */
5334 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5335 ok(queriedEventMask == 0,
5336 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5337 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5338 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5339 queriedEventMask = 0; /* initialize to something other than we expect */
5340 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5341 ok(queriedEventMask == eventMask,
5342 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5345 DestroyWindow(parent);
5348 static int received_WM_NOTIFY = 0;
5349 static int modify_at_WM_NOTIFY = 0;
5350 static HWND hwndRichedit_WM_NOTIFY;
5352 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5354 if(message == WM_NOTIFY)
5356 received_WM_NOTIFY = 1;
5357 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5359 return DefWindowProcA(hwnd, message, wParam, lParam);
5362 static void test_WM_NOTIFY(void)
5368 /* register class to capture WM_NOTIFY */
5370 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5373 cls.hInstance = GetModuleHandleA(0);
5375 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5376 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5377 cls.lpszMenuName = NULL;
5378 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5379 if(!RegisterClassA(&cls)) assert(0);
5381 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5382 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5383 ok (parent != 0, "Failed to create parent window\n");
5385 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5386 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5388 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5390 /* Notifications for selection change should only be sent when selection
5391 actually changes. EM_SETCHARFORMAT is one message that calls
5392 ME_CommitUndo, which should check whether message should be sent */
5393 received_WM_NOTIFY = 0;
5394 cf2.cbSize = sizeof(CHARFORMAT2);
5395 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5397 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5398 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5399 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5400 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5402 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5404 received_WM_NOTIFY = 0;
5405 modify_at_WM_NOTIFY = 0;
5406 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5407 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5408 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5410 received_WM_NOTIFY = 0;
5411 modify_at_WM_NOTIFY = 0;
5412 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5413 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5415 received_WM_NOTIFY = 0;
5416 modify_at_WM_NOTIFY = 0;
5417 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5418 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5419 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5421 /* Test for WM_NOTIFY messages with redraw disabled. */
5422 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5423 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5424 received_WM_NOTIFY = 0;
5425 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5426 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5427 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5429 DestroyWindow(hwndRichedit_WM_NOTIFY);
5430 DestroyWindow(parent);
5433 static void test_undo_coalescing(void)
5437 char buffer[64] = {0};
5439 /* multi-line control inserts CR normally */
5440 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5441 0, 0, 200, 60, 0, 0, 0, 0);
5442 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5444 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5445 ok (result == FALSE, "Can undo after window creation.\n");
5446 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5447 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5448 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5449 ok (result == FALSE, "Can redo after window creation.\n");
5450 result = SendMessage(hwnd, EM_REDO, 0, 0);
5451 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5453 /* Test the effect of arrows keys during typing on undo transactions*/
5454 simulate_typing_characters(hwnd, "one two three");
5455 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5456 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5457 simulate_typing_characters(hwnd, " four five six");
5459 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5460 ok (result == FALSE, "Can redo before anything is undone.\n");
5461 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5462 ok (result == TRUE, "Cannot undo typed characters.\n");
5463 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5464 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5465 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5466 ok (result == TRUE, "Cannot redo after undo.\n");
5467 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5468 result = strcmp(buffer, "one two three");
5469 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5471 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5472 ok (result == TRUE, "Cannot undo typed characters.\n");
5473 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5474 ok (result == TRUE, "Failed to undo typed characters.\n");
5475 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5476 result = strcmp(buffer, "");
5477 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5479 /* Test the effect of focus changes during typing on undo transactions*/
5480 simulate_typing_characters(hwnd, "one two three");
5481 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5482 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5483 SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)NULL, 0);
5484 SendMessage(hwnd, WM_SETFOCUS, (WPARAM)NULL, 0);
5485 simulate_typing_characters(hwnd, " four five six");
5486 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5487 ok (result == TRUE, "Failed to undo typed characters.\n");
5488 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5489 result = strcmp(buffer, "one two three");
5490 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5492 /* Test the effect of the back key during typing on undo transactions */
5493 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5494 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5495 ok (result == TRUE, "Failed to clear the text.\n");
5496 simulate_typing_characters(hwnd, "one two threa");
5497 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5498 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5499 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5500 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5501 simulate_typing_characters(hwnd, "e four five six");
5502 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5503 ok (result == TRUE, "Failed to undo typed characters.\n");
5504 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5505 result = strcmp(buffer, "");
5506 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5508 /* Test the effect of the delete key during typing on undo transactions */
5509 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5510 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5511 ok(result == TRUE, "Failed to set the text.\n");
5512 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5513 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5514 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5515 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5516 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5517 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5518 ok (result == TRUE, "Failed to undo typed characters.\n");
5519 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5520 result = strcmp(buffer, "acd");
5521 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5522 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5523 ok (result == TRUE, "Failed to undo typed characters.\n");
5524 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5525 result = strcmp(buffer, "abcd");
5526 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5528 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5529 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5530 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5531 ok (result == TRUE, "Failed to clear the text.\n");
5532 simulate_typing_characters(hwnd, "one two three");
5533 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5534 ok (result == 0, "expected %d but got %d\n", 0, result);
5535 simulate_typing_characters(hwnd, " four five six");
5536 result = SendMessage(hwnd, EM_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, "one two three");
5540 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5541 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5542 ok (result == TRUE, "Failed to undo typed characters.\n");
5543 ok (result == TRUE, "Failed to undo typed characters.\n");
5544 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5545 result = strcmp(buffer, "");
5546 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5548 DestroyWindow(hwnd);
5551 LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5555 /* MSDN lied, length is actually the number of bytes. */
5556 length = bytes / sizeof(WCHAR);
5559 case WB_ISDELIMITER:
5560 return text[pos] == 'X';
5562 case WB_MOVEWORDLEFT:
5563 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5565 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5568 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5572 case WB_MOVEWORDRIGHT:
5573 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5575 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5578 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5582 ok(FALSE, "Unexpected code %d\n", code);
5588 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5589 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5591 static void test_word_movement(void)
5595 int sel_start, sel_end;
5596 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5598 /* multi-line control inserts CR normally */
5599 hwnd = new_richedit(NULL);
5601 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5602 ok (result == TRUE, "Failed to clear the text.\n");
5603 SendMessage(hwnd, EM_SETSEL, 0, 0);
5604 /* |one two three */
5606 SEND_CTRL_RIGHT(hwnd);
5607 /* one |two three */
5608 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5609 ok(sel_start == sel_end, "Selection should be empty\n");
5610 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5612 SEND_CTRL_RIGHT(hwnd);
5613 /* one two |three */
5614 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5615 ok(sel_start == sel_end, "Selection should be empty\n");
5616 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5618 SEND_CTRL_LEFT(hwnd);
5619 /* one |two three */
5620 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5621 ok(sel_start == sel_end, "Selection should be empty\n");
5622 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5624 SEND_CTRL_LEFT(hwnd);
5625 /* |one two three */
5626 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5627 ok(sel_start == sel_end, "Selection should be empty\n");
5628 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5630 SendMessage(hwnd, EM_SETSEL, 8, 8);
5631 /* one two | three */
5632 SEND_CTRL_RIGHT(hwnd);
5633 /* one two |three */
5634 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5635 ok(sel_start == sel_end, "Selection should be empty\n");
5636 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5638 SendMessage(hwnd, EM_SETSEL, 11, 11);
5639 /* one two th|ree */
5640 SEND_CTRL_LEFT(hwnd);
5641 /* one two |three */
5642 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5643 ok(sel_start == sel_end, "Selection should be empty\n");
5644 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5646 /* Test with a custom word break procedure that uses X as the delimiter. */
5647 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5648 ok (result == TRUE, "Failed to clear the text.\n");
5649 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5650 /* |one twoXthree */
5651 SEND_CTRL_RIGHT(hwnd);
5652 /* one twoX|three */
5653 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5654 ok(sel_start == sel_end, "Selection should be empty\n");
5655 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5657 DestroyWindow(hwnd);
5659 /* Make sure the behaviour is the same with a unicode richedit window,
5660 * and using unicode functions. */
5661 SetLastError(0xdeadbeef);
5662 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5663 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5664 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5665 if (!hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
5667 win_skip("Needed unicode functions are not implemented\n");
5671 /* Test with a custom word break procedure that uses X as the delimiter. */
5672 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5673 ok (result == TRUE, "Failed to clear the text.\n");
5674 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5675 /* |one twoXthree */
5676 SEND_CTRL_RIGHT(hwnd);
5677 /* one twoX|three */
5678 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5679 ok(sel_start == sel_end, "Selection should be empty\n");
5680 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5682 DestroyWindow(hwnd);
5685 static void test_EM_CHARFROMPOS(void)
5693 /* multi-line control inserts CR normally */
5694 hwnd = new_richedit(NULL);
5695 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5696 (LPARAM)"one two three four five six seven");
5698 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5699 ok(result == 0, "expected character index of 0 but got %d\n", result);
5701 DestroyWindow(hwnd);
5704 static void test_word_wrap(void)
5707 POINTL point = {0, 60}; /* This point must be below the first line */
5708 const char *text = "Must be long enough to test line wrapping";
5709 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5710 int res, pos, lines;
5712 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5713 * when specified on window creation and set later. */
5714 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5715 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5716 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5717 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5718 ok(res, "WM_SETTEXT failed.\n");
5719 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5720 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5721 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5722 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5724 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5725 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5726 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5727 DestroyWindow(hwnd);
5729 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5730 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5731 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5733 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5734 ok(res, "WM_SETTEXT failed.\n");
5735 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5736 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5737 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5738 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5740 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5741 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5742 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5743 DestroyWindow(hwnd);
5745 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5746 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5747 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5748 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5749 ok(res, "WM_SETTEXT failed.\n");
5750 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5751 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5753 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5754 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5755 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5756 DestroyWindow(hwnd);
5758 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5759 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5760 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5761 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5762 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5763 ok(res, "WM_SETTEXT failed.\n");
5764 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5765 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5767 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5768 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5769 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5771 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5772 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5773 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5774 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5775 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5777 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5778 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5779 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5780 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5781 DestroyWindow(hwnd);
5783 /* Test to see if wrapping happens with redraw disabled. */
5784 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5785 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5786 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5787 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5788 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5789 ok(res, "EM_REPLACESEL failed.\n");
5790 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5791 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5792 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5793 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5794 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5796 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5797 DestroyWindow(hwnd);
5800 static void test_auto_yscroll(void)
5802 HWND hwnd = new_richedit(NULL);
5803 int lines, ret, redraw;
5806 for (redraw = 0; redraw <= 1; redraw++) {
5807 trace("testing with WM_SETREDRAW=%d\n", redraw);
5808 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5809 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5810 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5811 ok(lines == 8, "%d lines instead of 8\n", lines);
5812 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5813 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5814 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5815 ret = GetWindowLong(hwnd, GWL_STYLE);
5816 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5818 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)NULL);
5819 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5820 ok(lines == 1, "%d lines instead of 1\n", lines);
5821 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5822 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5823 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5824 ret = GetWindowLong(hwnd, GWL_STYLE);
5825 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5828 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5829 DestroyWindow(hwnd);
5832 START_TEST( editor )
5834 /* Must explicitly LoadLibrary(). The test has no references to functions in
5835 * RICHED20.DLL, so the linker doesn't actually link to it. */
5836 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5837 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5841 test_EM_POSFROMCHAR();
5842 test_EM_SCROLLCARET();
5844 test_scrollbar_visibility();
5846 test_EM_LINELENGTH();
5847 test_EM_SETCHARFORMAT();
5848 test_EM_SETTEXTMODE();
5849 test_TM_PLAINTEXT();
5850 test_EM_SETOPTIONS();
5852 test_EM_GETTEXTRANGE();
5853 test_EM_GETSELTEXT();
5854 test_EM_SETUNDOLIMIT();
5856 test_EM_SETTEXTEX();
5857 test_EM_LIMITTEXT();
5858 test_EM_EXLIMITTEXT();
5859 test_EM_GETLIMITTEXT();
5861 test_EM_GETMODIFY();
5865 test_EM_STREAMOUT();
5866 test_EM_StreamIn_Undo();
5867 test_EM_FORMATRANGE();
5868 test_unicode_conversions();
5869 test_EM_GETTEXTLENGTHEX();
5870 test_EM_REPLACESEL(1);
5871 test_EM_REPLACESEL(0);
5873 test_EM_AUTOURLDETECT();
5875 test_undo_coalescing();
5876 test_word_movement();
5877 test_EM_CHARFROMPOS();
5878 test_SETPARAFORMAT();
5880 test_auto_yscroll();
5882 /* Set the environment variable WINETEST_RICHED20 to keep windows
5883 * responsive and open for 30 seconds. This is useful for debugging.
5885 if (getenv( "WINETEST_RICHED20" )) {
5886 keep_responsive(30);
5889 OleFlushClipboard();
5890 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());