2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include <wine/test.h>
36 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
38 #define ok_w3(format, szString1, szString2, szString3) \
39 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
40 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
41 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
42 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
43 format, string1, string2, string3);
45 static HMODULE hmoduleRichEdit;
47 static int is_win9x = 0;
49 static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) {
51 hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
52 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
53 hmoduleRichEdit, NULL);
54 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
58 static HWND new_richedit(HWND parent) {
59 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
62 /* Keeps the window reponsive for the deley_time in seconds.
63 * This is useful for debugging a test to see what is happening. */
64 static void keep_responsive(time_t delay_time)
69 /* The message pump uses PeekMessage() to empty the queue and then
70 * sleeps for 50ms before retrying the queue. */
71 end = time(NULL) + delay_time;
72 while (time(NULL) < end) {
73 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
74 TranslateMessage(&msg);
75 DispatchMessage(&msg);
82 static void processPendingMessages(void)
85 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
86 TranslateMessage(&msg);
87 DispatchMessage(&msg);
91 static void pressKeyWithModifier(HWND hwnd, BYTE mod_vk, BYTE vk)
93 BYTE mod_scan_code = MapVirtualKey(mod_vk, MAPVK_VK_TO_VSC);
94 BYTE scan_code = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
96 keybd_event(mod_vk, mod_scan_code, 0, 0);
97 keybd_event(vk, scan_code, 0, 0);
98 keybd_event(vk, scan_code, KEYEVENTF_KEYUP, 0);
99 keybd_event(mod_vk, mod_scan_code, KEYEVENTF_KEYUP, 0);
100 processPendingMessages();
103 static void simulate_typing_characters(HWND hwnd, const char* szChars)
107 while (*szChars != '\0') {
108 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
109 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
110 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
111 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
116 static BOOL hold_key(int vk)
121 result = GetKeyboardState(key_state);
122 ok(result, "GetKeyboardState failed.\n");
123 if (!result) return FALSE;
124 key_state[vk] |= 0x80;
125 result = SetKeyboardState(key_state);
126 ok(result, "SetKeyboardState failed.\n");
130 static BOOL release_key(int vk)
135 result = GetKeyboardState(key_state);
136 ok(result, "GetKeyboardState failed.\n");
137 if (!result) return FALSE;
138 key_state[vk] &= ~0x80;
139 result = SetKeyboardState(key_state);
140 ok(result, "SetKeyboardState failed.\n");
144 static const char haystack[] = "WINEWine wineWine wine WineWine";
156 struct find_s find_tests[] = {
157 /* Find in empty text */
158 {0, -1, "foo", FR_DOWN, -1},
159 {0, -1, "foo", 0, -1},
160 {0, -1, "", FR_DOWN, -1},
161 {20, 5, "foo", FR_DOWN, -1},
162 {5, 20, "foo", FR_DOWN, -1}
165 struct find_s find_tests2[] = {
167 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
168 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
170 /* Subsequent finds */
171 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
172 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
173 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
174 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
177 {19, 20, "Wine", FR_MATCHCASE, 13},
178 {10, 20, "Wine", FR_MATCHCASE, 4},
179 {20, 10, "Wine", FR_MATCHCASE, 13},
181 /* Case-insensitive */
182 {1, 31, "wInE", FR_DOWN, 4},
183 {1, 31, "Wine", FR_DOWN, 4},
185 /* High-to-low ranges */
186 {20, 5, "Wine", FR_DOWN, -1},
187 {2, 1, "Wine", FR_DOWN, -1},
188 {30, 29, "Wine", FR_DOWN, -1},
189 {20, 5, "Wine", 0, 13},
192 {5, 10, "", FR_DOWN, -1},
193 {10, 5, "", FR_DOWN, -1},
194 {0, -1, "", FR_DOWN, -1},
197 /* Whole-word search */
198 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
199 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
200 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
201 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
202 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
203 {11, -1, "winewine", FR_WHOLEWORD, 0},
204 {31, -1, "winewine", FR_WHOLEWORD, 23},
207 {5, 200, "XXX", FR_DOWN, -1},
208 {-20, 20, "Wine", FR_DOWN, -1},
209 {-20, 20, "Wine", FR_DOWN, -1},
210 {-15, -20, "Wine", FR_DOWN, -1},
211 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
213 /* Check the case noted in bug 4479 where matches at end aren't recognized */
214 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
215 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
216 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
217 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
218 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
220 /* The backwards case of bug 4479; bounds look right
221 * Fails because backward find is wrong */
222 {19, 20, "WINE", FR_MATCHCASE, 0},
223 {0, 20, "WINE", FR_MATCHCASE, -1},
225 {0, -1, "wineWine wine", 0, -1},
228 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) {
231 memset(&ft, 0, sizeof(ft));
232 ft.chrg.cpMin = f->start;
233 ft.chrg.cpMax = f->end;
234 ft.lpstrText = f->needle;
235 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft);
236 ok(findloc == f->expected_loc,
237 "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
238 name, id, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
241 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
245 int expected_end_loc;
247 memset(&ft, 0, sizeof(ft));
248 ft.chrg.cpMin = f->start;
249 ft.chrg.cpMax = f->end;
250 ft.lpstrText = f->needle;
251 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft);
252 ok(findloc == f->expected_loc,
253 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
254 name, id, f->needle, f->start, f->end, f->flags, findloc);
255 ok(ft.chrgText.cpMin == f->expected_loc,
256 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
257 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin);
258 expected_end_loc = ((f->expected_loc == -1) ? -1
259 : f->expected_loc + strlen(f->needle));
260 ok(ft.chrgText.cpMax == expected_end_loc,
261 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
262 name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax, expected_end_loc);
265 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
270 for (i = 0; i < num_tests; i++) {
271 check_EM_FINDTEXT(hwnd, name, &find[i], i);
272 check_EM_FINDTEXTEX(hwnd, name, &find[i], i);
276 static void test_EM_FINDTEXT(void)
278 HWND hwndRichEdit = new_richedit(NULL);
281 /* Empty rich edit control */
282 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
283 sizeof(find_tests)/sizeof(struct find_s));
285 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
288 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
289 sizeof(find_tests2)/sizeof(struct find_s));
291 /* Setting a format on an arbitrary range should have no effect in search
292 results. This tests correct offset reporting across runs. */
293 cf2.cbSize = sizeof(CHARFORMAT2);
294 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
296 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
297 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
298 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
299 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
301 /* Haystack text, again */
302 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
303 sizeof(find_tests2)/sizeof(struct find_s));
305 /* Yet another range */
306 cf2.dwMask = CFM_BOLD | cf2.dwMask;
307 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
308 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
309 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
311 /* Haystack text, again */
312 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
313 sizeof(find_tests2)/sizeof(struct find_s));
315 DestroyWindow(hwndRichEdit);
318 static const struct getline_s {
323 {0, 10, "foo bar\r"},
328 /* Buffer smaller than line length */
334 static void test_EM_GETLINE(void)
337 HWND hwndRichEdit = new_richedit(NULL);
338 static const int nBuf = 1024;
339 char dest[1024], origdest[1024];
340 const char text[] = "foo bar\n"
344 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
346 memset(origdest, 0xBB, nBuf);
347 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
350 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
351 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
352 memset(dest, 0xBB, nBuf);
353 *(WORD *) dest = gl[i].buffer_len;
355 /* EM_GETLINE appends a "\r\0" to the end of the line
356 * nCopied counts up to and including the '\r' */
357 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
358 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
360 /* two special cases since a parameter is passed via dest */
361 if (gl[i].buffer_len == 0)
362 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
364 else if (gl[i].buffer_len == 1)
365 ok(dest[0] == gl[i].text[0] && !dest[1] &&
366 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
369 /* Prepare hex strings of buffers to dump on failure. */
370 char expectedbuf[1024];
371 char resultbuf[1024];
374 for (j = 0; j < 32; j++)
375 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
376 expectedbuf[0] = '\0';
377 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
378 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
379 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
380 sprintf(expectedbuf+strlen(expectedbuf), "??");
381 for (; j < 32; j++) /* Bytes after declared buffer size */
382 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
384 /* Test the part of the buffer that is expected to be written according
385 * to the MSDN documentation fo EM_GETLINE, which does not state that
386 * a NULL terminating character will be added unless no text is copied.
388 * Windows 95, 98 & NT do not append a NULL terminating character, but
389 * Windows 2000 and up do append a NULL terminating character if there
390 * is space in the buffer. The test will ignore this difference. */
391 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
392 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
393 i, expected_bytes_written, expectedbuf, resultbuf);
394 /* Test the part of the buffer after the declared length to make sure
395 * there are no buffer overruns. */
396 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
397 nBuf - gl[i].buffer_len),
398 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
399 i, expected_bytes_written, expectedbuf, resultbuf);
403 DestroyWindow(hwndRichEdit);
406 static void test_EM_LINELENGTH(void)
408 HWND hwndRichEdit = new_richedit(NULL);
414 int offset_test[10][2] = {
429 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
431 for (i = 0; i < 10; i++) {
432 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
433 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
434 offset_test[i][0], result, offset_test[i][1]);
437 DestroyWindow(hwndRichEdit);
440 static int get_scroll_pos_y(HWND hwnd)
443 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
444 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
448 static void move_cursor(HWND hwnd, LONG charindex)
451 cr.cpMax = charindex;
452 cr.cpMin = charindex;
453 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
456 static void line_scroll(HWND hwnd, int amount)
458 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
461 static void test_EM_SCROLLCARET(void)
464 const char text[] = "aa\n"
465 "this is a long line of text that should be longer than the "
473 /* The richedit window height needs to be large enough vertically to fit in
474 * more than two lines of text, so the new_richedit function can't be used
475 * since a height of 60 was not large enough on some systems.
477 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
478 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
479 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
480 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
482 /* Can't verify this */
483 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
485 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
487 /* Caret above visible window */
488 line_scroll(hwndRichEdit, 3);
489 prevY = get_scroll_pos_y(hwndRichEdit);
490 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
491 curY = get_scroll_pos_y(hwndRichEdit);
492 ok(prevY != curY, "%d == %d\n", prevY, curY);
494 /* Caret below visible window */
495 move_cursor(hwndRichEdit, sizeof(text) - 1);
496 line_scroll(hwndRichEdit, -3);
497 prevY = get_scroll_pos_y(hwndRichEdit);
498 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
499 curY = get_scroll_pos_y(hwndRichEdit);
500 ok(prevY != curY, "%d == %d\n", prevY, curY);
502 /* Caret in visible window */
503 move_cursor(hwndRichEdit, sizeof(text) - 2);
504 prevY = get_scroll_pos_y(hwndRichEdit);
505 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
506 curY = get_scroll_pos_y(hwndRichEdit);
507 ok(prevY == curY, "%d != %d\n", prevY, curY);
509 /* Caret still in visible window */
510 line_scroll(hwndRichEdit, -1);
511 prevY = get_scroll_pos_y(hwndRichEdit);
512 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
513 curY = get_scroll_pos_y(hwndRichEdit);
514 ok(prevY == curY, "%d != %d\n", prevY, curY);
516 DestroyWindow(hwndRichEdit);
519 static void test_EM_POSFROMCHAR(void)
521 HWND hwndRichEdit = new_richedit(NULL);
524 unsigned int height = 0;
527 static const char text[] = "aa\n"
528 "this is a long line of text that should be longer than the "
537 /* Fill the control to lines to ensure that most of them are offscreen */
538 for (i = 0; i < 50; i++)
540 /* Do not modify the string; it is exactly 16 characters long. */
541 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
542 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
546 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
547 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
548 Richedit 3.0 accepts either of the above API conventions.
551 /* Testing Richedit 2.0 API format */
553 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
554 Since all lines are identical and drawn with the same font,
555 they should have the same height... right?
557 for (i = 0; i < 50; i++)
559 /* All the lines are 16 characters long */
560 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
563 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
564 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
565 xpos = LOWORD(result);
569 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
570 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
571 height = HIWORD(result);
575 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
576 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
580 /* Testing position at end of text */
581 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
582 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
583 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
585 /* Testing position way past end of text */
586 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
587 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
588 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
590 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
591 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
592 for (i = 0; i < 50; i++)
594 /* All the lines are 16 characters long */
595 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
596 ok((signed short)(HIWORD(result)) == (i - 1) * height,
597 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
598 (signed short)(HIWORD(result)), (i - 1) * height);
599 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
602 /* Testing position at end of text */
603 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
604 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
605 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
607 /* Testing position way past end of text */
608 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
609 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
610 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
612 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
614 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
616 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
617 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
618 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
619 xpos = LOWORD(result);
621 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
622 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
623 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
624 ok((signed short)(LOWORD(result)) < xpos,
625 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
626 (signed short)(LOWORD(result)), xpos);
627 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
629 /* Test around end of text that doesn't end in a newline. */
630 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
631 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
632 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
633 ok(pt.x > 1, "pt.x = %d\n", pt.x);
635 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
636 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
637 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
639 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
640 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
641 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
643 /* Try a negative position. */
644 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
645 ok(pt.x == 1, "pt.x = %d\n", pt.x);
647 DestroyWindow(hwndRichEdit);
650 static void test_EM_SETCHARFORMAT(void)
652 HWND hwndRichEdit = new_richedit(NULL);
655 int tested_effects[] = {
669 /* Invalid flags, CHARFORMAT2 structure blanked out */
670 memset(&cf2, 0, sizeof(cf2));
671 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
673 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
675 /* A valid flag, CHARFORMAT2 structure blanked out */
676 memset(&cf2, 0, sizeof(cf2));
677 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
679 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
681 /* A valid flag, CHARFORMAT2 structure blanked out */
682 memset(&cf2, 0, sizeof(cf2));
683 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
685 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
687 /* A valid flag, CHARFORMAT2 structure blanked out */
688 memset(&cf2, 0, sizeof(cf2));
689 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
691 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
693 /* A valid flag, CHARFORMAT2 structure blanked out */
694 memset(&cf2, 0, sizeof(cf2));
695 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
697 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
699 /* Invalid flags, CHARFORMAT2 structure minimally filled */
700 memset(&cf2, 0, sizeof(cf2));
701 cf2.cbSize = sizeof(CHARFORMAT2);
702 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
704 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
705 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
706 ok(rc == FALSE, "Should not be able to undo here.\n");
707 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
709 /* A valid flag, CHARFORMAT2 structure minimally filled */
710 memset(&cf2, 0, sizeof(cf2));
711 cf2.cbSize = sizeof(CHARFORMAT2);
712 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
714 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
715 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
716 ok(rc == FALSE, "Should not be able to undo here.\n");
717 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
719 /* A valid flag, CHARFORMAT2 structure minimally filled */
720 memset(&cf2, 0, sizeof(cf2));
721 cf2.cbSize = sizeof(CHARFORMAT2);
722 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
724 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
725 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
726 ok(rc == FALSE, "Should not be able to undo here.\n");
727 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
729 /* A valid flag, CHARFORMAT2 structure minimally filled */
730 memset(&cf2, 0, sizeof(cf2));
731 cf2.cbSize = sizeof(CHARFORMAT2);
732 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
734 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
735 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
736 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
737 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
739 /* A valid flag, CHARFORMAT2 structure minimally filled */
740 memset(&cf2, 0, sizeof(cf2));
741 cf2.cbSize = sizeof(CHARFORMAT2);
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
744 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
745 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
746 ok(rc == TRUE, "Should not be able to undo here.\n");
747 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
749 cf2.cbSize = sizeof(CHARFORMAT2);
750 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
753 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
754 cf2.cbSize = sizeof(CHARFORMAT2);
755 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
757 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
758 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
760 /* wParam==0 is default char format, does not set modify */
761 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
762 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
763 ok(rc == 0, "Text marked as modified, expected not modified!\n");
764 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
765 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
766 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
767 ok(rc == 0, "Text marked as modified, expected not modified!\n");
769 /* wParam==SCF_SELECTION sets modify if nonempty selection */
770 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
771 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
772 ok(rc == 0, "Text marked as modified, expected not modified!\n");
773 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
774 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
775 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
776 ok(rc == 0, "Text marked as modified, expected not modified!\n");
778 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
779 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
780 ok(rc == 0, "Text marked as modified, expected not modified!\n");
781 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
784 ok(rc == 0, "Text marked as modified, expected not modified!\n");
785 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
786 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
787 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
788 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
789 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
791 /* wParam==SCF_ALL sets modify regardless of whether text is present */
792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
793 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
794 ok(rc == 0, "Text marked as modified, expected not modified!\n");
795 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
796 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
797 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
798 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
800 DestroyWindow(hwndRichEdit);
802 /* EM_GETCHARFORMAT tests */
803 for (i = 0; tested_effects[i]; i++)
805 hwndRichEdit = new_richedit(NULL);
806 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
808 /* Need to set a TrueType font to get consistent CFM_BOLD results */
809 memset(&cf2, 0, sizeof(CHARFORMAT2));
810 cf2.cbSize = sizeof(CHARFORMAT2);
811 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
813 strcpy(cf2.szFaceName, "Courier New");
814 cf2.wWeight = FW_DONTCARE;
815 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
817 memset(&cf2, 0, sizeof(CHARFORMAT2));
818 cf2.cbSize = sizeof(CHARFORMAT2);
819 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
821 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
822 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
824 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
825 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
826 ok((cf2.dwEffects & tested_effects[i]) == 0,
827 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
829 memset(&cf2, 0, sizeof(CHARFORMAT2));
830 cf2.cbSize = sizeof(CHARFORMAT2);
831 cf2.dwMask = tested_effects[i];
832 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
833 cf2.dwMask = CFM_SUPERSCRIPT;
834 cf2.dwEffects = tested_effects[i];
835 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
836 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
838 memset(&cf2, 0, sizeof(CHARFORMAT2));
839 cf2.cbSize = sizeof(CHARFORMAT2);
840 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
841 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
842 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
843 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
845 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
846 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
847 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
848 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
850 memset(&cf2, 0, sizeof(CHARFORMAT2));
851 cf2.cbSize = sizeof(CHARFORMAT2);
852 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
853 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
854 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
855 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
857 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
858 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
859 ok((cf2.dwEffects & tested_effects[i]) == 0,
860 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
862 memset(&cf2, 0, sizeof(CHARFORMAT2));
863 cf2.cbSize = sizeof(CHARFORMAT2);
864 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
865 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
866 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
867 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
869 (cf2.dwMask & tested_effects[i]) == 0),
870 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
872 DestroyWindow(hwndRichEdit);
875 for (i = 0; tested_effects[i]; i++)
877 hwndRichEdit = new_richedit(NULL);
878 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
880 /* Need to set a TrueType font to get consistent CFM_BOLD results */
881 memset(&cf2, 0, sizeof(CHARFORMAT2));
882 cf2.cbSize = sizeof(CHARFORMAT2);
883 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
885 strcpy(cf2.szFaceName, "Courier New");
886 cf2.wWeight = FW_DONTCARE;
887 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
889 memset(&cf2, 0, sizeof(CHARFORMAT2));
890 cf2.cbSize = sizeof(CHARFORMAT2);
891 cf2.dwMask = tested_effects[i];
892 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
893 cf2.dwMask = CFM_SUPERSCRIPT;
894 cf2.dwEffects = tested_effects[i];
895 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
896 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
898 memset(&cf2, 0, sizeof(CHARFORMAT2));
899 cf2.cbSize = sizeof(CHARFORMAT2);
900 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
901 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
902 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
903 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
905 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
906 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
907 ok((cf2.dwEffects & tested_effects[i]) == 0,
908 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
910 memset(&cf2, 0, sizeof(CHARFORMAT2));
911 cf2.cbSize = sizeof(CHARFORMAT2);
912 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
913 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
914 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
915 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
917 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
918 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
919 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
920 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
922 memset(&cf2, 0, sizeof(CHARFORMAT2));
923 cf2.cbSize = sizeof(CHARFORMAT2);
924 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
925 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
926 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
927 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
929 (cf2.dwMask & tested_effects[i]) == 0),
930 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
932 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
934 DestroyWindow(hwndRichEdit);
937 /* Effects applied on an empty selection should take effect when selection is
938 replaced with text */
939 hwndRichEdit = new_richedit(NULL);
940 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
941 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
943 memset(&cf2, 0, sizeof(CHARFORMAT2));
944 cf2.cbSize = sizeof(CHARFORMAT2);
945 cf2.dwMask = CFM_BOLD;
946 cf2.dwEffects = CFE_BOLD;
947 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
949 /* Selection is now nonempty */
950 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
952 memset(&cf2, 0, sizeof(CHARFORMAT2));
953 cf2.cbSize = sizeof(CHARFORMAT2);
954 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
955 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
957 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
958 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
959 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
960 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
963 /* Set two effects on an empty selection */
964 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
967 memset(&cf2, 0, sizeof(CHARFORMAT2));
968 cf2.cbSize = sizeof(CHARFORMAT2);
969 cf2.dwMask = CFM_BOLD;
970 cf2.dwEffects = CFE_BOLD;
971 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 cf2.dwMask = CFM_ITALIC;
973 cf2.dwEffects = CFE_ITALIC;
974 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
976 /* Selection is now nonempty */
977 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
979 memset(&cf2, 0, sizeof(CHARFORMAT2));
980 cf2.cbSize = sizeof(CHARFORMAT2);
981 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
982 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
985 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
986 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
987 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
989 /* Setting the (empty) selection to exactly the same place as before should
990 NOT clear the insertion style! */
991 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
992 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
994 memset(&cf2, 0, sizeof(CHARFORMAT2));
995 cf2.cbSize = sizeof(CHARFORMAT2);
996 cf2.dwMask = CFM_BOLD;
997 cf2.dwEffects = CFE_BOLD;
998 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1000 /* Empty selection in same place, insert style should NOT be forgotten here. */
1001 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1003 /* Selection is now nonempty */
1004 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1006 memset(&cf2, 0, sizeof(CHARFORMAT2));
1007 cf2.cbSize = sizeof(CHARFORMAT2);
1008 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1009 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1011 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1012 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1013 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1014 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1016 /* Ditto with EM_EXSETSEL */
1017 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1018 cr.cpMin = 2; cr.cpMax = 2;
1019 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1021 memset(&cf2, 0, sizeof(CHARFORMAT2));
1022 cf2.cbSize = sizeof(CHARFORMAT2);
1023 cf2.dwMask = CFM_BOLD;
1024 cf2.dwEffects = CFE_BOLD;
1025 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1027 /* Empty selection in same place, insert style should NOT be forgotten here. */
1028 cr.cpMin = 2; cr.cpMax = 2;
1029 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1031 /* Selection is now nonempty */
1032 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1034 memset(&cf2, 0, sizeof(CHARFORMAT2));
1035 cf2.cbSize = sizeof(CHARFORMAT2);
1036 cr.cpMin = 2; cr.cpMax = 6;
1037 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1038 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1040 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1041 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1042 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1043 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1045 DestroyWindow(hwndRichEdit);
1048 static void test_EM_SETTEXTMODE(void)
1050 HWND hwndRichEdit = new_richedit(NULL);
1051 CHARFORMAT2 cf2, cf2test;
1055 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1056 /*Insert text into the control*/
1058 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1060 /*Attempt to change the control to plain text mode*/
1061 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1062 ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc);
1064 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1065 If rich text is pasted, it should have the same formatting as the rest
1066 of the text in the control*/
1068 /*Italicize the text
1069 *NOTE: If the default text was already italicized, the test will simply
1070 reverse; in other words, it will copy a regular "wine" into a plain
1071 text window that uses an italicized format*/
1072 cf2.cbSize = sizeof(CHARFORMAT2);
1073 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1076 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1077 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1079 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1080 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1082 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1083 however, SCF_ALL has been implemented*/
1084 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1085 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1087 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1088 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1090 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1092 /*Select the string "wine"*/
1095 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1097 /*Copy the italicized "wine" to the clipboard*/
1098 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1100 /*Reset the formatting to default*/
1101 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1102 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1103 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1105 /*Clear the text in the control*/
1106 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1108 /*Switch to Plain Text Mode*/
1109 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1110 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1112 /*Input "wine" again in normal format*/
1113 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1115 /*Paste the italicized "wine" into the control*/
1116 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1118 /*Select a character from the first "wine" string*/
1121 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1123 /*Retrieve its formatting*/
1124 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1127 /*Select a character from the second "wine" string*/
1130 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1132 /*Retrieve its formatting*/
1133 cf2test.cbSize = sizeof(CHARFORMAT2);
1134 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1137 /*Compare the two formattings*/
1138 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1139 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1140 cf2.dwEffects, cf2test.dwEffects);
1141 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1142 printing "wine" in the current format(normal)
1143 pasting "wine" from the clipboard(italicized)
1144 comparing the two formats(should differ)*/
1146 /*Attempt to switch with text in control*/
1147 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1148 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1151 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1153 /*Switch into Rich Text mode*/
1154 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1155 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1157 /*Print "wine" in normal formatting into the control*/
1158 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1160 /*Paste italicized "wine" into the control*/
1161 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1163 /*Select text from the first "wine" string*/
1166 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1168 /*Retrieve its formatting*/
1169 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1172 /*Select text from the second "wine" string*/
1175 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1177 /*Retrieve its formatting*/
1178 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1181 /*Test that the two formattings are not the same*/
1182 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1183 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1184 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1186 DestroyWindow(hwndRichEdit);
1189 static void test_SETPARAFORMAT(void)
1191 HWND hwndRichEdit = new_richedit(NULL);
1194 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1195 fmt.cbSize = sizeof(PARAFORMAT2);
1196 fmt.dwMask = PFM_ALIGNMENT;
1197 fmt.wAlignment = PFA_LEFT;
1199 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1200 ok(ret != 0, "expected non-zero got %d\n", ret);
1202 fmt.cbSize = sizeof(PARAFORMAT2);
1204 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1205 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1206 * between richedit different native builds of riched20.dll
1207 * used on different Windows versions. */
1208 ret &= ~PFM_TABLEROWDELIMITER;
1209 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1211 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1212 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1214 DestroyWindow(hwndRichEdit);
1217 static void test_TM_PLAINTEXT(void)
1219 /*Tests plain text properties*/
1221 HWND hwndRichEdit = new_richedit(NULL);
1222 CHARFORMAT2 cf2, cf2test;
1226 /*Switch to plain text mode*/
1228 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1231 /*Fill control with text*/
1233 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1235 /*Select some text and bold it*/
1239 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1240 cf2.cbSize = sizeof(CHARFORMAT2);
1241 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1244 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1245 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1247 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1248 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1250 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2);
1251 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1253 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2);
1254 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1256 /*Get the formatting of those characters*/
1258 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1260 /*Get the formatting of some other characters*/
1261 cf2test.cbSize = sizeof(CHARFORMAT2);
1264 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1265 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1267 /*Test that they are the same as plain text allows only one formatting*/
1269 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1270 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1271 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1273 /*Fill the control with a "wine" string, which when inserted will be bold*/
1275 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1277 /*Copy the bolded "wine" string*/
1281 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1282 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1284 /*Swap back to rich text*/
1286 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1287 SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1289 /*Set the default formatting to bold italics*/
1291 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2);
1292 cf2.dwMask |= CFM_ITALIC;
1293 cf2.dwEffects ^= CFE_ITALIC;
1294 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1295 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1297 /*Set the text in the control to "wine", which will be bold and italicized*/
1299 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1301 /*Paste the plain text "wine" string, which should take the insert
1302 formatting, which at the moment is bold italics*/
1304 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1306 /*Select the first "wine" string and retrieve its formatting*/
1310 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1311 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2);
1313 /*Select the second "wine" string and retrieve its formatting*/
1317 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1318 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test);
1320 /*Compare the two formattings. They should be the same.*/
1322 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1323 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1324 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1325 DestroyWindow(hwndRichEdit);
1328 static void test_WM_GETTEXT(void)
1330 HWND hwndRichEdit = new_richedit(NULL);
1331 static const char text[] = "Hello. My name is RichEdit!";
1332 static const char text2[] = "Hello. My name is RichEdit!\r";
1333 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1334 char buffer[1024] = {0};
1337 /* Baseline test with normal-sized buffer */
1338 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1339 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1340 ok(result == lstrlen(buffer),
1341 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1342 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1343 result = strcmp(buffer,text);
1345 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1347 /* Test for returned value of WM_GETTEXTLENGTH */
1348 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1349 ok(result == lstrlen(text),
1350 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1351 result, lstrlen(text));
1353 /* Test for behavior in overflow case */
1354 memset(buffer, 0, 1024);
1355 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1357 result == lstrlenA(text) - 1, /* XP, win2k3 */
1358 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1359 result = strcmp(buffer,text);
1361 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1363 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1365 /* Baseline test with normal-sized buffer and carriage return */
1366 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1367 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1368 ok(result == lstrlen(buffer),
1369 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1370 result = strcmp(buffer,text2_after);
1372 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1374 /* Test for returned value of WM_GETTEXTLENGTH */
1375 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1376 ok(result == lstrlen(text2_after),
1377 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1378 result, lstrlen(text2_after));
1380 /* Test for behavior of CRLF conversion in case of overflow */
1381 memset(buffer, 0, 1024);
1382 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1384 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1385 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1386 result = strcmp(buffer,text2);
1388 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1390 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1392 DestroyWindow(hwndRichEdit);
1395 static void test_EM_GETTEXTRANGE(void)
1397 HWND hwndRichEdit = new_richedit(NULL);
1398 const char * text1 = "foo bar\r\nfoo bar";
1399 const char * text2 = "foo bar\rfoo bar";
1400 const char * expect = "bar\rfoo";
1401 char buffer[1024] = {0};
1403 TEXTRANGEA textRange;
1405 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1407 textRange.lpstrText = buffer;
1408 textRange.chrg.cpMin = 4;
1409 textRange.chrg.cpMax = 11;
1410 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1411 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1412 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1414 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1416 textRange.lpstrText = buffer;
1417 textRange.chrg.cpMin = 4;
1418 textRange.chrg.cpMax = 11;
1419 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1420 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1421 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1423 /* cpMax of text length is used instead of -1 in this case */
1424 textRange.lpstrText = buffer;
1425 textRange.chrg.cpMin = 0;
1426 textRange.chrg.cpMax = -1;
1427 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1428 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1429 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1431 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1432 textRange.lpstrText = buffer;
1433 textRange.chrg.cpMin = -1;
1434 textRange.chrg.cpMax = 1;
1435 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1436 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1437 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1439 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1440 textRange.lpstrText = buffer;
1441 textRange.chrg.cpMin = 1;
1442 textRange.chrg.cpMax = -1;
1443 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1444 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1445 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1447 /* no end character is copied if cpMax - cpMin < 0 */
1448 textRange.lpstrText = buffer;
1449 textRange.chrg.cpMin = 5;
1450 textRange.chrg.cpMax = 5;
1451 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1452 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1453 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1455 /* cpMax of text length is used if cpMax > text length*/
1456 textRange.lpstrText = buffer;
1457 textRange.chrg.cpMin = 0;
1458 textRange.chrg.cpMax = 1000;
1459 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1460 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1461 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1463 DestroyWindow(hwndRichEdit);
1466 static void test_EM_GETSELTEXT(void)
1468 HWND hwndRichEdit = new_richedit(NULL);
1469 const char * text1 = "foo bar\r\nfoo bar";
1470 const char * text2 = "foo bar\rfoo bar";
1471 const char * expect = "bar\rfoo";
1472 char buffer[1024] = {0};
1475 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1477 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1478 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1479 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1482 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1484 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1485 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1486 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1489 DestroyWindow(hwndRichEdit);
1492 /* FIXME: need to test unimplemented options and robustly test wparam */
1493 static void test_EM_SETOPTIONS(void)
1496 static const char text[] = "Hello. My name is RichEdit!";
1497 char buffer[1024] = {0};
1498 DWORD dwStyle, options, oldOptions;
1499 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1500 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1501 ES_SELECTIONBAR|ES_VERTICAL;
1503 /* Test initial options. */
1504 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1505 0, 0, 200, 60, NULL, NULL,
1506 hmoduleRichEdit, NULL);
1507 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1508 RICHEDIT_CLASS, (int) GetLastError());
1509 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1510 ok(options == 0, "Incorrect initial options %x\n", options);
1511 DestroyWindow(hwndRichEdit);
1513 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1514 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1515 0, 0, 200, 60, NULL, NULL,
1516 hmoduleRichEdit, NULL);
1517 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1518 RICHEDIT_CLASS, (int) GetLastError());
1519 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1520 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1521 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1522 "Incorrect initial options %x\n", options);
1524 /* NEGATIVE TESTING - NO OPTIONS SET */
1525 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1526 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1528 /* testing no readonly by sending 'a' to the control*/
1529 SetFocus(hwndRichEdit);
1530 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1531 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1533 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1534 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1536 /* READONLY - sending 'a' to the control */
1537 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1538 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1539 SetFocus(hwndRichEdit);
1540 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1541 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1542 ok(buffer[0]==text[0],
1543 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1545 /* EM_SETOPTIONS changes the window style, but changing the
1546 * window style does not change the options. */
1547 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1548 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1549 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1550 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1551 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1552 /* Confirm that the text is still read only. */
1553 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1554 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1555 ok(buffer[0]==text[0],
1556 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1558 oldOptions = options;
1559 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1560 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1561 ok(options == oldOptions,
1562 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1564 DestroyWindow(hwndRichEdit);
1567 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1569 CHARFORMAT2W text_format;
1570 text_format.cbSize = sizeof(text_format);
1571 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1572 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1573 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1576 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1578 int link_present = 0;
1580 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1582 { /* control text is url; should get CFE_LINK */
1583 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1587 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1591 static HWND new_static_wnd(HWND parent) {
1592 return new_window("Static", 0, parent);
1595 static void test_EM_AUTOURLDETECT(void)
1597 /* DO NOT change the properties of the first two elements. To shorten the
1598 tests, all tests after WM_SETTEXT test just the first two elements -
1599 one non-URL and one URL */
1605 {"http://www.winehq.org", 1},
1606 {"http//winehq.org", 0},
1607 {"ww.winehq.org", 0},
1608 {"www.winehq.org", 1},
1609 {"ftp://192.168.1.1", 1},
1610 {"ftp//192.168.1.1", 0},
1611 {"mailto:your@email.com", 1},
1612 {"prospero:prosperoserver", 1},
1614 {"news:newserver", 1},
1615 {"wais:waisserver", 1}
1620 HWND hwndRichEdit, parent;
1622 /* All of the following should cause the URL to be detected */
1623 const char * templates_delim[] = {
1624 "This is some text with X on it",
1625 "This is some text with (X) on it",
1626 "This is some text with X\r on it",
1627 "This is some text with ---X--- on it",
1628 "This is some text with \"X\" on it",
1629 "This is some text with 'X' on it",
1630 "This is some text with 'X' on it",
1631 "This is some text with :X: on it",
1633 "This text ends with X",
1635 "This is some text with X) on it",
1636 "This is some text with X--- on it",
1637 "This is some text with X\" on it",
1638 "This is some text with X' on it",
1639 "This is some text with X: on it",
1641 "This is some text with (X on it",
1642 "This is some text with \rX on it",
1643 "This is some text with ---X on it",
1644 "This is some text with \"X on it",
1645 "This is some text with 'X on it",
1646 "This is some text with :X on it",
1648 /* None of these should cause the URL to be detected */
1649 const char * templates_non_delim[] = {
1650 "This is some text with |X| on it",
1651 "This is some text with *X* on it",
1652 "This is some text with /X/ on it",
1653 "This is some text with +X+ on it",
1654 "This is some text with %X% on it",
1655 "This is some text with #X# on it",
1656 "This is some text with @X@ on it",
1657 "This is some text with \\X\\ on it",
1658 "This is some text with |X on it",
1659 "This is some text with *X on it",
1660 "This is some text with /X on it",
1661 "This is some text with +X on it",
1662 "This is some text with %X on it",
1663 "This is some text with #X on it",
1664 "This is some text with @X on it",
1665 "This is some text with \\X on it",
1667 /* All of these cause the URL detection to be extended by one more byte,
1668 thus demonstrating that the tested character is considered as part
1670 const char * templates_xten_delim[] = {
1671 "This is some text with X| on it",
1672 "This is some text with X* on it",
1673 "This is some text with X/ on it",
1674 "This is some text with X+ on it",
1675 "This is some text with X% on it",
1676 "This is some text with X# on it",
1677 "This is some text with X@ on it",
1678 "This is some text with X\\ on it",
1682 parent = new_static_wnd(NULL);
1683 hwndRichEdit = new_richedit(parent);
1684 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1685 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1686 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1687 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1688 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1689 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1690 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1691 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1692 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1693 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1694 /* for each url, check the text to see if CFE_LINK effect is present */
1695 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1697 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1698 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1699 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1701 /* Link detection should happen immediately upon WM_SETTEXT */
1702 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1703 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1704 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1706 DestroyWindow(hwndRichEdit);
1708 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1709 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1710 hwndRichEdit = new_richedit(parent);
1712 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1717 at_pos = strchr(templates_delim[j], 'X');
1718 at_offset = at_pos - templates_delim[j];
1719 strncpy(buffer, templates_delim[j], at_offset);
1720 buffer[at_offset] = '\0';
1721 strcat(buffer, urls[i].text);
1722 strcat(buffer, templates_delim[j] + at_offset + 1);
1723 end_offset = at_offset + strlen(urls[i].text);
1725 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1726 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1728 /* This assumes no templates start with the URL itself, and that they
1729 have at least two characters before the URL text */
1730 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1731 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1732 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1733 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1734 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1735 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1739 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1740 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1741 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1742 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1746 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1747 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1748 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1749 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1751 if (buffer[end_offset] != '\0')
1753 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1754 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1755 if (buffer[end_offset +1] != '\0')
1757 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1758 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1763 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1768 at_pos = strchr(templates_non_delim[j], 'X');
1769 at_offset = at_pos - templates_non_delim[j];
1770 strncpy(buffer, templates_non_delim[j], at_offset);
1771 buffer[at_offset] = '\0';
1772 strcat(buffer, urls[i].text);
1773 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1774 end_offset = at_offset + strlen(urls[i].text);
1776 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1777 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1779 /* This assumes no templates start with the URL itself, and that they
1780 have at least two characters before the URL text */
1781 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1782 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1783 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1784 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1785 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1786 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1788 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1789 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1790 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1791 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1792 if (buffer[end_offset] != '\0')
1794 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1795 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1796 if (buffer[end_offset +1] != '\0')
1798 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1799 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1804 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1809 at_pos = strchr(templates_xten_delim[j], 'X');
1810 at_offset = at_pos - templates_xten_delim[j];
1811 strncpy(buffer, templates_xten_delim[j], at_offset);
1812 buffer[at_offset] = '\0';
1813 strcat(buffer, urls[i].text);
1814 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1815 end_offset = at_offset + strlen(urls[i].text);
1817 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1818 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1820 /* This assumes no templates start with the URL itself, and that they
1821 have at least two characters before the URL text */
1822 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1823 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1824 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1825 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1831 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1832 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1833 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1834 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1835 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1836 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
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);
1847 if (buffer[end_offset +1] != '\0')
1849 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1850 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1851 if (buffer[end_offset +2] != '\0')
1853 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1854 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1859 DestroyWindow(hwndRichEdit);
1860 hwndRichEdit = NULL;
1863 /* Test detection of URLs within normal text - WM_CHAR case. */
1864 /* Test only the first two URL examples for brevity */
1865 for (i = 0; i < 2; i++) {
1866 hwndRichEdit = new_richedit(parent);
1868 /* Also for brevity, test only the first three delimiters */
1869 for (j = 0; j < 3; j++) {
1875 at_pos = strchr(templates_delim[j], 'X');
1876 at_offset = at_pos - templates_delim[j];
1877 end_offset = at_offset + strlen(urls[i].text);
1879 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1880 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1881 for (u = 0; templates_delim[j][u]; u++) {
1882 if (templates_delim[j][u] == '\r') {
1883 simulate_typing_characters(hwndRichEdit, "\r");
1884 } else if (templates_delim[j][u] != 'X') {
1885 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1887 for (v = 0; urls[i].text[v]; v++) {
1888 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1892 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1894 /* This assumes no templates start with the URL itself, and that they
1895 have at least two characters before the URL text */
1896 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1897 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1898 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1899 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1900 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1901 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1905 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1906 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1907 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1908 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1912 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1913 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1914 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1915 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1917 if (buffer[end_offset] != '\0')
1919 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1920 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1921 if (buffer[end_offset +1] != '\0')
1923 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1924 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1928 /* The following will insert a paragraph break after the first character
1929 of the URL candidate, thus breaking the URL. It is expected that the
1930 CFE_LINK attribute should break across both pieces of the URL */
1931 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1932 simulate_typing_characters(hwndRichEdit, "\r");
1933 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1935 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1936 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1937 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1938 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1939 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1940 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1942 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1943 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1944 /* end_offset moved because of paragraph break */
1945 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1946 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
1947 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
1948 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
1950 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
1951 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
1952 if (buffer[end_offset +2] != '\0')
1954 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1955 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1959 /* The following will remove the just-inserted paragraph break, thus
1960 restoring the URL */
1961 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
1962 simulate_typing_characters(hwndRichEdit, "\b");
1963 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1965 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1966 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1967 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1968 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1974 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1975 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1976 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1977 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1981 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1982 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1983 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1984 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1986 if (buffer[end_offset] != '\0')
1988 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1989 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1990 if (buffer[end_offset +1] != '\0')
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1997 DestroyWindow(hwndRichEdit);
1998 hwndRichEdit = NULL;
2001 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2002 /* Test just the first two URL examples for brevity */
2003 for (i = 0; i < 2; i++) {
2006 hwndRichEdit = new_richedit(parent);
2008 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2010 1) Set entire text, a la WM_SETTEXT
2011 2) Set a selection of the text to the URL
2012 3) Set a portion of the text at a time, which eventually results in
2014 All of them should give equivalent results
2017 /* Set entire text in one go, like WM_SETTEXT */
2018 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2023 st.codepage = CP_ACP;
2024 st.flags = ST_DEFAULT;
2026 at_pos = strchr(templates_delim[j], 'X');
2027 at_offset = at_pos - templates_delim[j];
2028 strncpy(buffer, templates_delim[j], at_offset);
2029 buffer[at_offset] = '\0';
2030 strcat(buffer, urls[i].text);
2031 strcat(buffer, templates_delim[j] + at_offset + 1);
2032 end_offset = at_offset + strlen(urls[i].text);
2034 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2035 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2037 /* This assumes no templates start with the URL itself, and that they
2038 have at least two characters before the URL text */
2039 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2040 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2041 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2042 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2043 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2044 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2048 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2049 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2050 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2051 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2055 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2056 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2057 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2058 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2060 if (buffer[end_offset] != '\0')
2062 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2063 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2064 if (buffer[end_offset +1] != '\0')
2066 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2067 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2072 /* Set selection with X to the URL */
2073 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2078 at_pos = strchr(templates_delim[j], 'X');
2079 at_offset = at_pos - templates_delim[j];
2080 end_offset = at_offset + strlen(urls[i].text);
2082 st.codepage = CP_ACP;
2083 st.flags = ST_DEFAULT;
2084 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2085 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2086 st.flags = ST_SELECTION;
2087 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2088 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2089 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2091 /* This assumes no templates start with the URL itself, and that they
2092 have at least two characters before the URL text */
2093 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2094 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2095 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2096 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2097 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2098 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2102 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2103 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2104 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2105 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2109 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2110 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2111 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2112 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2114 if (buffer[end_offset] != '\0')
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2118 if (buffer[end_offset +1] != '\0')
2120 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2121 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2126 /* Set selection with X to the first character of the URL, then the rest */
2127 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2132 at_pos = strchr(templates_delim[j], 'X');
2133 at_offset = at_pos - templates_delim[j];
2134 end_offset = at_offset + strlen(urls[i].text);
2136 strcpy(buffer, "YY");
2137 buffer[0] = urls[i].text[0];
2139 st.codepage = CP_ACP;
2140 st.flags = ST_DEFAULT;
2141 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2143 st.flags = ST_SELECTION;
2144 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2146 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2147 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2148 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2150 /* This assumes no templates start with the URL itself, and that they
2151 have at least two characters before the URL text */
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2156 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2157 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2161 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2162 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2163 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2164 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2173 if (buffer[end_offset] != '\0')
2175 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2176 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2177 if (buffer[end_offset +1] != '\0')
2179 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2180 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2185 DestroyWindow(hwndRichEdit);
2186 hwndRichEdit = NULL;
2189 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2190 /* Test just the first two URL examples for brevity */
2191 for (i = 0; i < 2; i++) {
2192 hwndRichEdit = new_richedit(parent);
2194 /* Set selection with X to the URL */
2195 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2200 at_pos = strchr(templates_delim[j], 'X');
2201 at_offset = at_pos - templates_delim[j];
2202 end_offset = at_offset + strlen(urls[i].text);
2204 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2205 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2206 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2207 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2208 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2210 /* This assumes no templates start with the URL itself, and that they
2211 have at least two characters before the URL text */
2212 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2213 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2214 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2215 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2216 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2217 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2221 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2222 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2223 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2224 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2228 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2229 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2230 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2231 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2233 if (buffer[end_offset] != '\0')
2235 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2236 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2237 if (buffer[end_offset +1] != '\0')
2239 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2240 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2245 /* Set selection with X to the first character of the URL, then the rest */
2246 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2251 at_pos = strchr(templates_delim[j], 'X');
2252 at_offset = at_pos - templates_delim[j];
2253 end_offset = at_offset + strlen(urls[i].text);
2255 strcpy(buffer, "YY");
2256 buffer[0] = urls[i].text[0];
2258 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2260 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2261 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2262 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2263 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2264 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2266 /* This assumes no templates start with the URL itself, and that they
2267 have at least two characters before the URL text */
2268 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2269 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2270 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2271 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2272 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2273 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2277 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2278 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2279 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2280 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2284 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2285 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2286 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2287 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2289 if (buffer[end_offset] != '\0')
2291 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2292 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2293 if (buffer[end_offset +1] != '\0')
2295 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2296 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2301 DestroyWindow(hwndRichEdit);
2302 hwndRichEdit = NULL;
2305 DestroyWindow(parent);
2308 static void test_EM_SCROLL(void)
2311 int r; /* return value */
2312 int expr; /* expected return value */
2313 HWND hwndRichEdit = new_richedit(NULL);
2314 int y_before, y_after; /* units of lines of text */
2316 /* test a richedit box containing a single line of text */
2317 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2319 for (i = 0; i < 4; i++) {
2320 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2322 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2323 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2324 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2325 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2326 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2327 "(i == %d)\n", y_after, i);
2331 * test a richedit box that will scroll. There are two general
2332 * cases: the case without any long lines and the case with a long
2335 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2339 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2340 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2341 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2342 "LONG LINE \nb\nc\nd\ne");
2343 for (j = 0; j < 12; j++) /* reset scroll position to top */
2344 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2346 /* get first visible line */
2347 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2348 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2350 /* get new current first visible line */
2351 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2353 ok(((r & 0xffffff00) == 0x00010000) &&
2354 ((r & 0x000000ff) != 0x00000000),
2355 "EM_SCROLL page down didn't scroll by a small positive number of "
2356 "lines (r == 0x%08x)\n", r);
2357 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2358 "(line %d scrolled to line %d\n", y_before, y_after);
2362 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2363 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2364 ok(((r & 0xffffff00) == 0x0001ff00),
2365 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2366 "(r == 0x%08x)\n", r);
2367 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2368 "%d scrolled to line %d\n", y_before, y_after);
2372 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2374 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2376 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2377 "(r == 0x%08x)\n", r);
2378 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2379 "1 line (%d scrolled to %d)\n", y_before, y_after);
2383 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2385 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2387 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2388 "(r == 0x%08x)\n", r);
2389 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2390 "line (%d scrolled to %d)\n", y_before, y_after);
2394 r = SendMessage(hwndRichEdit, EM_SCROLL,
2395 SB_LINEUP, 0); /* lineup beyond top */
2397 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2400 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2401 ok(y_before == y_after,
2402 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2406 r = SendMessage(hwndRichEdit, EM_SCROLL,
2407 SB_PAGEUP, 0);/*page up beyond top */
2409 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2412 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2413 ok(y_before == y_after,
2414 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2416 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2417 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2418 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2419 r = SendMessage(hwndRichEdit, EM_SCROLL,
2420 SB_PAGEDOWN, 0); /* page down beyond bot */
2421 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2424 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2425 ok(y_before == y_after,
2426 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2429 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2430 SendMessage(hwndRichEdit, EM_SCROLL,
2431 SB_LINEDOWN, 0); /* line down beyond bot */
2432 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2435 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2436 ok(y_before == y_after,
2437 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2440 DestroyWindow(hwndRichEdit);
2443 unsigned int recursionLevel = 0;
2444 unsigned int WM_SIZE_recursionLevel = 0;
2445 BOOL bailedOutOfRecursion = FALSE;
2446 LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2448 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2452 if (bailedOutOfRecursion) return 0;
2453 if (recursionLevel >= 32) {
2454 bailedOutOfRecursion = TRUE;
2461 WM_SIZE_recursionLevel++;
2462 r = richeditProc(hwnd, message, wParam, lParam);
2463 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2464 ShowScrollBar(hwnd, SB_VERT, TRUE);
2465 WM_SIZE_recursionLevel--;
2468 r = richeditProc(hwnd, message, wParam, lParam);
2475 static void test_scrollbar_visibility(void)
2478 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2483 /* These tests show that richedit should temporarily refrain from automatically
2484 hiding or showing its scrollbars (vertical at least) when an explicit request
2485 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2486 Some applications depend on forced showing (when otherwise richedit would
2487 hide the vertical scrollbar) and are thrown on an endless recursive loop
2488 if richedit auto-hides the scrollbar again. Apparently they never heard of
2489 the ES_DISABLENOSCROLL style... */
2491 hwndRichEdit = new_richedit(NULL);
2493 /* Test default scrollbar visibility behavior */
2494 memset(&si, 0, sizeof(si));
2495 si.cbSize = sizeof(si);
2496 si.fMask = SIF_PAGE | SIF_RANGE;
2497 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2498 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2499 "Vertical scrollbar is visible, should be invisible.\n");
2500 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2501 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2502 si.nPage, si.nMin, si.nMax);
2504 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2505 memset(&si, 0, sizeof(si));
2506 si.cbSize = sizeof(si);
2507 si.fMask = SIF_PAGE | SIF_RANGE;
2508 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2509 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2510 "Vertical scrollbar is visible, should be invisible.\n");
2511 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2512 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2513 si.nPage, si.nMin, si.nMax);
2515 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2516 memset(&si, 0, sizeof(si));
2517 si.cbSize = sizeof(si);
2518 si.fMask = SIF_PAGE | SIF_RANGE;
2519 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2520 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2521 "Vertical scrollbar is invisible, should be visible.\n");
2522 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2523 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2524 si.nPage, si.nMin, si.nMax);
2526 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2527 even though it hides the scrollbar */
2528 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2529 memset(&si, 0, sizeof(si));
2530 si.cbSize = sizeof(si);
2531 si.fMask = SIF_PAGE | SIF_RANGE;
2532 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2533 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2534 "Vertical scrollbar is visible, should be invisible.\n");
2535 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2536 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2537 si.nPage, si.nMin, si.nMax);
2539 /* Setting non-scrolling text again does *not* reset scrollbar range */
2540 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2541 memset(&si, 0, sizeof(si));
2542 si.cbSize = sizeof(si);
2543 si.fMask = SIF_PAGE | SIF_RANGE;
2544 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2545 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2546 "Vertical scrollbar is visible, should be invisible.\n");
2547 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2548 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2549 si.nPage, si.nMin, si.nMax);
2551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2552 memset(&si, 0, sizeof(si));
2553 si.cbSize = sizeof(si);
2554 si.fMask = SIF_PAGE | SIF_RANGE;
2555 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2556 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2557 "Vertical scrollbar is visible, should be invisible.\n");
2558 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2559 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2560 si.nPage, si.nMin, si.nMax);
2562 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2563 memset(&si, 0, sizeof(si));
2564 si.cbSize = sizeof(si);
2565 si.fMask = SIF_PAGE | SIF_RANGE;
2566 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2567 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2568 "Vertical scrollbar is visible, should be invisible.\n");
2569 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2570 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2571 si.nPage, si.nMin, si.nMax);
2573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2574 memset(&si, 0, sizeof(si));
2575 si.cbSize = sizeof(si);
2576 si.fMask = SIF_PAGE | SIF_RANGE;
2577 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2578 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2579 "Vertical scrollbar is visible, should be invisible.\n");
2580 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2581 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2582 si.nPage, si.nMin, si.nMax);
2584 DestroyWindow(hwndRichEdit);
2586 /* Test again, with ES_DISABLENOSCROLL style */
2587 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2589 /* Test default scrollbar visibility behavior */
2590 memset(&si, 0, sizeof(si));
2591 si.cbSize = sizeof(si);
2592 si.fMask = SIF_PAGE | SIF_RANGE;
2593 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2594 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2595 "Vertical scrollbar is invisible, should be visible.\n");
2596 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2597 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2598 si.nPage, si.nMin, si.nMax);
2600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2601 memset(&si, 0, sizeof(si));
2602 si.cbSize = sizeof(si);
2603 si.fMask = SIF_PAGE | SIF_RANGE;
2604 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2605 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2606 "Vertical scrollbar is invisible, should be visible.\n");
2607 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2608 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2609 si.nPage, si.nMin, si.nMax);
2611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2612 memset(&si, 0, sizeof(si));
2613 si.cbSize = sizeof(si);
2614 si.fMask = SIF_PAGE | SIF_RANGE;
2615 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2616 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2617 "Vertical scrollbar is invisible, should be visible.\n");
2618 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2619 "reported page/range is %d (%d..%d)\n",
2620 si.nPage, si.nMin, si.nMax);
2622 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2623 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2624 memset(&si, 0, sizeof(si));
2625 si.cbSize = sizeof(si);
2626 si.fMask = SIF_PAGE | SIF_RANGE;
2627 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2628 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2629 "Vertical scrollbar is invisible, should be visible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2631 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632 si.nPage, si.nMin, si.nMax);
2634 /* Setting non-scrolling text again does *not* reset scrollbar range */
2635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2636 memset(&si, 0, sizeof(si));
2637 si.cbSize = sizeof(si);
2638 si.fMask = SIF_PAGE | SIF_RANGE;
2639 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2640 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2641 "Vertical scrollbar is invisible, should be visible.\n");
2642 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2643 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2644 si.nPage, si.nMin, si.nMax);
2646 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2647 memset(&si, 0, sizeof(si));
2648 si.cbSize = sizeof(si);
2649 si.fMask = SIF_PAGE | SIF_RANGE;
2650 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2651 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2652 "Vertical scrollbar is invisible, should be visible.\n");
2653 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2654 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2655 si.nPage, si.nMin, si.nMax);
2657 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2658 memset(&si, 0, sizeof(si));
2659 si.cbSize = sizeof(si);
2660 si.fMask = SIF_PAGE | SIF_RANGE;
2661 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2662 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2663 "Vertical scrollbar is invisible, should be visible.\n");
2664 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2665 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2666 si.nPage, si.nMin, si.nMax);
2668 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2669 memset(&si, 0, sizeof(si));
2670 si.cbSize = sizeof(si);
2671 si.fMask = SIF_PAGE | SIF_RANGE;
2672 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2673 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2674 "Vertical scrollbar is invisible, should be visible.\n");
2675 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2676 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2677 si.nPage, si.nMin, si.nMax);
2679 DestroyWindow(hwndRichEdit);
2681 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2682 hwndRichEdit = new_richedit(NULL);
2684 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2685 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2686 memset(&si, 0, sizeof(si));
2687 si.cbSize = sizeof(si);
2688 si.fMask = SIF_PAGE | SIF_RANGE;
2689 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2690 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2691 "Vertical scrollbar is invisible, should be visible.\n");
2693 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2694 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2695 si.nPage, si.nMin, si.nMax);
2698 /* Ditto, see above */
2699 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2700 memset(&si, 0, sizeof(si));
2701 si.cbSize = sizeof(si);
2702 si.fMask = SIF_PAGE | SIF_RANGE;
2703 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2704 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2705 "Vertical scrollbar is invisible, should be visible.\n");
2707 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2708 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2709 si.nPage, si.nMin, si.nMax);
2712 /* Ditto, see above */
2713 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719 "Vertical scrollbar is invisible, should be visible.\n");
2721 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2722 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2723 si.nPage, si.nMin, si.nMax);
2726 /* Ditto, see above */
2727 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2728 memset(&si, 0, sizeof(si));
2729 si.cbSize = sizeof(si);
2730 si.fMask = SIF_PAGE | SIF_RANGE;
2731 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2732 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2733 "Vertical scrollbar is invisible, should be visible.\n");
2735 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2736 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2737 si.nPage, si.nMin, si.nMax);
2740 /* Ditto, see above */
2741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2747 "Vertical scrollbar is invisible, should be visible.\n");
2749 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751 si.nPage, si.nMin, si.nMax);
2754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2755 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2761 "Vertical scrollbar is visible, should be invisible.\n");
2762 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2763 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2764 si.nPage, si.nMin, si.nMax);
2766 DestroyWindow(hwndRichEdit);
2768 hwndRichEdit = new_richedit(NULL);
2770 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2771 memset(&si, 0, sizeof(si));
2772 si.cbSize = sizeof(si);
2773 si.fMask = SIF_PAGE | SIF_RANGE;
2774 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2775 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2776 "Vertical scrollbar is visible, should be invisible.\n");
2777 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2778 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2779 si.nPage, si.nMin, si.nMax);
2781 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2782 memset(&si, 0, sizeof(si));
2783 si.cbSize = sizeof(si);
2784 si.fMask = SIF_PAGE | SIF_RANGE;
2785 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2786 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2787 "Vertical scrollbar is visible, should be invisible.\n");
2788 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2789 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2790 si.nPage, si.nMin, si.nMax);
2792 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2793 memset(&si, 0, sizeof(si));
2794 si.cbSize = sizeof(si);
2795 si.fMask = SIF_PAGE | SIF_RANGE;
2796 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2797 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2798 "Vertical scrollbar is visible, should be invisible.\n");
2799 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2800 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2801 si.nPage, si.nMin, si.nMax);
2803 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2804 memset(&si, 0, sizeof(si));
2805 si.cbSize = sizeof(si);
2806 si.fMask = SIF_PAGE | SIF_RANGE;
2807 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2808 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2809 "Vertical scrollbar is visible, should be invisible.\n");
2810 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2811 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2812 si.nPage, si.nMin, si.nMax);
2814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2815 memset(&si, 0, sizeof(si));
2816 si.cbSize = sizeof(si);
2817 si.fMask = SIF_PAGE | SIF_RANGE;
2818 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2819 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2820 "Vertical scrollbar is invisible, should be visible.\n");
2821 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2822 "reported page/range is %d (%d..%d)\n",
2823 si.nPage, si.nMin, si.nMax);
2825 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2826 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2834 "reported page/range is %d (%d..%d)\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843 "Vertical scrollbar is visible, should be invisible.\n");
2844 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2845 "reported page/range is %d (%d..%d)\n",
2846 si.nPage, si.nMin, si.nMax);
2848 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2849 EM_SCROLL will make visible any forcefully invisible scrollbar */
2850 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2851 memset(&si, 0, sizeof(si));
2852 si.cbSize = sizeof(si);
2853 si.fMask = SIF_PAGE | SIF_RANGE;
2854 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2855 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2856 "Vertical scrollbar is invisible, should be visible.\n");
2857 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2858 "reported page/range is %d (%d..%d)\n",
2859 si.nPage, si.nMin, si.nMax);
2861 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2862 memset(&si, 0, sizeof(si));
2863 si.cbSize = sizeof(si);
2864 si.fMask = SIF_PAGE | SIF_RANGE;
2865 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2866 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2867 "Vertical scrollbar is visible, should be invisible.\n");
2868 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2869 "reported page/range is %d (%d..%d)\n",
2870 si.nPage, si.nMin, si.nMax);
2872 /* Again, EM_SCROLL, with SB_LINEUP */
2873 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2874 memset(&si, 0, sizeof(si));
2875 si.cbSize = sizeof(si);
2876 si.fMask = SIF_PAGE | SIF_RANGE;
2877 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2878 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2879 "Vertical scrollbar is invisible, should be visible.\n");
2880 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2881 "reported page/range is %d (%d..%d)\n",
2882 si.nPage, si.nMin, si.nMax);
2884 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2885 memset(&si, 0, sizeof(si));
2886 si.cbSize = sizeof(si);
2887 si.fMask = SIF_PAGE | SIF_RANGE;
2888 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2889 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2890 "Vertical scrollbar is visible, should be invisible.\n");
2891 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2892 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2893 si.nPage, si.nMin, si.nMax);
2895 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2896 memset(&si, 0, sizeof(si));
2897 si.cbSize = sizeof(si);
2898 si.fMask = SIF_PAGE | SIF_RANGE;
2899 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2900 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2901 "Vertical scrollbar is invisible, should be visible.\n");
2902 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2903 "reported page/range is %d (%d..%d)\n",
2904 si.nPage, si.nMin, si.nMax);
2906 DestroyWindow(hwndRichEdit);
2909 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2910 hwndRichEdit = new_richedit(NULL);
2912 #define ENABLE_WS_VSCROLL(hwnd) \
2913 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2914 #define DISABLE_WS_VSCROLL(hwnd) \
2915 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2917 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2918 ENABLE_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 invisible, should be visible.\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 /* Ditto, see above */
2930 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2931 memset(&si, 0, sizeof(si));
2932 si.cbSize = sizeof(si);
2933 si.fMask = SIF_PAGE | SIF_RANGE;
2934 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2935 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2936 "Vertical scrollbar is invisible, should be visible.\n");
2937 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2938 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2939 si.nPage, si.nMin, si.nMax);
2941 /* Ditto, see above */
2942 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2943 memset(&si, 0, sizeof(si));
2944 si.cbSize = sizeof(si);
2945 si.fMask = SIF_PAGE | SIF_RANGE;
2946 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2947 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2948 "Vertical scrollbar is invisible, should be visible.\n");
2949 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2950 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2951 si.nPage, si.nMin, si.nMax);
2953 /* Ditto, see above */
2954 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2955 memset(&si, 0, sizeof(si));
2956 si.cbSize = sizeof(si);
2957 si.fMask = SIF_PAGE | SIF_RANGE;
2958 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2959 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2960 "Vertical scrollbar is invisible, should be visible.\n");
2961 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2962 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2963 si.nPage, si.nMin, si.nMax);
2965 /* Ditto, see above */
2966 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2967 memset(&si, 0, sizeof(si));
2968 si.cbSize = sizeof(si);
2969 si.fMask = SIF_PAGE | SIF_RANGE;
2970 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2971 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2972 "Vertical scrollbar is invisible, should be visible.\n");
2973 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2974 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2975 si.nPage, si.nMin, si.nMax);
2977 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2978 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2979 memset(&si, 0, sizeof(si));
2980 si.cbSize = sizeof(si);
2981 si.fMask = SIF_PAGE | SIF_RANGE;
2982 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2983 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2984 "Vertical scrollbar is visible, should be invisible.\n");
2985 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2986 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2987 si.nPage, si.nMin, si.nMax);
2989 DestroyWindow(hwndRichEdit);
2991 hwndRichEdit = new_richedit(NULL);
2993 DISABLE_WS_VSCROLL(hwndRichEdit);
2994 memset(&si, 0, sizeof(si));
2995 si.cbSize = sizeof(si);
2996 si.fMask = SIF_PAGE | SIF_RANGE;
2997 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2998 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2999 "Vertical scrollbar is visible, should be invisible.\n");
3000 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3001 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3002 si.nPage, si.nMin, si.nMax);
3004 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3005 memset(&si, 0, sizeof(si));
3006 si.cbSize = sizeof(si);
3007 si.fMask = SIF_PAGE | SIF_RANGE;
3008 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3009 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3010 "Vertical scrollbar is visible, should be invisible.\n");
3011 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3012 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3013 si.nPage, si.nMin, si.nMax);
3015 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3016 memset(&si, 0, sizeof(si));
3017 si.cbSize = sizeof(si);
3018 si.fMask = SIF_PAGE | SIF_RANGE;
3019 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3020 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3021 "Vertical scrollbar is visible, should be invisible.\n");
3022 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3023 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3024 si.nPage, si.nMin, si.nMax);
3026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3027 memset(&si, 0, sizeof(si));
3028 si.cbSize = sizeof(si);
3029 si.fMask = SIF_PAGE | SIF_RANGE;
3030 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3031 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3032 "Vertical scrollbar is visible, should be invisible.\n");
3033 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3034 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3035 si.nPage, si.nMin, si.nMax);
3037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3038 memset(&si, 0, sizeof(si));
3039 si.cbSize = sizeof(si);
3040 si.fMask = SIF_PAGE | SIF_RANGE;
3041 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3042 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3043 "Vertical scrollbar is invisible, should be visible.\n");
3044 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3045 "reported page/range is %d (%d..%d)\n",
3046 si.nPage, si.nMin, si.nMax);
3048 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3049 DISABLE_WS_VSCROLL(hwndRichEdit);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3057 "reported page/range is %d (%d..%d)\n",
3058 si.nPage, si.nMin, si.nMax);
3060 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066 "Vertical scrollbar is visible, should be invisible.\n");
3067 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3068 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3069 si.nPage, si.nMin, si.nMax);
3071 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3072 memset(&si, 0, sizeof(si));
3073 si.cbSize = sizeof(si);
3074 si.fMask = SIF_PAGE | SIF_RANGE;
3075 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3077 "Vertical scrollbar is invisible, should be visible.\n");
3078 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3079 "reported page/range is %d (%d..%d)\n",
3080 si.nPage, si.nMin, si.nMax);
3082 DISABLE_WS_VSCROLL(hwndRichEdit);
3083 memset(&si, 0, sizeof(si));
3084 si.cbSize = sizeof(si);
3085 si.fMask = SIF_PAGE | SIF_RANGE;
3086 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088 "Vertical scrollbar is visible, should be invisible.\n");
3089 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3090 "reported page/range is %d (%d..%d)\n",
3091 si.nPage, si.nMin, si.nMax);
3093 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3094 EM_SCROLL will make visible any forcefully invisible scrollbar */
3095 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3096 memset(&si, 0, sizeof(si));
3097 si.cbSize = sizeof(si);
3098 si.fMask = SIF_PAGE | SIF_RANGE;
3099 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3100 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3101 "Vertical scrollbar is invisible, should be visible.\n");
3102 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3103 "reported page/range is %d (%d..%d)\n",
3104 si.nPage, si.nMin, si.nMax);
3106 DISABLE_WS_VSCROLL(hwndRichEdit);
3107 memset(&si, 0, sizeof(si));
3108 si.cbSize = sizeof(si);
3109 si.fMask = SIF_PAGE | SIF_RANGE;
3110 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3111 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3112 "Vertical scrollbar is visible, should be invisible.\n");
3113 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3114 "reported page/range is %d (%d..%d)\n",
3115 si.nPage, si.nMin, si.nMax);
3117 /* Again, EM_SCROLL, with SB_LINEUP */
3118 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3119 memset(&si, 0, sizeof(si));
3120 si.cbSize = sizeof(si);
3121 si.fMask = SIF_PAGE | SIF_RANGE;
3122 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3123 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3124 "Vertical scrollbar is invisible, should be visible.\n");
3125 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3126 "reported page/range is %d (%d..%d)\n",
3127 si.nPage, si.nMin, si.nMax);
3129 DestroyWindow(hwndRichEdit);
3131 /* This window proc models what is going on with Corman Lisp 3.0.
3132 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3133 force the scrollbar into visibility. Recursion should NOT happen
3134 as a result of this action.
3136 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3138 richeditProc = cls.lpfnWndProc;
3139 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3140 cls.lpszClassName = "RicheditStupidOverride";
3141 if(!RegisterClassA(&cls)) assert(0);
3144 WM_SIZE_recursionLevel = 0;
3145 bailedOutOfRecursion = FALSE;
3146 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3147 ok(!bailedOutOfRecursion,
3148 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3151 WM_SIZE_recursionLevel = 0;
3152 bailedOutOfRecursion = FALSE;
3153 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3154 ok(!bailedOutOfRecursion,
3155 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3157 /* Unblock window in order to process WM_DESTROY */
3159 bailedOutOfRecursion = FALSE;
3160 WM_SIZE_recursionLevel = 0;
3161 DestroyWindow(hwndRichEdit);
3165 static void test_EM_SETUNDOLIMIT(void)
3167 /* cases we test for:
3168 * default behaviour - limiting at 100 undo's
3169 * undo disabled - setting a limit of 0
3170 * undo limited - undo limit set to some to some number, like 2
3171 * bad input - sending a negative number should default to 100 undo's */
3173 HWND hwndRichEdit = new_richedit(NULL);
3178 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3181 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3182 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3183 also, multiple pastes don't combine like WM_CHAR would */
3184 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3186 /* first case - check the default */
3187 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3188 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3189 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3190 for (i=0; i<100; i++) /* Undo 100 of them */
3191 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3192 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3193 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3195 /* second case - cannot undo */
3196 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3197 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3198 SendMessage(hwndRichEdit,
3199 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3200 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3201 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3203 /* third case - set it to an arbitrary number */
3204 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3205 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3206 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3207 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3208 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3209 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3210 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3211 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3212 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3213 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3214 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3215 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3216 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3217 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3219 /* fourth case - setting negative numbers should default to 100 undos */
3220 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3221 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3223 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3225 DestroyWindow(hwndRichEdit);
3228 static void test_ES_PASSWORD(void)
3230 /* This isn't hugely testable, so we're just going to run it through its paces */
3232 HWND hwndRichEdit = new_richedit(NULL);
3235 /* First, check the default of a regular control */
3236 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3238 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3240 /* Now, set it to something normal */
3241 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3242 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3244 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3246 /* Now, set it to something odd */
3247 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3248 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3250 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3251 DestroyWindow(hwndRichEdit);
3254 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3259 char** str = (char**)dwCookie;
3262 memcpy(*str, pbBuff, *pcb);
3268 static void test_WM_SETTEXT(void)
3270 HWND hwndRichEdit = new_richedit(NULL);
3271 const char * TestItem1 = "TestSomeText";
3272 const char * TestItem2 = "TestSomeText\r";
3273 const char * TestItem2_after = "TestSomeText\r\n";
3274 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3275 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3276 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3277 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3278 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3279 const char * TestItem5_after = "TestSomeText TestSomeText";
3280 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3281 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3282 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3283 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3285 const char rtftextA[] = "{\\rtf sometext}";
3286 const char urtftextA[] = "{\\urtf sometext}";
3287 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3288 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3289 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3291 char buf[1024] = {0};
3292 WCHAR bufW[1024] = {0};
3295 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3296 any solitary \r to be converted to \r\n on return. Properly paired
3297 \r\n are not affected. It also shows that the special sequence \r\r\n
3298 gets converted to a single space.
3301 #define TEST_SETTEXT(a, b) \
3302 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3303 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3304 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3305 ok (result == lstrlen(buf), \
3306 "WM_GETTEXT returned %ld instead of expected %u\n", \
3307 result, lstrlen(buf)); \
3308 result = strcmp(b, buf); \
3310 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3312 TEST_SETTEXT(TestItem1, TestItem1)
3313 TEST_SETTEXT(TestItem2, TestItem2_after)
3314 TEST_SETTEXT(TestItem3, TestItem3_after)
3315 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3316 TEST_SETTEXT(TestItem4, TestItem4_after)
3317 TEST_SETTEXT(TestItem5, TestItem5_after)
3318 TEST_SETTEXT(TestItem6, TestItem6_after)
3319 TEST_SETTEXT(TestItem7, TestItem7_after)
3321 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3322 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3323 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3324 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3325 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3326 DestroyWindow(hwndRichEdit);
3329 #define TEST_SETTEXTW(a, b) \
3330 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3331 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3332 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3333 ok (result == lstrlenW(bufW), \
3334 "WM_GETTEXT returned %ld instead of expected %u\n", \
3335 result, lstrlenW(bufW)); \
3336 result = lstrcmpW(b, bufW); \
3337 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3341 skip("Cannot perform unicode tests\n");
3344 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3345 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3346 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3347 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3348 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3349 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3350 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3351 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3352 DestroyWindow(hwndRichEdit);
3353 #undef TEST_SETTEXTW
3356 static void test_EM_STREAMOUT(void)
3358 HWND hwndRichEdit = new_richedit(NULL);
3361 char buf[1024] = {0};
3364 const char * TestItem1 = "TestSomeText";
3365 const char * TestItem2 = "TestSomeText\r";
3366 const char * TestItem3 = "TestSomeText\r\n";
3368 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3370 es.dwCookie = (DWORD_PTR)&p;
3372 es.pfnCallback = test_WM_SETTEXT_esCallback;
3373 memset(buf, 0, sizeof(buf));
3374 SendMessage(hwndRichEdit, EM_STREAMOUT,
3375 (WPARAM)(SF_TEXT), (LPARAM)&es);
3377 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3378 ok(strcmp(buf, TestItem1) == 0,
3379 "streamed text different, got %s\n", buf);
3381 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3383 es.dwCookie = (DWORD_PTR)&p;
3385 es.pfnCallback = test_WM_SETTEXT_esCallback;
3386 memset(buf, 0, sizeof(buf));
3387 SendMessage(hwndRichEdit, EM_STREAMOUT,
3388 (WPARAM)(SF_TEXT), (LPARAM)&es);
3390 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3391 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3392 ok(strcmp(buf, TestItem3) == 0,
3393 "streamed text different from, got %s\n", buf);
3394 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3396 es.dwCookie = (DWORD_PTR)&p;
3398 es.pfnCallback = test_WM_SETTEXT_esCallback;
3399 memset(buf, 0, sizeof(buf));
3400 SendMessage(hwndRichEdit, EM_STREAMOUT,
3401 (WPARAM)(SF_TEXT), (LPARAM)&es);
3403 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3404 ok(strcmp(buf, TestItem3) == 0,
3405 "streamed text different, got %s\n", buf);
3407 DestroyWindow(hwndRichEdit);
3410 static void test_EM_STREAMOUT_FONTTBL(void)
3412 HWND hwndRichEdit = new_richedit(NULL);
3414 char buf[1024] = {0};
3419 const char * TestItem = "TestSomeText";
3421 /* fills in the richedit control with some text */
3422 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3424 /* streams out the text in rtf format */
3426 es.dwCookie = (DWORD_PTR)&p;
3428 es.pfnCallback = test_WM_SETTEXT_esCallback;
3429 memset(buf, 0, sizeof(buf));
3430 SendMessage(hwndRichEdit, EM_STREAMOUT,
3431 (WPARAM)(SF_RTF), (LPARAM)&es);
3433 /* scans for \fonttbl, error if not found */
3434 fontTbl = strstr(buf, "\\fonttbl");
3435 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3438 /* scans for terminating closing bracket */
3440 while(*fontTbl && brackCount)
3444 else if(*fontTbl == '}')
3448 /* checks whether closing bracket is ok */
3449 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3452 /* char before closing fonttbl block should be a closed bracket */
3454 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3456 /* char after fonttbl block should be a crlf */
3458 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3461 DestroyWindow(hwndRichEdit);
3465 static void test_EM_SETTEXTEX(void)
3467 HWND hwndRichEdit, parent;
3469 int sel_start, sel_end;
3472 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3474 'T', 'e', 'x', 't', 0};
3475 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3481 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3482 '\r','t','S','o','m','e','T','e','x','t',0};
3483 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3487 const char * TestItem2_after = "TestSomeText\r\n";
3488 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3491 '\r','\n','\r','\n', 0};
3492 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3496 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3500 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3503 '\r','\r','\n','\r',
3505 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3509 #define MAX_BUF_LEN 1024
3510 WCHAR buf[MAX_BUF_LEN];
3511 char bufACP[MAX_BUF_LEN];
3518 /* Test the scroll position with and without a parent window.
3520 * For some reason the scroll position is 0 after EM_SETTEXTEX
3521 * with the ST_SELECTION flag only when the control has a parent
3522 * window, even though the selection is at the end. */
3524 cls.lpfnWndProc = DefWindowProcA;
3527 cls.hInstance = GetModuleHandleA(0);
3529 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3530 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3531 cls.lpszMenuName = NULL;
3532 cls.lpszClassName = "ParentTestClass";
3533 if(!RegisterClassA(&cls)) assert(0);
3535 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3536 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3537 ok (parent != 0, "Failed to create parent window\n");
3539 hwndRichEdit = CreateWindowEx(0,
3540 RICHEDIT_CLASS, NULL,
3541 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3542 0, 0, 200, 60, parent, NULL,
3543 hmoduleRichEdit, NULL);
3545 setText.codepage = CP_ACP;
3546 setText.flags = ST_SELECTION;
3547 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3548 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3549 si.cbSize = sizeof(si);
3551 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3552 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3553 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3554 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3555 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3557 DestroyWindow(parent);
3559 /* Test without a parent window */
3560 hwndRichEdit = new_richedit(NULL);
3561 setText.codepage = CP_ACP;
3562 setText.flags = ST_SELECTION;
3563 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3564 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3565 si.cbSize = sizeof(si);
3567 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3568 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3569 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3570 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3571 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3573 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3574 * but this time it is because the selection is at the beginning. */
3575 setText.codepage = CP_ACP;
3576 setText.flags = ST_DEFAULT;
3577 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3578 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3579 si.cbSize = sizeof(si);
3581 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3582 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3583 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3584 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3585 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3587 setText.codepage = 1200; /* no constant for unicode */
3588 getText.codepage = 1200; /* no constant for unicode */
3589 getText.cb = MAX_BUF_LEN;
3590 getText.flags = GT_DEFAULT;
3591 getText.lpDefaultChar = NULL;
3592 getText.lpUsedDefChar = NULL;
3595 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3596 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3597 ok(lstrcmpW(buf, TestItem1) == 0,
3598 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3600 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3601 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3603 setText.codepage = 1200; /* no constant for unicode */
3604 getText.codepage = 1200; /* no constant for unicode */
3605 getText.cb = MAX_BUF_LEN;
3606 getText.flags = GT_DEFAULT;
3607 getText.lpDefaultChar = NULL;
3608 getText.lpUsedDefChar = NULL;
3610 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3611 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3612 ok(lstrcmpW(buf, TestItem2) == 0,
3613 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3615 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3616 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3617 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3618 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3620 /* Baseline test for just-enough buffer space for string */
3621 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3622 getText.codepage = 1200; /* no constant for unicode */
3623 getText.flags = GT_DEFAULT;
3624 getText.lpDefaultChar = NULL;
3625 getText.lpUsedDefChar = NULL;
3626 memset(buf, 0, MAX_BUF_LEN);
3627 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3628 ok(lstrcmpW(buf, TestItem2) == 0,
3629 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3631 /* When there is enough space for one character, but not both, of the CRLF
3632 pair at the end of the string, the CR is not copied at all. That is,
3633 the caller must not see CRLF pairs truncated to CR at the end of the
3636 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3637 getText.codepage = 1200; /* no constant for unicode */
3638 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3639 getText.lpDefaultChar = NULL;
3640 getText.lpUsedDefChar = NULL;
3641 memset(buf, 0, MAX_BUF_LEN);
3642 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3643 ok(lstrcmpW(buf, TestItem1) == 0,
3644 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3647 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3648 setText.codepage = 1200; /* no constant for unicode */
3649 getText.codepage = 1200; /* no constant for unicode */
3650 getText.cb = MAX_BUF_LEN;
3651 getText.flags = GT_DEFAULT;
3652 getText.lpDefaultChar = NULL;
3653 getText.lpUsedDefChar = NULL;
3655 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3656 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3657 ok(lstrcmpW(buf, TestItem3_after) == 0,
3658 "EM_SETTEXTEX did not convert properly\n");
3660 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3661 setText.codepage = 1200; /* no constant for unicode */
3662 getText.codepage = 1200; /* no constant for unicode */
3663 getText.cb = MAX_BUF_LEN;
3664 getText.flags = GT_DEFAULT;
3665 getText.lpDefaultChar = NULL;
3666 getText.lpUsedDefChar = NULL;
3668 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3669 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3670 ok(lstrcmpW(buf, TestItem3_after) == 0,
3671 "EM_SETTEXTEX did not convert properly\n");
3673 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3674 setText.codepage = 1200; /* no constant for unicode */
3675 getText.codepage = 1200; /* no constant for unicode */
3676 getText.cb = MAX_BUF_LEN;
3677 getText.flags = GT_DEFAULT;
3678 getText.lpDefaultChar = NULL;
3679 getText.lpUsedDefChar = NULL;
3681 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3682 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3683 ok(lstrcmpW(buf, TestItem4_after) == 0,
3684 "EM_SETTEXTEX did not convert properly\n");
3686 /* !ST_SELECTION && Unicode && !\rtf */
3687 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3688 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3691 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3692 ok(lstrlenW(buf) == 0,
3693 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3695 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3697 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3698 /* select some text */
3701 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3702 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3703 setText.flags = ST_SELECTION;
3704 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3706 "EM_SETTEXTEX with NULL lParam to replace selection"
3707 " with no text should return 0. Got %i\n",
3710 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3712 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3713 /* select some text */
3716 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3717 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3718 setText.flags = ST_SELECTION;
3719 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3720 (WPARAM)&setText, (LPARAM) TestItem1);
3722 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3723 ok(result == lstrlenW(TestItem1),
3724 "EM_SETTEXTEX with NULL lParam to replace selection"
3725 " with no text should return 0. Got %i\n",
3727 ok(lstrlenW(buf) == 22,
3728 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3731 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3732 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3734 es.dwCookie = (DWORD_PTR)&p;
3736 es.pfnCallback = test_WM_SETTEXT_esCallback;
3737 memset(buf, 0, sizeof(buf));
3738 SendMessage(hwndRichEdit, EM_STREAMOUT,
3739 (WPARAM)(SF_RTF), (LPARAM)&es);
3740 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3742 /* !ST_SELECTION && !Unicode && \rtf */
3743 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3744 getText.codepage = 1200; /* no constant for unicode */
3745 getText.cb = MAX_BUF_LEN;
3746 getText.flags = GT_DEFAULT;
3747 getText.lpDefaultChar = NULL;
3748 getText.lpUsedDefChar = NULL;
3751 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3752 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3753 ok(lstrcmpW(buf, TestItem1) == 0,
3754 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3756 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3757 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3758 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3759 getText.codepage = CP_ACP;
3760 getText.cb = MAX_BUF_LEN;
3761 getText.flags = GT_DEFAULT;
3762 getText.lpDefaultChar = NULL;
3763 getText.lpUsedDefChar = NULL;
3765 setText.flags = ST_SELECTION;
3766 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3767 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3768 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3769 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3770 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3772 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3773 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3775 es.dwCookie = (DWORD_PTR)&p;
3777 es.pfnCallback = test_WM_SETTEXT_esCallback;
3778 memset(buf, 0, sizeof(buf));
3779 SendMessage(hwndRichEdit, EM_STREAMOUT,
3780 (WPARAM)(SF_RTF), (LPARAM)&es);
3781 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3783 /* select some text */
3786 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3788 /* ST_SELECTION && !Unicode && \rtf */
3789 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3790 getText.codepage = 1200; /* no constant for unicode */
3791 getText.cb = MAX_BUF_LEN;
3792 getText.flags = GT_DEFAULT;
3793 getText.lpDefaultChar = NULL;
3794 getText.lpUsedDefChar = NULL;
3796 setText.flags = ST_SELECTION;
3797 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3798 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3799 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3801 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3802 setText.codepage = 1200; /* no constant for unicode */
3803 getText.codepage = CP_ACP;
3804 getText.cb = MAX_BUF_LEN;
3807 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3808 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3810 /* select some text */
3813 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3815 /* ST_SELECTION && !Unicode && !\rtf */
3816 setText.codepage = CP_ACP;
3817 getText.codepage = 1200; /* no constant for unicode */
3818 getText.cb = MAX_BUF_LEN;
3819 getText.flags = GT_DEFAULT;
3820 getText.lpDefaultChar = NULL;
3821 getText.lpUsedDefChar = NULL;
3823 setText.flags = ST_SELECTION;
3824 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3825 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3826 ok(lstrcmpW(buf, TestItem1alt) == 0,
3827 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3828 " using ST_SELECTION and non-Unicode\n");
3830 /* Test setting text using rich text format */
3832 setText.codepage = CP_ACP;
3833 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3834 getText.codepage = CP_ACP;
3835 getText.cb = MAX_BUF_LEN;
3836 getText.flags = GT_DEFAULT;
3837 getText.lpDefaultChar = NULL;
3838 getText.lpUsedDefChar = NULL;
3839 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3840 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3843 setText.codepage = CP_ACP;
3844 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3845 getText.codepage = CP_ACP;
3846 getText.cb = MAX_BUF_LEN;
3847 getText.flags = GT_DEFAULT;
3848 getText.lpDefaultChar = NULL;
3849 getText.lpUsedDefChar = NULL;
3850 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3851 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3853 DestroyWindow(hwndRichEdit);
3856 static void test_EM_LIMITTEXT(void)
3860 HWND hwndRichEdit = new_richedit(NULL);
3862 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3863 * about setting the length to -1 for multiline edit controls doesn't happen.
3866 /* Don't check default gettextlimit case. That's done in other tests */
3868 /* Set textlimit to 100 */
3869 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3870 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3872 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3874 /* Set textlimit to 0 */
3875 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3876 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3878 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3880 /* Set textlimit to -1 */
3881 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3882 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3884 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3886 /* Set textlimit to -2 */
3887 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3888 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3890 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3892 DestroyWindow (hwndRichEdit);
3896 static void test_EM_EXLIMITTEXT(void)
3898 int i, selBegin, selEnd, len1, len2;
3900 char text[1024 + 1];
3901 char buffer[1024 + 1];
3902 int textlimit = 0; /* multiple of 100 */
3903 HWND hwndRichEdit = new_richedit(NULL);
3905 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3906 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3909 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3910 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3912 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3915 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3916 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3918 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3920 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3921 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3922 /* default for WParam = 0 */
3923 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3925 textlimit = sizeof(text)-1;
3926 memset(text, 'W', textlimit);
3927 text[sizeof(text)-1] = 0;
3928 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3929 /* maxed out text */
3930 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3932 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3933 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3934 len1 = selEnd - selBegin;
3936 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
3937 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
3938 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
3939 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3940 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3941 len2 = selEnd - selBegin;
3944 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3947 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3948 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3949 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
3950 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3951 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3952 len1 = selEnd - selBegin;
3955 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3958 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
3959 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3960 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
3961 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3962 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3963 len2 = selEnd - selBegin;
3966 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
3969 /* set text up to the limit, select all the text, then add a char */
3971 memset(text, 'W', textlimit);
3972 text[textlimit] = 0;
3973 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3974 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3975 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3976 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
3977 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3978 result = strcmp(buffer, "A");
3979 ok(0 == result, "got string = \"%s\"\n", buffer);
3981 /* WM_SETTEXT not limited */
3983 memset(text, 'W', textlimit);
3984 text[textlimit] = 0;
3985 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
3986 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3987 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3989 ok(10 == i, "expected 10 chars\n");
3990 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3991 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
3993 /* try inserting more text at end */
3994 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
3995 ok(0 == i, "WM_CHAR wasn't processed\n");
3996 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
3998 ok(10 == i, "expected 10 chars, got %i\n", i);
3999 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4000 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4002 /* try inserting text at beginning */
4003 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4004 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4005 ok(0 == i, "WM_CHAR wasn't processed\n");
4006 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4008 ok(10 == i, "expected 10 chars, got %i\n", i);
4009 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4010 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4012 /* WM_CHAR is limited */
4014 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4015 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4016 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4017 ok(0 == i, "WM_CHAR wasn't processed\n");
4018 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4019 ok(0 == i, "WM_CHAR wasn't processed\n");
4020 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4022 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4024 DestroyWindow(hwndRichEdit);
4027 static void test_EM_GETLIMITTEXT(void)
4030 HWND hwndRichEdit = new_richedit(NULL);
4032 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4033 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4035 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4036 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4037 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4039 DestroyWindow(hwndRichEdit);
4042 static void test_WM_SETFONT(void)
4044 /* There is no invalid input or error conditions for this function.
4045 * NULL wParam and lParam just fall back to their default values
4046 * It should be noted that even if you use a gibberish name for your fonts
4047 * here, it will still work because the name is stored. They will display as
4048 * System, but will report their name to be whatever they were created as */
4050 HWND hwndRichEdit = new_richedit(NULL);
4051 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4052 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4053 FF_DONTCARE, "Marlett");
4054 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4055 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4056 FF_DONTCARE, "MS Sans Serif");
4057 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4058 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4059 FF_DONTCARE, "Courier");
4060 LOGFONTA sentLogFont;
4061 CHARFORMAT2A returnedCF2A;
4063 returnedCF2A.cbSize = sizeof(returnedCF2A);
4065 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4066 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4067 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4069 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4070 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4071 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4072 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4074 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4075 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4076 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4077 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4078 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4079 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4081 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4082 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4083 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4084 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4085 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4086 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4088 /* This last test is special since we send in NULL. We clear the variables
4089 * and just compare to "System" instead of the sent in font name. */
4090 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4091 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4092 returnedCF2A.cbSize = sizeof(returnedCF2A);
4094 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4095 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4096 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4097 ok (!strcmp("System",returnedCF2A.szFaceName),
4098 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4100 DestroyWindow(hwndRichEdit);
4104 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4109 const char** str = (const char**)dwCookie;
4110 int size = strlen(*str);
4111 if(size > 3) /* let's make it piecemeal for fun */
4118 memcpy(pbBuff, *str, *pcb);
4124 static void test_EM_GETMODIFY(void)
4126 HWND hwndRichEdit = new_richedit(NULL);
4129 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4131 'T', 'e', 'x', 't', 0};
4132 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4134 'O', 't', 'h', 'e', 'r',
4135 'T', 'e', 'x', 't', 0};
4136 const char* streamText = "hello world";
4141 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4142 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4143 FF_DONTCARE, "Courier");
4145 setText.codepage = 1200; /* no constant for unicode */
4146 setText.flags = ST_KEEPUNDO;
4149 /* modify flag shouldn't be set when richedit is first created */
4150 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4152 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4154 /* setting modify flag should actually set it */
4155 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4156 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4158 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4160 /* clearing modify flag should actually clear it */
4161 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4162 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4164 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4166 /* setting font doesn't change modify flag */
4167 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4168 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4169 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4171 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4173 /* setting text should set modify flag */
4174 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4175 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4176 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4178 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4180 /* undo previous text doesn't reset modify flag */
4181 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4182 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4184 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4186 /* set text with no flag to keep undo stack should not set modify flag */
4187 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4189 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4190 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4192 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4194 /* WM_SETTEXT doesn't modify */
4195 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4196 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4197 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4199 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4201 /* clear the text */
4202 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4203 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4204 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4206 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4209 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4210 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4211 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4212 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4213 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4215 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4217 /* copy/paste text 1 */
4218 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4219 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4220 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4221 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4222 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4224 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4226 /* copy/paste text 2 */
4227 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4228 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4229 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4230 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4231 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4232 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4234 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4237 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4238 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4239 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4240 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4242 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4245 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4246 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4247 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4248 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4250 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4252 /* set char format */
4253 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4254 cf2.cbSize = sizeof(CHARFORMAT2);
4255 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
4257 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4258 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4259 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4260 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
4261 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4262 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4264 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4266 /* set para format */
4267 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4268 pf2.cbSize = sizeof(PARAFORMAT2);
4269 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4271 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4272 pf2.wAlignment = PFA_RIGHT;
4273 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4274 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4276 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4279 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4280 es.dwCookie = (DWORD_PTR)&streamText;
4282 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4283 SendMessage(hwndRichEdit, EM_STREAMIN,
4284 (WPARAM)(SF_TEXT), (LPARAM)&es);
4285 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4287 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4289 DestroyWindow(hwndRichEdit);
4295 LRESULT expected_retval;
4296 int expected_getsel_start;
4297 int expected_getsel_end;
4298 int _getsel_todo_wine;
4301 const struct exsetsel_s exsetsel_tests[] = {
4303 {5, 10, 10, 5, 10, 0},
4304 {15, 17, 17, 15, 17, 0},
4305 /* test cpMax > strlen() */
4306 {0, 100, 18, 0, 18, 1},
4307 /* test cpMin == cpMax */
4309 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4310 {-1, 0, 5, 5, 5, 0},
4311 {-1, 17, 5, 5, 5, 0},
4312 {-1, 18, 5, 5, 5, 0},
4313 /* test cpMin < 0 && cpMax < 0 */
4314 {-1, -1, 17, 17, 17, 0},
4315 {-4, -5, 17, 17, 17, 0},
4316 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4317 {0, -1, 18, 0, 18, 1},
4318 {17, -5, 18, 17, 18, 1},
4319 {18, -3, 17, 17, 17, 0},
4320 /* test if cpMin > cpMax */
4321 {15, 19, 18, 15, 18, 1},
4322 {19, 15, 18, 15, 18, 1}
4325 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4330 cr.cpMin = setsel->min;
4331 cr.cpMax = setsel->max;
4332 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4334 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4336 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4338 if (setsel->_getsel_todo_wine) {
4340 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4343 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4347 static void test_EM_EXSETSEL(void)
4349 HWND hwndRichEdit = new_richedit(NULL);
4351 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4353 /* sending some text to the window */
4354 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4355 /* 01234567890123456*/
4358 for (i = 0; i < num_tests; i++) {
4359 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4362 DestroyWindow(hwndRichEdit);
4365 static void test_EM_REPLACESEL(int redraw)
4367 HWND hwndRichEdit = new_richedit(NULL);
4368 char buffer[1024] = {0};
4373 /* sending some text to the window */
4374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4375 /* 01234567890123456*/
4378 /* FIXME add more tests */
4379 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4380 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4381 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4382 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4383 r = strcmp(buffer, "testing");
4384 ok(0 == r, "expected %d, got %d\n", 0, r);
4386 DestroyWindow(hwndRichEdit);
4388 hwndRichEdit = new_richedit(NULL);
4390 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4391 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4393 /* Test behavior with carriage returns and newlines */
4394 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4395 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4396 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4397 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4398 r = strcmp(buffer, "RichEdit1");
4399 ok(0 == r, "expected %d, got %d\n", 0, r);
4401 getText.codepage = CP_ACP;
4402 getText.flags = GT_DEFAULT;
4403 getText.lpDefaultChar = NULL;
4404 getText.lpUsedDefChar = NULL;
4405 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4406 ok(strcmp(buffer, "RichEdit1") == 0,
4407 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4409 /* Test number of lines reported after EM_REPLACESEL */
4410 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4411 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4414 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4415 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4416 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4417 r = strcmp(buffer, "RichEdit1\r\n");
4418 ok(0 == r, "expected %d, got %d\n", 0, r);
4420 getText.codepage = CP_ACP;
4421 getText.flags = GT_DEFAULT;
4422 getText.lpDefaultChar = NULL;
4423 getText.lpUsedDefChar = NULL;
4424 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4425 ok(strcmp(buffer, "RichEdit1\r") == 0,
4426 "EM_GETTEXTEX returned incorrect string\n");
4428 /* Test number of lines reported after EM_REPLACESEL */
4429 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4430 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4432 /* Win98's riched20 and WinXP's riched20 disagree on what to return from
4433 EM_REPLACESEL. The general rule seems to be that Win98's riched20
4434 returns the number of characters *inserted* into the control (after
4435 required conversions), but WinXP's riched20 returns the number of
4436 characters interpreted from the original lParam. Wine's builtin riched20
4437 implements the WinXP behavior.
4439 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4440 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4441 ok(11 == r /* WinXP */ || 10 == r /* Win98 */,
4442 "EM_REPLACESEL returned %d, expected 11 or 10\n", r);
4444 /* Test number of lines reported after EM_REPLACESEL */
4445 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4446 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4448 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4449 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4450 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4451 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4453 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4454 r = strcmp(buffer, "RichEdit1\r\n");
4455 ok(0 == r, "expected %d, got %d\n", 0, r);
4457 getText.codepage = CP_ACP;
4458 getText.flags = GT_DEFAULT;
4459 getText.lpDefaultChar = NULL;
4460 getText.lpUsedDefChar = NULL;
4461 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4462 ok(strcmp(buffer, "RichEdit1\r") == 0,
4463 "EM_GETTEXTEX returned incorrect string\n");
4465 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4466 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4467 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4468 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4470 /* The following tests show that richedit should handle the special \r\r\n
4471 sequence by turning it into a single space on insertion. However,
4472 EM_REPLACESEL on WinXP returns the number of characters in the original
4476 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4477 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4478 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4479 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4480 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4481 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4482 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4484 /* Test the actual string */
4486 getText.codepage = CP_ACP;
4487 getText.flags = GT_DEFAULT;
4488 getText.lpDefaultChar = NULL;
4489 getText.lpUsedDefChar = NULL;
4490 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4491 ok(strcmp(buffer, "\r\r") == 0,
4492 "EM_GETTEXTEX returned incorrect string\n");
4494 /* Test number of lines reported after EM_REPLACESEL */
4495 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4496 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4498 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4499 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4500 ok(3 == r /* WinXP */ || 1 == r /* Win98 */,
4501 "EM_REPLACESEL returned %d, expected 3 or 1\n", r);
4502 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4503 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4504 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4505 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4507 /* Test the actual string */
4509 getText.codepage = CP_ACP;
4510 getText.flags = GT_DEFAULT;
4511 getText.lpDefaultChar = NULL;
4512 getText.lpUsedDefChar = NULL;
4513 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4514 ok(strcmp(buffer, " ") == 0,
4515 "EM_GETTEXTEX returned incorrect string\n");
4517 /* Test number of lines reported after EM_REPLACESEL */
4518 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4519 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4521 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4522 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4523 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4524 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4525 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4526 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4527 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4528 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4530 /* Test the actual string */
4532 getText.codepage = CP_ACP;
4533 getText.flags = GT_DEFAULT;
4534 getText.lpDefaultChar = NULL;
4535 getText.lpUsedDefChar = NULL;
4536 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4537 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4538 "EM_GETTEXTEX returned incorrect string\n");
4540 /* Test number of lines reported after EM_REPLACESEL */
4541 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4542 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4544 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4545 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4546 ok(5 == r /* WinXP */ || 2 == r /* Win98 */,
4547 "EM_REPLACESEL returned %d, expected 5 or 2\n", r);
4548 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4549 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4550 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4551 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4553 /* Test the actual string */
4555 getText.codepage = CP_ACP;
4556 getText.flags = GT_DEFAULT;
4557 getText.lpDefaultChar = NULL;
4558 getText.lpUsedDefChar = NULL;
4559 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4560 ok(strcmp(buffer, " \r") == 0,
4561 "EM_GETTEXTEX returned incorrect string\n");
4563 /* Test number of lines reported after EM_REPLACESEL */
4564 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4565 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4567 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4568 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4569 ok(5 == r /* WinXP */ || 3 == r /* Win98 */,
4570 "EM_REPLACESEL returned %d, expected 5 or 3\n", r);
4571 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4572 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4573 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4574 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4576 /* Test the actual string */
4578 getText.codepage = CP_ACP;
4579 getText.flags = GT_DEFAULT;
4580 getText.lpDefaultChar = NULL;
4581 getText.lpUsedDefChar = NULL;
4582 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4583 ok(strcmp(buffer, " \r\r") == 0,
4584 "EM_GETTEXTEX returned incorrect string\n");
4586 /* Test number of lines reported after EM_REPLACESEL */
4587 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4588 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4590 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4591 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4592 ok(6 == r /* WinXP */ || 5 == r /* Win98 */,
4593 "EM_REPLACESEL returned %d, expected 6 or 5\n", r);
4594 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4595 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4596 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4597 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4599 /* Test the actual string */
4601 getText.codepage = CP_ACP;
4602 getText.flags = GT_DEFAULT;
4603 getText.lpDefaultChar = NULL;
4604 getText.lpUsedDefChar = NULL;
4605 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4606 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4607 "EM_GETTEXTEX returned incorrect string\n");
4609 /* Test number of lines reported after EM_REPLACESEL */
4610 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4611 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4613 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4614 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4615 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4616 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4617 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4618 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4619 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4621 /* Test the actual string */
4623 getText.codepage = CP_ACP;
4624 getText.flags = GT_DEFAULT;
4625 getText.lpDefaultChar = NULL;
4626 getText.lpUsedDefChar = NULL;
4627 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4628 ok(strcmp(buffer, "\r\r") == 0,
4629 "EM_GETTEXTEX returned incorrect string\n");
4631 /* Test number of lines reported after EM_REPLACESEL */
4632 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4633 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4635 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4636 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4637 ok(9 == r /* WinXP */ || 7 == r /* Win98 */,
4638 "EM_REPLACESEL returned %d, expected 9 or 7\n", r);
4639 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4640 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4641 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4642 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4644 /* Test the actual string */
4646 getText.codepage = CP_ACP;
4647 getText.flags = GT_DEFAULT;
4648 getText.lpDefaultChar = NULL;
4649 getText.lpUsedDefChar = NULL;
4650 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4651 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4652 "EM_GETTEXTEX returned incorrect string\n");
4654 /* Test number of lines reported after EM_REPLACESEL */
4655 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4656 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4659 /* This is needed to avoid interferring with keybd_event calls
4660 * on other tests that simulate keyboard events. */
4661 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4663 DestroyWindow(hwndRichEdit);
4666 static void test_WM_PASTE(void)
4669 char buffer[1024] = {0};
4670 const char* text1 = "testing paste\r";
4671 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4672 const char* text1_after = "testing paste\r\n";
4673 const char* text2 = "testing paste\r\rtesting paste";
4674 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4675 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4676 HWND hwndRichEdit = new_richedit(NULL);
4678 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4679 * to test the state of the modifiers (Ctrl/Alt/Shift).
4681 * Therefore Ctrl-<key> keystrokes need to be simulated with
4682 * keybd_event or by using SetKeyboardState to set the modifiers
4683 * and SendMessage to simulate the keystrokes.
4686 /* Sent keystrokes with keybd_event */
4687 #define SEND_CTRL_C(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'C')
4688 #define SEND_CTRL_X(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'X')
4689 #define SEND_CTRL_V(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'V')
4690 #define SEND_CTRL_Z(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Z')
4691 #define SEND_CTRL_Y(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, 'Y')
4693 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4694 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4696 SEND_CTRL_C(hwndRichEdit); /* Copy */
4697 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4698 SEND_CTRL_V(hwndRichEdit); /* Paste */
4699 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4700 /* Pasted text should be visible at this step */
4701 result = strcmp(text1_step1, buffer);
4703 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4705 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4706 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4707 /* Text should be the same as before (except for \r -> \r\n conversion) */
4708 result = strcmp(text1_after, buffer);
4710 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4712 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4713 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4714 SEND_CTRL_C(hwndRichEdit); /* Copy */
4715 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4716 SEND_CTRL_V(hwndRichEdit); /* Paste */
4717 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4718 /* Pasted text should be visible at this step */
4719 result = strcmp(text3, buffer);
4721 "test paste: strcmp = %i\n", result);
4722 SEND_CTRL_Z(hwndRichEdit); /* Undo */
4723 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4724 /* Text should be the same as before (except for \r -> \r\n conversion) */
4725 result = strcmp(text2_after, buffer);
4727 "test paste: strcmp = %i\n", result);
4728 SEND_CTRL_Y(hwndRichEdit); /* Redo */
4729 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4730 /* Text should revert to post-paste state */
4731 result = strcmp(buffer,text3);
4733 "test paste: strcmp = %i\n", result);
4741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4742 /* Send WM_CHAR to simulates Ctrl-V */
4743 SendMessage(hwndRichEdit, WM_CHAR, 22,
4744 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4745 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4746 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4747 result = strcmp(buffer,"");
4749 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4751 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4752 * with SetKeyboard state. */
4754 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4755 /* Simulates paste (Ctrl-V) */
4756 hold_key(VK_CONTROL);
4757 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4758 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) & 1);
4759 release_key(VK_CONTROL);
4760 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4761 result = strcmp(buffer,"paste");
4763 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4765 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4766 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4767 /* Simulates copy (Ctrl-C) */
4768 hold_key(VK_CONTROL);
4769 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4770 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) & 1);
4771 release_key(VK_CONTROL);
4772 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4773 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4774 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4775 result = strcmp(buffer,"testing");
4777 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4779 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4780 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4781 /* Simulates select all (Ctrl-A) */
4782 hold_key(VK_CONTROL);
4783 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4784 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) & 1);
4785 /* Simulates select cut (Ctrl-X) */
4786 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4787 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) & 1);
4788 release_key(VK_CONTROL);
4789 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4790 result = strcmp(buffer,"");
4792 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4793 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4794 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4795 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4796 result = strcmp(buffer,"cut\r\n");
4797 todo_wine ok(result == 0,
4798 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4799 /* Simulates undo (Ctrl-Z) */
4800 hold_key(VK_CONTROL);
4801 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4802 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) & 1);
4803 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4804 result = strcmp(buffer,"");
4806 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4807 /* Simulates redo (Ctrl-Y) */
4808 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4809 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) & 1);
4810 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4811 result = strcmp(buffer,"cut\r\n");
4812 todo_wine ok(result == 0,
4813 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4814 release_key(VK_CONTROL);
4816 DestroyWindow(hwndRichEdit);
4819 static void test_EM_FORMATRANGE(void)
4821 int i, tpp_x, tpp_y;
4823 HWND hwndRichEdit = new_richedit(NULL);
4824 static const struct {
4825 const char *string; /* The string */
4826 int first; /* First 'pagebreak', 0 for don't care */
4827 int second; /* Second 'pagebreak', 0 for don't care */
4829 {"WINE wine", 0, 0},
4830 {"WINE wineWine", 0, 0},
4831 {"WINE\r\nwine\r\nwine", 5, 10},
4832 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4833 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4836 hdc = GetDC(hwndRichEdit);
4837 ok(hdc != NULL, "Could not get HDC\n");
4839 /* Calculate the twips per pixel */
4840 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4841 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4843 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4845 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4848 GETTEXTLENGTHEX gtl;
4852 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4854 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4855 gtl.codepage = CP_ACP;
4856 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4858 /* Get some size information for the string */
4859 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4861 /* Define the box to be half the width needed and a bit larger than the height.
4862 * Changes to the width means we have at least 2 pages. Changes to the height
4863 * is done so we can check the changing of fr.rc.bottom.
4865 fr.hdc = fr.hdcTarget = hdc;
4866 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4867 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4868 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4870 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4872 ok(r == len, "Expected %d, got %d\n", len, r);
4875 /* We know that the page can't hold the full string. See how many characters
4876 * are on the first one
4880 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4882 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4884 if (fmtstrings[i].first)
4886 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4889 ok(r < len, "Expected < %d, got %d\n", len, r);
4891 /* Do another page */
4893 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4894 if (fmtstrings[i].second)
4896 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4899 ok (r < len, "Expected < %d, got %d\n", len, r);
4901 /* There is at least on more page, but we don't care */
4903 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4905 ok(r == len, "Expected %d, got %d\n", len, r);
4909 ReleaseDC(NULL, hdc);
4910 DestroyWindow(hwndRichEdit);
4913 static int nCallbackCount = 0;
4915 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4918 const char text[] = {'t','e','s','t'};
4920 if (sizeof(text) <= cb)
4922 if ((int)dwCookie != nCallbackCount)
4928 memcpy (pbBuff, text, sizeof(text));
4929 *pcb = sizeof(text);
4936 return 1; /* indicates callback failed */
4939 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
4944 const char** str = (const char**)dwCookie;
4945 int size = strlen(*str);
4951 memcpy(pbBuff, *str, *pcb);
4957 struct StringWithLength {
4962 /* This callback is used to handled the null characters in a string. */
4963 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
4968 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
4969 int size = str->length;
4975 memcpy(pbBuff, str->buffer, *pcb);
4976 str->buffer += *pcb;
4977 str->length -= *pcb;
4982 static void test_EM_STREAMIN(void)
4984 HWND hwndRichEdit = new_richedit(NULL);
4987 char buffer[1024] = {0};
4989 const char * streamText0 = "{\\rtf1 TestSomeText}";
4990 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
4991 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
4993 const char * streamText1 =
4994 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
4995 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
4998 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
4999 const char * streamText2 =
5000 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5001 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5002 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5003 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5004 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5005 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5006 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5008 const char * streamText3 = "RichEdit1";
5010 struct StringWithLength cookieForStream4;
5011 const char * streamText4 =
5012 "This text just needs to be long enough to cause run to be split onto "
5013 "two separate lines and make sure the null terminating character is "
5014 "handled properly.\0";
5015 int length4 = strlen(streamText4) + 1;
5016 cookieForStream4.buffer = (char *)streamText4;
5017 cookieForStream4.length = length4;
5019 /* Minimal test without \par at the end */
5020 es.dwCookie = (DWORD_PTR)&streamText0;
5022 es.pfnCallback = test_EM_STREAMIN_esCallback;
5023 SendMessage(hwndRichEdit, EM_STREAMIN,
5024 (WPARAM)(SF_RTF), (LPARAM)&es);
5026 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5028 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5029 result = strcmp (buffer,"TestSomeText");
5031 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5032 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5034 /* Native richedit 2.0 ignores last \par */
5035 es.dwCookie = (DWORD_PTR)&streamText0a;
5037 es.pfnCallback = test_EM_STREAMIN_esCallback;
5038 SendMessage(hwndRichEdit, EM_STREAMIN,
5039 (WPARAM)(SF_RTF), (LPARAM)&es);
5041 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5043 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5044 result = strcmp (buffer,"TestSomeText");
5046 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5047 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5049 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5050 es.dwCookie = (DWORD_PTR)&streamText0b;
5052 es.pfnCallback = test_EM_STREAMIN_esCallback;
5053 SendMessage(hwndRichEdit, EM_STREAMIN,
5054 (WPARAM)(SF_RTF), (LPARAM)&es);
5056 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5058 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5059 result = strcmp (buffer,"TestSomeText\r\n");
5061 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5062 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5064 es.dwCookie = (DWORD_PTR)&streamText1;
5066 es.pfnCallback = test_EM_STREAMIN_esCallback;
5067 SendMessage(hwndRichEdit, EM_STREAMIN,
5068 (WPARAM)(SF_RTF), (LPARAM)&es);
5070 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5072 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5073 result = strcmp (buffer,"TestSomeText");
5075 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5076 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5078 es.dwCookie = (DWORD_PTR)&streamText2;
5080 SendMessage(hwndRichEdit, EM_STREAMIN,
5081 (WPARAM)(SF_RTF), (LPARAM)&es);
5083 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5085 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5086 ok (strlen(buffer) == 0,
5087 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5088 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5090 es.dwCookie = (DWORD_PTR)&streamText3;
5092 SendMessage(hwndRichEdit, EM_STREAMIN,
5093 (WPARAM)(SF_RTF), (LPARAM)&es);
5095 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5097 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5098 ok (strlen(buffer) == 0,
5099 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5100 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5102 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5104 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5105 SendMessage(hwndRichEdit, EM_STREAMIN,
5106 (WPARAM)(SF_TEXT), (LPARAM)&es);
5108 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5109 ok (result == length4,
5110 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5111 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5113 DestroyWindow(hwndRichEdit);
5116 static void test_EM_StreamIn_Undo(void)
5118 /* The purpose of this test is to determine when a EM_StreamIn should be
5119 * undoable. This is important because WM_PASTE currently uses StreamIn and
5120 * pasting should always be undoable but streaming isn't always.
5123 * StreamIn plain text without SFF_SELECTION.
5124 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5125 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5126 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5127 * Feel free to add tests for other text modes or StreamIn things.
5131 HWND hwndRichEdit = new_richedit(NULL);
5134 char buffer[1024] = {0};
5135 const char randomtext[] = "Some text";
5137 es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback;
5139 /* StreamIn, no SFF_SELECTION */
5140 es.dwCookie = nCallbackCount;
5141 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5142 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5143 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5144 SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es);
5145 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5146 result = strcmp (buffer,"test");
5148 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5150 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5151 ok (result == FALSE,
5152 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5154 /* StreamIn, SFF_SELECTION, but nothing selected */
5155 es.dwCookie = nCallbackCount;
5156 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5157 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5158 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5159 SendMessage(hwndRichEdit, EM_STREAMIN,
5160 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5161 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5162 result = strcmp (buffer,"testSome text");
5164 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5166 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5168 "EM_STREAMIN with SFF_SELECTION but no selection set "
5169 "should create an undo\n");
5171 /* StreamIn, SFF_SELECTION, with a selection */
5172 es.dwCookie = nCallbackCount;
5173 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5174 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5175 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5176 SendMessage(hwndRichEdit, EM_STREAMIN,
5177 (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es);
5178 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5179 result = strcmp (buffer,"Sometesttext");
5181 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5183 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5185 "EM_STREAMIN with SFF_SELECTION and selection set "
5186 "should create an undo\n");
5188 DestroyWindow(hwndRichEdit);
5191 static BOOL is_em_settextex_supported(HWND hwnd)
5193 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5194 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5197 static void test_unicode_conversions(void)
5199 static const WCHAR tW[] = {'t',0};
5200 static const WCHAR teW[] = {'t','e',0};
5201 static const WCHAR textW[] = {'t','e','s','t',0};
5202 static const char textA[] = "test";
5206 int em_settextex_supported, ret;
5208 #define set_textA(hwnd, wm_set_text, txt) \
5210 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5211 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5212 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5213 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5214 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5216 #define expect_textA(hwnd, wm_get_text, txt) \
5218 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5219 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5220 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5221 memset(bufA, 0xAA, sizeof(bufA)); \
5222 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5223 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5224 ret = lstrcmpA(bufA, txt); \
5225 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5228 #define set_textW(hwnd, wm_set_text, txt) \
5230 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5231 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5232 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5233 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5234 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5236 #define expect_textW(hwnd, wm_get_text, txt) \
5238 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5239 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5240 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5241 memset(bufW, 0xAA, sizeof(bufW)); \
5244 assert(wm_get_text == EM_GETTEXTEX); \
5245 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5246 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5250 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5251 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5253 ret = lstrcmpW(bufW, txt); \
5254 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5256 #define expect_empty(hwnd, wm_get_text) \
5258 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5259 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5260 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5261 memset(bufA, 0xAA, sizeof(bufA)); \
5262 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5263 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5264 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5267 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5268 0, 0, 200, 60, 0, 0, 0, 0);
5269 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5271 ret = IsWindowUnicode(hwnd);
5273 ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n");
5275 ok(ret, "RichEdit20W should be unicode under NT\n");
5277 /* EM_SETTEXTEX is supported starting from version 3.0 */
5278 em_settextex_supported = is_em_settextex_supported(hwnd);
5279 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5280 em_settextex_supported ? "" : "NOT ");
5282 expect_empty(hwnd, WM_GETTEXT);
5283 expect_empty(hwnd, EM_GETTEXTEX);
5285 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0);
5286 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5287 expect_textA(hwnd, WM_GETTEXT, "t");
5288 expect_textA(hwnd, EM_GETTEXTEX, "t");
5289 expect_textW(hwnd, EM_GETTEXTEX, tW);
5291 ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0);
5292 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5293 expect_textA(hwnd, WM_GETTEXT, "te");
5294 expect_textA(hwnd, EM_GETTEXTEX, "te");
5295 expect_textW(hwnd, EM_GETTEXTEX, teW);
5297 set_textA(hwnd, WM_SETTEXT, NULL);
5298 expect_empty(hwnd, WM_GETTEXT);
5299 expect_empty(hwnd, EM_GETTEXTEX);
5302 set_textA(hwnd, WM_SETTEXT, textW);
5304 set_textA(hwnd, WM_SETTEXT, textA);
5305 expect_textA(hwnd, WM_GETTEXT, textA);
5306 expect_textA(hwnd, EM_GETTEXTEX, textA);
5307 expect_textW(hwnd, EM_GETTEXTEX, textW);
5309 if (em_settextex_supported)
5311 set_textA(hwnd, EM_SETTEXTEX, textA);
5312 expect_textA(hwnd, WM_GETTEXT, textA);
5313 expect_textA(hwnd, EM_GETTEXTEX, textA);
5314 expect_textW(hwnd, EM_GETTEXTEX, textW);
5319 set_textW(hwnd, WM_SETTEXT, textW);
5320 expect_textW(hwnd, WM_GETTEXT, textW);
5321 expect_textA(hwnd, WM_GETTEXT, textA);
5322 expect_textW(hwnd, EM_GETTEXTEX, textW);
5323 expect_textA(hwnd, EM_GETTEXTEX, textA);
5325 if (em_settextex_supported)
5327 set_textW(hwnd, EM_SETTEXTEX, textW);
5328 expect_textW(hwnd, WM_GETTEXT, textW);
5329 expect_textA(hwnd, WM_GETTEXT, textA);
5330 expect_textW(hwnd, EM_GETTEXTEX, textW);
5331 expect_textA(hwnd, EM_GETTEXTEX, textA);
5334 DestroyWindow(hwnd);
5336 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5337 0, 0, 200, 60, 0, 0, 0, 0);
5338 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5340 ret = IsWindowUnicode(hwnd);
5341 ok(!ret, "RichEdit20A should NOT be unicode\n");
5343 set_textA(hwnd, WM_SETTEXT, textA);
5344 expect_textA(hwnd, WM_GETTEXT, textA);
5345 expect_textA(hwnd, EM_GETTEXTEX, textA);
5346 expect_textW(hwnd, EM_GETTEXTEX, textW);
5348 if (em_settextex_supported)
5350 set_textA(hwnd, EM_SETTEXTEX, textA);
5351 expect_textA(hwnd, WM_GETTEXT, textA);
5352 expect_textA(hwnd, EM_GETTEXTEX, textA);
5353 expect_textW(hwnd, EM_GETTEXTEX, textW);
5358 set_textW(hwnd, WM_SETTEXT, textW);
5359 expect_textW(hwnd, WM_GETTEXT, textW);
5360 expect_textA(hwnd, WM_GETTEXT, textA);
5361 expect_textW(hwnd, EM_GETTEXTEX, textW);
5362 expect_textA(hwnd, EM_GETTEXTEX, textA);
5364 if (em_settextex_supported)
5366 set_textW(hwnd, EM_SETTEXTEX, textW);
5367 expect_textW(hwnd, WM_GETTEXT, textW);
5368 expect_textA(hwnd, WM_GETTEXT, textA);
5369 expect_textW(hwnd, EM_GETTEXTEX, textW);
5370 expect_textA(hwnd, EM_GETTEXTEX, textA);
5373 DestroyWindow(hwnd);
5376 static void test_WM_CHAR(void)
5380 const char * char_list = "abc\rabc\r";
5381 const char * expected_content_single = "abcabc";
5382 const char * expected_content_multi = "abc\r\nabc\r\n";
5383 char buffer[64] = {0};
5386 /* single-line control must IGNORE carriage returns */
5387 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5388 0, 0, 200, 60, 0, 0, 0, 0);
5389 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5392 while (*p != '\0') {
5393 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5394 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5395 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5396 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5400 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5401 ret = strcmp(buffer, expected_content_single);
5402 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5404 DestroyWindow(hwnd);
5406 /* multi-line control inserts CR normally */
5407 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5408 0, 0, 200, 60, 0, 0, 0, 0);
5409 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5412 while (*p != '\0') {
5413 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5414 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5415 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5416 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5420 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5421 ret = strcmp(buffer, expected_content_multi);
5422 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5424 DestroyWindow(hwnd);
5427 static void test_EM_GETTEXTLENGTHEX(void)
5430 GETTEXTLENGTHEX gtl;
5432 const char * base_string = "base string";
5433 const char * test_string = "a\nb\n\n\r\n";
5434 const char * test_string_after = "a";
5435 const char * test_string_2 = "a\rtest\rstring";
5436 char buffer[64] = {0};
5440 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5441 0, 0, 200, 60, 0, 0, 0, 0);
5443 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5444 0, 0, 200, 60, 0, 0, 0, 0);
5445 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5447 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5448 gtl.codepage = CP_ACP;
5449 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5450 ok(ret == 0, "ret %d\n",ret);
5452 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5453 gtl.codepage = CP_ACP;
5454 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5455 ok(ret == 0, "ret %d\n",ret);
5457 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5459 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5460 gtl.codepage = CP_ACP;
5461 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5462 ok(ret == strlen(base_string), "ret %d\n",ret);
5464 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5465 gtl.codepage = CP_ACP;
5466 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5467 ok(ret == strlen(base_string), "ret %d\n",ret);
5469 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5471 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5472 gtl.codepage = CP_ACP;
5473 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5474 ok(ret == 1, "ret %d\n",ret);
5476 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5477 gtl.codepage = CP_ACP;
5478 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5479 ok(ret == 1, "ret %d\n",ret);
5481 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5482 ret = strcmp(buffer, test_string_after);
5483 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5485 DestroyWindow(hwnd);
5489 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5490 0, 0, 200, 60, 0, 0, 0, 0);
5492 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP | ES_MULTILINE,
5493 0, 0, 200, 60, 0, 0, 0, 0);
5494 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5496 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5497 gtl.codepage = CP_ACP;
5498 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5499 ok(ret == 0, "ret %d\n",ret);
5501 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5502 gtl.codepage = CP_ACP;
5503 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5504 ok(ret == 0, "ret %d\n",ret);
5506 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5508 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5509 gtl.codepage = CP_ACP;
5510 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5511 ok(ret == strlen(base_string), "ret %d\n",ret);
5513 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5514 gtl.codepage = CP_ACP;
5515 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5516 ok(ret == strlen(base_string), "ret %d\n",ret);
5518 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5520 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5521 gtl.codepage = CP_ACP;
5522 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5523 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5525 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5526 gtl.codepage = CP_ACP;
5527 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5528 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5530 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5532 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5533 gtl.codepage = CP_ACP;
5534 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5535 ok(ret == 10, "ret %d\n",ret);
5537 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5538 gtl.codepage = CP_ACP;
5539 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5540 ok(ret == 6, "ret %d\n",ret);
5542 DestroyWindow(hwnd);
5546 /* globals that parent and child access when checking event masks & notifications */
5547 static HWND eventMaskEditHwnd = 0;
5548 static int queriedEventMask;
5549 static int watchForEventMask = 0;
5551 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5552 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5554 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5556 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5558 return DefWindowProcA(hwnd, message, wParam, lParam);
5561 /* test event masks in combination with WM_COMMAND */
5562 static void test_eventMask(void)
5567 const char text[] = "foo bar\n";
5570 /* register class to capture WM_COMMAND */
5572 cls.lpfnWndProc = ParentMsgCheckProcA;
5575 cls.hInstance = GetModuleHandleA(0);
5577 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5578 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5579 cls.lpszMenuName = NULL;
5580 cls.lpszClassName = "EventMaskParentClass";
5581 if(!RegisterClassA(&cls)) assert(0);
5583 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5584 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5585 ok (parent != 0, "Failed to create parent window\n");
5587 eventMaskEditHwnd = new_richedit(parent);
5588 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5590 eventMask = ENM_CHANGE | ENM_UPDATE;
5591 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask);
5592 ok(ret == ENM_NONE, "wrong event mask\n");
5593 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5594 ok(ret == eventMask, "failed to set event mask\n");
5596 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5597 queriedEventMask = 0; /* initialize to something other than we expect */
5598 watchForEventMask = EN_CHANGE;
5599 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5600 ok(ret == TRUE, "failed to set text\n");
5601 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5602 notification in response to WM_SETTEXT */
5603 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5604 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5606 /* check to see if EN_CHANGE is sent when redraw is turned off */
5607 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5608 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5609 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5610 /* redraw is disabled by making the window invisible. */
5611 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5612 queriedEventMask = 0; /* initialize to something other than we expect */
5613 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5614 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5615 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5616 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5617 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5619 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5620 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5621 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5622 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5623 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5624 watchForEventMask = EN_UPDATE;
5625 queriedEventMask = 0; /* initialize to something other than we expect */
5626 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5627 ok(queriedEventMask == 0,
5628 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5629 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5630 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5631 queriedEventMask = 0; /* initialize to something other than we expect */
5632 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5633 ok(queriedEventMask == eventMask,
5634 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5637 DestroyWindow(parent);
5640 static int received_WM_NOTIFY = 0;
5641 static int modify_at_WM_NOTIFY = 0;
5642 static BOOL filter_on_WM_NOTIFY = FALSE;
5643 static HWND hwndRichedit_WM_NOTIFY;
5645 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5647 if(message == WM_NOTIFY)
5649 received_WM_NOTIFY = 1;
5650 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5651 if (filter_on_WM_NOTIFY) return TRUE;
5653 return DefWindowProcA(hwnd, message, wParam, lParam);
5656 static void test_WM_NOTIFY(void)
5661 int sel_start, sel_end;
5663 /* register class to capture WM_NOTIFY */
5665 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5668 cls.hInstance = GetModuleHandleA(0);
5670 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5671 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5672 cls.lpszMenuName = NULL;
5673 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5674 if(!RegisterClassA(&cls)) assert(0);
5676 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5677 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5678 ok (parent != 0, "Failed to create parent window\n");
5680 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5681 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5683 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5685 /* Notifications for selection change should only be sent when selection
5686 actually changes. EM_SETCHARFORMAT is one message that calls
5687 ME_CommitUndo, which should check whether message should be sent */
5688 received_WM_NOTIFY = 0;
5689 cf2.cbSize = sizeof(CHARFORMAT2);
5690 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
5692 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5693 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5694 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5695 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5697 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5699 received_WM_NOTIFY = 0;
5700 modify_at_WM_NOTIFY = 0;
5701 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5702 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5703 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5705 received_WM_NOTIFY = 0;
5706 modify_at_WM_NOTIFY = 0;
5707 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5708 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5710 received_WM_NOTIFY = 0;
5711 modify_at_WM_NOTIFY = 0;
5712 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5713 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5714 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5716 /* Test for WM_NOTIFY messages with redraw disabled. */
5717 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5718 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5719 received_WM_NOTIFY = 0;
5720 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5721 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5722 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5724 /* Test filtering key events. */
5725 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5726 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5727 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5728 received_WM_NOTIFY = 0;
5729 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5730 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5731 ok(sel_start == 1 && sel_end == 1,
5732 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5733 filter_on_WM_NOTIFY = TRUE;
5734 received_WM_NOTIFY = 0;
5735 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5736 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5737 ok(sel_start == 1 && sel_end == 1,
5738 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5740 /* test with owner set to NULL */
5741 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5742 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5743 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5744 ok(sel_start == 1 && sel_end == 1,
5745 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5747 DestroyWindow(hwndRichedit_WM_NOTIFY);
5748 DestroyWindow(parent);
5751 static void test_undo_coalescing(void)
5755 char buffer[64] = {0};
5757 /* multi-line control inserts CR normally */
5759 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5760 0, 0, 200, 60, 0, 0, 0, 0);
5762 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP|ES_MULTILINE,
5763 0, 0, 200, 60, 0, 0, 0, 0);
5764 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5766 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5767 ok (result == FALSE, "Can undo after window creation.\n");
5768 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5769 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5770 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5771 ok (result == FALSE, "Can redo after window creation.\n");
5772 result = SendMessage(hwnd, EM_REDO, 0, 0);
5773 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5775 /* Test the effect of arrows keys during typing on undo transactions*/
5776 simulate_typing_characters(hwnd, "one two three");
5777 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5778 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5779 simulate_typing_characters(hwnd, " four five six");
5781 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5782 ok (result == FALSE, "Can redo before anything is undone.\n");
5783 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5784 ok (result == TRUE, "Cannot undo typed characters.\n");
5785 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5786 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5787 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5788 ok (result == TRUE, "Cannot redo after undo.\n");
5789 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5790 result = strcmp(buffer, "one two three");
5791 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5793 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5794 ok (result == TRUE, "Cannot undo typed characters.\n");
5795 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5796 ok (result == TRUE, "Failed to undo typed characters.\n");
5797 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5798 result = strcmp(buffer, "");
5799 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5801 /* Test the effect of focus changes during typing on undo transactions*/
5802 simulate_typing_characters(hwnd, "one two three");
5803 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5804 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5805 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5806 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5807 simulate_typing_characters(hwnd, " four five six");
5808 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5809 ok (result == TRUE, "Failed to undo typed characters.\n");
5810 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5811 result = strcmp(buffer, "one two three");
5812 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5814 /* Test the effect of the back key during typing on undo transactions */
5815 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5816 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5817 ok (result == TRUE, "Failed to clear the text.\n");
5818 simulate_typing_characters(hwnd, "one two threa");
5819 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5820 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5821 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5822 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5823 simulate_typing_characters(hwnd, "e four five six");
5824 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5825 ok (result == TRUE, "Failed to undo typed characters.\n");
5826 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5827 result = strcmp(buffer, "");
5828 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5830 /* Test the effect of the delete key during typing on undo transactions */
5831 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5832 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5833 ok(result == TRUE, "Failed to set the text.\n");
5834 SendMessage(hwnd, EM_SETSEL, (WPARAM)1, (LPARAM)1);
5835 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5836 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5837 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5838 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5839 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5840 ok (result == TRUE, "Failed to undo typed characters.\n");
5841 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5842 result = strcmp(buffer, "acd");
5843 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5844 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5845 ok (result == TRUE, "Failed to undo typed characters.\n");
5846 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5847 result = strcmp(buffer, "abcd");
5848 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5850 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5851 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5852 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5853 ok (result == TRUE, "Failed to clear the text.\n");
5854 simulate_typing_characters(hwnd, "one two three");
5855 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5856 ok (result == 0, "expected %d but got %d\n", 0, result);
5857 simulate_typing_characters(hwnd, " four five six");
5858 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5859 ok (result == TRUE, "Failed to undo typed characters.\n");
5860 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5861 result = strcmp(buffer, "one two three");
5862 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5863 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5864 ok (result == TRUE, "Failed to undo typed characters.\n");
5865 ok (result == TRUE, "Failed to undo typed characters.\n");
5866 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5867 result = strcmp(buffer, "");
5868 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5870 DestroyWindow(hwnd);
5873 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5877 /* MSDN lied, length is actually the number of bytes. */
5878 length = bytes / sizeof(WCHAR);
5881 case WB_ISDELIMITER:
5882 return text[pos] == 'X';
5884 case WB_MOVEWORDLEFT:
5885 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5887 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5890 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5894 case WB_MOVEWORDRIGHT:
5895 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5897 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
5900 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5904 ok(FALSE, "Unexpected code %d\n", code);
5910 #define SEND_CTRL_LEFT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_LEFT)
5911 #define SEND_CTRL_RIGHT(hwnd) pressKeyWithModifier(hwnd, VK_CONTROL, VK_RIGHT)
5913 static void test_word_movement(void)
5917 int sel_start, sel_end;
5918 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
5920 /* multi-line control inserts CR normally */
5921 hwnd = new_richedit(NULL);
5923 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
5924 ok (result == TRUE, "Failed to clear the text.\n");
5925 SendMessage(hwnd, EM_SETSEL, 0, 0);
5926 /* |one two three */
5928 SEND_CTRL_RIGHT(hwnd);
5929 /* one |two three */
5930 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5931 ok(sel_start == sel_end, "Selection should be empty\n");
5932 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5934 SEND_CTRL_RIGHT(hwnd);
5935 /* one two |three */
5936 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5937 ok(sel_start == sel_end, "Selection should be empty\n");
5938 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5940 SEND_CTRL_LEFT(hwnd);
5941 /* one |two three */
5942 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5943 ok(sel_start == sel_end, "Selection should be empty\n");
5944 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
5946 SEND_CTRL_LEFT(hwnd);
5947 /* |one two three */
5948 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5949 ok(sel_start == sel_end, "Selection should be empty\n");
5950 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
5952 SendMessage(hwnd, EM_SETSEL, 8, 8);
5953 /* one two | three */
5954 SEND_CTRL_RIGHT(hwnd);
5955 /* one two |three */
5956 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5957 ok(sel_start == sel_end, "Selection should be empty\n");
5958 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5960 SendMessage(hwnd, EM_SETSEL, 11, 11);
5961 /* one two th|ree */
5962 SEND_CTRL_LEFT(hwnd);
5963 /* one two |three */
5964 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5965 ok(sel_start == sel_end, "Selection should be empty\n");
5966 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
5968 /* Test with a custom word break procedure that uses X as the delimiter. */
5969 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
5970 ok (result == TRUE, "Failed to clear the text.\n");
5971 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5972 /* |one twoXthree */
5973 SEND_CTRL_RIGHT(hwnd);
5974 /* one twoX|three */
5975 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5976 ok(sel_start == sel_end, "Selection should be empty\n");
5977 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
5979 DestroyWindow(hwnd);
5981 /* Make sure the behaviour is the same with a unicode richedit window,
5982 * and using unicode functions. */
5985 skip("Cannot test with unicode richedit window\n");
5989 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
5990 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
5991 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
5993 /* Test with a custom word break procedure that uses X as the delimiter. */
5994 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
5995 ok (result == TRUE, "Failed to clear the text.\n");
5996 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
5997 /* |one twoXthree */
5998 SEND_CTRL_RIGHT(hwnd);
5999 /* one twoX|three */
6000 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6001 ok(sel_start == sel_end, "Selection should be empty\n");
6002 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6004 DestroyWindow(hwnd);
6007 static void test_EM_CHARFROMPOS(void)
6016 /* multi-line control inserts CR normally */
6017 hwnd = new_richedit(NULL);
6018 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6019 (LPARAM)"one two three four five six seven\reight");
6021 GetClientRect(hwnd, &rcClient);
6023 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6024 ok(result == 34, "expected character index of 34 but got %d\n", result);
6026 /* Test with points outside the bounds of the richedit control. */
6029 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6030 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6034 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6035 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6039 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6040 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6044 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6045 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6048 point.y = rcClient.bottom + 1;
6049 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6050 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6053 point.y = rcClient.bottom;
6054 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6055 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6057 DestroyWindow(hwnd);
6060 static void test_word_wrap(void)
6063 POINTL point = {0, 60}; /* This point must be below the first line */
6064 const char *text = "Must be long enough to test line wrapping";
6065 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6066 int res, pos, lines;
6068 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6069 * when specified on window creation and set later. */
6070 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6071 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6072 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6073 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6074 ok(res, "WM_SETTEXT failed.\n");
6075 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6076 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6077 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6078 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6080 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6081 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6082 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6083 DestroyWindow(hwnd);
6085 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6086 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6087 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6089 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6090 ok(res, "WM_SETTEXT failed.\n");
6091 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6092 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6093 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6094 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6096 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6097 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6098 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6099 DestroyWindow(hwnd);
6101 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6102 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6103 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6104 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6105 ok(res, "WM_SETTEXT failed.\n");
6106 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6107 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6109 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6110 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6111 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6112 DestroyWindow(hwnd);
6114 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6115 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6116 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6117 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6118 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6119 ok(res, "WM_SETTEXT failed.\n");
6120 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6121 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6123 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6124 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6125 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6127 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6128 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6129 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6130 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6131 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6133 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6134 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6135 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6136 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6137 DestroyWindow(hwnd);
6139 /* Test to see if wrapping happens with redraw disabled. */
6140 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6141 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6142 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6143 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6144 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6145 ok(res, "EM_REPLACESEL failed.\n");
6146 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6147 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6148 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6149 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6150 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6152 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6153 DestroyWindow(hwnd);
6156 static void test_autoscroll(void)
6158 HWND hwnd = new_richedit(NULL);
6159 int lines, ret, redraw;
6162 for (redraw = 0; redraw <= 1; redraw++) {
6163 trace("testing with WM_SETREDRAW=%d\n", redraw);
6164 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6165 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6166 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6167 ok(lines == 8, "%d lines instead of 8\n", lines);
6168 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6169 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6170 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6171 ret = GetWindowLong(hwnd, GWL_STYLE);
6172 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6174 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6175 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6176 ok(lines == 1, "%d lines instead of 1\n", lines);
6177 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6178 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6179 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6180 ret = GetWindowLong(hwnd, GWL_STYLE);
6181 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6184 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6185 DestroyWindow(hwnd);
6187 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6188 * auto vertical/horizontal scrolling options. */
6189 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6190 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6191 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6192 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6193 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6194 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6195 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6196 ret = GetWindowLong(hwnd, GWL_STYLE);
6197 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6198 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6199 DestroyWindow(hwnd);
6201 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6202 WS_POPUP|ES_MULTILINE,
6203 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6204 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6205 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6206 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6207 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6208 ret = GetWindowLong(hwnd, GWL_STYLE);
6209 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6210 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6211 DestroyWindow(hwnd);
6215 static void test_format_rect(void)
6218 RECT rc, expected, clientRect;
6222 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6223 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6224 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6225 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6227 GetClientRect(hwnd, &clientRect);
6229 expected = clientRect;
6231 expected.right -= 1;
6232 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6233 ok(rc.top == expected.top && rc.left == expected.left &&
6234 rc.bottom == expected.bottom && rc.right == expected.right,
6235 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6236 rc.top, rc.left, rc.bottom, rc.right,
6237 expected.top, expected.left, expected.bottom, expected.right);
6239 for (n = -3; n <= 3; n++)
6246 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6249 expected.top = max(0, rc.top);
6250 expected.left = max(0, rc.left);
6251 expected.bottom = min(clientRect.bottom, rc.bottom);
6252 expected.right = min(clientRect.right, rc.right);
6253 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6254 ok(rc.top == expected.top && rc.left == expected.left &&
6255 rc.bottom == expected.bottom && rc.right == expected.right,
6256 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6257 n, rc.top, rc.left, rc.bottom, rc.right,
6258 expected.top, expected.left, expected.bottom, expected.right);
6262 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6263 expected = clientRect;
6264 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6265 ok(rc.top == expected.top && rc.left == expected.left &&
6266 rc.bottom == expected.bottom && rc.right == expected.right,
6267 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6268 rc.top, rc.left, rc.bottom, rc.right,
6269 expected.top, expected.left, expected.bottom, expected.right);
6271 /* Adding the selectionbar adds the selectionbar width to the left side. */
6272 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6273 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6274 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6275 expected.left += 8; /* selection bar width */
6276 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6277 ok(rc.top == expected.top && rc.left == expected.left &&
6278 rc.bottom == expected.bottom && rc.right == expected.right,
6279 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6280 rc.top, rc.left, rc.bottom, rc.right,
6281 expected.top, expected.left, expected.bottom, expected.right);
6284 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6285 expected = clientRect;
6286 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6287 ok(rc.top == expected.top && rc.left == expected.left &&
6288 rc.bottom == expected.bottom && rc.right == expected.right,
6289 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6290 rc.top, rc.left, rc.bottom, rc.right,
6291 expected.top, expected.left, expected.bottom, expected.right);
6293 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6294 * even if the left side is already 0. */
6295 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6296 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6297 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6298 expected.left -= 8; /* selection bar width */
6299 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6300 ok(rc.top == expected.top && rc.left == expected.left &&
6301 rc.bottom == expected.bottom && rc.right == expected.right,
6302 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6303 rc.top, rc.left, rc.bottom, rc.right,
6304 expected.top, expected.left, expected.bottom, expected.right);
6306 /* Set the absolute value of the formatting rectangle. */
6308 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6309 expected = clientRect;
6310 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6311 ok(rc.top == expected.top && rc.left == expected.left &&
6312 rc.bottom == expected.bottom && rc.right == expected.right,
6313 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6314 n, rc.top, rc.left, rc.bottom, rc.right,
6315 expected.top, expected.left, expected.bottom, expected.right);
6317 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6318 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6319 * tests show that this isn't true. */
6322 rc.bottom = clientRect.bottom - 15;
6323 rc.right = clientRect.right - 15;
6325 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6326 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6327 ok(rc.top == expected.top && rc.left == expected.left &&
6328 rc.bottom == expected.bottom && rc.right == expected.right,
6329 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6330 rc.top, rc.left, rc.bottom, rc.right,
6331 expected.top, expected.left, expected.bottom, expected.right);
6333 /* For some reason it does not limit the values to the client rect with
6334 * a WPARAM value of 1. */
6337 rc.bottom = clientRect.bottom + 15;
6338 rc.right = clientRect.right + 15;
6340 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6341 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6342 ok(rc.top == expected.top && rc.left == expected.left &&
6343 rc.bottom == expected.bottom && rc.right == expected.right,
6344 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6345 rc.top, rc.left, rc.bottom, rc.right,
6346 expected.top, expected.left, expected.bottom, expected.right);
6348 DestroyWindow(hwnd);
6350 /* The extended window style affects the formatting rectangle. */
6351 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6352 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6353 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6354 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6356 GetClientRect(hwnd, &clientRect);
6358 expected = clientRect;
6361 expected.right -= 1;
6362 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6363 ok(rc.top == expected.top && rc.left == expected.left &&
6364 rc.bottom == expected.bottom && rc.right == expected.right,
6365 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6366 rc.top, rc.left, rc.bottom, rc.right,
6367 expected.top, expected.left, expected.bottom, expected.right);
6377 expected.right += 1;
6378 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6379 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6380 ok(rc.top == expected.top && rc.left == expected.left &&
6381 rc.bottom == expected.bottom && rc.right == expected.right,
6382 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6383 rc.top, rc.left, rc.bottom, rc.right,
6384 expected.top, expected.left, expected.bottom, expected.right);
6386 DestroyWindow(hwnd);
6389 static void test_WM_GETDLGCODE(void)
6395 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6397 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6398 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6399 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6400 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6402 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6403 expected = expected | DLGC_WANTMESSAGE;
6404 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6406 DestroyWindow(hwnd);
6408 msg.message = WM_KEYDOWN;
6409 msg.wParam = VK_RETURN;
6410 msg.lParam = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) | 0x0001;
6413 msg.time = GetTickCount();
6415 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6416 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6417 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6418 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6420 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6421 expected = expected | DLGC_WANTMESSAGE;
6422 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6424 DestroyWindow(hwnd);
6426 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6427 ES_MULTILINE|WS_POPUP,
6428 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6429 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6431 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6432 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6433 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6435 DestroyWindow(hwnd);
6437 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6438 ES_WANTRETURN|WS_POPUP,
6439 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6440 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6442 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6443 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6444 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6446 DestroyWindow(hwnd);
6448 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6450 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6451 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6453 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6454 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6455 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6457 DestroyWindow(hwnd);
6459 msg.wParam = VK_TAB;
6460 msg.lParam = MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) | 0x0001;
6462 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6463 ES_MULTILINE|WS_POPUP,
6464 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6465 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6467 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6468 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6469 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6471 DestroyWindow(hwnd);
6473 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6475 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6476 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6478 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6479 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6480 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6482 DestroyWindow(hwnd);
6484 hold_key(VK_CONTROL);
6486 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6487 ES_MULTILINE|WS_POPUP,
6488 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6489 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6491 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6492 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6493 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6495 DestroyWindow(hwnd);
6497 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6499 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6500 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6502 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6503 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6504 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6506 DestroyWindow(hwnd);
6508 release_key(VK_CONTROL);
6511 msg.lParam = MapVirtualKey('a', MAPVK_VK_TO_VSC) | 0x0001;
6513 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6514 ES_MULTILINE|WS_POPUP,
6515 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6516 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6518 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6519 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6520 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6522 DestroyWindow(hwnd);
6524 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6526 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6527 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6529 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6530 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6531 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6533 DestroyWindow(hwnd);
6535 msg.message = WM_CHAR;
6537 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6538 ES_MULTILINE|WS_POPUP,
6539 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6540 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6542 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6543 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6544 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6546 DestroyWindow(hwnd);
6548 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6550 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6551 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6553 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6554 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6555 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6557 DestroyWindow(hwnd);
6560 static void test_zoom(void)
6566 int numerator, denominator;
6568 hwnd = new_richedit(NULL);
6569 GetClientRect(hwnd, &rc);
6570 pt.x = (rc.right - rc.left) / 2;
6571 pt.y = (rc.bottom - rc.top) / 2;
6572 ClientToScreen(hwnd, &pt);
6574 /* Test initial zoom value */
6575 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6576 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6577 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6578 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6580 /* test scroll wheel */
6581 hold_key(VK_CONTROL);
6582 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6583 MAKELPARAM(pt.x, pt.y));
6584 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6585 release_key(VK_CONTROL);
6587 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6588 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6589 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6590 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6592 /* Test how much the mouse wheel can zoom in and out. */
6593 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)490, (LPARAM)100);
6594 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6596 hold_key(VK_CONTROL);
6597 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6598 MAKELPARAM(pt.x, pt.y));
6599 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6600 release_key(VK_CONTROL);
6602 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6603 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6604 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6605 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6607 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)491, (LPARAM)100);
6608 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6610 hold_key(VK_CONTROL);
6611 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6612 MAKELPARAM(pt.x, pt.y));
6613 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6614 release_key(VK_CONTROL);
6616 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6617 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6618 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6619 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6621 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)20, (LPARAM)100);
6622 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6624 hold_key(VK_CONTROL);
6625 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6626 MAKELPARAM(pt.x, pt.y));
6627 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6628 release_key(VK_CONTROL);
6630 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6631 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6632 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6633 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6635 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)19, (LPARAM)100);
6636 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6638 hold_key(VK_CONTROL);
6639 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6640 MAKELPARAM(pt.x, pt.y));
6641 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6642 release_key(VK_CONTROL);
6644 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6645 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6646 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6647 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6649 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6650 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)50, (LPARAM)13);
6651 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6653 hold_key(VK_CONTROL);
6654 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6655 MAKELPARAM(pt.x, pt.y));
6656 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6657 release_key(VK_CONTROL);
6659 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6660 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6661 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6662 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6664 /* Test bounds checking on EM_SETZOOM */
6665 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)127);
6666 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6668 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)127, (LPARAM)2);
6669 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6671 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)2, (LPARAM)128);
6672 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6674 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6675 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6676 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6677 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6679 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)128, (LPARAM)2);
6680 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6682 /* See if negative numbers are accepted. */
6683 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)-100, (LPARAM)-100);
6684 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6686 /* See if negative numbers are accepted. */
6687 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)100);
6688 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6690 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6691 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6692 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6693 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6695 /* Reset the zoom value */
6696 ret = SendMessage(hwnd, EM_SETZOOM, (WPARAM)0, (LPARAM)0);
6697 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6699 DestroyWindow(hwnd);
6702 START_TEST( editor )
6704 /* Must explicitly LoadLibrary(). The test has no references to functions in
6705 * RICHED20.DLL, so the linker doesn't actually link to it. */
6706 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
6707 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
6709 is_win9x = GetVersion() & 0x80000000;
6714 test_EM_POSFROMCHAR();
6715 test_EM_SCROLLCARET();
6717 test_scrollbar_visibility();
6719 test_EM_LINELENGTH();
6720 test_EM_SETCHARFORMAT();
6721 test_EM_SETTEXTMODE();
6722 test_TM_PLAINTEXT();
6723 test_EM_SETOPTIONS();
6725 test_EM_GETTEXTRANGE();
6726 test_EM_GETSELTEXT();
6727 test_EM_SETUNDOLIMIT();
6729 test_EM_SETTEXTEX();
6730 test_EM_LIMITTEXT();
6731 test_EM_EXLIMITTEXT();
6732 test_EM_GETLIMITTEXT();
6734 test_EM_GETMODIFY();
6738 test_EM_STREAMOUT();
6739 test_EM_STREAMOUT_FONTTBL();
6740 test_EM_StreamIn_Undo();
6741 test_EM_FORMATRANGE();
6742 test_unicode_conversions();
6743 test_EM_GETTEXTLENGTHEX();
6744 test_EM_REPLACESEL(1);
6745 test_EM_REPLACESEL(0);
6747 test_EM_AUTOURLDETECT();
6749 test_undo_coalescing();
6750 test_word_movement();
6751 test_EM_CHARFROMPOS();
6752 test_SETPARAFORMAT();
6756 test_WM_GETDLGCODE();
6759 /* Set the environment variable WINETEST_RICHED20 to keep windows
6760 * responsive and open for 30 seconds. This is useful for debugging.
6762 if (getenv( "WINETEST_RICHED20" )) {
6763 keep_responsive(30);
6766 OleFlushClipboard();
6767 ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError());