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 /* Try a negative position. */
653 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
654 ok(pt.x == 1, "pt.x = %d\n", pt.x);
656 DestroyWindow(hwndRichEdit);
659 static void test_EM_SETCHARFORMAT(void)
661 HWND hwndRichEdit = new_richedit(NULL);
664 int tested_effects[] = {
678 /* Invalid flags, CHARFORMAT2 structure blanked out */
679 memset(&cf2, 0, sizeof(cf2));
680 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
682 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
684 /* A valid flag, CHARFORMAT2 structure blanked out */
685 memset(&cf2, 0, sizeof(cf2));
686 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
688 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
690 /* A valid flag, CHARFORMAT2 structure blanked out */
691 memset(&cf2, 0, sizeof(cf2));
692 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
694 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
696 /* A valid flag, CHARFORMAT2 structure blanked out */
697 memset(&cf2, 0, sizeof(cf2));
698 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
700 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
702 /* A valid flag, CHARFORMAT2 structure blanked out */
703 memset(&cf2, 0, sizeof(cf2));
704 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
706 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
708 /* Invalid flags, CHARFORMAT2 structure minimally filled */
709 memset(&cf2, 0, sizeof(cf2));
710 cf2.cbSize = sizeof(CHARFORMAT2);
711 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
713 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
714 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
715 ok(rc == FALSE, "Should not be able to undo here.\n");
716 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
718 /* A valid flag, CHARFORMAT2 structure minimally filled */
719 memset(&cf2, 0, sizeof(cf2));
720 cf2.cbSize = sizeof(CHARFORMAT2);
721 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
723 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
724 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
725 ok(rc == FALSE, "Should not be able to undo here.\n");
726 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
728 /* A valid flag, CHARFORMAT2 structure minimally filled */
729 memset(&cf2, 0, sizeof(cf2));
730 cf2.cbSize = sizeof(CHARFORMAT2);
731 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
733 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
734 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
735 ok(rc == FALSE, "Should not be able to undo here.\n");
736 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
738 /* A valid flag, CHARFORMAT2 structure minimally filled */
739 memset(&cf2, 0, sizeof(cf2));
740 cf2.cbSize = sizeof(CHARFORMAT2);
741 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
743 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
744 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
745 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
746 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
748 /* A valid flag, CHARFORMAT2 structure minimally filled */
749 memset(&cf2, 0, sizeof(cf2));
750 cf2.cbSize = sizeof(CHARFORMAT2);
751 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
753 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
754 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
755 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
756 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
758 cf2.cbSize = sizeof(CHARFORMAT2);
759 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
762 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
763 cf2.cbSize = sizeof(CHARFORMAT2);
764 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
766 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
767 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
769 /* wParam==0 is default char format, does not set modify */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
778 /* wParam==SCF_SELECTION sets modify if nonempty selection */
779 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
780 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
781 ok(rc == 0, "Text marked as modified, expected not modified!\n");
782 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
783 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
784 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
785 ok(rc == 0, "Text marked as modified, expected not modified!\n");
787 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == 0, "Text marked as modified, expected not modified!\n");
790 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
792 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
793 ok(rc == 0, "Text marked as modified, expected not modified!\n");
794 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
800 /* wParam==SCF_ALL sets modify regardless of whether text is present */
801 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
802 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
803 ok(rc == 0, "Text marked as modified, expected not modified!\n");
804 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
805 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
806 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
807 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
809 DestroyWindow(hwndRichEdit);
811 /* EM_GETCHARFORMAT tests */
812 for (i = 0; tested_effects[i]; i++)
814 hwndRichEdit = new_richedit(NULL);
815 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
817 /* Need to set a TrueType font to get consistent CFM_BOLD results */
818 memset(&cf2, 0, sizeof(CHARFORMAT2));
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
822 strcpy(cf2.szFaceName, "Courier New");
823 cf2.wWeight = FW_DONTCARE;
824 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
826 memset(&cf2, 0, sizeof(CHARFORMAT2));
827 cf2.cbSize = sizeof(CHARFORMAT2);
828 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
829 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
830 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
831 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
833 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
834 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
835 ok((cf2.dwEffects & tested_effects[i]) == 0,
836 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 cf2.dwMask = tested_effects[i];
841 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
842 cf2.dwMask = CFM_SUPERSCRIPT;
843 cf2.dwEffects = tested_effects[i];
844 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
845 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
847 memset(&cf2, 0, sizeof(CHARFORMAT2));
848 cf2.cbSize = sizeof(CHARFORMAT2);
849 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
850 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
851 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
852 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
854 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
855 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
856 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
857 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
859 memset(&cf2, 0, sizeof(CHARFORMAT2));
860 cf2.cbSize = sizeof(CHARFORMAT2);
861 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
862 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
863 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
864 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
866 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
867 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
868 ok((cf2.dwEffects & tested_effects[i]) == 0,
869 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
871 memset(&cf2, 0, sizeof(CHARFORMAT2));
872 cf2.cbSize = sizeof(CHARFORMAT2);
873 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
874 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
875 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
876 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
878 (cf2.dwMask & tested_effects[i]) == 0),
879 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
881 DestroyWindow(hwndRichEdit);
884 for (i = 0; tested_effects[i]; i++)
886 hwndRichEdit = new_richedit(NULL);
887 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
889 /* Need to set a TrueType font to get consistent CFM_BOLD results */
890 memset(&cf2, 0, sizeof(CHARFORMAT2));
891 cf2.cbSize = sizeof(CHARFORMAT2);
892 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
894 strcpy(cf2.szFaceName, "Courier New");
895 cf2.wWeight = FW_DONTCARE;
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 cf2.dwMask = tested_effects[i];
901 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
902 cf2.dwMask = CFM_SUPERSCRIPT;
903 cf2.dwEffects = tested_effects[i];
904 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
905 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
907 memset(&cf2, 0, sizeof(CHARFORMAT2));
908 cf2.cbSize = sizeof(CHARFORMAT2);
909 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
910 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
911 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
912 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
914 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
915 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
916 ok((cf2.dwEffects & tested_effects[i]) == 0,
917 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
919 memset(&cf2, 0, sizeof(CHARFORMAT2));
920 cf2.cbSize = sizeof(CHARFORMAT2);
921 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
922 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
923 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
924 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
926 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
927 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
928 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
929 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
931 memset(&cf2, 0, sizeof(CHARFORMAT2));
932 cf2.cbSize = sizeof(CHARFORMAT2);
933 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
934 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
935 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
936 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
938 (cf2.dwMask & tested_effects[i]) == 0),
939 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
940 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
941 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
943 DestroyWindow(hwndRichEdit);
946 /* Effects applied on an empty selection should take effect when selection is
947 replaced with text */
948 hwndRichEdit = new_richedit(NULL);
949 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
950 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 cf2.dwMask = CFM_BOLD;
955 cf2.dwEffects = CFE_BOLD;
956 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
958 /* Selection is now nonempty */
959 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
961 memset(&cf2, 0, sizeof(CHARFORMAT2));
962 cf2.cbSize = sizeof(CHARFORMAT2);
963 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
964 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
966 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
967 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
968 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
969 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
972 /* Set two effects on an empty selection */
973 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
974 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
976 memset(&cf2, 0, sizeof(CHARFORMAT2));
977 cf2.cbSize = sizeof(CHARFORMAT2);
978 cf2.dwMask = CFM_BOLD;
979 cf2.dwEffects = CFE_BOLD;
980 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
981 cf2.dwMask = CFM_ITALIC;
982 cf2.dwEffects = CFE_ITALIC;
983 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
985 /* Selection is now nonempty */
986 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
988 memset(&cf2, 0, sizeof(CHARFORMAT2));
989 cf2.cbSize = sizeof(CHARFORMAT2);
990 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
991 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
993 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
994 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
995 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
996 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
998 /* Setting the (empty) selection to exactly the same place as before should
999 NOT clear the insertion style! */
1000 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1003 memset(&cf2, 0, sizeof(CHARFORMAT2));
1004 cf2.cbSize = sizeof(CHARFORMAT2);
1005 cf2.dwMask = CFM_BOLD;
1006 cf2.dwEffects = CFE_BOLD;
1007 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1009 /* Empty selection in same place, insert style should NOT be forgotten here. */
1010 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1012 /* Selection is now nonempty */
1013 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1015 memset(&cf2, 0, sizeof(CHARFORMAT2));
1016 cf2.cbSize = sizeof(CHARFORMAT2);
1017 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1018 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1020 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1021 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1022 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1023 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1025 /* Ditto with EM_EXSETSEL */
1026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1027 cr.cpMin = 2; cr.cpMax = 2;
1028 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1030 memset(&cf2, 0, sizeof(CHARFORMAT2));
1031 cf2.cbSize = sizeof(CHARFORMAT2);
1032 cf2.dwMask = CFM_BOLD;
1033 cf2.dwEffects = CFE_BOLD;
1034 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1036 /* Empty selection in same place, insert style should NOT be forgotten here. */
1037 cr.cpMin = 2; cr.cpMax = 2;
1038 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1040 /* Selection is now nonempty */
1041 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1043 memset(&cf2, 0, sizeof(CHARFORMAT2));
1044 cf2.cbSize = sizeof(CHARFORMAT2);
1045 cr.cpMin = 2; cr.cpMax = 6;
1046 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1047 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1049 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1050 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1051 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1052 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1054 DestroyWindow(hwndRichEdit);
1057 static void test_EM_SETTEXTMODE(void)
1059 HWND hwndRichEdit = new_richedit(NULL);
1060 CHARFORMAT2 cf2, cf2test;
1064 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1065 /*Insert text into the control*/
1067 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1069 /*Attempt to change the control to plain text mode*/
1070 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1071 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1073 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1074 If rich text is pasted, it should have the same formatting as the rest
1075 of the text in the control*/
1077 /*Italicize the text
1078 *NOTE: If the default text was already italicized, the test will simply
1079 reverse; in other words, it will copy a regular "wine" into a plain
1080 text window that uses an italicized format*/
1081 cf2.cbSize = sizeof(CHARFORMAT2);
1082 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1085 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1086 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1088 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1089 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1091 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1092 however, SCF_ALL has been implemented*/
1093 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1094 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1096 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1097 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1099 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1101 /*Select the string "wine"*/
1104 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1106 /*Copy the italicized "wine" to the clipboard*/
1107 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1109 /*Reset the formatting to default*/
1110 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1111 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1112 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1114 /*Clear the text in the control*/
1115 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1117 /*Switch to Plain Text Mode*/
1118 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1119 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1121 /*Input "wine" again in normal format*/
1122 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1124 /*Paste the italicized "wine" into the control*/
1125 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1127 /*Select a character from the first "wine" string*/
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1132 /*Retrieve its formatting*/
1133 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1136 /*Select a character from the second "wine" string*/
1139 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1141 /*Retrieve its formatting*/
1142 cf2test.cbSize = sizeof(CHARFORMAT2);
1143 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1146 /*Compare the two formattings*/
1147 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1148 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1149 cf2.dwEffects, cf2test.dwEffects);
1150 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1151 printing "wine" in the current format(normal)
1152 pasting "wine" from the clipboard(italicized)
1153 comparing the two formats(should differ)*/
1155 /*Attempt to switch with text in control*/
1156 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1157 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1160 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1162 /*Switch into Rich Text mode*/
1163 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1164 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1166 /*Print "wine" in normal formatting into the control*/
1167 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1169 /*Paste italicized "wine" into the control*/
1170 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1172 /*Select text from the first "wine" string*/
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1181 /*Select text from the second "wine" string*/
1184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1186 /*Retrieve its formatting*/
1187 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1190 /*Test that the two formattings are not the same*/
1191 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1192 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1193 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1195 DestroyWindow(hwndRichEdit);
1198 static void test_SETPARAFORMAT(void)
1200 HWND hwndRichEdit = new_richedit(NULL);
1203 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1204 fmt.cbSize = sizeof(PARAFORMAT2);
1205 fmt.dwMask = PFM_ALIGNMENT;
1206 fmt.wAlignment = PFA_LEFT;
1208 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1209 ok(ret != 0, "expected non-zero got %d\n", ret);
1211 fmt.cbSize = sizeof(PARAFORMAT2);
1213 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1214 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1215 * between richedit different native builds of riched20.dll
1216 * used on different Windows versions. */
1217 ret &= ~PFM_TABLEROWDELIMITER;
1218 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1220 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1221 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1223 DestroyWindow(hwndRichEdit);
1226 static void test_TM_PLAINTEXT(void)
1228 /*Tests plain text properties*/
1230 HWND hwndRichEdit = new_richedit(NULL);
1231 CHARFORMAT2 cf2, cf2test;
1235 /*Switch to plain text mode*/
1237 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1238 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1240 /*Fill control with text*/
1242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1244 /*Select some text and bold it*/
1248 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1249 cf2.cbSize = sizeof(CHARFORMAT2);
1250 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1253 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1254 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1256 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1257 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1259 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1260 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1262 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1263 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1265 /*Get the formatting of those characters*/
1267 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1269 /*Get the formatting of some other characters*/
1270 cf2test.cbSize = sizeof(CHARFORMAT2);
1273 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1274 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1276 /*Test that they are the same as plain text allows only one formatting*/
1278 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1279 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1280 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1282 /*Fill the control with a "wine" string, which when inserted will be bold*/
1284 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1286 /*Copy the bolded "wine" string*/
1290 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1291 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1293 /*Swap back to rich text*/
1295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1296 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1298 /*Set the default formatting to bold italics*/
1300 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1301 cf2.dwMask |= CFM_ITALIC;
1302 cf2.dwEffects ^= CFE_ITALIC;
1303 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1304 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1306 /*Set the text in the control to "wine", which will be bold and italicized*/
1308 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1310 /*Paste the plain text "wine" string, which should take the insert
1311 formatting, which at the moment is bold italics*/
1313 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1315 /*Select the first "wine" string and retrieve its formatting*/
1319 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1320 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1322 /*Select the second "wine" string and retrieve its formatting*/
1326 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1327 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1329 /*Compare the two formattings. They should be the same.*/
1331 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1332 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1333 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1334 DestroyWindow(hwndRichEdit);
1337 static void test_WM_GETTEXT(void)
1339 HWND hwndRichEdit = new_richedit(NULL);
1340 static const char text[] = "Hello. My name is RichEdit!";
1341 static const char text2[] = "Hello. My name is RichEdit!\r";
1342 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1343 char buffer[1024] = {0};
1346 /* Baseline test with normal-sized buffer */
1347 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1348 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1349 ok(result == lstrlen(buffer),
1350 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1351 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1352 result = strcmp(buffer,text);
1354 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1356 /* Test for returned value of WM_GETTEXTLENGTH */
1357 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1358 ok(result == lstrlen(text),
1359 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1360 result, lstrlen(text));
1362 /* Test for behavior in overflow case */
1363 memset(buffer, 0, 1024);
1364 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1366 result == lstrlenA(text) - 1, /* XP, win2k3 */
1367 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1368 result = strcmp(buffer,text);
1370 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1374 /* Baseline test with normal-sized buffer and carriage return */
1375 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1376 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1377 ok(result == lstrlen(buffer),
1378 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1379 result = strcmp(buffer,text2_after);
1381 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1383 /* Test for returned value of WM_GETTEXTLENGTH */
1384 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1385 ok(result == lstrlen(text2_after),
1386 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1387 result, lstrlen(text2_after));
1389 /* Test for behavior of CRLF conversion in case of overflow */
1390 memset(buffer, 0, 1024);
1391 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1393 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1394 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1395 result = strcmp(buffer,text2);
1397 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1399 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1401 DestroyWindow(hwndRichEdit);
1404 static void test_EM_GETTEXTRANGE(void)
1406 HWND hwndRichEdit = new_richedit(NULL);
1407 const char * text1 = "foo bar\r\nfoo bar";
1408 const char * text2 = "foo bar\rfoo bar";
1409 const char * expect = "bar\rfoo";
1410 char buffer[1024] = {0};
1412 TEXTRANGEA textRange;
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1423 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1425 textRange.lpstrText = buffer;
1426 textRange.chrg.cpMin = 4;
1427 textRange.chrg.cpMax = 11;
1428 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1429 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1430 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1432 DestroyWindow(hwndRichEdit);
1435 static void test_EM_GETSELTEXT(void)
1437 HWND hwndRichEdit = new_richedit(NULL);
1438 const char * text1 = "foo bar\r\nfoo bar";
1439 const char * text2 = "foo bar\rfoo bar";
1440 const char * expect = "bar\rfoo";
1441 char buffer[1024] = {0};
1444 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1446 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1447 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1448 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1449 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1451 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1453 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1454 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1455 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1456 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1458 DestroyWindow(hwndRichEdit);
1461 /* FIXME: need to test unimplemented options and robustly test wparam */
1462 static void test_EM_SETOPTIONS(void)
1464 HWND hwndRichEdit = new_richedit(NULL);
1465 static const char text[] = "Hello. My name is RichEdit!";
1466 char buffer[1024] = {0};
1468 /* NEGATIVE TESTING - NO OPTIONS SET */
1469 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1470 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1472 /* testing no readonly by sending 'a' to the control*/
1473 SetFocus(hwndRichEdit);
1474 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1475 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1477 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1478 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1480 /* READONLY - sending 'a' to the control */
1481 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1482 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1483 SetFocus(hwndRichEdit);
1484 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1485 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1486 ok(buffer[0]==text[0],
1487 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1489 DestroyWindow(hwndRichEdit);
1492 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1494 CHARFORMAT2W text_format;
1495 text_format.cbSize = sizeof(text_format);
1496 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1497 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1498 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1501 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1503 int link_present = 0;
1505 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1507 { /* control text is url; should get CFE_LINK */
1508 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1512 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1516 static HWND new_static_wnd(HWND parent) {
1517 return new_window("Static", 0, parent);
1520 static void test_EM_AUTOURLDETECT(void)
1522 /* DO NOT change the properties of the first two elements. To shorten the
1523 tests, all tests after WM_SETTEXT test just the first two elements -
1524 one non-URL and one URL */
1530 {"http://www.winehq.org", 1},
1531 {"http//winehq.org", 0},
1532 {"ww.winehq.org", 0},
1533 {"www.winehq.org", 1},
1534 {"ftp://192.168.1.1", 1},
1535 {"ftp//192.168.1.1", 0},
1536 {"mailto:your@email.com", 1},
1537 {"prospero:prosperoserver", 1},
1539 {"news:newserver", 1},
1540 {"wais:waisserver", 1}
1545 HWND hwndRichEdit, parent;
1547 /* All of the following should cause the URL to be detected */
1548 const char * templates_delim[] = {
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\r on it",
1552 "This is some text with ---X--- on it",
1553 "This is some text with \"X\" on it",
1554 "This is some text with 'X' on it",
1555 "This is some text with 'X' on it",
1556 "This is some text with :X: on it",
1558 "This text ends with X",
1560 "This is some text with X) on it",
1561 "This is some text with X--- on it",
1562 "This is some text with X\" on it",
1563 "This is some text with X' on it",
1564 "This is some text with X: on it",
1566 "This is some text with (X on it",
1567 "This is some text with \rX on it",
1568 "This is some text with ---X on it",
1569 "This is some text with \"X on it",
1570 "This is some text with 'X on it",
1571 "This is some text with :X on it",
1573 /* None of these should cause the URL to be detected */
1574 const char * templates_non_delim[] = {
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",
1587 "This is some text with %X on it",
1588 "This is some text with #X on it",
1589 "This is some text with @X on it",
1590 "This is some text with \\X on it",
1592 /* All of these cause the URL detection to be extended by one more byte,
1593 thus demonstrating that the tested character is considered as part
1595 const char * templates_xten_delim[] = {
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",
1600 "This is some text with X% on it",
1601 "This is some text with X# on it",
1602 "This is some text with X@ on it",
1603 "This is some text with X\\ on it",
1607 parent = new_static_wnd(NULL);
1608 hwndRichEdit = new_richedit(parent);
1609 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1610 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1611 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1612 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1613 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1614 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1615 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1616 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1617 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1618 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1619 /* for each url, check the text to see if CFE_LINK effect is present */
1620 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1622 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1624 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1626 /* Link detection should happen immediately upon WM_SETTEXT */
1627 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1628 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1629 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1631 DestroyWindow(hwndRichEdit);
1633 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1634 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1635 hwndRichEdit = new_richedit(parent);
1637 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1642 at_pos = strchr(templates_delim[j], 'X');
1643 at_offset = at_pos - templates_delim[j];
1644 strncpy(buffer, templates_delim[j], at_offset);
1645 buffer[at_offset] = '\0';
1646 strcat(buffer, urls[i].text);
1647 strcat(buffer, templates_delim[j] + at_offset + 1);
1648 end_offset = at_offset + strlen(urls[i].text);
1650 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1651 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1653 /* This assumes no templates start with the URL itself, and that they
1654 have at least two characters before the URL text */
1655 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1656 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1657 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1658 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1659 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1660 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1664 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1665 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1666 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1667 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1671 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1672 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1673 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1674 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1676 if (buffer[end_offset] != '\0')
1678 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1679 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1680 if (buffer[end_offset +1] != '\0')
1682 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1683 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1688 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1693 at_pos = strchr(templates_non_delim[j], 'X');
1694 at_offset = at_pos - templates_non_delim[j];
1695 strncpy(buffer, templates_non_delim[j], at_offset);
1696 buffer[at_offset] = '\0';
1697 strcat(buffer, urls[i].text);
1698 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1699 end_offset = at_offset + strlen(urls[i].text);
1701 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1702 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1704 /* This assumes no templates start with the URL itself, and that they
1705 have at least two characters before the URL text */
1706 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1707 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1708 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1709 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1710 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1711 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1713 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1714 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1715 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1716 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1717 if (buffer[end_offset] != '\0')
1719 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1720 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1721 if (buffer[end_offset +1] != '\0')
1723 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1724 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1729 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1734 at_pos = strchr(templates_xten_delim[j], 'X');
1735 at_offset = at_pos - templates_xten_delim[j];
1736 strncpy(buffer, templates_xten_delim[j], at_offset);
1737 buffer[at_offset] = '\0';
1738 strcat(buffer, urls[i].text);
1739 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1740 end_offset = at_offset + strlen(urls[i].text);
1742 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1743 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1745 /* This assumes no templates start with the URL itself, and that they
1746 have at least two characters before the URL text */
1747 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1748 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1749 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1750 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1751 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1752 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1756 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1757 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1758 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1759 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1760 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1761 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1765 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1766 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1767 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1768 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1769 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1770 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1772 if (buffer[end_offset +1] != '\0')
1774 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1775 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1776 if (buffer[end_offset +2] != '\0')
1778 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1779 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1784 DestroyWindow(hwndRichEdit);
1785 hwndRichEdit = NULL;
1788 /* Test detection of URLs within normal text - WM_CHAR case. */
1789 /* Test only the first two URL examples for brevity */
1790 for (i = 0; i < 2; i++) {
1791 hwndRichEdit = new_richedit(parent);
1793 /* Also for brevity, test only the first three delimiters */
1794 for (j = 0; j < 3; j++) {
1800 at_pos = strchr(templates_delim[j], 'X');
1801 at_offset = at_pos - templates_delim[j];
1802 end_offset = at_offset + strlen(urls[i].text);
1804 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1805 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1806 for (u = 0; templates_delim[j][u]; u++) {
1807 if (templates_delim[j][u] == '\r') {
1808 simulate_typing_characters(hwndRichEdit, "\r");
1809 } else if (templates_delim[j][u] != 'X') {
1810 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1812 for (v = 0; urls[i].text[v]; v++) {
1813 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1817 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1819 /* This assumes no templates start with the URL itself, and that they
1820 have at least two characters before the URL text */
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1823 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1824 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1825 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1826 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1830 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1831 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1832 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1833 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1837 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1838 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1839 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1840 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1842 if (buffer[end_offset] != '\0')
1844 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1845 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1846 if (buffer[end_offset +1] != '\0')
1848 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1849 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1853 /* The following will insert a paragraph break after the first character
1854 of the URL candidate, thus breaking the URL. It is expected that the
1855 CFE_LINK attribute should break across both pieces of the URL */
1856 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1857 simulate_typing_characters(hwndRichEdit, "\r");
1858 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1860 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1861 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1862 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1863 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1864 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1865 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1869 /* end_offset moved because of paragraph break */
1870 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1871 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1872 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1873 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1875 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1876 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1877 if (buffer[end_offset +2] != '\0')
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1884 /* The following will remove the just-inserted paragraph break, thus
1885 restoring the URL */
1886 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1887 simulate_typing_characters(hwndRichEdit, "\b");
1888 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1890 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1891 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1892 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1893 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1894 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1895 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1899 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1900 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1901 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1902 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1908 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1909 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1911 if (buffer[end_offset] != '\0')
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1915 if (buffer[end_offset +1] != '\0')
1917 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1918 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1922 DestroyWindow(hwndRichEdit);
1923 hwndRichEdit = NULL;
1926 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
1927 /* Test just the first two URL examples for brevity */
1928 for (i = 0; i < 2; i++) {
1931 hwndRichEdit = new_richedit(parent);
1933 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
1935 1) Set entire text, a la WM_SETTEXT
1936 2) Set a selection of the text to the URL
1937 3) Set a portion of the text at a time, which eventually results in
1939 All of them should give equivalent results
1942 /* Set entire text in one go, like WM_SETTEXT */
1943 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1948 st.codepage = CP_ACP;
1949 st.flags = ST_DEFAULT;
1951 at_pos = strchr(templates_delim[j], 'X');
1952 at_offset = at_pos - templates_delim[j];
1953 strncpy(buffer, templates_delim[j], at_offset);
1954 buffer[at_offset] = '\0';
1955 strcat(buffer, urls[i].text);
1956 strcat(buffer, templates_delim[j] + at_offset + 1);
1957 end_offset = at_offset + strlen(urls[i].text);
1959 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1960 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
1962 /* This assumes no templates start with the URL itself, and that they
1963 have at least two characters before the URL text */
1964 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1965 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1966 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1967 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1968 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1969 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1973 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1974 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1975 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1976 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1982 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1983 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1985 if (buffer[end_offset] != '\0')
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1989 if (buffer[end_offset +1] != '\0')
1991 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1992 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1997 /* Set selection with X to the URL */
1998 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2003 at_pos = strchr(templates_delim[j], 'X');
2004 at_offset = at_pos - templates_delim[j];
2005 end_offset = at_offset + strlen(urls[i].text);
2007 st.codepage = CP_ACP;
2008 st.flags = ST_DEFAULT;
2009 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2010 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2011 st.flags = ST_SELECTION;
2012 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2013 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2014 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2016 /* This assumes no templates start with the URL itself, and that they
2017 have at least two characters before the URL text */
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2020 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2021 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2027 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2028 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2029 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2030 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2034 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2035 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2036 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2037 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2039 if (buffer[end_offset] != '\0')
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2043 if (buffer[end_offset +1] != '\0')
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2051 /* Set selection with X to the first character of the URL, then the rest */
2052 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2057 at_pos = strchr(templates_delim[j], 'X');
2058 at_offset = at_pos - templates_delim[j];
2059 end_offset = at_offset + strlen(urls[i].text);
2061 strcpy(buffer, "YY");
2062 buffer[0] = urls[i].text[0];
2064 st.codepage = CP_ACP;
2065 st.flags = ST_DEFAULT;
2066 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2067 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2068 st.flags = ST_SELECTION;
2069 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2070 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2071 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2072 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2073 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2075 /* This assumes no templates start with the URL itself, and that they
2076 have at least two characters before the URL text */
2077 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2078 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2079 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2080 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2081 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2082 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2086 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2087 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2088 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2089 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2098 if (buffer[end_offset] != '\0')
2100 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2101 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2102 if (buffer[end_offset +1] != '\0')
2104 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2105 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2110 DestroyWindow(hwndRichEdit);
2111 hwndRichEdit = NULL;
2114 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2115 /* Test just the first two URL examples for brevity */
2116 for (i = 0; i < 2; i++) {
2117 hwndRichEdit = new_richedit(parent);
2119 /* Set selection with X to the URL */
2120 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2125 at_pos = strchr(templates_delim[j], 'X');
2126 at_offset = at_pos - templates_delim[j];
2127 end_offset = at_offset + strlen(urls[i].text);
2129 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2130 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2131 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2132 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2133 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2135 /* This assumes no templates start with the URL itself, and that they
2136 have at least two characters before the URL text */
2137 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2138 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2141 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2142 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2146 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2147 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2148 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2149 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2153 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2154 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2155 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2156 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2158 if (buffer[end_offset] != '\0')
2160 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2161 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2162 if (buffer[end_offset +1] != '\0')
2164 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2165 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2170 /* Set selection with X to the first character of the URL, then the rest */
2171 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2176 at_pos = strchr(templates_delim[j], 'X');
2177 at_offset = at_pos - templates_delim[j];
2178 end_offset = at_offset + strlen(urls[i].text);
2180 strcpy(buffer, "YY");
2181 buffer[0] = urls[i].text[0];
2183 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2184 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2185 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2186 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2187 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2188 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2189 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2191 /* This assumes no templates start with the URL itself, and that they
2192 have at least two characters before the URL text */
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2195 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2196 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2197 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2198 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2202 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2203 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2204 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2205 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2209 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2210 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2214 if (buffer[end_offset] != '\0')
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2218 if (buffer[end_offset +1] != '\0')
2220 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2221 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2226 DestroyWindow(hwndRichEdit);
2227 hwndRichEdit = NULL;
2230 DestroyWindow(parent);
2233 static void test_EM_SCROLL(void)
2236 int r; /* return value */
2237 int expr; /* expected return value */
2238 HWND hwndRichEdit = new_richedit(NULL);
2239 int y_before, y_after; /* units of lines of text */
2241 /* test a richedit box containing a single line of text */
2242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2244 for (i = 0; i < 4; i++) {
2245 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2247 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2248 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2249 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2250 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2251 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2252 "(i == %d)\n", y_after, i);
2256 * test a richedit box that will scroll. There are two general
2257 * cases: the case without any long lines and the case with a long
2260 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2262 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2264 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2265 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2266 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2267 "LONG LINE \nb\nc\nd\ne");
2268 for (j = 0; j < 12; j++) /* reset scroll position to top */
2269 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2271 /* get first visible line */
2272 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2273 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2275 /* get new current first visible line */
2276 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2278 ok(((r & 0xffffff00) == 0x00010000) &&
2279 ((r & 0x000000ff) != 0x00000000),
2280 "EM_SCROLL page down didn't scroll by a small positive number of "
2281 "lines (r == 0x%08x)\n", r);
2282 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2283 "(line %d scrolled to line %d\n", y_before, y_after);
2287 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2288 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2289 ok(((r & 0xffffff00) == 0x0001ff00),
2290 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2291 "(r == 0x%08x)\n", r);
2292 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2293 "%d scrolled to line %d\n", y_before, y_after);
2297 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2299 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2301 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2302 "(r == 0x%08x)\n", r);
2303 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2304 "1 line (%d scrolled to %d)\n", y_before, y_after);
2308 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2310 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2312 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2313 "(r == 0x%08x)\n", r);
2314 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2315 "line (%d scrolled to %d)\n", y_before, y_after);
2319 r = SendMessage(hwndRichEdit, EM_SCROLL,
2320 SB_LINEUP, 0); /* lineup beyond top */
2322 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2325 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2326 ok(y_before == y_after,
2327 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2331 r = SendMessage(hwndRichEdit, EM_SCROLL,
2332 SB_PAGEUP, 0);/*page up beyond top */
2334 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2337 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2338 ok(y_before == y_after,
2339 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2341 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2342 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2343 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2344 r = SendMessage(hwndRichEdit, EM_SCROLL,
2345 SB_PAGEDOWN, 0); /* page down beyond bot */
2346 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2349 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2350 ok(y_before == y_after,
2351 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2354 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2355 SendMessage(hwndRichEdit, EM_SCROLL,
2356 SB_LINEDOWN, 0); /* line down beyond bot */
2357 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2360 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2361 ok(y_before == y_after,
2362 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2365 DestroyWindow(hwndRichEdit);
2368 unsigned int recursionLevel = 0;
2369 unsigned int WM_SIZE_recursionLevel = 0;
2370 BOOL bailedOutOfRecursion = FALSE;
2371 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2373 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2377 if (bailedOutOfRecursion) return 0;
2378 if (recursionLevel >= 32) {
2379 bailedOutOfRecursion = TRUE;
2386 WM_SIZE_recursionLevel++;
2387 r = richeditProc(hwnd, message, wParam, lParam);
2388 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2389 ShowScrollBar(hwnd, SB_VERT, TRUE);
2390 WM_SIZE_recursionLevel--;
2393 r = richeditProc(hwnd, message, wParam, lParam);
2400 static void test_scrollbar_visibility(void)
2403 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2408 /* These tests show that richedit should temporarily refrain from automatically
2409 hiding or showing its scrollbars (vertical at least) when an explicit request
2410 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2411 Some applications depend on forced showing (when otherwise richedit would
2412 hide the vertical scrollbar) and are thrown on an endless recursive loop
2413 if richedit auto-hides the scrollbar again. Apparently they never heard of
2414 the ES_DISABLENOSCROLL style... */
2416 hwndRichEdit = new_richedit(NULL);
2418 /* Test default scrollbar visibility behavior */
2419 memset(&si, 0, sizeof(si));
2420 si.cbSize = sizeof(si);
2421 si.fMask = SIF_PAGE | SIF_RANGE;
2422 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2423 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2424 "Vertical scrollbar is visible, should be invisible.\n");
2425 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2426 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2427 si.nPage, si.nMin, si.nMax);
2429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2430 memset(&si, 0, sizeof(si));
2431 si.cbSize = sizeof(si);
2432 si.fMask = SIF_PAGE | SIF_RANGE;
2433 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2434 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2435 "Vertical scrollbar is visible, should be invisible.\n");
2436 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2437 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2438 si.nPage, si.nMin, si.nMax);
2440 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2441 memset(&si, 0, sizeof(si));
2442 si.cbSize = sizeof(si);
2443 si.fMask = SIF_PAGE | SIF_RANGE;
2444 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2445 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2446 "Vertical scrollbar is invisible, should be visible.\n");
2447 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2448 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2449 si.nPage, si.nMin, si.nMax);
2451 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2452 even though it hides the scrollbar */
2453 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2454 memset(&si, 0, sizeof(si));
2455 si.cbSize = sizeof(si);
2456 si.fMask = SIF_PAGE | SIF_RANGE;
2457 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2458 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2459 "Vertical scrollbar is visible, should be invisible.\n");
2460 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2461 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2462 si.nPage, si.nMin, si.nMax);
2464 /* Setting non-scrolling text again does *not* reset scrollbar range */
2465 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2466 memset(&si, 0, sizeof(si));
2467 si.cbSize = sizeof(si);
2468 si.fMask = SIF_PAGE | SIF_RANGE;
2469 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2470 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2471 "Vertical scrollbar is visible, should be invisible.\n");
2472 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2473 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2474 si.nPage, si.nMin, si.nMax);
2476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2477 memset(&si, 0, sizeof(si));
2478 si.cbSize = sizeof(si);
2479 si.fMask = SIF_PAGE | SIF_RANGE;
2480 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2481 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2482 "Vertical scrollbar is visible, should be invisible.\n");
2483 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2484 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2485 si.nPage, si.nMin, si.nMax);
2487 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2488 memset(&si, 0, sizeof(si));
2489 si.cbSize = sizeof(si);
2490 si.fMask = SIF_PAGE | SIF_RANGE;
2491 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2492 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2493 "Vertical scrollbar is visible, should be invisible.\n");
2494 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2495 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2496 si.nPage, si.nMin, si.nMax);
2498 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2499 memset(&si, 0, sizeof(si));
2500 si.cbSize = sizeof(si);
2501 si.fMask = SIF_PAGE | SIF_RANGE;
2502 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2503 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2504 "Vertical scrollbar is visible, should be invisible.\n");
2505 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2506 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2507 si.nPage, si.nMin, si.nMax);
2509 DestroyWindow(hwndRichEdit);
2511 /* Test again, with ES_DISABLENOSCROLL style */
2512 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2514 /* Test default scrollbar visibility behavior */
2515 memset(&si, 0, sizeof(si));
2516 si.cbSize = sizeof(si);
2517 si.fMask = SIF_PAGE | SIF_RANGE;
2518 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2519 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2520 "Vertical scrollbar is invisible, should be visible.\n");
2521 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2522 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2523 si.nPage, si.nMin, si.nMax);
2525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2526 memset(&si, 0, sizeof(si));
2527 si.cbSize = sizeof(si);
2528 si.fMask = SIF_PAGE | SIF_RANGE;
2529 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2530 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2531 "Vertical scrollbar is invisible, should be visible.\n");
2532 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2533 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2534 si.nPage, si.nMin, si.nMax);
2536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2537 memset(&si, 0, sizeof(si));
2538 si.cbSize = sizeof(si);
2539 si.fMask = SIF_PAGE | SIF_RANGE;
2540 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2541 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2542 "Vertical scrollbar is invisible, should be visible.\n");
2543 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2544 "reported page/range is %d (%d..%d)\n",
2545 si.nPage, si.nMin, si.nMax);
2547 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2548 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2549 memset(&si, 0, sizeof(si));
2550 si.cbSize = sizeof(si);
2551 si.fMask = SIF_PAGE | SIF_RANGE;
2552 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2553 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2554 "Vertical scrollbar is invisible, should be visible.\n");
2555 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2556 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2557 si.nPage, si.nMin, si.nMax);
2559 /* Setting non-scrolling text again does *not* reset scrollbar range */
2560 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2566 "Vertical scrollbar is invisible, should be visible.\n");
2567 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2568 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2569 si.nPage, si.nMin, si.nMax);
2571 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2579 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2580 si.nPage, si.nMin, si.nMax);
2582 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2583 memset(&si, 0, sizeof(si));
2584 si.cbSize = sizeof(si);
2585 si.fMask = SIF_PAGE | SIF_RANGE;
2586 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2587 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2588 "Vertical scrollbar is invisible, should be visible.\n");
2589 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2590 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2591 si.nPage, si.nMin, si.nMax);
2593 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2594 memset(&si, 0, sizeof(si));
2595 si.cbSize = sizeof(si);
2596 si.fMask = SIF_PAGE | SIF_RANGE;
2597 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2598 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2599 "Vertical scrollbar is invisible, should be visible.\n");
2600 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2601 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2602 si.nPage, si.nMin, si.nMax);
2604 DestroyWindow(hwndRichEdit);
2606 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2607 hwndRichEdit = new_richedit(NULL);
2609 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2610 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2611 memset(&si, 0, sizeof(si));
2612 si.cbSize = sizeof(si);
2613 si.fMask = SIF_PAGE | SIF_RANGE;
2614 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2615 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2616 "Vertical scrollbar is invisible, should be visible.\n");
2618 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2619 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2620 si.nPage, si.nMin, si.nMax);
2623 /* Ditto, see above */
2624 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2625 memset(&si, 0, sizeof(si));
2626 si.cbSize = sizeof(si);
2627 si.fMask = SIF_PAGE | SIF_RANGE;
2628 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2629 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2630 "Vertical scrollbar is invisible, should be visible.\n");
2632 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2633 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2634 si.nPage, si.nMin, si.nMax);
2637 /* Ditto, see above */
2638 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2639 memset(&si, 0, sizeof(si));
2640 si.cbSize = sizeof(si);
2641 si.fMask = SIF_PAGE | SIF_RANGE;
2642 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2643 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2644 "Vertical scrollbar is invisible, should be visible.\n");
2646 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2647 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2648 si.nPage, si.nMin, si.nMax);
2651 /* Ditto, see above */
2652 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2653 memset(&si, 0, sizeof(si));
2654 si.cbSize = sizeof(si);
2655 si.fMask = SIF_PAGE | SIF_RANGE;
2656 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2657 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2658 "Vertical scrollbar is invisible, should be visible.\n");
2660 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2661 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2662 si.nPage, si.nMin, si.nMax);
2665 /* Ditto, see above */
2666 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2667 memset(&si, 0, sizeof(si));
2668 si.cbSize = sizeof(si);
2669 si.fMask = SIF_PAGE | SIF_RANGE;
2670 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2671 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2672 "Vertical scrollbar is invisible, should be visible.\n");
2674 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2675 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2676 si.nPage, si.nMin, si.nMax);
2679 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2680 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2681 memset(&si, 0, sizeof(si));
2682 si.cbSize = sizeof(si);
2683 si.fMask = SIF_PAGE | SIF_RANGE;
2684 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2685 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2686 "Vertical scrollbar is visible, should be invisible.\n");
2687 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2688 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2689 si.nPage, si.nMin, si.nMax);
2691 DestroyWindow(hwndRichEdit);
2693 hwndRichEdit = new_richedit(NULL);
2695 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2696 memset(&si, 0, sizeof(si));
2697 si.cbSize = sizeof(si);
2698 si.fMask = SIF_PAGE | SIF_RANGE;
2699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2700 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2701 "Vertical scrollbar is visible, should be invisible.\n");
2702 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2703 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2704 si.nPage, si.nMin, si.nMax);
2706 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2707 memset(&si, 0, sizeof(si));
2708 si.cbSize = sizeof(si);
2709 si.fMask = SIF_PAGE | SIF_RANGE;
2710 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2711 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2712 "Vertical scrollbar is visible, should be invisible.\n");
2713 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2714 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2715 si.nPage, si.nMin, si.nMax);
2717 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2718 memset(&si, 0, sizeof(si));
2719 si.cbSize = sizeof(si);
2720 si.fMask = SIF_PAGE | SIF_RANGE;
2721 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2722 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2723 "Vertical scrollbar is visible, should be invisible.\n");
2724 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2725 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2726 si.nPage, si.nMin, si.nMax);
2728 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2729 memset(&si, 0, sizeof(si));
2730 si.cbSize = sizeof(si);
2731 si.fMask = SIF_PAGE | SIF_RANGE;
2732 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2733 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2734 "Vertical scrollbar is visible, should be invisible.\n");
2735 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2736 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2737 si.nPage, si.nMin, si.nMax);
2739 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2740 memset(&si, 0, sizeof(si));
2741 si.cbSize = sizeof(si);
2742 si.fMask = SIF_PAGE | SIF_RANGE;
2743 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2744 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2745 "Vertical scrollbar is invisible, should be visible.\n");
2746 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2747 "reported page/range is %d (%d..%d)\n",
2748 si.nPage, si.nMin, si.nMax);
2750 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2751 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2752 memset(&si, 0, sizeof(si));
2753 si.cbSize = sizeof(si);
2754 si.fMask = SIF_PAGE | SIF_RANGE;
2755 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2756 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2757 "Vertical scrollbar is visible, should be invisible.\n");
2758 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2759 "reported page/range is %d (%d..%d)\n",
2760 si.nPage, si.nMin, si.nMax);
2762 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2763 memset(&si, 0, sizeof(si));
2764 si.cbSize = sizeof(si);
2765 si.fMask = SIF_PAGE | SIF_RANGE;
2766 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2767 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2768 "Vertical scrollbar is visible, should be invisible.\n");
2769 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2770 "reported page/range is %d (%d..%d)\n",
2771 si.nPage, si.nMin, si.nMax);
2773 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2774 EM_SCROLL will make visible any forcefully invisible scrollbar */
2775 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2776 memset(&si, 0, sizeof(si));
2777 si.cbSize = sizeof(si);
2778 si.fMask = SIF_PAGE | SIF_RANGE;
2779 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2780 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2781 "Vertical scrollbar is invisible, should be visible.\n");
2782 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2783 "reported page/range is %d (%d..%d)\n",
2784 si.nPage, si.nMin, si.nMax);
2786 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2787 memset(&si, 0, sizeof(si));
2788 si.cbSize = sizeof(si);
2789 si.fMask = SIF_PAGE | SIF_RANGE;
2790 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2791 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2792 "Vertical scrollbar is visible, should be invisible.\n");
2793 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2794 "reported page/range is %d (%d..%d)\n",
2795 si.nPage, si.nMin, si.nMax);
2797 /* Again, EM_SCROLL, with SB_LINEUP */
2798 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2799 memset(&si, 0, sizeof(si));
2800 si.cbSize = sizeof(si);
2801 si.fMask = SIF_PAGE | SIF_RANGE;
2802 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2803 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2804 "Vertical scrollbar is invisible, should be visible.\n");
2805 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2806 "reported page/range is %d (%d..%d)\n",
2807 si.nPage, si.nMin, si.nMax);
2809 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2810 memset(&si, 0, sizeof(si));
2811 si.cbSize = sizeof(si);
2812 si.fMask = SIF_PAGE | SIF_RANGE;
2813 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2814 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2815 "Vertical scrollbar is visible, should be invisible.\n");
2816 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2817 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2818 si.nPage, si.nMin, si.nMax);
2820 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2821 memset(&si, 0, sizeof(si));
2822 si.cbSize = sizeof(si);
2823 si.fMask = SIF_PAGE | SIF_RANGE;
2824 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2825 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2826 "Vertical scrollbar is invisible, should be visible.\n");
2827 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2828 "reported page/range is %d (%d..%d)\n",
2829 si.nPage, si.nMin, si.nMax);
2831 DestroyWindow(hwndRichEdit);
2834 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2835 hwndRichEdit = new_richedit(NULL);
2837 #define ENABLE_WS_VSCROLL(hwnd) \
2838 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2839 #define DISABLE_WS_VSCROLL(hwnd) \
2840 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2842 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2843 ENABLE_WS_VSCROLL(hwndRichEdit);
2844 memset(&si, 0, sizeof(si));
2845 si.cbSize = sizeof(si);
2846 si.fMask = SIF_PAGE | SIF_RANGE;
2847 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2848 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2849 "Vertical scrollbar is invisible, should be visible.\n");
2850 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2851 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2852 si.nPage, si.nMin, si.nMax);
2854 /* Ditto, see above */
2855 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2856 memset(&si, 0, sizeof(si));
2857 si.cbSize = sizeof(si);
2858 si.fMask = SIF_PAGE | SIF_RANGE;
2859 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2860 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2861 "Vertical scrollbar is invisible, should be visible.\n");
2862 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2863 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2864 si.nPage, si.nMin, si.nMax);
2866 /* Ditto, see above */
2867 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2868 memset(&si, 0, sizeof(si));
2869 si.cbSize = sizeof(si);
2870 si.fMask = SIF_PAGE | SIF_RANGE;
2871 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2872 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2873 "Vertical scrollbar is invisible, should be visible.\n");
2874 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2875 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2876 si.nPage, si.nMin, si.nMax);
2878 /* Ditto, see above */
2879 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2880 memset(&si, 0, sizeof(si));
2881 si.cbSize = sizeof(si);
2882 si.fMask = SIF_PAGE | SIF_RANGE;
2883 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2884 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2885 "Vertical scrollbar is invisible, should be visible.\n");
2886 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2887 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2888 si.nPage, si.nMin, si.nMax);
2890 /* Ditto, see above */
2891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2892 memset(&si, 0, sizeof(si));
2893 si.cbSize = sizeof(si);
2894 si.fMask = SIF_PAGE | SIF_RANGE;
2895 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2896 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2897 "Vertical scrollbar is invisible, should be visible.\n");
2898 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2899 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2900 si.nPage, si.nMin, si.nMax);
2902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2903 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2904 memset(&si, 0, sizeof(si));
2905 si.cbSize = sizeof(si);
2906 si.fMask = SIF_PAGE | SIF_RANGE;
2907 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2908 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2909 "Vertical scrollbar is visible, should be invisible.\n");
2910 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2911 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2912 si.nPage, si.nMin, si.nMax);
2914 DestroyWindow(hwndRichEdit);
2916 hwndRichEdit = new_richedit(NULL);
2918 DISABLE_WS_VSCROLL(hwndRichEdit);
2919 memset(&si, 0, sizeof(si));
2920 si.cbSize = sizeof(si);
2921 si.fMask = SIF_PAGE | SIF_RANGE;
2922 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2923 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2924 "Vertical scrollbar is visible, should be invisible.\n");
2925 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2926 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2927 si.nPage, si.nMin, si.nMax);
2929 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2930 memset(&si, 0, sizeof(si));
2931 si.cbSize = sizeof(si);
2932 si.fMask = SIF_PAGE | SIF_RANGE;
2933 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2934 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2935 "Vertical scrollbar is visible, should be invisible.\n");
2936 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2937 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2938 si.nPage, si.nMin, si.nMax);
2940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2941 memset(&si, 0, sizeof(si));
2942 si.cbSize = sizeof(si);
2943 si.fMask = SIF_PAGE | SIF_RANGE;
2944 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2945 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2946 "Vertical scrollbar is visible, should be invisible.\n");
2947 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2948 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2949 si.nPage, si.nMin, si.nMax);
2951 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2952 memset(&si, 0, sizeof(si));
2953 si.cbSize = sizeof(si);
2954 si.fMask = SIF_PAGE | SIF_RANGE;
2955 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2956 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2957 "Vertical scrollbar is visible, should be invisible.\n");
2958 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2959 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2960 si.nPage, si.nMin, si.nMax);
2962 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2963 memset(&si, 0, sizeof(si));
2964 si.cbSize = sizeof(si);
2965 si.fMask = SIF_PAGE | SIF_RANGE;
2966 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2967 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2968 "Vertical scrollbar is invisible, should be visible.\n");
2969 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2970 "reported page/range is %d (%d..%d)\n",
2971 si.nPage, si.nMin, si.nMax);
2973 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2974 DISABLE_WS_VSCROLL(hwndRichEdit);
2975 memset(&si, 0, sizeof(si));
2976 si.cbSize = sizeof(si);
2977 si.fMask = SIF_PAGE | SIF_RANGE;
2978 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2979 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2980 "Vertical scrollbar is visible, should be invisible.\n");
2981 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2982 "reported page/range is %d (%d..%d)\n",
2983 si.nPage, si.nMin, si.nMax);
2985 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2986 memset(&si, 0, sizeof(si));
2987 si.cbSize = sizeof(si);
2988 si.fMask = SIF_PAGE | SIF_RANGE;
2989 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2990 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2991 "Vertical scrollbar is visible, should be invisible.\n");
2992 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2993 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2994 si.nPage, si.nMin, si.nMax);
2996 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2997 memset(&si, 0, sizeof(si));
2998 si.cbSize = sizeof(si);
2999 si.fMask = SIF_PAGE | SIF_RANGE;
3000 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3001 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3002 "Vertical scrollbar is invisible, should be visible.\n");
3003 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3004 "reported page/range is %d (%d..%d)\n",
3005 si.nPage, si.nMin, si.nMax);
3007 DISABLE_WS_VSCROLL(hwndRichEdit);
3008 memset(&si, 0, sizeof(si));
3009 si.cbSize = sizeof(si);
3010 si.fMask = SIF_PAGE | SIF_RANGE;
3011 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3012 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3013 "Vertical scrollbar is visible, should be invisible.\n");
3014 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3015 "reported page/range is %d (%d..%d)\n",
3016 si.nPage, si.nMin, si.nMax);
3018 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3019 EM_SCROLL will make visible any forcefully invisible scrollbar */
3020 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3021 memset(&si, 0, sizeof(si));
3022 si.cbSize = sizeof(si);
3023 si.fMask = SIF_PAGE | SIF_RANGE;
3024 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3025 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3026 "Vertical scrollbar is invisible, should be visible.\n");
3027 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3028 "reported page/range is %d (%d..%d)\n",
3029 si.nPage, si.nMin, si.nMax);
3031 DISABLE_WS_VSCROLL(hwndRichEdit);
3032 memset(&si, 0, sizeof(si));
3033 si.cbSize = sizeof(si);
3034 si.fMask = SIF_PAGE | SIF_RANGE;
3035 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3036 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3037 "Vertical scrollbar is visible, should be invisible.\n");
3038 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3039 "reported page/range is %d (%d..%d)\n",
3040 si.nPage, si.nMin, si.nMax);
3042 /* Again, EM_SCROLL, with SB_LINEUP */
3043 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3044 memset(&si, 0, sizeof(si));
3045 si.cbSize = sizeof(si);
3046 si.fMask = SIF_PAGE | SIF_RANGE;
3047 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3048 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3049 "Vertical scrollbar is invisible, should be visible.\n");
3050 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3051 "reported page/range is %d (%d..%d)\n",
3052 si.nPage, si.nMin, si.nMax);
3054 DestroyWindow(hwndRichEdit);
3056 /* This window proc models what is going on with Corman Lisp 3.0.
3057 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3058 force the scrollbar into visibility. Recursion should NOT happen
3059 as a result of this action.
3061 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3063 richeditProc = cls.lpfnWndProc;
3064 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3065 cls.lpszClassName = "RicheditStupidOverride";
3066 if(!RegisterClassA(&cls)) assert(0);
3069 WM_SIZE_recursionLevel = 0;
3070 bailedOutOfRecursion = FALSE;
3071 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3072 ok(!bailedOutOfRecursion,
3073 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3076 WM_SIZE_recursionLevel = 0;
3077 bailedOutOfRecursion = FALSE;
3078 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3079 ok(!bailedOutOfRecursion,
3080 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3082 /* Unblock window in order to process WM_DESTROY */
3084 bailedOutOfRecursion = FALSE;
3085 WM_SIZE_recursionLevel = 0;
3086 DestroyWindow(hwndRichEdit);
3090 static void test_EM_SETUNDOLIMIT(void)
3092 /* cases we test for:
3093 * default behaviour - limiting at 100 undo's
3094 * undo disabled - setting a limit of 0
3095 * undo limited - undo limit set to some to some number, like 2
3096 * bad input - sending a negative number should default to 100 undo's */
3098 HWND hwndRichEdit = new_richedit(NULL);
3103 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3106 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3107 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3108 also, multiple pastes don't combine like WM_CHAR would */
3109 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3111 /* first case - check the default */
3112 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3113 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3114 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3115 for (i=0; i<100; i++) /* Undo 100 of them */
3116 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3117 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3118 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3120 /* second case - cannot undo */
3121 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3122 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3123 SendMessage(hwndRichEdit,
3124 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3125 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3126 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3128 /* third case - set it to an arbitrary number */
3129 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3130 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3131 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3132 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3133 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3134 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3135 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3136 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3137 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3138 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3139 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3140 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3141 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3142 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3144 /* fourth case - setting negative numbers should default to 100 undos */
3145 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3146 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3148 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3150 DestroyWindow(hwndRichEdit);
3153 static void test_ES_PASSWORD(void)
3155 /* This isn't hugely testable, so we're just going to run it through its paces */
3157 HWND hwndRichEdit = new_richedit(NULL);
3160 /* First, check the default of a regular control */
3161 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3163 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3165 /* Now, set it to something normal */
3166 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3167 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3169 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3171 /* Now, set it to something odd */
3172 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3173 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3175 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3176 DestroyWindow(hwndRichEdit);
3179 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3184 char** str = (char**)dwCookie;
3187 memcpy(*str, pbBuff, *pcb);
3193 static void test_WM_SETTEXT()
3195 HWND hwndRichEdit = new_richedit(NULL);
3196 const char * TestItem1 = "TestSomeText";
3197 const char * TestItem2 = "TestSomeText\r";
3198 const char * TestItem2_after = "TestSomeText\r\n";
3199 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3200 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3201 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3202 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3203 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3204 const char * TestItem5_after = "TestSomeText TestSomeText";
3205 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3206 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3207 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3208 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3210 char buf[1024] = {0};
3215 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3216 any solitary \r to be converted to \r\n on return. Properly paired
3217 \r\n are not affected. It also shows that the special sequence \r\r\n
3218 gets converted to a single space.
3221 #define TEST_SETTEXT(a, b) \
3222 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3223 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3224 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3225 ok (result == lstrlen(buf), \
3226 "WM_GETTEXT returned %ld instead of expected %u\n", \
3227 result, lstrlen(buf)); \
3228 result = strcmp(b, buf); \
3230 "WM_SETTEXT round trip: strcmp = %ld\n", result);
3232 TEST_SETTEXT(TestItem1, TestItem1)
3233 TEST_SETTEXT(TestItem2, TestItem2_after)
3234 TEST_SETTEXT(TestItem3, TestItem3_after)
3235 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3236 TEST_SETTEXT(TestItem4, TestItem4_after)
3237 TEST_SETTEXT(TestItem5, TestItem5_after)
3238 TEST_SETTEXT(TestItem6, TestItem6_after)
3239 TEST_SETTEXT(TestItem7, TestItem7_after)
3241 /* The following test demonstrates that WM_SETTEXT supports RTF strings */
3242 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3244 es.dwCookie = (DWORD_PTR)&p;
3246 es.pfnCallback = test_WM_SETTEXT_esCallback;
3247 memset(buf, 0, sizeof(buf));
3248 SendMessage(hwndRichEdit, EM_STREAMOUT,
3249 (WPARAM)(SF_RTF), (LPARAM)&es);
3250 trace("EM_STREAMOUT produced: \n%s\n", buf);
3251 TEST_SETTEXT(buf, TestItem1)
3254 DestroyWindow(hwndRichEdit);
3257 static void test_EM_STREAMOUT(void)
3259 HWND hwndRichEdit = new_richedit(NULL);
3262 char buf[1024] = {0};
3265 const char * TestItem1 = "TestSomeText";
3266 const char * TestItem2 = "TestSomeText\r";
3267 const char * TestItem3 = "TestSomeText\r\n";
3269 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3271 es.dwCookie = (DWORD_PTR)&p;
3273 es.pfnCallback = test_WM_SETTEXT_esCallback;
3274 memset(buf, 0, sizeof(buf));
3275 SendMessage(hwndRichEdit, EM_STREAMOUT,
3276 (WPARAM)(SF_TEXT), (LPARAM)&es);
3278 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3279 ok(strcmp(buf, TestItem1) == 0,
3280 "streamed text different, got %s\n", buf);
3282 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3284 es.dwCookie = (DWORD_PTR)&p;
3286 es.pfnCallback = test_WM_SETTEXT_esCallback;
3287 memset(buf, 0, sizeof(buf));
3288 SendMessage(hwndRichEdit, EM_STREAMOUT,
3289 (WPARAM)(SF_TEXT), (LPARAM)&es);
3291 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3292 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3293 ok(strcmp(buf, TestItem3) == 0,
3294 "streamed text different from, got %s\n", buf);
3295 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3297 es.dwCookie = (DWORD_PTR)&p;
3299 es.pfnCallback = test_WM_SETTEXT_esCallback;
3300 memset(buf, 0, sizeof(buf));
3301 SendMessage(hwndRichEdit, EM_STREAMOUT,
3302 (WPARAM)(SF_TEXT), (LPARAM)&es);
3304 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3305 ok(strcmp(buf, TestItem3) == 0,
3306 "streamed text different, got %s\n", buf);
3308 DestroyWindow(hwndRichEdit);
3311 static void test_EM_SETTEXTEX(void)
3313 HWND hwndRichEdit = new_richedit(NULL);
3316 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3318 'T', 'e', 'x', 't', 0};
3319 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3325 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3326 '\r','t','S','o','m','e','T','e','x','t',0};
3327 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3331 const char * TestItem2_after = "TestSomeText\r\n";
3332 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3335 '\r','\n','\r','\n', 0};
3336 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3340 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3344 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3347 '\r','\r','\n','\r',
3349 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3353 #define MAX_BUF_LEN 1024
3354 WCHAR buf[MAX_BUF_LEN];
3355 char bufACP[MAX_BUF_LEN];
3361 setText.codepage = 1200; /* no constant for unicode */
3362 getText.codepage = 1200; /* no constant for unicode */
3363 getText.cb = MAX_BUF_LEN;
3364 getText.flags = GT_DEFAULT;
3365 getText.lpDefaultChar = NULL;
3366 getText.lpUsedDefChar = NULL;
3369 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3370 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3371 ok(lstrcmpW(buf, TestItem1) == 0,
3372 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3374 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3375 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3377 setText.codepage = 1200; /* no constant for unicode */
3378 getText.codepage = 1200; /* no constant for unicode */
3379 getText.cb = MAX_BUF_LEN;
3380 getText.flags = GT_DEFAULT;
3381 getText.lpDefaultChar = NULL;
3382 getText.lpUsedDefChar = NULL;
3384 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3385 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3386 ok(lstrcmpW(buf, TestItem2) == 0,
3387 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3389 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3390 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3391 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3392 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3394 /* Baseline test for just-enough buffer space for string */
3395 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3396 getText.codepage = 1200; /* no constant for unicode */
3397 getText.flags = GT_DEFAULT;
3398 getText.lpDefaultChar = NULL;
3399 getText.lpUsedDefChar = NULL;
3400 memset(buf, 0, MAX_BUF_LEN);
3401 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3402 ok(lstrcmpW(buf, TestItem2) == 0,
3403 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3405 /* When there is enough space for one character, but not both, of the CRLF
3406 pair at the end of the string, the CR is not copied at all. That is,
3407 the caller must not see CRLF pairs truncated to CR at the end of the
3410 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3411 getText.codepage = 1200; /* no constant for unicode */
3412 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3413 getText.lpDefaultChar = NULL;
3414 getText.lpUsedDefChar = NULL;
3415 memset(buf, 0, MAX_BUF_LEN);
3416 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3417 ok(lstrcmpW(buf, TestItem1) == 0,
3418 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3421 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3422 setText.codepage = 1200; /* no constant for unicode */
3423 getText.codepage = 1200; /* no constant for unicode */
3424 getText.cb = MAX_BUF_LEN;
3425 getText.flags = GT_DEFAULT;
3426 getText.lpDefaultChar = NULL;
3427 getText.lpUsedDefChar = NULL;
3429 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3430 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3431 ok(lstrcmpW(buf, TestItem3_after) == 0,
3432 "EM_SETTEXTEX did not convert properly\n");
3434 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3435 setText.codepage = 1200; /* no constant for unicode */
3436 getText.codepage = 1200; /* no constant for unicode */
3437 getText.cb = MAX_BUF_LEN;
3438 getText.flags = GT_DEFAULT;
3439 getText.lpDefaultChar = NULL;
3440 getText.lpUsedDefChar = NULL;
3442 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3443 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3444 ok(lstrcmpW(buf, TestItem3_after) == 0,
3445 "EM_SETTEXTEX did not convert properly\n");
3447 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3448 setText.codepage = 1200; /* no constant for unicode */
3449 getText.codepage = 1200; /* no constant for unicode */
3450 getText.cb = MAX_BUF_LEN;
3451 getText.flags = GT_DEFAULT;
3452 getText.lpDefaultChar = NULL;
3453 getText.lpUsedDefChar = NULL;
3455 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3456 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3457 ok(lstrcmpW(buf, TestItem4_after) == 0,
3458 "EM_SETTEXTEX did not convert properly\n");
3460 /* !ST_SELECTION && Unicode && !\rtf */
3461 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3462 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3465 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3466 ok(lstrlenW(buf) == 0,
3467 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3469 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3471 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3472 /* select some text */
3475 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3476 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3477 setText.flags = ST_SELECTION;
3478 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3480 "EM_SETTEXTEX with NULL lParam to replace selection"
3481 " with no text should return 0. Got %i\n",
3484 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3486 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3487 /* select some text */
3490 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3491 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3492 setText.flags = ST_SELECTION;
3493 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3494 (WPARAM)&setText, (LPARAM) TestItem1);
3496 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3497 ok(result == lstrlenW(TestItem1),
3498 "EM_SETTEXTEX with NULL lParam to replace selection"
3499 " with no text should return 0. Got %i\n",
3501 ok(lstrlenW(buf) == 22,
3502 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3505 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3508 es.dwCookie = (DWORD_PTR)&p;
3510 es.pfnCallback = test_WM_SETTEXT_esCallback;
3511 memset(buf, 0, sizeof(buf));
3512 SendMessage(hwndRichEdit, EM_STREAMOUT,
3513 (WPARAM)(SF_RTF), (LPARAM)&es);
3514 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3516 /* !ST_SELECTION && !Unicode && \rtf */
3517 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3518 getText.codepage = 1200; /* no constant for unicode */
3519 getText.cb = MAX_BUF_LEN;
3520 getText.flags = GT_DEFAULT;
3521 getText.lpDefaultChar = NULL;
3522 getText.lpUsedDefChar = NULL;
3525 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3526 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3527 ok(lstrcmpW(buf, TestItem1) == 0,
3528 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3530 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3531 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3533 es.dwCookie = (DWORD_PTR)&p;
3535 es.pfnCallback = test_WM_SETTEXT_esCallback;
3536 memset(buf, 0, sizeof(buf));
3537 SendMessage(hwndRichEdit, EM_STREAMOUT,
3538 (WPARAM)(SF_RTF), (LPARAM)&es);
3539 trace("EM_STREAMOUT produced: \n%s\n", (char *)buf);
3541 /* select some text */
3544 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3546 /* ST_SELECTION && !Unicode && \rtf */
3547 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3548 getText.codepage = 1200; /* no constant for unicode */
3549 getText.cb = MAX_BUF_LEN;
3550 getText.flags = GT_DEFAULT;
3551 getText.lpDefaultChar = NULL;
3552 getText.lpUsedDefChar = NULL;
3554 setText.flags = ST_SELECTION;
3555 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3556 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3557 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3559 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3560 setText.codepage = 1200; /* no constant for unicode */
3561 getText.codepage = CP_ACP;
3562 getText.cb = MAX_BUF_LEN;
3565 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3566 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3568 /* select some text */
3571 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3573 /* ST_SELECTION && !Unicode && !\rtf */
3574 setText.codepage = CP_ACP;
3575 getText.codepage = 1200; /* no constant for unicode */
3576 getText.cb = MAX_BUF_LEN;
3577 getText.flags = GT_DEFAULT;
3578 getText.lpDefaultChar = NULL;
3579 getText.lpUsedDefChar = NULL;
3581 setText.flags = ST_SELECTION;
3582 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3583 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3584 ok(lstrcmpW(buf, TestItem1alt) == 0,
3585 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3586 " using ST_SELECTION and non-Unicode\n");
3588 /* Test setting text using rich text format */
3590 setText.codepage = CP_ACP;
3591 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3592 getText.codepage = CP_ACP;
3593 getText.cb = MAX_BUF_LEN;
3594 getText.flags = GT_DEFAULT;
3595 getText.lpDefaultChar = NULL;
3596 getText.lpUsedDefChar = NULL;
3597 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3598 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3601 setText.codepage = CP_ACP;
3602 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3603 getText.codepage = CP_ACP;
3604 getText.cb = MAX_BUF_LEN;
3605 getText.flags = GT_DEFAULT;
3606 getText.lpDefaultChar = NULL;
3607 getText.lpUsedDefChar = NULL;
3608 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3609 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3611 DestroyWindow(hwndRichEdit);
3614 static void test_EM_LIMITTEXT(void)
3618 HWND hwndRichEdit = new_richedit(NULL);
3620 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3621 * about setting the length to -1 for multiline edit controls doesn't happen.
3624 /* Don't check default gettextlimit case. That's done in other tests */
3626 /* Set textlimit to 100 */
3627 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3628 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3630 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3632 /* Set textlimit to 0 */
3633 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3634 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3636 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3638 /* Set textlimit to -1 */
3639 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3640 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3642 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3644 /* Set textlimit to -2 */
3645 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3646 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3648 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3650 DestroyWindow (hwndRichEdit);
3654 static void test_EM_EXLIMITTEXT(void)
3656 int i, selBegin, selEnd, len1, len2;
3658 char text[1024 + 1];
3659 char buffer[1024 + 1];
3660 int textlimit = 0; /* multiple of 100 */
3661 HWND hwndRichEdit = new_richedit(NULL);
3663 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3664 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3667 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3668 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3670 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3673 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3674 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3676 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3678 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3679 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3680 /* default for WParam = 0 */
3681 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3683 textlimit = sizeof(text)-1;
3684 memset(text, 'W', textlimit);
3685 text[sizeof(text)-1] = 0;
3686 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3687 /* maxed out text */
3688 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3690 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3691 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3692 len1 = selEnd - selBegin;
3694 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3695 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3696 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3697 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3698 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3699 len2 = selEnd - selBegin;
3702 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3705 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3706 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3707 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3708 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3709 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3710 len1 = selEnd - selBegin;
3713 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3716 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3717 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3718 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3719 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3720 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3721 len2 = selEnd - selBegin;
3724 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3727 /* set text up to the limit, select all the text, then add a char */
3729 memset(text, 'W', textlimit);
3730 text[textlimit] = 0;
3731 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3732 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3733 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3734 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3735 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3736 result = strcmp(buffer, "A");
3737 ok(0 == result, "got string = \"%s\"\n", buffer);
3739 /* WM_SETTEXT not limited */
3741 memset(text, 'W', textlimit);
3742 text[textlimit] = 0;
3743 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3744 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3745 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3747 ok(10 == i, "expected 10 chars\n");
3748 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3749 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3751 /* try inserting more text at end */
3752 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3753 ok(0 == i, "WM_CHAR wasn't processed\n");
3754 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3756 ok(10 == i, "expected 10 chars, got %i\n", i);
3757 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3758 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3760 /* try inserting text at beginning */
3761 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
3762 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3763 ok(0 == i, "WM_CHAR wasn't processed\n");
3764 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3766 ok(10 == i, "expected 10 chars, got %i\n", i);
3767 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3768 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3770 /* WM_CHAR is limited */
3772 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3773 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3774 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3775 ok(0 == i, "WM_CHAR wasn't processed\n");
3776 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3777 ok(0 == i, "WM_CHAR wasn't processed\n");
3778 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3780 ok(1 == i, "expected 1 chars, got %i instead\n", i);
3782 DestroyWindow(hwndRichEdit);
3785 static void test_EM_GETLIMITTEXT(void)
3788 HWND hwndRichEdit = new_richedit(NULL);
3790 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3791 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
3793 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
3794 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3795 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
3797 DestroyWindow(hwndRichEdit);
3800 static void test_WM_SETFONT(void)
3802 /* There is no invalid input or error conditions for this function.
3803 * NULL wParam and lParam just fall back to their default values
3804 * It should be noted that even if you use a gibberish name for your fonts
3805 * here, it will still work because the name is stored. They will display as
3806 * System, but will report their name to be whatever they were created as */
3808 HWND hwndRichEdit = new_richedit(NULL);
3809 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3810 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3811 FF_DONTCARE, "Marlett");
3812 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3813 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3814 FF_DONTCARE, "MS Sans Serif");
3815 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3816 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3817 FF_DONTCARE, "Courier");
3818 LOGFONTA sentLogFont;
3819 CHARFORMAT2A returnedCF2A;
3821 returnedCF2A.cbSize = sizeof(returnedCF2A);
3823 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3824 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
3825 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3827 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
3828 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3829 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
3830 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3832 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
3833 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3834 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
3835 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3836 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
3837 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3839 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
3840 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3841 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
3842 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
3843 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
3844 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
3846 /* This last test is special since we send in NULL. We clear the variables
3847 * and just compare to "System" instead of the sent in font name. */
3848 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
3849 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
3850 returnedCF2A.cbSize = sizeof(returnedCF2A);
3852 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
3853 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
3854 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
3855 ok (!strcmp("System",returnedCF2A.szFaceName),
3856 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
3858 DestroyWindow(hwndRichEdit);
3862 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
3867 const char** str = (const char**)dwCookie;
3868 int size = strlen(*str);
3869 if(size > 3) /* let's make it piecemeal for fun */
3876 memcpy(pbBuff, *str, *pcb);
3882 static void test_EM_GETMODIFY(void)
3884 HWND hwndRichEdit = new_richedit(NULL);
3887 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3889 'T', 'e', 'x', 't', 0};
3890 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3892 'O', 't', 'h', 'e', 'r',
3893 'T', 'e', 'x', 't', 0};
3894 const char* streamText = "hello world";
3899 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
3900 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
3901 FF_DONTCARE, "Courier");
3903 setText.codepage = 1200; /* no constant for unicode */
3904 setText.flags = ST_KEEPUNDO;
3907 /* modify flag shouldn't be set when richedit is first created */
3908 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3910 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
3912 /* setting modify flag should actually set it */
3913 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
3914 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3916 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
3918 /* clearing modify flag should actually clear it */
3919 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3920 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3922 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
3924 /* setting font doesn't change modify flag */
3925 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3926 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
3927 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3929 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
3931 /* setting text should set modify flag */
3932 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3933 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3934 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3936 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
3938 /* undo previous text doesn't reset modify flag */
3939 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3940 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3942 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
3944 /* set text with no flag to keep undo stack should not set modify flag */
3945 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3947 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3948 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3950 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
3952 /* WM_SETTEXT doesn't modify */
3953 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3954 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3955 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3957 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
3959 /* clear the text */
3960 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3961 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
3962 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3964 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
3967 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3968 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3969 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3970 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
3971 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3973 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
3975 /* copy/paste text 1 */
3976 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3977 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3978 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3979 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3980 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3982 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
3984 /* copy/paste text 2 */
3985 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3986 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
3987 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3988 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
3989 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3990 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
3992 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
3995 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
3996 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
3997 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3998 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4000 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4003 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4004 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4005 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4006 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4008 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4010 /* set char format */
4011 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4012 cf2.cbSize = sizeof(CHARFORMAT2);
4013 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4015 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4016 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4017 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4018 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4019 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4020 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4022 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4024 /* set para format */
4025 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4026 pf2.cbSize = sizeof(PARAFORMAT2);
4027 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4029 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4030 pf2.wAlignment = PFA_RIGHT;
4031 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4032 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4034 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4037 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4038 es.dwCookie = (DWORD_PTR)&streamText;
4040 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4041 SendMessage(hwndRichEdit, EM_STREAMIN,
4042 (WPARAM)(SF_TEXT), (LPARAM)&es);
4043 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4045 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4047 DestroyWindow(hwndRichEdit);
4053 long expected_retval;
4054 int expected_getsel_start;
4055 int expected_getsel_end;
4056 int _exsetsel_todo_wine;
4057 int _getsel_todo_wine;
4060 const struct exsetsel_s exsetsel_tests[] = {
4062 {5, 10, 10, 5, 10, 0, 0},
4063 {15, 17, 17, 15, 17, 0, 0},
4064 /* test cpMax > strlen() */
4065 {0, 100, 18, 0, 18, 0, 1},
4066 /* test cpMin == cpMax */
4067 {5, 5, 5, 5, 5, 0, 0},
4068 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4069 {-1, 0, 5, 5, 5, 0, 0},
4070 {-1, 17, 5, 5, 5, 0, 0},
4071 {-1, 18, 5, 5, 5, 0, 0},
4072 /* test cpMin < 0 && cpMax < 0 */
4073 {-1, -1, 17, 17, 17, 0, 0},
4074 {-4, -5, 17, 17, 17, 0, 0},
4075 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4076 {0, -1, 18, 0, 18, 0, 1},
4077 {17, -5, 18, 17, 18, 0, 1},
4078 {18, -3, 17, 17, 17, 0, 0},
4079 /* test if cpMin > cpMax */
4080 {15, 19, 18, 15, 18, 0, 1},
4081 {19, 15, 18, 15, 18, 0, 1}
4084 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4089 cr.cpMin = setsel->min;
4090 cr.cpMax = setsel->max;
4091 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4093 if (setsel->_exsetsel_todo_wine) {
4095 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4098 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4101 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4103 if (setsel->_getsel_todo_wine) {
4105 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);
4108 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);
4112 static void test_EM_EXSETSEL(void)
4114 HWND hwndRichEdit = new_richedit(NULL);
4116 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4118 /* sending some text to the window */
4119 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4120 /* 01234567890123456*/
4123 for (i = 0; i < num_tests; i++) {
4124 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4127 DestroyWindow(hwndRichEdit);
4130 static void test_EM_REPLACESEL(int redraw)
4132 HWND hwndRichEdit = new_richedit(NULL);
4133 char buffer[1024] = {0};
4138 /* sending some text to the window */
4139 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4140 /* 01234567890123456*/
4143 /* FIXME add more tests */
4144 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4145 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4146 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4147 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4148 r = strcmp(buffer, "testing");
4149 ok(0 == r, "expected %d, got %d\n", 0, r);
4151 DestroyWindow(hwndRichEdit);
4153 hwndRichEdit = new_richedit(NULL);
4155 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4156 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4158 /* Test behavior with carriage returns and newlines */
4159 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4160 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4161 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4162 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4163 r = strcmp(buffer, "RichEdit1");
4164 ok(0 == r, "expected %d, got %d\n", 0, r);
4166 getText.codepage = CP_ACP;
4167 getText.flags = GT_DEFAULT;
4168 getText.lpDefaultChar = NULL;
4169 getText.lpUsedDefChar = NULL;
4170 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4171 ok(strcmp(buffer, "RichEdit1") == 0,
4172 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4174 /* Test number of lines reported after EM_REPLACESEL */
4175 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4176 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4179 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4180 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4181 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4182 r = strcmp(buffer, "RichEdit1\r\n");
4183 ok(0 == r, "expected %d, got %d\n", 0, r);
4185 getText.codepage = CP_ACP;
4186 getText.flags = GT_DEFAULT;
4187 getText.lpDefaultChar = NULL;
4188 getText.lpUsedDefChar = NULL;
4189 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4190 ok(strcmp(buffer, "RichEdit1\r") == 0,
4191 "EM_GETTEXTEX returned incorrect string\n");
4193 /* Test number of lines reported after EM_REPLACESEL */
4194 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4195 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4197 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4198 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4199 returns the number of characters *inserted* into the control (after
4200 required conversions), but WinXP's riched20 returns the number of
4201 characters interpreted from the original lParam. Wine's builtin riched20
4202 implements the WinXP behavior.
4204 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4205 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4206 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4207 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4209 /* Test number of lines reported after EM_REPLACESEL */
4210 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4211 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4213 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4214 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4215 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4216 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4218 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4219 r = strcmp(buffer, "RichEdit1\r\n");
4220 ok(0 == r, "expected %d, got %d\n", 0, r);
4222 getText.codepage = CP_ACP;
4223 getText.flags = GT_DEFAULT;
4224 getText.lpDefaultChar = NULL;
4225 getText.lpUsedDefChar = NULL;
4226 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4227 ok(strcmp(buffer, "RichEdit1\r") == 0,
4228 "EM_GETTEXTEX returned incorrect string\n");
4230 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4231 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4232 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4233 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4235 /* The following tests show that richedit should handle the special \r\r\n
4236 sequence by turning it into a single space on insertion. However,
4237 EM_REPLACESEL on WinXP returns the number of characters in the original
4241 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4242 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4243 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4244 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4245 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4246 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4247 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4249 /* Test the actual string */
4251 getText.codepage = CP_ACP;
4252 getText.flags = GT_DEFAULT;
4253 getText.lpDefaultChar = NULL;
4254 getText.lpUsedDefChar = NULL;
4255 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4256 ok(strcmp(buffer, "\r\r") == 0,
4257 "EM_GETTEXTEX returned incorrect string\n");
4259 /* Test number of lines reported after EM_REPLACESEL */
4260 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4261 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4263 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4264 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4265 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4266 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4267 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4268 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4269 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4270 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4272 /* Test the actual string */
4274 getText.codepage = CP_ACP;
4275 getText.flags = GT_DEFAULT;
4276 getText.lpDefaultChar = NULL;
4277 getText.lpUsedDefChar = NULL;
4278 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4279 ok(strcmp(buffer, " ") == 0,
4280 "EM_GETTEXTEX returned incorrect string\n");
4282 /* Test number of lines reported after EM_REPLACESEL */
4283 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4284 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4286 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4287 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4288 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4289 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4290 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4291 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4292 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4293 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4295 /* Test the actual string */
4297 getText.codepage = CP_ACP;
4298 getText.flags = GT_DEFAULT;
4299 getText.lpDefaultChar = NULL;
4300 getText.lpUsedDefChar = NULL;
4301 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4302 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4303 "EM_GETTEXTEX returned incorrect string\n");
4305 /* Test number of lines reported after EM_REPLACESEL */
4306 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4307 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4309 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4310 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4311 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4312 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4313 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4314 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4315 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4316 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4318 /* Test the actual string */
4320 getText.codepage = CP_ACP;
4321 getText.flags = GT_DEFAULT;
4322 getText.lpDefaultChar = NULL;
4323 getText.lpUsedDefChar = NULL;
4324 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4325 ok(strcmp(buffer, " \r") == 0,
4326 "EM_GETTEXTEX returned incorrect string\n");
4328 /* Test number of lines reported after EM_REPLACESEL */
4329 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4330 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4332 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4333 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4334 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4335 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4336 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4337 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4338 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4339 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4341 /* Test the actual string */
4343 getText.codepage = CP_ACP;
4344 getText.flags = GT_DEFAULT;
4345 getText.lpDefaultChar = NULL;
4346 getText.lpUsedDefChar = NULL;
4347 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4348 ok(strcmp(buffer, " \r\r") == 0,
4349 "EM_GETTEXTEX returned incorrect string\n");
4351 /* Test number of lines reported after EM_REPLACESEL */
4352 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4353 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4355 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4356 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4357 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4358 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4359 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4360 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4361 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4362 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4364 /* Test the actual string */
4366 getText.codepage = CP_ACP;
4367 getText.flags = GT_DEFAULT;
4368 getText.lpDefaultChar = NULL;
4369 getText.lpUsedDefChar = NULL;
4370 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4371 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4372 "EM_GETTEXTEX returned incorrect string\n");
4374 /* Test number of lines reported after EM_REPLACESEL */
4375 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4376 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4378 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4379 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4380 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4381 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4382 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4383 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4384 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4386 /* Test the actual string */
4388 getText.codepage = CP_ACP;
4389 getText.flags = GT_DEFAULT;
4390 getText.lpDefaultChar = NULL;
4391 getText.lpUsedDefChar = NULL;
4392 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4393 ok(strcmp(buffer, "\r\r") == 0,
4394 "EM_GETTEXTEX returned incorrect string\n");
4396 /* Test number of lines reported after EM_REPLACESEL */
4397 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4398 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4400 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4401 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4402 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4403 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4404 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4405 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4406 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4407 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4409 /* Test the actual string */
4411 getText.codepage = CP_ACP;
4412 getText.flags = GT_DEFAULT;
4413 getText.lpDefaultChar = NULL;
4414 getText.lpUsedDefChar = NULL;
4415 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4416 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4417 "EM_GETTEXTEX returned incorrect string\n");
4419 /* Test number of lines reported after EM_REPLACESEL */
4420 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4421 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4424 /* This is needed to avoid interferring with keybd_event calls
4425 * on other tests that simulate keyboard events. */
4426 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4428 DestroyWindow(hwndRichEdit);
4431 static void test_WM_PASTE(void)
4434 char buffer[1024] = {0};
4435 const char* text1 = "testing paste\r";
4436 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4437 const char* text1_after = "testing paste\r\n";
4438 const char* text2 = "testing paste\r\rtesting paste";
4439 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4440 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4441 HWND hwndRichEdit = new_richedit(NULL);
4443 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4444 * to test the state of the modifiers (Ctrl/Alt/Shift).
4446 * Therefore Ctrl-<key> keystrokes need to be simulated with
4447 * keybd_event or by using SetKeyboardState to set the modifiers
4448 * and SendMessage to simulate the keystrokes.
4451 /* Sent keystrokes with keybd_event */
4452 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4453 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4454 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4455 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4456 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4458 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4459 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4461 SEND_CTRL_C(hwndRichEdit); /* Copy */
4462 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4463 SEND_CTRL_V(hwndRichEdit); /* Paste */
4464 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4465 /* Pasted text should be visible at this step */
4466 result = strcmp(text1_step1, buffer);
4468 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4470 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4471 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4472 /* Text should be the same as before (except for \r -> \r\n conversion) */
4473 result = strcmp(text1_after, buffer);
4475 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4477 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4478 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4479 SEND_CTRL_C(hwndRichEdit); /* Copy */
4480 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4481 SEND_CTRL_V(hwndRichEdit); /* Paste */
4482 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4483 /* Pasted text should be visible at this step */
4484 result = strcmp(text3, buffer);
4486 "test paste: strcmp = %i\n", result);
4487 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4488 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4489 /* Text should be the same as before (except for \r -> \r\n conversion) */
4490 result = strcmp(text2_after, buffer);
4492 "test paste: strcmp = %i\n", result);
4493 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4494 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4495 /* Text should revert to post-paste state */
4496 result = strcmp(buffer,text3);
4498 "test paste: strcmp = %i\n", result);
4506 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4507 /* Send WM_CHAR to simulates Ctrl-V */
4508 SendMessage(hwndRichEdit, WM_CHAR, 22,
4509 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4510 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4511 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4512 result = strcmp(buffer,"");
4514 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4516 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4517 * with SetKeyboard state. */
4519 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4520 /* Simulates paste (Ctrl-V) */
4521 hold_key(VK_CONTROL);
4522 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4523 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4524 release_key(VK_CONTROL);
4525 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4526 result = strcmp(buffer,"paste");
4528 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4530 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4531 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4532 /* Simulates copy (Ctrl-C) */
4533 hold_key(VK_CONTROL);
4534 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4535 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4536 release_key(VK_CONTROL);
4537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4538 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4539 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4540 result = strcmp(buffer,"testing");
4542 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4544 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4545 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4546 /* Simulates select all (Ctrl-A) */
4547 hold_key(VK_CONTROL);
4548 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4549 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4550 /* Simulates select cut (Ctrl-X) */
4551 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4552 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4553 release_key(VK_CONTROL);
4554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4555 result = strcmp(buffer,"");
4557 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4558 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4559 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4560 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4561 result = strcmp(buffer,"cut\r\n");
4562 todo_wine ok(result == 0,
4563 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4564 /* Simulates undo (Ctrl-Z) */
4565 hold_key(VK_CONTROL);
4566 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4567 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4568 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4569 result = strcmp(buffer,"");
4571 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4572 /* Simulates redo (Ctrl-Y) */
4573 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4574 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4575 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4576 result = strcmp(buffer,"cut\r\n");
4577 todo_wine ok(result == 0,
4578 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4579 release_key(VK_CONTROL);
4581 DestroyWindow(hwndRichEdit);
4584 static void test_EM_FORMATRANGE(void)
4589 HWND hwndRichEdit = new_richedit(NULL);
4591 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
4593 hdc = GetDC(hwndRichEdit);
4594 ok(hdc != NULL, "Could not get HDC\n");
4596 fr.hdc = fr.hdcTarget = hdc;
4597 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4598 fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES);
4599 fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES);
4603 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4605 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4608 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4610 ok(r == 20 || r == 9, "EM_FORMATRANGE expect 20 or 9, got %d\n", r);
4616 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4618 ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r);
4621 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4623 ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r);
4626 DestroyWindow(hwndRichEdit);
4629 static int nCallbackCount = 0;
4631 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4634 const char text[] = {'t','e','s','t'};
4636 if (sizeof(text) <= cb)
4638 if ((int)dwCookie != nCallbackCount)
4644 memcpy (pbBuff, text, sizeof(text));
4645 *pcb = sizeof(text);
4652 return 1; /* indicates callback failed */
4655 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4660 const char** str = (const char**)dwCookie;
4661 int size = strlen(*str);
4667 memcpy(pbBuff, *str, *pcb);
4673 struct StringWithLength {
4678 /* This callback is used to handled the null characters in a string. */
4679 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4684 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4685 int size = str->length;
4691 memcpy(pbBuff, str->buffer, *pcb);
4692 str->buffer += *pcb;
4693 str->length -= *pcb;
4698 static void test_EM_STREAMIN(void)
4700 HWND hwndRichEdit = new_richedit(NULL);
4703 char buffer[1024] = {0};
4705 const char * streamText0 = "{\\rtf1 TestSomeText}";
4706 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4707 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4709 const char * streamText1 =
4710 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4711 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4714 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4715 const char * streamText2 =
4716 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
4717 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
4718 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
4719 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
4720 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
4721 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
4722 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
4724 const char * streamText3 = "RichEdit1";
4726 struct StringWithLength cookieForStream4;
4727 const char * streamText4 =
4728 "This text just needs to be long enough to cause run to be split onto "
4729 "two separate lines and make sure the null terminating character is "
4730 "handled properly.\0";
4731 int length4 = strlen(streamText4) + 1;
4732 cookieForStream4.buffer = (char *)streamText4;
4733 cookieForStream4.length = length4;
4735 /* Minimal test without \par at the end */
4736 es.dwCookie = (DWORD_PTR)&streamText0;
4738 es.pfnCallback = test_EM_STREAMIN_esCallback;
4739 SendMessage(hwndRichEdit, EM_STREAMIN,
4740 (WPARAM)(SF_RTF), (LPARAM)&es);
4742 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4744 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
4745 result = strcmp (buffer,"TestSomeText");
4747 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
4748 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
4750 /* Native richedit 2.0 ignores last \par */
4751 es.dwCookie = (DWORD_PTR)&streamText0a;
4753 es.pfnCallback = test_EM_STREAMIN_esCallback;
4754 SendMessage(hwndRichEdit, EM_STREAMIN,
4755 (WPARAM)(SF_RTF), (LPARAM)&es);
4757 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4759 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
4760 result = strcmp (buffer,"TestSomeText");
4762 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
4763 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
4765 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
4766 es.dwCookie = (DWORD_PTR)&streamText0b;
4768 es.pfnCallback = test_EM_STREAMIN_esCallback;
4769 SendMessage(hwndRichEdit, EM_STREAMIN,
4770 (WPARAM)(SF_RTF), (LPARAM)&es);
4772 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4774 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
4775 result = strcmp (buffer,"TestSomeText\r\n");
4777 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
4778 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
4780 es.dwCookie = (DWORD_PTR)&streamText1;
4782 es.pfnCallback = test_EM_STREAMIN_esCallback;
4783 SendMessage(hwndRichEdit, EM_STREAMIN,
4784 (WPARAM)(SF_RTF), (LPARAM)&es);
4786 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4788 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
4789 result = strcmp (buffer,"TestSomeText");
4791 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4792 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
4794 es.dwCookie = (DWORD_PTR)&streamText2;
4796 SendMessage(hwndRichEdit, EM_STREAMIN,
4797 (WPARAM)(SF_RTF), (LPARAM)&es);
4799 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4801 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
4802 ok (strlen(buffer) == 0,
4803 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4804 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
4806 es.dwCookie = (DWORD_PTR)&streamText3;
4808 SendMessage(hwndRichEdit, EM_STREAMIN,
4809 (WPARAM)(SF_RTF), (LPARAM)&es);
4811 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4813 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
4814 ok (strlen(buffer) == 0,
4815 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
4816 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
4818 es.dwCookie = (DWORD_PTR)&cookieForStream4;
4820 es.pfnCallback = test_EM_STREAMIN_esCallback2;
4821 SendMessage(hwndRichEdit, EM_STREAMIN,
4822 (WPARAM)(SF_TEXT), (LPARAM)&es);
4824 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4825 ok (result == length4,
4826 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
4827 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
4829 DestroyWindow(hwndRichEdit);
4832 static void test_EM_StreamIn_Undo(void)
4834 /* The purpose of this test is to determine when a EM_StreamIn should be
4835 * undoable. This is important because WM_PASTE currently uses StreamIn and
4836 * pasting should always be undoable but streaming isn't always.
4839 * StreamIn plain text without SFF_SELECTION.
4840 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
4841 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
4842 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
4843 * Feel free to add tests for other text modes or StreamIn things.
4847 HWND hwndRichEdit = new_richedit(NULL);
4850 char buffer[1024] = {0};
4851 const char randomtext[] = "Some text";
4853 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
4855 /* StreamIn, no SFF_SELECTION */
4856 es.dwCookie = nCallbackCount;
4857 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4858 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4859 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4860 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
4861 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4862 result = strcmp (buffer,"test");
4864 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
4866 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4867 ok (result == FALSE,
4868 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
4870 /* StreamIn, SFF_SELECTION, but nothing selected */
4871 es.dwCookie = nCallbackCount;
4872 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4873 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4874 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
4875 SendMessage(hwndRichEdit, EM_STREAMIN,
4876 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4877 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4878 result = strcmp (buffer,"testSome text");
4880 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4882 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4884 "EM_STREAMIN with SFF_SELECTION but no selection set "
4885 "should create an undo\n");
4887 /* StreamIn, SFF_SELECTION, with a selection */
4888 es.dwCookie = nCallbackCount;
4889 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
4890 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
4891 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
4892 SendMessage(hwndRichEdit, EM_STREAMIN,
4893 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
4894 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4895 result = strcmp (buffer,"Sometesttext");
4897 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
4899 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
4901 "EM_STREAMIN with SFF_SELECTION and selection set "
4902 "should create an undo\n");
4904 DestroyWindow(hwndRichEdit);
4907 static BOOL is_em_settextex_supported(HWND hwnd)
4909 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
4910 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
4913 static void test_unicode_conversions(void)
4915 static const WCHAR tW[] = {'t',0};
4916 static const WCHAR teW[] = {'t','e',0};
4917 static const WCHAR textW[] = {'t','e','s','t',0};
4918 static const char textA[] = "test";
4922 int is_win9x, em_settextex_supported, ret;
4924 is_win9x = GetVersion() & 0x80000000;
4926 #define set_textA(hwnd, wm_set_text, txt) \
4928 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
4929 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4930 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4931 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4932 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
4934 #define expect_textA(hwnd, wm_get_text, txt) \
4936 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4937 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4938 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4939 memset(bufA, 0xAA, sizeof(bufA)); \
4940 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4941 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4942 ret = lstrcmpA(bufA, txt); \
4943 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
4946 #define set_textW(hwnd, wm_set_text, txt) \
4948 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
4949 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
4950 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
4951 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
4952 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
4954 #define expect_textW(hwnd, wm_get_text, txt) \
4956 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
4957 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4958 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4959 memset(bufW, 0xAA, sizeof(bufW)); \
4962 assert(wm_get_text == EM_GETTEXTEX); \
4963 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4964 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
4968 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
4969 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
4971 ret = lstrcmpW(bufW, txt); \
4972 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
4974 #define expect_empty(hwnd, wm_get_text) \
4976 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
4977 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
4978 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
4979 memset(bufA, 0xAA, sizeof(bufA)); \
4980 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
4981 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
4982 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
4985 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
4986 0, 0, 200, 60, 0, 0, 0, 0);
4987 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
4989 ret = IsWindowUnicode(hwnd);
4991 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
4993 ok(ret, "RichEdit20W should be unicode under NT\n");
4995 /* EM_SETTEXTEX is supported starting from version 3.0 */
4996 em_settextex_supported = is_em_settextex_supported(hwnd);
4997 trace("EM_SETTEXTEX is %ssupported on this platform\n",
4998 em_settextex_supported ? "" : "NOT ");
5000 expect_empty(hwnd, WM_GETTEXT);
5001 expect_empty(hwnd, EM_GETTEXTEX);
5003 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5004 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5005 expect_textA(hwnd, WM_GETTEXT, "t");
5006 expect_textA(hwnd, EM_GETTEXTEX, "t");
5007 expect_textW(hwnd, EM_GETTEXTEX, tW);
5009 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5010 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5011 expect_textA(hwnd, WM_GETTEXT, "te");
5012 expect_textA(hwnd, EM_GETTEXTEX, "te");
5013 expect_textW(hwnd, EM_GETTEXTEX, teW);
5015 set_textA(hwnd, WM_SETTEXT, NULL);
5016 expect_empty(hwnd, WM_GETTEXT);
5017 expect_empty(hwnd, EM_GETTEXTEX);
5020 set_textA(hwnd, WM_SETTEXT, textW);
5022 set_textA(hwnd, WM_SETTEXT, textA);
5023 expect_textA(hwnd, WM_GETTEXT, textA);
5024 expect_textA(hwnd, EM_GETTEXTEX, textA);
5025 expect_textW(hwnd, EM_GETTEXTEX, textW);
5027 if (em_settextex_supported)
5029 set_textA(hwnd, EM_SETTEXTEX, textA);
5030 expect_textA(hwnd, WM_GETTEXT, textA);
5031 expect_textA(hwnd, EM_GETTEXTEX, textA);
5032 expect_textW(hwnd, EM_GETTEXTEX, textW);
5037 set_textW(hwnd, WM_SETTEXT, textW);
5038 expect_textW(hwnd, WM_GETTEXT, textW);
5039 expect_textA(hwnd, WM_GETTEXT, textA);
5040 expect_textW(hwnd, EM_GETTEXTEX, textW);
5041 expect_textA(hwnd, EM_GETTEXTEX, textA);
5043 if (em_settextex_supported)
5045 set_textW(hwnd, EM_SETTEXTEX, textW);
5046 expect_textW(hwnd, WM_GETTEXT, textW);
5047 expect_textA(hwnd, WM_GETTEXT, textA);
5048 expect_textW(hwnd, EM_GETTEXTEX, textW);
5049 expect_textA(hwnd, EM_GETTEXTEX, textA);
5052 DestroyWindow(hwnd);
5054 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5055 0, 0, 200, 60, 0, 0, 0, 0);
5056 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5058 ret = IsWindowUnicode(hwnd);
5059 ok(!ret, "RichEdit20A should NOT be unicode\n");
5061 set_textA(hwnd, WM_SETTEXT, textA);
5062 expect_textA(hwnd, WM_GETTEXT, textA);
5063 expect_textA(hwnd, EM_GETTEXTEX, textA);
5064 expect_textW(hwnd, EM_GETTEXTEX, textW);
5066 if (em_settextex_supported)
5068 set_textA(hwnd, EM_SETTEXTEX, textA);
5069 expect_textA(hwnd, WM_GETTEXT, textA);
5070 expect_textA(hwnd, EM_GETTEXTEX, textA);
5071 expect_textW(hwnd, EM_GETTEXTEX, textW);
5076 set_textW(hwnd, WM_SETTEXT, textW);
5077 expect_textW(hwnd, WM_GETTEXT, textW);
5078 expect_textA(hwnd, WM_GETTEXT, textA);
5079 expect_textW(hwnd, EM_GETTEXTEX, textW);
5080 expect_textA(hwnd, EM_GETTEXTEX, textA);
5082 if (em_settextex_supported)
5084 set_textW(hwnd, EM_SETTEXTEX, textW);
5085 expect_textW(hwnd, WM_GETTEXT, textW);
5086 expect_textA(hwnd, WM_GETTEXT, textA);
5087 expect_textW(hwnd, EM_GETTEXTEX, textW);
5088 expect_textA(hwnd, EM_GETTEXTEX, textA);
5091 DestroyWindow(hwnd);
5094 static void test_WM_CHAR(void)
5098 const char * char_list = "abc\rabc\r";
5099 const char * expected_content_single = "abcabc";
5100 const char * expected_content_multi = "abc\r\nabc\r\n";
5101 char buffer[64] = {0};
5104 /* single-line control must IGNORE carriage returns */
5105 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5106 0, 0, 200, 60, 0, 0, 0, 0);
5107 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5110 while (*p != '\0') {
5111 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5112 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5113 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5114 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5118 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5119 ret = strcmp(buffer, expected_content_single);
5120 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5122 DestroyWindow(hwnd);
5124 /* multi-line control inserts CR normally */
5125 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5126 0, 0, 200, 60, 0, 0, 0, 0);
5127 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5130 while (*p != '\0') {
5131 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5132 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5133 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5134 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5138 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5139 ret = strcmp(buffer, expected_content_multi);
5140 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5142 DestroyWindow(hwnd);
5145 static void test_EM_GETTEXTLENGTHEX(void)
5148 GETTEXTLENGTHEX gtl;
5150 const char * base_string = "base string";
5151 const char * test_string = "a\nb\n\n\r\n";
5152 const char * test_string_after = "a";
5153 const char * test_string_2 = "a\rtest\rstring";
5154 char buffer[64] = {0};
5157 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5158 0, 0, 200, 60, 0, 0, 0, 0);
5159 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5161 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5162 gtl.codepage = CP_ACP;
5163 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5164 ok(ret == 0, "ret %d\n",ret);
5166 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5167 gtl.codepage = CP_ACP;
5168 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5169 ok(ret == 0, "ret %d\n",ret);
5171 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5173 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5174 gtl.codepage = CP_ACP;
5175 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5176 ok(ret == strlen(base_string), "ret %d\n",ret);
5178 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5179 gtl.codepage = CP_ACP;
5180 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5181 ok(ret == strlen(base_string), "ret %d\n",ret);
5183 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5185 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5186 gtl.codepage = CP_ACP;
5187 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5188 ok(ret == 1, "ret %d\n",ret);
5190 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5191 gtl.codepage = CP_ACP;
5192 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5193 ok(ret == 1, "ret %d\n",ret);
5195 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5196 ret = strcmp(buffer, test_string_after);
5197 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5199 DestroyWindow(hwnd);
5202 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5203 0, 0, 200, 60, 0, 0, 0, 0);
5204 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5206 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5207 gtl.codepage = CP_ACP;
5208 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5209 ok(ret == 0, "ret %d\n",ret);
5211 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5212 gtl.codepage = CP_ACP;
5213 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5214 ok(ret == 0, "ret %d\n",ret);
5216 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5218 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5219 gtl.codepage = CP_ACP;
5220 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5221 ok(ret == strlen(base_string), "ret %d\n",ret);
5223 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5224 gtl.codepage = CP_ACP;
5225 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5226 ok(ret == strlen(base_string), "ret %d\n",ret);
5228 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5230 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5231 gtl.codepage = CP_ACP;
5232 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5233 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5235 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5236 gtl.codepage = CP_ACP;
5237 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5238 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5240 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5242 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5243 gtl.codepage = CP_ACP;
5244 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5245 ok(ret == 10, "ret %d\n",ret);
5247 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5248 gtl.codepage = CP_ACP;
5249 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5250 ok(ret == 6, "ret %d\n",ret);
5252 DestroyWindow(hwnd);
5256 /* globals that parent and child access when checking event masks & notifications */
5257 static HWND eventMaskEditHwnd = 0;
5258 static int queriedEventMask;
5259 static int watchForEventMask = 0;
5261 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5262 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5264 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5266 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5268 return DefWindowProcA(hwnd, message, wParam, lParam);
5271 /* test event masks in combination with WM_COMMAND */
5272 static void test_eventMask(void)
5277 const char text[] = "foo bar\n";
5280 /* register class to capture WM_COMMAND */
5282 cls.lpfnWndProc = ParentMsgCheckProcA;
5285 cls.hInstance = GetModuleHandleA(0);
5287 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5288 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5289 cls.lpszMenuName = NULL;
5290 cls.lpszClassName = "EventMaskParentClass";
5291 if(!RegisterClassA(&cls)) assert(0);
5293 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5294 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5295 ok (parent != 0, "Failed to create parent window\n");
5297 eventMaskEditHwnd = new_richedit(parent);
5298 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5300 eventMask = ENM_CHANGE | ENM_UPDATE;
5301 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5302 ok(ret == ENM_NONE, "wrong event mask\n");
5303 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5304 ok(ret == eventMask, "failed to set event mask\n");
5306 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5307 queriedEventMask = 0; /* initialize to something other than we expect */
5308 watchForEventMask = EN_CHANGE;
5309 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5310 ok(ret == TRUE, "failed to set text\n");
5311 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5312 notification in response to WM_SETTEXT */
5313 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5314 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5316 /* check to see if EN_CHANGE is sent when redraw is turned off */
5317 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5318 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5319 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5320 /* redraw is disabled by making the window invisible. */
5321 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5322 queriedEventMask = 0; /* initialize to something other than we expect */
5323 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5324 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5325 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5326 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5327 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5329 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5330 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5331 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5332 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5333 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5334 watchForEventMask = EN_UPDATE;
5335 queriedEventMask = 0; /* initialize to something other than we expect */
5336 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5337 ok(queriedEventMask == 0,
5338 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5339 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5340 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5341 queriedEventMask = 0; /* initialize to something other than we expect */
5342 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5343 ok(queriedEventMask == eventMask,
5344 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5347 DestroyWindow(parent);
5350 static int received_WM_NOTIFY = 0;
5351 static int modify_at_WM_NOTIFY = 0;
5352 static HWND hwndRichedit_WM_NOTIFY;
5354 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5356 if(message == WM_NOTIFY)
5358 received_WM_NOTIFY = 1;
5359 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5361 return DefWindowProcA(hwnd, message, wParam, lParam);
5364 static void test_WM_NOTIFY(void)
5370 /* register class to capture WM_NOTIFY */
5372 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5375 cls.hInstance = GetModuleHandleA(0);
5377 cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
5378 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5379 cls.lpszMenuName = NULL;
5380 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5381 if(!RegisterClassA(&cls)) assert(0);
5383 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5384 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5385 ok (parent != 0, "Failed to create parent window\n");
5387 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5388 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5390 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5392 /* Notifications for selection change should only be sent when selection
5393 actually changes. EM_SETCHARFORMAT is one message that calls
5394 ME_CommitUndo, which should check whether message should be sent */
5395 received_WM_NOTIFY = 0;
5396 cf2.cbSize = sizeof(CHARFORMAT2);
5397 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5399 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5400 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5401 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5402 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5404 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5406 received_WM_NOTIFY = 0;
5407 modify_at_WM_NOTIFY = 0;
5408 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5409 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5410 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5412 received_WM_NOTIFY = 0;
5413 modify_at_WM_NOTIFY = 0;
5414 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5415 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5417 received_WM_NOTIFY = 0;
5418 modify_at_WM_NOTIFY = 0;
5419 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5420 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5421 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5423 /* Test for WM_NOTIFY messages with redraw disabled. */
5424 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5425 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5426 received_WM_NOTIFY = 0;
5427 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5428 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5429 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5431 DestroyWindow(hwndRichedit_WM_NOTIFY);
5432 DestroyWindow(parent);
5435 static void test_undo_coalescing(void)
5439 char buffer[64] = {0};
5441 /* multi-line control inserts CR normally */
5442 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5443 0, 0, 200, 60, 0, 0, 0, 0);
5444 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5446 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5447 ok (result == FALSE, "Can undo after window creation.\n");
5448 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5449 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5450 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5451 ok (result == FALSE, "Can redo after window creation.\n");
5452 result = SendMessage(hwnd, EM_REDO, 0, 0);
5453 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5455 /* Test the effect of arrows keys during typing on undo transactions*/
5456 simulate_typing_characters(hwnd, "one two three");
5457 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5458 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5459 simulate_typing_characters(hwnd, " four five six");
5461 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5462 ok (result == FALSE, "Can redo before anything is undone.\n");
5463 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5464 ok (result == TRUE, "Cannot undo typed characters.\n");
5465 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5466 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5467 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5468 ok (result == TRUE, "Cannot redo after undo.\n");
5469 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5470 result = strcmp(buffer, "one two three");
5471 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5473 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5474 ok (result == TRUE, "Cannot undo typed characters.\n");
5475 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5476 ok (result == TRUE, "Failed to undo typed characters.\n");
5477 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5478 result = strcmp(buffer, "");
5479 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5481 /* Test the effect of focus changes during typing on undo transactions*/
5482 simulate_typing_characters(hwnd, "one two three");
5483 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5484 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5485 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5486 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5487 simulate_typing_characters(hwnd, " four five six");
5488 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5489 ok (result == TRUE, "Failed to undo typed characters.\n");
5490 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5491 result = strcmp(buffer, "one two three");
5492 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5494 /* Test the effect of the back key during typing on undo transactions */
5495 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5496 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5497 ok (result == TRUE, "Failed to clear the text.\n");
5498 simulate_typing_characters(hwnd, "one two threa");
5499 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5500 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5501 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5502 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5503 simulate_typing_characters(hwnd, "e four five six");
5504 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5505 ok (result == TRUE, "Failed to undo typed characters.\n");
5506 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5507 result = strcmp(buffer, "");
5508 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5510 /* Test the effect of the delete key during typing on undo transactions */
5511 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5512 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5513 ok(result == TRUE, "Failed to set the text.\n");
5514 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5515 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5516 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5517 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5518 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5519 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5520 ok (result == TRUE, "Failed to undo typed characters.\n");
5521 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5522 result = strcmp(buffer, "acd");
5523 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5524 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5525 ok (result == TRUE, "Failed to undo typed characters.\n");
5526 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5527 result = strcmp(buffer, "abcd");
5528 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5530 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5531 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5532 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5533 ok (result == TRUE, "Failed to clear the text.\n");
5534 simulate_typing_characters(hwnd, "one two three");
5535 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5536 ok (result == 0, "expected %d but got %d\n", 0, result);
5537 simulate_typing_characters(hwnd, " four five six");
5538 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5539 ok (result == TRUE, "Failed to undo typed characters.\n");
5540 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5541 result = strcmp(buffer, "one two three");
5542 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5543 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5544 ok (result == TRUE, "Failed to undo typed characters.\n");
5545 ok (result == TRUE, "Failed to undo typed characters.\n");
5546 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5547 result = strcmp(buffer, "");
5548 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5550 DestroyWindow(hwnd);
5553 LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5557 /* MSDN lied, length is actually the number of bytes. */
5558 length = bytes / sizeof(WCHAR);
5561 case WB_ISDELIMITER:
5562 return text[pos] == 'X';
5564 case WB_MOVEWORDLEFT:
5565 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5567 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5570 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5574 case WB_MOVEWORDRIGHT:
5575 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5577 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5580 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5584 ok(FALSE, "Unexpected code %d\n", code);
5590 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5591 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5593 static void test_word_movement(void)
5597 int sel_start, sel_end;
5598 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5600 /* multi-line control inserts CR normally */
5601 hwnd = new_richedit(NULL);
5603 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5604 ok (result == TRUE, "Failed to clear the text.\n");
5605 SendMessage(hwnd, EM_SETSEL, 0, 0);
5606 /* |one two three */
5608 SEND_CTRL_RIGHT(hwnd);
5609 /* one |two three */
5610 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5611 ok(sel_start == sel_end, "Selection should be empty\n");
5612 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5614 SEND_CTRL_RIGHT(hwnd);
5615 /* one two |three */
5616 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5617 ok(sel_start == sel_end, "Selection should be empty\n");
5618 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5620 SEND_CTRL_LEFT(hwnd);
5621 /* one |two three */
5622 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5623 ok(sel_start == sel_end, "Selection should be empty\n");
5624 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5626 SEND_CTRL_LEFT(hwnd);
5627 /* |one two three */
5628 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5629 ok(sel_start == sel_end, "Selection should be empty\n");
5630 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5632 SendMessage(hwnd, EM_SETSEL, 8, 8);
5633 /* one two | three */
5634 SEND_CTRL_RIGHT(hwnd);
5635 /* one two |three */
5636 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5637 ok(sel_start == sel_end, "Selection should be empty\n");
5638 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5640 SendMessage(hwnd, EM_SETSEL, 11, 11);
5641 /* one two th|ree */
5642 SEND_CTRL_LEFT(hwnd);
5643 /* one two |three */
5644 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5645 ok(sel_start == sel_end, "Selection should be empty\n");
5646 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5648 /* Test with a custom word break procedure that uses X as the delimiter. */
5649 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5650 ok (result == TRUE, "Failed to clear the text.\n");
5651 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5652 /* |one twoXthree */
5653 SEND_CTRL_RIGHT(hwnd);
5654 /* one twoX|three */
5655 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5656 ok(sel_start == sel_end, "Selection should be empty\n");
5657 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5659 DestroyWindow(hwnd);
5661 /* Make sure the behaviour is the same with a unicode richedit window,
5662 * and using unicode functions. */
5663 SetLastError(0xdeadbeef);
5664 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5665 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5666 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5667 if (!hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
5669 win_skip("Needed unicode functions are not implemented\n");
5673 /* Test with a custom word break procedure that uses X as the delimiter. */
5674 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5675 ok (result == TRUE, "Failed to clear the text.\n");
5676 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5677 /* |one twoXthree */
5678 SEND_CTRL_RIGHT(hwnd);
5679 /* one twoX|three */
5680 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5681 ok(sel_start == sel_end, "Selection should be empty\n");
5682 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5684 DestroyWindow(hwnd);
5687 static void test_EM_CHARFROMPOS(void)
5695 /* multi-line control inserts CR normally */
5696 hwnd = new_richedit(NULL);
5697 result = SendMessageA(hwnd, WM_SETTEXT, 0,
5698 (LPARAM)"one two three four five six seven");
5700 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
5701 ok(result == 0, "expected character index of 0 but got %d\n", result);
5703 DestroyWindow(hwnd);
5706 static void test_word_wrap(void)
5709 POINTL point = {0, 60}; /* This point must be below the first line */
5710 const char *text = "Must be long enough to test line wrapping";
5711 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
5712 int res, pos, lines;
5714 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
5715 * when specified on window creation and set later. */
5716 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5717 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5718 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5719 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5720 ok(res, "WM_SETTEXT failed.\n");
5721 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5722 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5723 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5724 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5726 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
5727 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5728 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5729 DestroyWindow(hwnd);
5731 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
5732 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5733 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5735 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5736 ok(res, "WM_SETTEXT failed.\n");
5737 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5738 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5739 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5740 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5742 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5743 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5744 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5745 DestroyWindow(hwnd);
5747 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
5748 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5749 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5750 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5751 ok(res, "WM_SETTEXT failed.\n");
5752 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5753 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5755 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5756 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5757 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5758 DestroyWindow(hwnd);
5760 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
5761 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
5762 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
5763 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5764 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
5765 ok(res, "WM_SETTEXT failed.\n");
5766 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5767 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5769 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
5770 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5771 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5773 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
5774 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
5775 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5776 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5777 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
5779 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
5780 todo_wine ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
5781 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
5782 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
5783 DestroyWindow(hwnd);
5785 /* Test to see if wrapping happens with redraw disabled. */
5786 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
5787 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
5788 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
5789 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
5790 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
5791 ok(res, "EM_REPLACESEL failed.\n");
5792 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5793 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
5794 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
5795 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5796 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
5798 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5799 DestroyWindow(hwnd);
5802 static void test_auto_yscroll(void)
5804 HWND hwnd = new_richedit(NULL);
5805 int lines, ret, redraw;
5808 for (redraw = 0; redraw <= 1; redraw++) {
5809 trace("testing with WM_SETREDRAW=%d\n", redraw);
5810 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
5811 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
5812 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5813 ok(lines == 8, "%d lines instead of 8\n", lines);
5814 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5815 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5816 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
5817 ret = GetWindowLong(hwnd, GWL_STYLE);
5818 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
5820 SendMessage(hwnd, WM_SETTEXT, 0, 0);
5821 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
5822 ok(lines == 1, "%d lines instead of 1\n", lines);
5823 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
5824 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
5825 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
5826 ret = GetWindowLong(hwnd, GWL_STYLE);
5827 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
5830 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
5831 DestroyWindow(hwnd);
5834 START_TEST( editor )
5836 /* Must explicitly LoadLibrary(). The test has no references to functions in
5837 * RICHED20.DLL, so the linker doesn't actually link to it. */
5838 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
5839 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
5843 test_EM_POSFROMCHAR();
5844 test_EM_SCROLLCARET();
5846 test_scrollbar_visibility();
5848 test_EM_LINELENGTH();
5849 test_EM_SETCHARFORMAT();
5850 test_EM_SETTEXTMODE();
5851 test_TM_PLAINTEXT();
5852 test_EM_SETOPTIONS();
5854 test_EM_GETTEXTRANGE();
5855 test_EM_GETSELTEXT();
5856 test_EM_SETUNDOLIMIT();
5858 test_EM_SETTEXTEX();
5859 test_EM_LIMITTEXT();
5860 test_EM_EXLIMITTEXT();
5861 test_EM_GETLIMITTEXT();
5863 test_EM_GETMODIFY();
5867 test_EM_STREAMOUT();
5868 test_EM_StreamIn_Undo();
5869 test_EM_FORMATRANGE();
5870 test_unicode_conversions();
5871 test_EM_GETTEXTLENGTHEX();
5872 test_EM_REPLACESEL(1);
5873 test_EM_REPLACESEL(0);
5875 test_EM_AUTOURLDETECT();
5877 test_undo_coalescing();
5878 test_word_movement();
5879 test_EM_CHARFROMPOS();
5880 test_SETPARAFORMAT();
5882 test_auto_yscroll();
5884 /* Set the environment variable WINETEST_RICHED20 to keep windows
5885 * responsive and open for 30 seconds. This is useful for debugging.
5887 if (getenv( "WINETEST_RICHED20" )) {
5888 keep_responsive(30);
5891 OleFlushClipboard();
5892 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());