2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
34 #include <wine/test.h>
36 #define ID_RICHEDITTESTDBUTTON 0x123
38 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
40 #define ok_w3(format, szString1, szString2, szString3) \
41 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
42 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
44 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
45 format, string1, string2, string3);
47 static HMODULE hmoduleRichEdit;
49 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
51 hwnd = CreateWindowA(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_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
60 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
61 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
62 hmoduleRichEdit, NULL);
63 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
67 static HWND new_richedit(HWND parent) {
68 return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent);
71 static HWND new_richeditW(HWND parent) {
72 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
75 /* Keeps the window reponsive for the deley_time in seconds.
76 * This is useful for debugging a test to see what is happening. */
77 static void keep_responsive(time_t delay_time)
82 /* The message pump uses PeekMessage() to empty the queue and then
83 * sleeps for 50ms before retrying the queue. */
84 end = time(NULL) + delay_time;
85 while (time(NULL) < end) {
86 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
87 TranslateMessage(&msg);
88 DispatchMessage(&msg);
95 static void simulate_typing_characters(HWND hwnd, const char* szChars)
99 while (*szChars != '\0') {
100 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
101 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
102 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
103 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
108 static BOOL hold_key(int vk)
113 result = GetKeyboardState(key_state);
114 ok(result, "GetKeyboardState failed.\n");
115 if (!result) return FALSE;
116 key_state[vk] |= 0x80;
117 result = SetKeyboardState(key_state);
118 ok(result, "SetKeyboardState failed.\n");
122 static BOOL release_key(int vk)
127 result = GetKeyboardState(key_state);
128 ok(result, "GetKeyboardState failed.\n");
129 if (!result) return FALSE;
130 key_state[vk] &= ~0x80;
131 result = SetKeyboardState(key_state);
132 ok(result, "SetKeyboardState failed.\n");
136 static const char haystack[] = "WINEWine wineWine wine WineWine";
148 static struct find_s find_tests[] = {
149 /* Find in empty text */
150 {0, -1, "foo", FR_DOWN, -1},
151 {0, -1, "foo", 0, -1},
152 {0, -1, "", FR_DOWN, -1},
153 {20, 5, "foo", FR_DOWN, -1},
154 {5, 20, "foo", FR_DOWN, -1}
157 static struct find_s find_tests2[] = {
159 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
160 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
162 /* Subsequent finds */
163 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
164 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
165 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
166 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
169 {19, 20, "Wine", FR_MATCHCASE, 13},
170 {10, 20, "Wine", FR_MATCHCASE, 4},
171 {20, 10, "Wine", FR_MATCHCASE, 13},
173 /* Case-insensitive */
174 {1, 31, "wInE", FR_DOWN, 4},
175 {1, 31, "Wine", FR_DOWN, 4},
177 /* High-to-low ranges */
178 {20, 5, "Wine", FR_DOWN, -1},
179 {2, 1, "Wine", FR_DOWN, -1},
180 {30, 29, "Wine", FR_DOWN, -1},
181 {20, 5, "Wine", 0, 13},
184 {5, 10, "", FR_DOWN, -1},
185 {10, 5, "", FR_DOWN, -1},
186 {0, -1, "", FR_DOWN, -1},
189 /* Whole-word search */
190 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
191 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
192 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
193 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
194 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
195 {11, -1, "winewine", FR_WHOLEWORD, 0},
196 {31, -1, "winewine", FR_WHOLEWORD, 23},
199 {5, 200, "XXX", FR_DOWN, -1},
200 {-20, 20, "Wine", FR_DOWN, -1},
201 {-20, 20, "Wine", FR_DOWN, -1},
202 {-15, -20, "Wine", FR_DOWN, -1},
203 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
205 /* Check the case noted in bug 4479 where matches at end aren't recognized */
206 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
207 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
208 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
210 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
212 /* The backwards case of bug 4479; bounds look right
213 * Fails because backward find is wrong */
214 {19, 20, "WINE", FR_MATCHCASE, 0},
215 {0, 20, "WINE", FR_MATCHCASE, -1},
217 {0, -1, "wineWine wine", 0, -1},
220 static WCHAR *atowstr(const char *str)
224 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
225 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
226 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
230 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
236 memset(&ftw, 0, sizeof(ftw));
237 ftw.chrg.cpMin = f->start;
238 ftw.chrg.cpMax = f->end;
239 ftw.lpstrText = atowstr(f->needle);
241 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ftw);
242 ok(findloc == f->expected_loc,
243 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
244 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
246 findloc = SendMessage(hwnd, EM_FINDTEXTW, f->flags, (LPARAM) &ftw);
247 ok(findloc == f->expected_loc,
248 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
249 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
251 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
254 memset(&fta, 0, sizeof(fta));
255 fta.chrg.cpMin = f->start;
256 fta.chrg.cpMax = f->end;
257 fta.lpstrText = f->needle;
259 findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &fta);
260 ok(findloc == f->expected_loc,
261 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
262 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
266 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
267 int id, BOOL unicode)
270 int expected_end_loc;
274 memset(&ftw, 0, sizeof(ftw));
275 ftw.chrg.cpMin = f->start;
276 ftw.chrg.cpMax = f->end;
277 ftw.lpstrText = atowstr(f->needle);
278 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ftw);
279 ok(findloc == f->expected_loc,
280 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
281 name, id, f->needle, f->start, f->end, f->flags, findloc);
282 ok(ftw.chrgText.cpMin == f->expected_loc,
283 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
284 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
285 expected_end_loc = ((f->expected_loc == -1) ? -1
286 : f->expected_loc + strlen(f->needle));
287 ok(ftw.chrgText.cpMax == expected_end_loc,
288 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
289 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
290 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
293 memset(&fta, 0, sizeof(fta));
294 fta.chrg.cpMin = f->start;
295 fta.chrg.cpMax = f->end;
296 fta.lpstrText = f->needle;
297 findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &fta);
298 ok(findloc == f->expected_loc,
299 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
300 name, id, f->needle, f->start, f->end, f->flags, findloc);
301 ok(fta.chrgText.cpMin == f->expected_loc,
302 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
303 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
304 expected_end_loc = ((f->expected_loc == -1) ? -1
305 : f->expected_loc + strlen(f->needle));
306 ok(fta.chrgText.cpMax == expected_end_loc,
307 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
308 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
312 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
313 int num_tests, BOOL unicode)
317 for (i = 0; i < num_tests; i++) {
318 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
319 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
323 static void test_EM_FINDTEXT(BOOL unicode)
329 hwndRichEdit = new_richeditW(NULL);
331 hwndRichEdit = new_richedit(NULL);
333 /* Empty rich edit control */
334 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
335 sizeof(find_tests)/sizeof(struct find_s), unicode);
337 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack);
340 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
341 sizeof(find_tests2)/sizeof(struct find_s), unicode);
343 /* Setting a format on an arbitrary range should have no effect in search
344 results. This tests correct offset reporting across runs. */
345 cf2.cbSize = sizeof(CHARFORMAT2);
346 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
347 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
348 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
349 SendMessage(hwndRichEdit, EM_SETSEL, 6, 20);
350 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
352 /* Haystack text, again */
353 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
354 sizeof(find_tests2)/sizeof(struct find_s), unicode);
356 /* Yet another range */
357 cf2.dwMask = CFM_BOLD | cf2.dwMask;
358 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
359 SendMessage(hwndRichEdit, EM_SETSEL, 11, 15);
360 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
362 /* Haystack text, again */
363 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
364 sizeof(find_tests2)/sizeof(struct find_s), unicode);
366 DestroyWindow(hwndRichEdit);
369 static const struct getline_s {
374 {0, 10, "foo bar\r"},
379 /* Buffer smaller than line length */
385 static void test_EM_GETLINE(void)
388 HWND hwndRichEdit = new_richedit(NULL);
389 static const int nBuf = 1024;
390 char dest[1024], origdest[1024];
391 const char text[] = "foo bar\n"
395 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
397 memset(origdest, 0xBB, nBuf);
398 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
401 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
402 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
403 memset(dest, 0xBB, nBuf);
404 *(WORD *) dest = gl[i].buffer_len;
406 /* EM_GETLINE appends a "\r\0" to the end of the line
407 * nCopied counts up to and including the '\r' */
408 nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest);
409 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
411 /* two special cases since a parameter is passed via dest */
412 if (gl[i].buffer_len == 0)
413 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
415 else if (gl[i].buffer_len == 1)
416 ok(dest[0] == gl[i].text[0] && !dest[1] &&
417 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
420 /* Prepare hex strings of buffers to dump on failure. */
421 char expectedbuf[1024];
422 char resultbuf[1024];
425 for (j = 0; j < 32; j++)
426 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
427 expectedbuf[0] = '\0';
428 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
429 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
430 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
431 sprintf(expectedbuf+strlen(expectedbuf), "??");
432 for (; j < 32; j++) /* Bytes after declared buffer size */
433 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
435 /* Test the part of the buffer that is expected to be written according
436 * to the MSDN documentation fo EM_GETLINE, which does not state that
437 * a NULL terminating character will be added unless no text is copied.
439 * Windows NT does not append a NULL terminating character, but
440 * Windows 2000 and up do append a NULL terminating character if there
441 * is space in the buffer. The test will ignore this difference. */
442 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
443 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
444 i, expected_bytes_written, expectedbuf, resultbuf);
445 /* Test the part of the buffer after the declared length to make sure
446 * there are no buffer overruns. */
447 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
448 nBuf - gl[i].buffer_len),
449 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
450 i, expected_bytes_written, expectedbuf, resultbuf);
454 DestroyWindow(hwndRichEdit);
457 static void test_EM_LINELENGTH(void)
459 HWND hwndRichEdit = new_richedit(NULL);
465 int offset_test[10][2] = {
480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
482 for (i = 0; i < 10; i++) {
483 result = SendMessage(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
484 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
485 offset_test[i][0], result, offset_test[i][1]);
488 DestroyWindow(hwndRichEdit);
491 static int get_scroll_pos_y(HWND hwnd)
494 SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p);
495 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
499 static void move_cursor(HWND hwnd, LONG charindex)
502 cr.cpMax = charindex;
503 cr.cpMin = charindex;
504 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
507 static void line_scroll(HWND hwnd, int amount)
509 SendMessage(hwnd, EM_LINESCROLL, 0, amount);
512 static void test_EM_SCROLLCARET(void)
515 const char text[] = "aa\n"
516 "this is a long line of text that should be longer than the "
524 /* The richedit window height needs to be large enough vertically to fit in
525 * more than two lines of text, so the new_richedit function can't be used
526 * since a height of 60 was not large enough on some systems.
528 HWND hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
529 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
530 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
531 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
533 /* Can't verify this */
534 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
536 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
538 /* Caret above visible window */
539 line_scroll(hwndRichEdit, 3);
540 prevY = get_scroll_pos_y(hwndRichEdit);
541 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
542 curY = get_scroll_pos_y(hwndRichEdit);
543 ok(prevY != curY, "%d == %d\n", prevY, curY);
545 /* Caret below visible window */
546 move_cursor(hwndRichEdit, sizeof(text) - 1);
547 line_scroll(hwndRichEdit, -3);
548 prevY = get_scroll_pos_y(hwndRichEdit);
549 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
550 curY = get_scroll_pos_y(hwndRichEdit);
551 ok(prevY != curY, "%d == %d\n", prevY, curY);
553 /* Caret in visible window */
554 move_cursor(hwndRichEdit, sizeof(text) - 2);
555 prevY = get_scroll_pos_y(hwndRichEdit);
556 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
557 curY = get_scroll_pos_y(hwndRichEdit);
558 ok(prevY == curY, "%d != %d\n", prevY, curY);
560 /* Caret still in visible window */
561 line_scroll(hwndRichEdit, -1);
562 prevY = get_scroll_pos_y(hwndRichEdit);
563 SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0);
564 curY = get_scroll_pos_y(hwndRichEdit);
565 ok(prevY == curY, "%d != %d\n", prevY, curY);
567 DestroyWindow(hwndRichEdit);
570 static void test_EM_POSFROMCHAR(void)
572 HWND hwndRichEdit = new_richedit(NULL);
575 unsigned int height = 0;
580 static const char text[] = "aa\n"
581 "this is a long line of text that should be longer than the "
590 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
591 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
592 (sig.lsUsb[3] & 0x08000000) != 0);
594 /* Fill the control to lines to ensure that most of them are offscreen */
595 for (i = 0; i < 50; i++)
597 /* Do not modify the string; it is exactly 16 characters long. */
598 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
599 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
603 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
604 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
605 Richedit 3.0 accepts either of the above API conventions.
608 /* Testing Richedit 2.0 API format */
610 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
611 Since all lines are identical and drawn with the same font,
612 they should have the same height... right?
614 for (i = 0; i < 50; i++)
616 /* All the lines are 16 characters long */
617 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
620 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
621 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
622 xpos = LOWORD(result);
626 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
627 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
628 height = HIWORD(result);
632 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
633 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
637 /* Testing position at end of text */
638 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
639 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
640 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
642 /* Testing position way past end of text */
643 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
644 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
645 expected = (rtl ? 8 : 1);
646 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
648 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
649 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
650 for (i = 0; i < 50; i++)
652 /* All the lines are 16 characters long */
653 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
654 ok((signed short)(HIWORD(result)) == (i - 1) * height,
655 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
656 (signed short)(HIWORD(result)), (i - 1) * height);
657 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
660 /* Testing position at end of text */
661 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
662 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
663 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
665 /* Testing position way past end of text */
666 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
667 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
668 expected = (rtl ? 8 : 1);
669 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
671 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
672 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
673 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
675 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
676 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
677 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
678 xpos = LOWORD(result);
680 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
681 result = SendMessage(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
682 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
683 ok((signed short)(LOWORD(result)) < xpos,
684 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
685 (signed short)(LOWORD(result)), xpos);
686 SendMessage(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
688 /* Test around end of text that doesn't end in a newline. */
689 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "12345678901234");
690 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
691 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
692 ok(pt.x > 1, "pt.x = %d\n", pt.x);
694 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
695 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
696 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
697 xpos = (rtl ? pt.x + 7 : pt.x);
698 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
699 SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
700 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
702 /* Try a negative position. */
703 SendMessage(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
704 ok(pt.x == 1, "pt.x = %d\n", pt.x);
706 DestroyWindow(hwndRichEdit);
709 static void test_EM_SETCHARFORMAT(void)
711 HWND hwndRichEdit = new_richedit(NULL);
714 int tested_effects[] = {
730 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
731 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
732 (sig.lsUsb[3] & 0x08000000) != 0);
734 /* Invalid flags, CHARFORMAT2 structure blanked out */
735 memset(&cf2, 0, sizeof(cf2));
736 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
738 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
740 /* A valid flag, CHARFORMAT2 structure blanked out */
741 memset(&cf2, 0, sizeof(cf2));
742 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
744 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
746 /* A valid flag, CHARFORMAT2 structure blanked out */
747 memset(&cf2, 0, sizeof(cf2));
748 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
750 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
752 /* A valid flag, CHARFORMAT2 structure blanked out */
753 memset(&cf2, 0, sizeof(cf2));
754 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
756 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
758 /* A valid flag, CHARFORMAT2 structure blanked out */
759 memset(&cf2, 0, sizeof(cf2));
760 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
762 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
764 /* Invalid flags, CHARFORMAT2 structure minimally filled */
765 memset(&cf2, 0, sizeof(cf2));
766 cf2.cbSize = sizeof(CHARFORMAT2);
767 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0,
769 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
770 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
771 ok(rc == FALSE, "Should not be able to undo here.\n");
772 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
774 /* A valid flag, CHARFORMAT2 structure minimally filled */
775 memset(&cf2, 0, sizeof(cf2));
776 cf2.cbSize = sizeof(CHARFORMAT2);
777 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT,
779 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
780 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
781 ok(rc == FALSE, "Should not be able to undo here.\n");
782 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
784 /* A valid flag, CHARFORMAT2 structure minimally filled */
785 memset(&cf2, 0, sizeof(cf2));
786 cf2.cbSize = sizeof(CHARFORMAT2);
787 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION,
789 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
790 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
791 ok(rc == FALSE, "Should not be able to undo here.\n");
792 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
794 /* A valid flag, CHARFORMAT2 structure minimally filled */
795 memset(&cf2, 0, sizeof(cf2));
796 cf2.cbSize = sizeof(CHARFORMAT2);
797 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD,
799 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
800 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
801 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
802 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
804 /* A valid flag, CHARFORMAT2 structure minimally filled */
805 memset(&cf2, 0, sizeof(cf2));
806 cf2.cbSize = sizeof(CHARFORMAT2);
807 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
809 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
810 rc = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
811 ok(rc == TRUE, "Should not be able to undo here.\n");
812 SendMessage(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
814 cf2.cbSize = sizeof(CHARFORMAT2);
815 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
818 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
819 cf2.cbSize = sizeof(CHARFORMAT2);
820 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
822 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
823 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
825 /* wParam==0 is default char format, does not set modify */
826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
827 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
828 ok(rc == 0, "Text marked as modified, expected not modified!\n");
829 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
830 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
833 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
834 ok(rc == 0, "Text marked as modified, expected not modified!\n");
837 skip("RTL language found\n");
839 /* wParam==SCF_SELECTION sets modify if nonempty selection */
840 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
841 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
842 ok(rc == 0, "Text marked as modified, expected not modified!\n");
843 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
844 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
845 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
846 ok(rc == 0, "Text marked as modified, expected not modified!\n");
848 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
849 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
850 ok(rc == 0, "Text marked as modified, expected not modified!\n");
851 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
852 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
853 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
854 ok(rc == 0, "Text marked as modified, expected not modified!\n");
855 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
856 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
857 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
858 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
859 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
861 /* wParam==SCF_ALL sets modify regardless of whether text is present */
862 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
863 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
864 ok(rc == 0, "Text marked as modified, expected not modified!\n");
865 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
866 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
867 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
868 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
870 DestroyWindow(hwndRichEdit);
872 /* EM_GETCHARFORMAT tests */
873 for (i = 0; tested_effects[i]; i++)
875 hwndRichEdit = new_richedit(NULL);
876 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
878 /* Need to set a TrueType font to get consistent CFM_BOLD results */
879 memset(&cf2, 0, sizeof(CHARFORMAT2));
880 cf2.cbSize = sizeof(CHARFORMAT2);
881 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
883 strcpy(cf2.szFaceName, "Courier New");
884 cf2.wWeight = FW_DONTCARE;
885 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
887 memset(&cf2, 0, sizeof(CHARFORMAT2));
888 cf2.cbSize = sizeof(CHARFORMAT2);
889 SendMessage(hwndRichEdit, EM_SETSEL, 0, 4);
890 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
891 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
892 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
894 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
895 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
896 ok((cf2.dwEffects & tested_effects[i]) == 0,
897 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
899 memset(&cf2, 0, sizeof(CHARFORMAT2));
900 cf2.cbSize = sizeof(CHARFORMAT2);
901 cf2.dwMask = tested_effects[i];
902 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
903 cf2.dwMask = CFM_SUPERSCRIPT;
904 cf2.dwEffects = tested_effects[i];
905 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
906 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
908 memset(&cf2, 0, sizeof(CHARFORMAT2));
909 cf2.cbSize = sizeof(CHARFORMAT2);
910 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
911 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
912 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
913 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
915 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
916 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
917 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
918 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
920 memset(&cf2, 0, sizeof(CHARFORMAT2));
921 cf2.cbSize = sizeof(CHARFORMAT2);
922 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
923 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
924 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
925 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
927 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
928 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
929 ok((cf2.dwEffects & tested_effects[i]) == 0,
930 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
932 memset(&cf2, 0, sizeof(CHARFORMAT2));
933 cf2.cbSize = sizeof(CHARFORMAT2);
934 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
935 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
936 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
937 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
939 (cf2.dwMask & tested_effects[i]) == 0),
940 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
942 DestroyWindow(hwndRichEdit);
945 for (i = 0; tested_effects[i]; i++)
947 hwndRichEdit = new_richedit(NULL);
948 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
950 /* Need to set a TrueType font to get consistent CFM_BOLD results */
951 memset(&cf2, 0, sizeof(CHARFORMAT2));
952 cf2.cbSize = sizeof(CHARFORMAT2);
953 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
955 strcpy(cf2.szFaceName, "Courier New");
956 cf2.wWeight = FW_DONTCARE;
957 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cf2);
959 memset(&cf2, 0, sizeof(CHARFORMAT2));
960 cf2.cbSize = sizeof(CHARFORMAT2);
961 cf2.dwMask = tested_effects[i];
962 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
963 cf2.dwMask = CFM_SUPERSCRIPT;
964 cf2.dwEffects = tested_effects[i];
965 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
966 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
968 memset(&cf2, 0, sizeof(CHARFORMAT2));
969 cf2.cbSize = sizeof(CHARFORMAT2);
970 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
971 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
972 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
973 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
975 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
976 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
977 ok((cf2.dwEffects & tested_effects[i]) == 0,
978 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
980 memset(&cf2, 0, sizeof(CHARFORMAT2));
981 cf2.cbSize = sizeof(CHARFORMAT2);
982 SendMessage(hwndRichEdit, EM_SETSEL, 2, 4);
983 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
984 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
985 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
987 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
988 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
989 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
990 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
992 memset(&cf2, 0, sizeof(CHARFORMAT2));
993 cf2.cbSize = sizeof(CHARFORMAT2);
994 SendMessage(hwndRichEdit, EM_SETSEL, 1, 3);
995 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
996 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
997 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
999 (cf2.dwMask & tested_effects[i]) == 0),
1000 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
1001 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
1002 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
1004 DestroyWindow(hwndRichEdit);
1007 /* Effects applied on an empty selection should take effect when selection is
1008 replaced with text */
1009 hwndRichEdit = new_richedit(NULL);
1010 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1011 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1013 memset(&cf2, 0, sizeof(CHARFORMAT2));
1014 cf2.cbSize = sizeof(CHARFORMAT2);
1015 cf2.dwMask = CFM_BOLD;
1016 cf2.dwEffects = CFE_BOLD;
1017 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1019 /* Selection is now nonempty */
1020 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1022 memset(&cf2, 0, sizeof(CHARFORMAT2));
1023 cf2.cbSize = sizeof(CHARFORMAT2);
1024 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1025 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1027 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1028 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1029 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1030 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1033 /* Set two effects on an empty selection */
1034 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1035 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1037 memset(&cf2, 0, sizeof(CHARFORMAT2));
1038 cf2.cbSize = sizeof(CHARFORMAT2);
1039 cf2.dwMask = CFM_BOLD;
1040 cf2.dwEffects = CFE_BOLD;
1041 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1042 cf2.dwMask = CFM_ITALIC;
1043 cf2.dwEffects = CFE_ITALIC;
1044 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1046 /* Selection is now nonempty */
1047 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1049 memset(&cf2, 0, sizeof(CHARFORMAT2));
1050 cf2.cbSize = sizeof(CHARFORMAT2);
1051 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1052 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1054 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1055 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1056 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1057 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1059 /* Setting the (empty) selection to exactly the same place as before should
1060 NOT clear the insertion style! */
1061 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1062 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1064 memset(&cf2, 0, sizeof(CHARFORMAT2));
1065 cf2.cbSize = sizeof(CHARFORMAT2);
1066 cf2.dwMask = CFM_BOLD;
1067 cf2.dwEffects = CFE_BOLD;
1068 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1070 /* Empty selection in same place, insert style should NOT be forgotten here. */
1071 SendMessage(hwndRichEdit, EM_SETSEL, 2, 2);
1073 /* Selection is now nonempty */
1074 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1076 memset(&cf2, 0, sizeof(CHARFORMAT2));
1077 cf2.cbSize = sizeof(CHARFORMAT2);
1078 SendMessage(hwndRichEdit, EM_SETSEL, 2, 6);
1079 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1081 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1082 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1083 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1084 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1086 /* Ditto with EM_EXSETSEL */
1087 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1088 cr.cpMin = 2; cr.cpMax = 2;
1089 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1091 memset(&cf2, 0, sizeof(CHARFORMAT2));
1092 cf2.cbSize = sizeof(CHARFORMAT2);
1093 cf2.dwMask = CFM_BOLD;
1094 cf2.dwEffects = CFE_BOLD;
1095 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1097 /* Empty selection in same place, insert style should NOT be forgotten here. */
1098 cr.cpMin = 2; cr.cpMax = 2;
1099 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1101 /* Selection is now nonempty */
1102 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1104 memset(&cf2, 0, sizeof(CHARFORMAT2));
1105 cf2.cbSize = sizeof(CHARFORMAT2);
1106 cr.cpMin = 2; cr.cpMax = 6;
1107 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1108 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &cf2);
1110 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1111 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1112 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1113 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1115 DestroyWindow(hwndRichEdit);
1118 static void test_EM_SETTEXTMODE(void)
1120 HWND hwndRichEdit = new_richedit(NULL);
1121 CHARFORMAT2 cf2, cf2test;
1125 /*Attempt to use mutually exclusive modes*/
1126 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT|TM_RICHTEXT, 0);
1127 ok(rc == E_INVALIDARG,
1128 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1130 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1131 /*Insert text into the control*/
1133 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1135 /*Attempt to change the control to plain text mode*/
1136 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1137 ok(rc == E_UNEXPECTED,
1138 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1140 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1141 If rich text is pasted, it should have the same formatting as the rest
1142 of the text in the control*/
1144 /*Italicize the text
1145 *NOTE: If the default text was already italicized, the test will simply
1146 reverse; in other words, it will copy a regular "wine" into a plain
1147 text window that uses an italicized format*/
1148 cf2.cbSize = sizeof(CHARFORMAT2);
1149 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT,
1152 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1153 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1155 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1156 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1158 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1159 however, SCF_ALL has been implemented*/
1160 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1161 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1163 rc = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
1164 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1166 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1168 /*Select the string "wine"*/
1171 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1173 /*Copy the italicized "wine" to the clipboard*/
1174 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1176 /*Reset the formatting to default*/
1177 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1178 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2);
1179 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1181 /*Clear the text in the control*/
1182 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1184 /*Switch to Plain Text Mode*/
1185 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0);
1186 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1188 /*Input "wine" again in normal format*/
1189 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1191 /*Paste the italicized "wine" into the control*/
1192 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1194 /*Select a character from the first "wine" string*/
1197 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1199 /*Retrieve its formatting*/
1200 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1203 /*Select a character from the second "wine" string*/
1206 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1208 /*Retrieve its formatting*/
1209 cf2test.cbSize = sizeof(CHARFORMAT2);
1210 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1213 /*Compare the two formattings*/
1214 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1215 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1216 cf2.dwEffects, cf2test.dwEffects);
1217 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1218 printing "wine" in the current format(normal)
1219 pasting "wine" from the clipboard(italicized)
1220 comparing the two formats(should differ)*/
1222 /*Attempt to switch with text in control*/
1223 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1224 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1227 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1229 /*Switch into Rich Text mode*/
1230 rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0);
1231 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1233 /*Print "wine" in normal formatting into the control*/
1234 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1236 /*Paste italicized "wine" into the control*/
1237 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1239 /*Select text from the first "wine" string*/
1242 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1244 /*Retrieve its formatting*/
1245 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1248 /*Select text from the second "wine" string*/
1251 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1253 /*Retrieve its formatting*/
1254 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION,
1257 /*Test that the two formattings are not the same*/
1258 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1259 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1260 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1262 DestroyWindow(hwndRichEdit);
1265 static void test_SETPARAFORMAT(void)
1267 HWND hwndRichEdit = new_richedit(NULL);
1270 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1271 fmt.cbSize = sizeof(PARAFORMAT2);
1272 fmt.dwMask = PFM_ALIGNMENT;
1273 fmt.wAlignment = PFA_LEFT;
1275 ret = SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &fmt);
1276 ok(ret != 0, "expected non-zero got %d\n", ret);
1278 fmt.cbSize = sizeof(PARAFORMAT2);
1280 ret = SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM) &fmt);
1281 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1282 * between richedit different native builds of riched20.dll
1283 * used on different Windows versions. */
1284 ret &= ~PFM_TABLEROWDELIMITER;
1285 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1287 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1288 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1290 DestroyWindow(hwndRichEdit);
1293 static void test_TM_PLAINTEXT(void)
1295 /*Tests plain text properties*/
1297 HWND hwndRichEdit = new_richedit(NULL);
1298 CHARFORMAT2 cf2, cf2test;
1302 /*Switch to plain text mode*/
1304 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1305 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1307 /*Fill control with text*/
1309 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not");
1311 /*Select some text and bold it*/
1315 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1316 cf2.cbSize = sizeof(CHARFORMAT2);
1317 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1319 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1320 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1322 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1323 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1325 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1326 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1328 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1329 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1331 /*Get the formatting of those characters*/
1333 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1335 /*Get the formatting of some other characters*/
1336 cf2test.cbSize = sizeof(CHARFORMAT2);
1339 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1340 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1342 /*Test that they are the same as plain text allows only one formatting*/
1344 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1345 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1346 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1348 /*Fill the control with a "wine" string, which when inserted will be bold*/
1350 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1352 /*Copy the bolded "wine" string*/
1356 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1357 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
1359 /*Swap back to rich text*/
1361 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "");
1362 SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1364 /*Set the default formatting to bold italics*/
1366 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1367 cf2.dwMask |= CFM_ITALIC;
1368 cf2.dwEffects ^= CFE_ITALIC;
1369 rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1370 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1372 /*Set the text in the control to "wine", which will be bold and italicized*/
1374 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine");
1376 /*Paste the plain text "wine" string, which should take the insert
1377 formatting, which at the moment is bold italics*/
1379 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
1381 /*Select the first "wine" string and retrieve its formatting*/
1385 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1386 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1388 /*Select the second "wine" string and retrieve its formatting*/
1392 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
1393 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1395 /*Compare the two formattings. They should be the same.*/
1397 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1398 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1399 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1400 DestroyWindow(hwndRichEdit);
1403 static void test_WM_GETTEXT(void)
1405 HWND hwndRichEdit = new_richedit(NULL);
1406 static const char text[] = "Hello. My name is RichEdit!";
1407 static const char text2[] = "Hello. My name is RichEdit!\r";
1408 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1409 char buffer[1024] = {0};
1412 /* Baseline test with normal-sized buffer */
1413 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1414 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1415 ok(result == lstrlen(buffer),
1416 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1417 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1418 result = strcmp(buffer,text);
1420 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1422 /* Test for returned value of WM_GETTEXTLENGTH */
1423 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1424 ok(result == lstrlen(text),
1425 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1426 result, lstrlen(text));
1428 /* Test for behavior in overflow case */
1429 memset(buffer, 0, 1024);
1430 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1432 result == lstrlenA(text) - 1, /* XP, win2k3 */
1433 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1434 result = strcmp(buffer,text);
1436 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1438 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1440 /* Baseline test with normal-sized buffer and carriage return */
1441 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
1442 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1443 ok(result == lstrlen(buffer),
1444 "WM_GETTEXT returned %d, expected %d\n", result, lstrlen(buffer));
1445 result = strcmp(buffer,text2_after);
1447 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1449 /* Test for returned value of WM_GETTEXTLENGTH */
1450 result = SendMessage(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1451 ok(result == lstrlen(text2_after),
1452 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1453 result, lstrlen(text2_after));
1455 /* Test for behavior of CRLF conversion in case of overflow */
1456 memset(buffer, 0, 1024);
1457 result = SendMessage(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1459 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1460 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1461 result = strcmp(buffer,text2);
1463 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1465 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1467 DestroyWindow(hwndRichEdit);
1470 static void test_EM_GETTEXTRANGE(void)
1472 HWND hwndRichEdit = new_richedit(NULL);
1473 const char * text1 = "foo bar\r\nfoo bar";
1474 const char * text2 = "foo bar\rfoo bar";
1475 const char * expect = "bar\rfoo";
1476 char buffer[1024] = {0};
1478 TEXTRANGEA textRange;
1480 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1482 textRange.lpstrText = buffer;
1483 textRange.chrg.cpMin = 4;
1484 textRange.chrg.cpMax = 11;
1485 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1486 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1487 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1489 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1491 textRange.lpstrText = buffer;
1492 textRange.chrg.cpMin = 4;
1493 textRange.chrg.cpMax = 11;
1494 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1495 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1496 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1498 /* cpMax of text length is used instead of -1 in this case */
1499 textRange.lpstrText = buffer;
1500 textRange.chrg.cpMin = 0;
1501 textRange.chrg.cpMax = -1;
1502 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1503 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1504 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1506 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1507 textRange.lpstrText = buffer;
1508 textRange.chrg.cpMin = -1;
1509 textRange.chrg.cpMax = 1;
1510 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1511 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1512 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1514 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1515 textRange.lpstrText = buffer;
1516 textRange.chrg.cpMin = 1;
1517 textRange.chrg.cpMax = -1;
1518 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1519 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1520 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1522 /* no end character is copied if cpMax - cpMin < 0 */
1523 textRange.lpstrText = buffer;
1524 textRange.chrg.cpMin = 5;
1525 textRange.chrg.cpMax = 5;
1526 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1527 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1528 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1530 /* cpMax of text length is used if cpMax > text length*/
1531 textRange.lpstrText = buffer;
1532 textRange.chrg.cpMin = 0;
1533 textRange.chrg.cpMax = 1000;
1534 result = SendMessage(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1535 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1536 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1538 DestroyWindow(hwndRichEdit);
1541 static void test_EM_GETSELTEXT(void)
1543 HWND hwndRichEdit = new_richedit(NULL);
1544 const char * text1 = "foo bar\r\nfoo bar";
1545 const char * text2 = "foo bar\rfoo bar";
1546 const char * expect = "bar\rfoo";
1547 char buffer[1024] = {0};
1550 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1552 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1553 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1554 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1555 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1557 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1559 SendMessage(hwndRichEdit, EM_SETSEL, 4, 11);
1560 result = SendMessage(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1561 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1562 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1564 DestroyWindow(hwndRichEdit);
1567 /* FIXME: need to test unimplemented options and robustly test wparam */
1568 static void test_EM_SETOPTIONS(void)
1571 static const char text[] = "Hello. My name is RichEdit!";
1572 char buffer[1024] = {0};
1573 DWORD dwStyle, options, oldOptions;
1574 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1575 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1576 ES_SELECTIONBAR|ES_VERTICAL;
1578 /* Test initial options. */
1579 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL, WS_POPUP,
1580 0, 0, 200, 60, NULL, NULL,
1581 hmoduleRichEdit, NULL);
1582 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1583 RICHEDIT_CLASS, (int) GetLastError());
1584 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1585 ok(options == 0, "Incorrect initial options %x\n", options);
1586 DestroyWindow(hwndRichEdit);
1588 hwndRichEdit = CreateWindow(RICHEDIT_CLASS, NULL,
1589 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1590 0, 0, 200, 60, NULL, NULL,
1591 hmoduleRichEdit, NULL);
1592 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1593 RICHEDIT_CLASS, (int) GetLastError());
1594 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1595 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1596 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1597 "Incorrect initial options %x\n", options);
1599 /* NEGATIVE TESTING - NO OPTIONS SET */
1600 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1601 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1603 /* testing no readonly by sending 'a' to the control*/
1604 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1605 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1607 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1608 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1610 /* READONLY - sending 'a' to the control */
1611 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
1612 SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1613 SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1614 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1615 ok(buffer[0]==text[0],
1616 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1618 /* EM_SETOPTIONS changes the window style, but changing the
1619 * window style does not change the options. */
1620 dwStyle = GetWindowLong(hwndRichEdit, GWL_STYLE);
1621 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1622 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1623 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1624 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1625 /* Confirm that the text is still read only. */
1626 SendMessage(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1627 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
1628 ok(buffer[0]==text[0],
1629 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1631 oldOptions = options;
1632 SetWindowLong(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1633 options = SendMessage(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1634 ok(options == oldOptions,
1635 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1637 DestroyWindow(hwndRichEdit);
1640 static int check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1642 CHARFORMAT2W text_format;
1643 text_format.cbSize = sizeof(text_format);
1644 SendMessage(hwnd, EM_SETSEL, sel_start, sel_end);
1645 SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format);
1646 return (text_format.dwEffects & CFE_LINK) ? 1 : 0;
1649 static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url)
1651 int link_present = 0;
1653 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1655 { /* control text is url; should get CFE_LINK */
1656 ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1660 ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1664 static HWND new_static_wnd(HWND parent) {
1665 return new_window("Static", 0, parent);
1668 static void test_EM_AUTOURLDETECT(void)
1670 /* DO NOT change the properties of the first two elements. To shorten the
1671 tests, all tests after WM_SETTEXT test just the first two elements -
1672 one non-URL and one URL */
1678 {"http://www.winehq.org", 1},
1679 {"http//winehq.org", 0},
1680 {"ww.winehq.org", 0},
1681 {"www.winehq.org", 1},
1682 {"ftp://192.168.1.1", 1},
1683 {"ftp//192.168.1.1", 0},
1684 {"mailto:your@email.com", 1},
1685 {"prospero:prosperoserver", 1},
1687 {"news:newserver", 1},
1688 {"wais:waisserver", 1}
1693 HWND hwndRichEdit, parent;
1695 /* All of the following should cause the URL to be detected */
1696 const char * templates_delim[] = {
1697 "This is some text with X on it",
1698 "This is some text with (X) on it",
1699 "This is some text with X\r on it",
1700 "This is some text with ---X--- on it",
1701 "This is some text with \"X\" on it",
1702 "This is some text with 'X' on it",
1703 "This is some text with 'X' on it",
1704 "This is some text with :X: on it",
1706 "This text ends with X",
1708 "This is some text with X) on it",
1709 "This is some text with X--- on it",
1710 "This is some text with X\" on it",
1711 "This is some text with X' on it",
1712 "This is some text with X: on it",
1714 "This is some text with (X on it",
1715 "This is some text with \rX on it",
1716 "This is some text with ---X on it",
1717 "This is some text with \"X on it",
1718 "This is some text with 'X on it",
1719 "This is some text with :X on it",
1721 /* None of these should cause the URL to be detected */
1722 const char * templates_non_delim[] = {
1723 "This is some text with |X| on it",
1724 "This is some text with *X* on it",
1725 "This is some text with /X/ on it",
1726 "This is some text with +X+ on it",
1727 "This is some text with %X% on it",
1728 "This is some text with #X# on it",
1729 "This is some text with @X@ on it",
1730 "This is some text with \\X\\ on it",
1731 "This is some text with |X on it",
1732 "This is some text with *X on it",
1733 "This is some text with /X on it",
1734 "This is some text with +X on it",
1735 "This is some text with %X on it",
1736 "This is some text with #X on it",
1737 "This is some text with @X on it",
1738 "This is some text with \\X on it",
1740 /* All of these cause the URL detection to be extended by one more byte,
1741 thus demonstrating that the tested character is considered as part
1743 const char * templates_xten_delim[] = {
1744 "This is some text with X| on it",
1745 "This is some text with X* on it",
1746 "This is some text with X/ on it",
1747 "This is some text with X+ on it",
1748 "This is some text with X% on it",
1749 "This is some text with X# on it",
1750 "This is some text with X@ on it",
1751 "This is some text with X\\ on it",
1755 parent = new_static_wnd(NULL);
1756 hwndRichEdit = new_richedit(parent);
1757 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1758 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1759 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1760 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1761 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1762 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1763 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1764 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1765 urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1766 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1767 /* for each url, check the text to see if CFE_LINK effect is present */
1768 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1770 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1772 check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text);
1774 /* Link detection should happen immediately upon WM_SETTEXT */
1775 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1776 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text);
1777 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1779 DestroyWindow(hwndRichEdit);
1781 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1782 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1783 hwndRichEdit = new_richedit(parent);
1785 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1790 at_pos = strchr(templates_delim[j], 'X');
1791 at_offset = at_pos - templates_delim[j];
1792 strncpy(buffer, templates_delim[j], at_offset);
1793 buffer[at_offset] = '\0';
1794 strcat(buffer, urls[i].text);
1795 strcat(buffer, templates_delim[j] + at_offset + 1);
1796 end_offset = at_offset + strlen(urls[i].text);
1798 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1801 /* This assumes no templates start with the URL itself, and that they
1802 have at least two characters before the URL text */
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1805 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1806 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1807 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1808 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1812 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1813 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1814 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1815 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1819 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1820 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1821 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1822 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1824 if (buffer[end_offset] != '\0')
1826 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1827 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1828 if (buffer[end_offset +1] != '\0')
1830 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1831 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1836 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1841 at_pos = strchr(templates_non_delim[j], 'X');
1842 at_offset = at_pos - templates_non_delim[j];
1843 strncpy(buffer, templates_non_delim[j], at_offset);
1844 buffer[at_offset] = '\0';
1845 strcat(buffer, urls[i].text);
1846 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1847 end_offset = at_offset + strlen(urls[i].text);
1849 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1850 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1852 /* This assumes no templates start with the URL itself, and that they
1853 have at least two characters before the URL text */
1854 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1855 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1856 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1857 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1858 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1859 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1861 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1862 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1863 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1864 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1865 if (buffer[end_offset] != '\0')
1867 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1868 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1869 if (buffer[end_offset +1] != '\0')
1871 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1872 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1877 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1882 at_pos = strchr(templates_xten_delim[j], 'X');
1883 at_offset = at_pos - templates_xten_delim[j];
1884 strncpy(buffer, templates_xten_delim[j], at_offset);
1885 buffer[at_offset] = '\0';
1886 strcat(buffer, urls[i].text);
1887 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1888 end_offset = at_offset + strlen(urls[i].text);
1890 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1891 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) buffer);
1893 /* This assumes no templates start with the URL itself, and that they
1894 have at least two characters before the URL text */
1895 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1896 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1904 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1905 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1906 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1907 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1908 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1909 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1913 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1914 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1915 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1916 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1917 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1918 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1920 if (buffer[end_offset +1] != '\0')
1922 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1923 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1924 if (buffer[end_offset +2] != '\0')
1926 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1927 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1932 DestroyWindow(hwndRichEdit);
1933 hwndRichEdit = NULL;
1936 /* Test detection of URLs within normal text - WM_CHAR case. */
1937 /* Test only the first two URL examples for brevity */
1938 for (i = 0; i < 2; i++) {
1939 hwndRichEdit = new_richedit(parent);
1941 /* Also for brevity, test only the first three delimiters */
1942 for (j = 0; j < 3; j++) {
1948 at_pos = strchr(templates_delim[j], 'X');
1949 at_offset = at_pos - templates_delim[j];
1950 end_offset = at_offset + strlen(urls[i].text);
1952 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1953 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
1954 for (u = 0; templates_delim[j][u]; u++) {
1955 if (templates_delim[j][u] == '\r') {
1956 simulate_typing_characters(hwndRichEdit, "\r");
1957 } else if (templates_delim[j][u] != 'X') {
1958 SendMessage(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1960 for (v = 0; urls[i].text[v]; v++) {
1961 SendMessage(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1965 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1967 /* This assumes no templates start with the URL itself, and that they
1968 have at least two characters before the URL text */
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1973 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1974 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1978 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1979 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1980 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1981 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1985 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1986 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1987 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1988 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1990 if (buffer[end_offset] != '\0')
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1994 if (buffer[end_offset +1] != '\0')
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2001 /* The following will insert a paragraph break after the first character
2002 of the URL candidate, thus breaking the URL. It is expected that the
2003 CFE_LINK attribute should break across both pieces of the URL */
2004 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
2005 simulate_typing_characters(hwndRichEdit, "\r");
2006 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2008 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2009 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2010 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2011 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2012 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2013 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2015 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2016 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2017 /* end_offset moved because of paragraph break */
2018 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2019 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2020 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2021 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2023 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2024 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2025 if (buffer[end_offset +2] != '\0')
2027 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2028 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2032 /* The following will remove the just-inserted paragraph break, thus
2033 restoring the URL */
2034 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2035 simulate_typing_characters(hwndRichEdit, "\b");
2036 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2038 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2039 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2042 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2043 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2047 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2048 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2049 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2050 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2054 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2055 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2056 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2057 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2059 if (buffer[end_offset] != '\0')
2061 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2062 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2063 if (buffer[end_offset +1] != '\0')
2065 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2066 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2070 DestroyWindow(hwndRichEdit);
2071 hwndRichEdit = NULL;
2074 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2075 /* Test just the first two URL examples for brevity */
2076 for (i = 0; i < 2; i++) {
2079 hwndRichEdit = new_richedit(parent);
2081 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2083 1) Set entire text, a la WM_SETTEXT
2084 2) Set a selection of the text to the URL
2085 3) Set a portion of the text at a time, which eventually results in
2087 All of them should give equivalent results
2090 /* Set entire text in one go, like WM_SETTEXT */
2091 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2096 st.codepage = CP_ACP;
2097 st.flags = ST_DEFAULT;
2099 at_pos = strchr(templates_delim[j], 'X');
2100 at_offset = at_pos - templates_delim[j];
2101 strncpy(buffer, templates_delim[j], at_offset);
2102 buffer[at_offset] = '\0';
2103 strcat(buffer, urls[i].text);
2104 strcat(buffer, templates_delim[j] + at_offset + 1);
2105 end_offset = at_offset + strlen(urls[i].text);
2107 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2108 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2110 /* This assumes no templates start with the URL itself, and that they
2111 have at least two characters before the URL text */
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2116 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2117 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2121 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2122 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2123 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2124 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2128 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2129 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2130 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2131 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2133 if (buffer[end_offset] != '\0')
2135 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2136 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2137 if (buffer[end_offset +1] != '\0')
2139 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2140 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2145 /* Set selection with X to the URL */
2146 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2151 at_pos = strchr(templates_delim[j], 'X');
2152 at_offset = at_pos - templates_delim[j];
2153 end_offset = at_offset + strlen(urls[i].text);
2155 st.codepage = CP_ACP;
2156 st.flags = ST_DEFAULT;
2157 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2158 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2159 st.flags = ST_SELECTION;
2160 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2161 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) urls[i].text);
2162 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2164 /* This assumes no templates start with the URL itself, and that they
2165 have at least two characters before the URL text */
2166 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2167 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2170 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2171 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2175 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2176 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2177 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2178 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2182 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2183 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2184 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2185 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2187 if (buffer[end_offset] != '\0')
2189 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2190 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2191 if (buffer[end_offset +1] != '\0')
2193 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2194 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2199 /* Set selection with X to the first character of the URL, then the rest */
2200 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2205 at_pos = strchr(templates_delim[j], 'X');
2206 at_offset = at_pos - templates_delim[j];
2207 end_offset = at_offset + strlen(urls[i].text);
2209 strcpy(buffer, "YY");
2210 buffer[0] = urls[i].text[0];
2212 st.codepage = CP_ACP;
2213 st.flags = ST_DEFAULT;
2214 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2215 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) templates_delim[j]);
2216 st.flags = ST_SELECTION;
2217 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2218 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM) buffer);
2219 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2220 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2221 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2223 /* This assumes no templates start with the URL itself, and that they
2224 have at least two characters before the URL text */
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2227 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2228 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2229 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2230 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2234 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2235 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2236 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2237 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2241 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2242 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2243 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2244 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2246 if (buffer[end_offset] != '\0')
2248 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2249 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2250 if (buffer[end_offset +1] != '\0')
2252 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2253 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2258 DestroyWindow(hwndRichEdit);
2259 hwndRichEdit = NULL;
2262 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2263 /* Test just the first two URL examples for brevity */
2264 for (i = 0; i < 2; i++) {
2265 hwndRichEdit = new_richedit(parent);
2267 /* Set selection with X to the URL */
2268 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2273 at_pos = strchr(templates_delim[j], 'X');
2274 at_offset = at_pos - templates_delim[j];
2275 end_offset = at_offset + strlen(urls[i].text);
2277 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2278 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2279 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2280 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) urls[i].text);
2281 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2283 /* This assumes no templates start with the URL itself, and that they
2284 have at least two characters before the URL text */
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2287 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2288 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2289 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2290 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2294 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2295 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2296 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2297 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2301 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2302 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2303 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2304 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2306 if (buffer[end_offset] != '\0')
2308 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2309 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2310 if (buffer[end_offset +1] != '\0')
2312 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2313 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2318 /* Set selection with X to the first character of the URL, then the rest */
2319 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2324 at_pos = strchr(templates_delim[j], 'X');
2325 at_offset = at_pos - templates_delim[j];
2326 end_offset = at_offset + strlen(urls[i].text);
2328 strcpy(buffer, "YY");
2329 buffer[0] = urls[i].text[0];
2331 SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2332 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) templates_delim[j]);
2333 SendMessage(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2334 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) buffer);
2335 SendMessage(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2336 SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2337 SendMessage(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2339 /* This assumes no templates start with the URL itself, and that they
2340 have at least two characters before the URL text */
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2343 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2344 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2345 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2346 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2350 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2351 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2352 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2353 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2357 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2358 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2359 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2360 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2362 if (buffer[end_offset] != '\0')
2364 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2365 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2366 if (buffer[end_offset +1] != '\0')
2368 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2369 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2374 DestroyWindow(hwndRichEdit);
2375 hwndRichEdit = NULL;
2378 DestroyWindow(parent);
2381 static void test_EM_SCROLL(void)
2384 int r; /* return value */
2385 int expr; /* expected return value */
2386 HWND hwndRichEdit = new_richedit(NULL);
2387 int y_before, y_after; /* units of lines of text */
2389 /* test a richedit box containing a single line of text */
2390 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */
2392 for (i = 0; i < 4; i++) {
2393 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2395 r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2396 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2397 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2398 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2399 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2400 "(i == %d)\n", y_after, i);
2404 * test a richedit box that will scroll. There are two general
2405 * cases: the case without any long lines and the case with a long
2408 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2410 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne");
2412 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2413 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2414 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2415 "LONG LINE \nb\nc\nd\ne");
2416 for (j = 0; j < 12; j++) /* reset scroll position to top */
2417 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2419 /* get first visible line */
2420 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2423 /* get new current first visible line */
2424 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2426 ok(((r & 0xffffff00) == 0x00010000) &&
2427 ((r & 0x000000ff) != 0x00000000),
2428 "EM_SCROLL page down didn't scroll by a small positive number of "
2429 "lines (r == 0x%08x)\n", r);
2430 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2431 "(line %d scrolled to line %d\n", y_before, y_after);
2435 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2436 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2437 ok(((r & 0xffffff00) == 0x0001ff00),
2438 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2439 "(r == 0x%08x)\n", r);
2440 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2441 "%d scrolled to line %d\n", y_before, y_after);
2445 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2447 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2449 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2450 "(r == 0x%08x)\n", r);
2451 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2452 "1 line (%d scrolled to %d)\n", y_before, y_after);
2456 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2458 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2460 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2461 "(r == 0x%08x)\n", r);
2462 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2463 "line (%d scrolled to %d)\n", y_before, y_after);
2467 r = SendMessage(hwndRichEdit, EM_SCROLL,
2468 SB_LINEUP, 0); /* lineup beyond top */
2470 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2473 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2474 ok(y_before == y_after,
2475 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2479 r = SendMessage(hwndRichEdit, EM_SCROLL,
2480 SB_PAGEUP, 0);/*page up beyond top */
2482 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2485 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2486 ok(y_before == y_after,
2487 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2489 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2490 SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2491 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2492 r = SendMessage(hwndRichEdit, EM_SCROLL,
2493 SB_PAGEDOWN, 0); /* page down beyond bot */
2494 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2497 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2498 ok(y_before == y_after,
2499 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2502 y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2503 r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2504 y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2507 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2508 ok(y_before == y_after,
2509 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2512 DestroyWindow(hwndRichEdit);
2515 static unsigned int recursionLevel = 0;
2516 static unsigned int WM_SIZE_recursionLevel = 0;
2517 static BOOL bailedOutOfRecursion = FALSE;
2518 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2520 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2524 if (bailedOutOfRecursion) return 0;
2525 if (recursionLevel >= 32) {
2526 bailedOutOfRecursion = TRUE;
2533 WM_SIZE_recursionLevel++;
2534 r = richeditProc(hwnd, message, wParam, lParam);
2535 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2536 ShowScrollBar(hwnd, SB_VERT, TRUE);
2537 WM_SIZE_recursionLevel--;
2540 r = richeditProc(hwnd, message, wParam, lParam);
2547 static void test_scrollbar_visibility(void)
2550 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2555 /* These tests show that richedit should temporarily refrain from automatically
2556 hiding or showing its scrollbars (vertical at least) when an explicit request
2557 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2558 Some applications depend on forced showing (when otherwise richedit would
2559 hide the vertical scrollbar) and are thrown on an endless recursive loop
2560 if richedit auto-hides the scrollbar again. Apparently they never heard of
2561 the ES_DISABLENOSCROLL style... */
2563 hwndRichEdit = new_richedit(NULL);
2565 /* Test default scrollbar visibility behavior */
2566 memset(&si, 0, sizeof(si));
2567 si.cbSize = sizeof(si);
2568 si.fMask = SIF_PAGE | SIF_RANGE;
2569 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2570 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2571 "Vertical scrollbar is visible, should be invisible.\n");
2572 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2573 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2574 si.nPage, si.nMin, si.nMax);
2576 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2577 memset(&si, 0, sizeof(si));
2578 si.cbSize = sizeof(si);
2579 si.fMask = SIF_PAGE | SIF_RANGE;
2580 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2581 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2582 "Vertical scrollbar is visible, should be invisible.\n");
2583 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2584 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2585 si.nPage, si.nMin, si.nMax);
2587 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2588 memset(&si, 0, sizeof(si));
2589 si.cbSize = sizeof(si);
2590 si.fMask = SIF_PAGE | SIF_RANGE;
2591 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2592 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2593 "Vertical scrollbar is invisible, should be visible.\n");
2594 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2595 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2596 si.nPage, si.nMin, si.nMax);
2598 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2599 even though it hides the scrollbar */
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 visible, should be invisible.\n");
2607 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2608 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2609 si.nPage, si.nMin, si.nMax);
2611 /* Setting non-scrolling text again does *not* reset scrollbar range */
2612 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2613 memset(&si, 0, sizeof(si));
2614 si.cbSize = sizeof(si);
2615 si.fMask = SIF_PAGE | SIF_RANGE;
2616 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2617 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2618 "Vertical scrollbar is visible, should be invisible.\n");
2619 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2620 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2621 si.nPage, si.nMin, si.nMax);
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 visible, should be invisible.\n");
2630 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2631 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2632 si.nPage, si.nMin, si.nMax);
2634 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2635 memset(&si, 0, sizeof(si));
2636 si.cbSize = sizeof(si);
2637 si.fMask = SIF_PAGE | SIF_RANGE;
2638 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2639 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2640 "Vertical scrollbar is visible, should be invisible.\n");
2641 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2642 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2643 si.nPage, si.nMin, si.nMax);
2645 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2651 "Vertical scrollbar is visible, should be invisible.\n");
2652 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2653 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2654 si.nPage, si.nMin, si.nMax);
2656 DestroyWindow(hwndRichEdit);
2658 /* Test again, with ES_DISABLENOSCROLL style */
2659 hwndRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2661 /* Test default scrollbar visibility behavior */
2662 memset(&si, 0, sizeof(si));
2663 si.cbSize = sizeof(si);
2664 si.fMask = SIF_PAGE | SIF_RANGE;
2665 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2666 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2667 "Vertical scrollbar is invisible, should be visible.\n");
2668 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2669 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2670 si.nPage, si.nMin, si.nMax);
2672 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2673 memset(&si, 0, sizeof(si));
2674 si.cbSize = sizeof(si);
2675 si.fMask = SIF_PAGE | SIF_RANGE;
2676 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2677 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2678 "Vertical scrollbar is invisible, should be visible.\n");
2679 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2680 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2681 si.nPage, si.nMin, si.nMax);
2683 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2684 memset(&si, 0, sizeof(si));
2685 si.cbSize = sizeof(si);
2686 si.fMask = SIF_PAGE | SIF_RANGE;
2687 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2688 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2689 "Vertical scrollbar is invisible, should be visible.\n");
2690 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2691 "reported page/range is %d (%d..%d)\n",
2692 si.nPage, si.nMin, si.nMax);
2694 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2695 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2696 memset(&si, 0, sizeof(si));
2697 si.cbSize = sizeof(si);
2698 si.fMask = SIF_PAGE | SIF_RANGE;
2699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2700 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2701 "Vertical scrollbar is invisible, should be visible.\n");
2702 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2703 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2704 si.nPage, si.nMin, si.nMax);
2706 /* Setting non-scrolling text again does *not* reset scrollbar range */
2707 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2708 memset(&si, 0, sizeof(si));
2709 si.cbSize = sizeof(si);
2710 si.fMask = SIF_PAGE | SIF_RANGE;
2711 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2712 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2713 "Vertical scrollbar is invisible, should be visible.\n");
2714 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2715 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2716 si.nPage, si.nMin, si.nMax);
2718 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2719 memset(&si, 0, sizeof(si));
2720 si.cbSize = sizeof(si);
2721 si.fMask = SIF_PAGE | SIF_RANGE;
2722 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2723 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2724 "Vertical scrollbar is invisible, should be visible.\n");
2725 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2726 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2727 si.nPage, si.nMin, si.nMax);
2729 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2730 memset(&si, 0, sizeof(si));
2731 si.cbSize = sizeof(si);
2732 si.fMask = SIF_PAGE | SIF_RANGE;
2733 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2734 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2735 "Vertical scrollbar is invisible, should be visible.\n");
2736 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2737 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2738 si.nPage, si.nMin, si.nMax);
2740 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2741 memset(&si, 0, sizeof(si));
2742 si.cbSize = sizeof(si);
2743 si.fMask = SIF_PAGE | SIF_RANGE;
2744 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2745 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2746 "Vertical scrollbar is invisible, should be visible.\n");
2747 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2748 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2749 si.nPage, si.nMin, si.nMax);
2751 DestroyWindow(hwndRichEdit);
2753 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2754 hwndRichEdit = new_richedit(NULL);
2756 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2757 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2758 memset(&si, 0, sizeof(si));
2759 si.cbSize = sizeof(si);
2760 si.fMask = SIF_PAGE | SIF_RANGE;
2761 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2762 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2763 "Vertical scrollbar is invisible, should be visible.\n");
2765 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2766 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2767 si.nPage, si.nMin, si.nMax);
2770 /* Ditto, see above */
2771 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2772 memset(&si, 0, sizeof(si));
2773 si.cbSize = sizeof(si);
2774 si.fMask = SIF_PAGE | SIF_RANGE;
2775 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2776 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2777 "Vertical scrollbar is invisible, should be visible.\n");
2779 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2780 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2781 si.nPage, si.nMin, si.nMax);
2784 /* Ditto, see above */
2785 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2786 memset(&si, 0, sizeof(si));
2787 si.cbSize = sizeof(si);
2788 si.fMask = SIF_PAGE | SIF_RANGE;
2789 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2790 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2791 "Vertical scrollbar is invisible, should be visible.\n");
2793 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2794 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2795 si.nPage, si.nMin, si.nMax);
2798 /* Ditto, see above */
2799 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2800 memset(&si, 0, sizeof(si));
2801 si.cbSize = sizeof(si);
2802 si.fMask = SIF_PAGE | SIF_RANGE;
2803 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2804 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2805 "Vertical scrollbar is invisible, should be visible.\n");
2807 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2808 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2809 si.nPage, si.nMin, si.nMax);
2812 /* Ditto, see above */
2813 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2814 memset(&si, 0, sizeof(si));
2815 si.cbSize = sizeof(si);
2816 si.fMask = SIF_PAGE | SIF_RANGE;
2817 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2818 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2819 "Vertical scrollbar is invisible, should be visible.\n");
2821 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2822 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2823 si.nPage, si.nMin, si.nMax);
2826 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2827 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2828 memset(&si, 0, sizeof(si));
2829 si.cbSize = sizeof(si);
2830 si.fMask = SIF_PAGE | SIF_RANGE;
2831 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2832 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2833 "Vertical scrollbar is visible, should be invisible.\n");
2834 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2835 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2836 si.nPage, si.nMin, si.nMax);
2838 DestroyWindow(hwndRichEdit);
2840 hwndRichEdit = new_richedit(NULL);
2842 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2843 memset(&si, 0, sizeof(si));
2844 si.cbSize = sizeof(si);
2845 si.fMask = SIF_PAGE | SIF_RANGE;
2846 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2847 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2848 "Vertical scrollbar is visible, should be invisible.\n");
2849 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2850 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2851 si.nPage, si.nMin, si.nMax);
2853 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2854 memset(&si, 0, sizeof(si));
2855 si.cbSize = sizeof(si);
2856 si.fMask = SIF_PAGE | SIF_RANGE;
2857 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2858 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2859 "Vertical scrollbar is visible, should be invisible.\n");
2860 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2861 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2862 si.nPage, si.nMin, si.nMax);
2864 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2865 memset(&si, 0, sizeof(si));
2866 si.cbSize = sizeof(si);
2867 si.fMask = SIF_PAGE | SIF_RANGE;
2868 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2869 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2870 "Vertical scrollbar is visible, should be invisible.\n");
2871 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2872 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2873 si.nPage, si.nMin, si.nMax);
2875 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2876 memset(&si, 0, sizeof(si));
2877 si.cbSize = sizeof(si);
2878 si.fMask = SIF_PAGE | SIF_RANGE;
2879 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2880 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2881 "Vertical scrollbar is visible, should be invisible.\n");
2882 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2883 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2884 si.nPage, si.nMin, si.nMax);
2886 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2887 memset(&si, 0, sizeof(si));
2888 si.cbSize = sizeof(si);
2889 si.fMask = SIF_PAGE | SIF_RANGE;
2890 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2891 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2892 "Vertical scrollbar is invisible, should be visible.\n");
2893 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2894 "reported page/range is %d (%d..%d)\n",
2895 si.nPage, si.nMin, si.nMax);
2897 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2898 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2899 memset(&si, 0, sizeof(si));
2900 si.cbSize = sizeof(si);
2901 si.fMask = SIF_PAGE | SIF_RANGE;
2902 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2903 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2904 "Vertical scrollbar is visible, should be invisible.\n");
2905 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2906 "reported page/range is %d (%d..%d)\n",
2907 si.nPage, si.nMin, si.nMax);
2909 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2910 memset(&si, 0, sizeof(si));
2911 si.cbSize = sizeof(si);
2912 si.fMask = SIF_PAGE | SIF_RANGE;
2913 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2914 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2915 "Vertical scrollbar is visible, should be invisible.\n");
2916 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2917 "reported page/range is %d (%d..%d)\n",
2918 si.nPage, si.nMin, si.nMax);
2920 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2921 EM_SCROLL will make visible any forcefully invisible scrollbar */
2922 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2923 memset(&si, 0, sizeof(si));
2924 si.cbSize = sizeof(si);
2925 si.fMask = SIF_PAGE | SIF_RANGE;
2926 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2927 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2928 "Vertical scrollbar is invisible, should be visible.\n");
2929 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2930 "reported page/range is %d (%d..%d)\n",
2931 si.nPage, si.nMin, si.nMax);
2933 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2934 memset(&si, 0, sizeof(si));
2935 si.cbSize = sizeof(si);
2936 si.fMask = SIF_PAGE | SIF_RANGE;
2937 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2938 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2939 "Vertical scrollbar is visible, should be invisible.\n");
2940 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2941 "reported page/range is %d (%d..%d)\n",
2942 si.nPage, si.nMin, si.nMax);
2944 /* Again, EM_SCROLL, with SB_LINEUP */
2945 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2946 memset(&si, 0, sizeof(si));
2947 si.cbSize = sizeof(si);
2948 si.fMask = SIF_PAGE | SIF_RANGE;
2949 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2950 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2951 "Vertical scrollbar is invisible, should be visible.\n");
2952 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2953 "reported page/range is %d (%d..%d)\n",
2954 si.nPage, si.nMin, si.nMax);
2956 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
2957 memset(&si, 0, sizeof(si));
2958 si.cbSize = sizeof(si);
2959 si.fMask = SIF_PAGE | SIF_RANGE;
2960 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2961 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2962 "Vertical scrollbar is visible, should be invisible.\n");
2963 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2964 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2965 si.nPage, si.nMin, si.nMax);
2967 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2968 memset(&si, 0, sizeof(si));
2969 si.cbSize = sizeof(si);
2970 si.fMask = SIF_PAGE | SIF_RANGE;
2971 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2972 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2973 "Vertical scrollbar is invisible, should be visible.\n");
2974 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2975 "reported page/range is %d (%d..%d)\n",
2976 si.nPage, si.nMin, si.nMax);
2978 DestroyWindow(hwndRichEdit);
2981 /* Test behavior with explicit visibility request, using SetWindowLong()() */
2982 hwndRichEdit = new_richedit(NULL);
2984 #define ENABLE_WS_VSCROLL(hwnd) \
2985 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2986 #define DISABLE_WS_VSCROLL(hwnd) \
2987 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2989 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2990 ENABLE_WS_VSCROLL(hwndRichEdit);
2991 memset(&si, 0, sizeof(si));
2992 si.cbSize = sizeof(si);
2993 si.fMask = SIF_PAGE | SIF_RANGE;
2994 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2995 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2996 "Vertical scrollbar is invisible, should be visible.\n");
2997 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2998 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2999 si.nPage, si.nMin, si.nMax);
3001 /* Ditto, see above */
3002 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3003 memset(&si, 0, sizeof(si));
3004 si.cbSize = sizeof(si);
3005 si.fMask = SIF_PAGE | SIF_RANGE;
3006 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3007 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3008 "Vertical scrollbar is invisible, should be visible.\n");
3009 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3010 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3011 si.nPage, si.nMin, si.nMax);
3013 /* Ditto, see above */
3014 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3015 memset(&si, 0, sizeof(si));
3016 si.cbSize = sizeof(si);
3017 si.fMask = SIF_PAGE | SIF_RANGE;
3018 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3019 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3020 "Vertical scrollbar is invisible, should be visible.\n");
3021 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3022 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3023 si.nPage, si.nMin, si.nMax);
3025 /* Ditto, see above */
3026 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
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 invisible, should be visible.\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 /* Ditto, see above */
3038 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3039 memset(&si, 0, sizeof(si));
3040 si.cbSize = sizeof(si);
3041 si.fMask = SIF_PAGE | SIF_RANGE;
3042 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3043 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3044 "Vertical scrollbar is invisible, should be visible.\n");
3045 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3046 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3047 si.nPage, si.nMin, si.nMax);
3049 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3050 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3051 memset(&si, 0, sizeof(si));
3052 si.cbSize = sizeof(si);
3053 si.fMask = SIF_PAGE | SIF_RANGE;
3054 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3055 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3056 "Vertical scrollbar is visible, should be invisible.\n");
3057 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3058 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3059 si.nPage, si.nMin, si.nMax);
3061 DestroyWindow(hwndRichEdit);
3063 hwndRichEdit = new_richedit(NULL);
3065 DISABLE_WS_VSCROLL(hwndRichEdit);
3066 memset(&si, 0, sizeof(si));
3067 si.cbSize = sizeof(si);
3068 si.fMask = SIF_PAGE | SIF_RANGE;
3069 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3070 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3071 "Vertical scrollbar is visible, should be invisible.\n");
3072 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3073 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3074 si.nPage, si.nMin, si.nMax);
3076 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3077 memset(&si, 0, sizeof(si));
3078 si.cbSize = sizeof(si);
3079 si.fMask = SIF_PAGE | SIF_RANGE;
3080 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3081 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3082 "Vertical scrollbar is visible, should be invisible.\n");
3083 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3084 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3085 si.nPage, si.nMin, si.nMax);
3087 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3088 memset(&si, 0, sizeof(si));
3089 si.cbSize = sizeof(si);
3090 si.fMask = SIF_PAGE | SIF_RANGE;
3091 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3092 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3093 "Vertical scrollbar is visible, should be invisible.\n");
3094 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3095 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3096 si.nPage, si.nMin, si.nMax);
3098 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3099 memset(&si, 0, sizeof(si));
3100 si.cbSize = sizeof(si);
3101 si.fMask = SIF_PAGE | SIF_RANGE;
3102 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3103 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3104 "Vertical scrollbar is visible, should be invisible.\n");
3105 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3106 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3107 si.nPage, si.nMin, si.nMax);
3109 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3110 memset(&si, 0, sizeof(si));
3111 si.cbSize = sizeof(si);
3112 si.fMask = SIF_PAGE | SIF_RANGE;
3113 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3114 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3115 "Vertical scrollbar is invisible, should be visible.\n");
3116 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3117 "reported page/range is %d (%d..%d)\n",
3118 si.nPage, si.nMin, si.nMax);
3120 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3121 DISABLE_WS_VSCROLL(hwndRichEdit);
3122 memset(&si, 0, sizeof(si));
3123 si.cbSize = sizeof(si);
3124 si.fMask = SIF_PAGE | SIF_RANGE;
3125 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3126 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3127 "Vertical scrollbar is visible, should be invisible.\n");
3128 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3129 "reported page/range is %d (%d..%d)\n",
3130 si.nPage, si.nMin, si.nMax);
3132 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
3133 memset(&si, 0, sizeof(si));
3134 si.cbSize = sizeof(si);
3135 si.fMask = SIF_PAGE | SIF_RANGE;
3136 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3137 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3138 "Vertical scrollbar is visible, should be invisible.\n");
3139 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3140 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3141 si.nPage, si.nMin, si.nMax);
3143 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3144 memset(&si, 0, sizeof(si));
3145 si.cbSize = sizeof(si);
3146 si.fMask = SIF_PAGE | SIF_RANGE;
3147 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3148 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3149 "Vertical scrollbar is invisible, should be visible.\n");
3150 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3151 "reported page/range is %d (%d..%d)\n",
3152 si.nPage, si.nMin, si.nMax);
3154 DISABLE_WS_VSCROLL(hwndRichEdit);
3155 memset(&si, 0, sizeof(si));
3156 si.cbSize = sizeof(si);
3157 si.fMask = SIF_PAGE | SIF_RANGE;
3158 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3159 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3160 "Vertical scrollbar is visible, should be invisible.\n");
3161 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3162 "reported page/range is %d (%d..%d)\n",
3163 si.nPage, si.nMin, si.nMax);
3165 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3166 EM_SCROLL will make visible any forcefully invisible scrollbar */
3167 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3168 memset(&si, 0, sizeof(si));
3169 si.cbSize = sizeof(si);
3170 si.fMask = SIF_PAGE | SIF_RANGE;
3171 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3172 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3173 "Vertical scrollbar is invisible, should be visible.\n");
3174 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3175 "reported page/range is %d (%d..%d)\n",
3176 si.nPage, si.nMin, si.nMax);
3178 DISABLE_WS_VSCROLL(hwndRichEdit);
3179 memset(&si, 0, sizeof(si));
3180 si.cbSize = sizeof(si);
3181 si.fMask = SIF_PAGE | SIF_RANGE;
3182 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3183 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3184 "Vertical scrollbar is visible, should be invisible.\n");
3185 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3186 "reported page/range is %d (%d..%d)\n",
3187 si.nPage, si.nMin, si.nMax);
3189 /* Again, EM_SCROLL, with SB_LINEUP */
3190 SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3191 memset(&si, 0, sizeof(si));
3192 si.cbSize = sizeof(si);
3193 si.fMask = SIF_PAGE | SIF_RANGE;
3194 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3195 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3196 "Vertical scrollbar is invisible, should be visible.\n");
3197 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3198 "reported page/range is %d (%d..%d)\n",
3199 si.nPage, si.nMin, si.nMax);
3201 DestroyWindow(hwndRichEdit);
3203 /* This window proc models what is going on with Corman Lisp 3.0.
3204 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3205 force the scrollbar into visibility. Recursion should NOT happen
3206 as a result of this action.
3208 r = GetClassInfoA(NULL, RICHEDIT_CLASS, &cls);
3210 richeditProc = cls.lpfnWndProc;
3211 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3212 cls.lpszClassName = "RicheditStupidOverride";
3213 if(!RegisterClassA(&cls)) assert(0);
3216 WM_SIZE_recursionLevel = 0;
3217 bailedOutOfRecursion = FALSE;
3218 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3219 ok(!bailedOutOfRecursion,
3220 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3223 WM_SIZE_recursionLevel = 0;
3224 bailedOutOfRecursion = FALSE;
3225 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3226 ok(!bailedOutOfRecursion,
3227 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3229 /* Unblock window in order to process WM_DESTROY */
3231 bailedOutOfRecursion = FALSE;
3232 WM_SIZE_recursionLevel = 0;
3233 DestroyWindow(hwndRichEdit);
3237 static void test_EM_SETUNDOLIMIT(void)
3239 /* cases we test for:
3240 * default behaviour - limiting at 100 undo's
3241 * undo disabled - setting a limit of 0
3242 * undo limited - undo limit set to some to some number, like 2
3243 * bad input - sending a negative number should default to 100 undo's */
3245 HWND hwndRichEdit = new_richedit(NULL);
3250 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
3253 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
3254 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3255 also, multiple pastes don't combine like WM_CHAR would */
3256 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3258 /* first case - check the default */
3259 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3260 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3261 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3262 for (i=0; i<100; i++) /* Undo 100 of them */
3263 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3264 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3265 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3267 /* second case - cannot undo */
3268 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3269 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3270 SendMessage(hwndRichEdit,
3271 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3272 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3273 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3275 /* third case - set it to an arbitrary number */
3276 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3277 SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3278 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3279 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3280 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
3281 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3282 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0),
3283 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3284 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3285 ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3286 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3287 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
3288 ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0),
3289 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3291 /* fourth case - setting negative numbers should default to 100 undos */
3292 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3293 result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3295 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3297 DestroyWindow(hwndRichEdit);
3300 static void test_ES_PASSWORD(void)
3302 /* This isn't hugely testable, so we're just going to run it through its paces */
3304 HWND hwndRichEdit = new_richedit(NULL);
3307 /* First, check the default of a regular control */
3308 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3310 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3312 /* Now, set it to something normal */
3313 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3314 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3316 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3318 /* Now, set it to something odd */
3319 SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3320 result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3322 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3323 DestroyWindow(hwndRichEdit);
3326 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3331 char** str = (char**)dwCookie;
3334 memcpy(*str, pbBuff, *pcb);
3340 static void test_WM_SETTEXT(void)
3342 HWND hwndRichEdit = new_richedit(NULL);
3343 const char * TestItem1 = "TestSomeText";
3344 const char * TestItem2 = "TestSomeText\r";
3345 const char * TestItem2_after = "TestSomeText\r\n";
3346 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3347 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3348 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3349 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3350 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3351 const char * TestItem5_after = "TestSomeText TestSomeText";
3352 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3353 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3354 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3355 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3357 const char rtftextA[] = "{\\rtf sometext}";
3358 const char urtftextA[] = "{\\urtf sometext}";
3359 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3360 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3361 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3363 char buf[1024] = {0};
3364 WCHAR bufW[1024] = {0};
3367 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3368 any solitary \r to be converted to \r\n on return. Properly paired
3369 \r\n are not affected. It also shows that the special sequence \r\r\n
3370 gets converted to a single space.
3373 #define TEST_SETTEXT(a, b) \
3374 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3375 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3376 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \
3377 ok (result == lstrlen(buf), \
3378 "WM_GETTEXT returned %ld instead of expected %u\n", \
3379 result, lstrlen(buf)); \
3380 result = strcmp(b, buf); \
3382 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3384 TEST_SETTEXT(TestItem1, TestItem1)
3385 TEST_SETTEXT(TestItem2, TestItem2_after)
3386 TEST_SETTEXT(TestItem3, TestItem3_after)
3387 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3388 TEST_SETTEXT(TestItem4, TestItem4_after)
3389 TEST_SETTEXT(TestItem5, TestItem5_after)
3390 TEST_SETTEXT(TestItem6, TestItem6_after)
3391 TEST_SETTEXT(TestItem7, TestItem7_after)
3393 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3394 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3395 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3396 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3397 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3398 DestroyWindow(hwndRichEdit);
3401 #define TEST_SETTEXTW(a, b) \
3402 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \
3403 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3404 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) bufW); \
3405 ok (result == lstrlenW(bufW), \
3406 "WM_GETTEXT returned %ld instead of expected %u\n", \
3407 result, lstrlenW(bufW)); \
3408 result = lstrcmpW(b, bufW); \
3409 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3411 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3412 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3413 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3414 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3415 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3416 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3417 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3418 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3419 DestroyWindow(hwndRichEdit);
3420 #undef TEST_SETTEXTW
3423 static void test_EM_STREAMOUT(void)
3425 HWND hwndRichEdit = new_richedit(NULL);
3428 char buf[1024] = {0};
3431 const char * TestItem1 = "TestSomeText";
3432 const char * TestItem2 = "TestSomeText\r";
3433 const char * TestItem3 = "TestSomeText\r\n";
3435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem1);
3437 es.dwCookie = (DWORD_PTR)&p;
3439 es.pfnCallback = test_WM_SETTEXT_esCallback;
3440 memset(buf, 0, sizeof(buf));
3441 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3443 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3444 ok(strcmp(buf, TestItem1) == 0,
3445 "streamed text different, got %s\n", buf);
3447 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem2);
3449 es.dwCookie = (DWORD_PTR)&p;
3451 es.pfnCallback = test_WM_SETTEXT_esCallback;
3452 memset(buf, 0, sizeof(buf));
3453 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3455 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3456 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3457 ok(strcmp(buf, TestItem3) == 0,
3458 "streamed text different from, got %s\n", buf);
3459 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem3);
3461 es.dwCookie = (DWORD_PTR)&p;
3463 es.pfnCallback = test_WM_SETTEXT_esCallback;
3464 memset(buf, 0, sizeof(buf));
3465 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3467 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3468 ok(strcmp(buf, TestItem3) == 0,
3469 "streamed text different, got %s\n", buf);
3471 DestroyWindow(hwndRichEdit);
3474 static void test_EM_STREAMOUT_FONTTBL(void)
3476 HWND hwndRichEdit = new_richedit(NULL);
3478 char buf[1024] = {0};
3483 const char * TestItem = "TestSomeText";
3485 /* fills in the richedit control with some text */
3486 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) TestItem);
3488 /* streams out the text in rtf format */
3490 es.dwCookie = (DWORD_PTR)&p;
3492 es.pfnCallback = test_WM_SETTEXT_esCallback;
3493 memset(buf, 0, sizeof(buf));
3494 SendMessage(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3496 /* scans for \fonttbl, error if not found */
3497 fontTbl = strstr(buf, "\\fonttbl");
3498 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3501 /* scans for terminating closing bracket */
3503 while(*fontTbl && brackCount)
3507 else if(*fontTbl == '}')
3511 /* checks whether closing bracket is ok */
3512 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3515 /* char before closing fonttbl block should be a closed bracket */
3517 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3519 /* char after fonttbl block should be a crlf */
3521 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3524 DestroyWindow(hwndRichEdit);
3528 static void test_EM_SETTEXTEX(void)
3530 HWND hwndRichEdit, parent;
3532 int sel_start, sel_end;
3535 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3537 'T', 'e', 'x', 't', 0};
3538 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3544 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3545 '\r','t','S','o','m','e','T','e','x','t',0};
3546 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3550 const char * TestItem2_after = "TestSomeText\r\n";
3551 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3554 '\r','\n','\r','\n', 0};
3555 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3559 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3563 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3566 '\r','\r','\n','\r',
3568 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3572 #define MAX_BUF_LEN 1024
3573 WCHAR buf[MAX_BUF_LEN];
3574 char bufACP[MAX_BUF_LEN];
3581 /* Test the scroll position with and without a parent window.
3583 * For some reason the scroll position is 0 after EM_SETTEXTEX
3584 * with the ST_SELECTION flag only when the control has a parent
3585 * window, even though the selection is at the end. */
3587 cls.lpfnWndProc = DefWindowProcA;
3590 cls.hInstance = GetModuleHandleA(0);
3592 cls.hCursor = LoadCursorA(0, IDC_ARROW);
3593 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3594 cls.lpszMenuName = NULL;
3595 cls.lpszClassName = "ParentTestClass";
3596 if(!RegisterClassA(&cls)) assert(0);
3598 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3599 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3600 ok (parent != 0, "Failed to create parent window\n");
3602 hwndRichEdit = CreateWindowEx(0,
3603 RICHEDIT_CLASS, NULL,
3604 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3605 0, 0, 200, 60, parent, NULL,
3606 hmoduleRichEdit, NULL);
3608 setText.codepage = CP_ACP;
3609 setText.flags = ST_SELECTION;
3610 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3611 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3612 si.cbSize = sizeof(si);
3614 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3615 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3616 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3617 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3618 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3620 DestroyWindow(parent);
3622 /* Test without a parent window */
3623 hwndRichEdit = new_richedit(NULL);
3624 setText.codepage = CP_ACP;
3625 setText.flags = ST_SELECTION;
3626 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3627 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3628 si.cbSize = sizeof(si);
3630 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3631 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3632 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3633 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3634 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3636 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3637 * but this time it is because the selection is at the beginning. */
3638 setText.codepage = CP_ACP;
3639 setText.flags = ST_DEFAULT;
3640 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3641 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3642 si.cbSize = sizeof(si);
3644 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3645 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3646 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3647 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3648 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3650 setText.codepage = 1200; /* no constant for unicode */
3651 getText.codepage = 1200; /* no constant for unicode */
3652 getText.cb = MAX_BUF_LEN;
3653 getText.flags = GT_DEFAULT;
3654 getText.lpDefaultChar = NULL;
3655 getText.lpUsedDefChar = NULL;
3658 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3659 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3660 ok(lstrcmpW(buf, TestItem1) == 0,
3661 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3663 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3664 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3666 setText.codepage = 1200; /* no constant for unicode */
3667 getText.codepage = 1200; /* no constant for unicode */
3668 getText.cb = MAX_BUF_LEN;
3669 getText.flags = GT_DEFAULT;
3670 getText.lpDefaultChar = NULL;
3671 getText.lpUsedDefChar = NULL;
3673 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2);
3674 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3675 ok(lstrcmpW(buf, TestItem2) == 0,
3676 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3678 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3679 SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3680 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3681 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3683 /* Baseline test for just-enough buffer space for string */
3684 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3685 getText.codepage = 1200; /* no constant for unicode */
3686 getText.flags = GT_DEFAULT;
3687 getText.lpDefaultChar = NULL;
3688 getText.lpUsedDefChar = NULL;
3689 memset(buf, 0, MAX_BUF_LEN);
3690 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3691 ok(lstrcmpW(buf, TestItem2) == 0,
3692 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3694 /* When there is enough space for one character, but not both, of the CRLF
3695 pair at the end of the string, the CR is not copied at all. That is,
3696 the caller must not see CRLF pairs truncated to CR at the end of the
3699 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3700 getText.codepage = 1200; /* no constant for unicode */
3701 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3702 getText.lpDefaultChar = NULL;
3703 getText.lpUsedDefChar = NULL;
3704 memset(buf, 0, MAX_BUF_LEN);
3705 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3706 ok(lstrcmpW(buf, TestItem1) == 0,
3707 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3710 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3711 setText.codepage = 1200; /* no constant for unicode */
3712 getText.codepage = 1200; /* no constant for unicode */
3713 getText.cb = MAX_BUF_LEN;
3714 getText.flags = GT_DEFAULT;
3715 getText.lpDefaultChar = NULL;
3716 getText.lpUsedDefChar = NULL;
3718 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3);
3719 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3720 ok(lstrcmpW(buf, TestItem3_after) == 0,
3721 "EM_SETTEXTEX did not convert properly\n");
3723 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3724 setText.codepage = 1200; /* no constant for unicode */
3725 getText.codepage = 1200; /* no constant for unicode */
3726 getText.cb = MAX_BUF_LEN;
3727 getText.flags = GT_DEFAULT;
3728 getText.lpDefaultChar = NULL;
3729 getText.lpUsedDefChar = NULL;
3731 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt);
3732 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3733 ok(lstrcmpW(buf, TestItem3_after) == 0,
3734 "EM_SETTEXTEX did not convert properly\n");
3736 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3737 setText.codepage = 1200; /* no constant for unicode */
3738 getText.codepage = 1200; /* no constant for unicode */
3739 getText.cb = MAX_BUF_LEN;
3740 getText.flags = GT_DEFAULT;
3741 getText.lpDefaultChar = NULL;
3742 getText.lpUsedDefChar = NULL;
3744 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4);
3745 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3746 ok(lstrcmpW(buf, TestItem4_after) == 0,
3747 "EM_SETTEXTEX did not convert properly\n");
3749 /* !ST_SELECTION && Unicode && !\rtf */
3750 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3751 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3754 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3755 ok(lstrlenW(buf) == 0,
3756 "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3758 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3760 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3761 /* select some text */
3764 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3765 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3766 setText.flags = ST_SELECTION;
3767 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3769 "EM_SETTEXTEX with NULL lParam to replace selection"
3770 " with no text should return 0. Got %i\n",
3773 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3775 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1);
3776 /* select some text */
3779 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3780 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3781 setText.flags = ST_SELECTION;
3782 result = SendMessage(hwndRichEdit, EM_SETTEXTEX,
3783 (WPARAM)&setText, (LPARAM) TestItem1);
3785 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3786 ok(result == lstrlenW(TestItem1),
3787 "EM_SETTEXTEX with NULL lParam to replace selection"
3788 " with no text should return 0. Got %i\n",
3790 ok(lstrlenW(buf) == 22,
3791 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3794 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3795 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3797 es.dwCookie = (DWORD_PTR)&p;
3799 es.pfnCallback = test_WM_SETTEXT_esCallback;
3800 memset(buf, 0, sizeof(buf));
3801 SendMessage(hwndRichEdit, EM_STREAMOUT,
3802 (WPARAM)(SF_RTF), (LPARAM)&es);
3803 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3805 /* !ST_SELECTION && !Unicode && \rtf */
3806 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3807 getText.codepage = 1200; /* no constant for unicode */
3808 getText.cb = MAX_BUF_LEN;
3809 getText.flags = GT_DEFAULT;
3810 getText.lpDefaultChar = NULL;
3811 getText.lpUsedDefChar = NULL;
3814 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3815 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3816 ok(lstrcmpW(buf, TestItem1) == 0,
3817 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3819 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3820 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3821 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3822 getText.codepage = CP_ACP;
3823 getText.cb = MAX_BUF_LEN;
3824 getText.flags = GT_DEFAULT;
3825 getText.lpDefaultChar = NULL;
3826 getText.lpUsedDefChar = NULL;
3828 setText.flags = ST_SELECTION;
3829 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
3830 result = SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) "{\\rtf not unicode}");
3831 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3832 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3833 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3835 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3836 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "TestSomeText"); /* TestItem1 */
3838 es.dwCookie = (DWORD_PTR)&p;
3840 es.pfnCallback = test_WM_SETTEXT_esCallback;
3841 memset(buf, 0, sizeof(buf));
3842 SendMessage(hwndRichEdit, EM_STREAMOUT,
3843 (WPARAM)(SF_RTF), (LPARAM)&es);
3844 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3846 /* select some text */
3849 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3851 /* ST_SELECTION && !Unicode && \rtf */
3852 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3853 getText.codepage = 1200; /* no constant for unicode */
3854 getText.cb = MAX_BUF_LEN;
3855 getText.flags = GT_DEFAULT;
3856 getText.lpDefaultChar = NULL;
3857 getText.lpUsedDefChar = NULL;
3859 setText.flags = ST_SELECTION;
3860 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) buf);
3861 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3862 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3864 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3865 setText.codepage = 1200; /* no constant for unicode */
3866 getText.codepage = CP_ACP;
3867 getText.cb = MAX_BUF_LEN;
3870 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); /* TestItem1 */
3871 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3873 /* select some text */
3876 SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr);
3878 /* ST_SELECTION && !Unicode && !\rtf */
3879 setText.codepage = CP_ACP;
3880 getText.codepage = 1200; /* no constant for unicode */
3881 getText.cb = MAX_BUF_LEN;
3882 getText.flags = GT_DEFAULT;
3883 getText.lpDefaultChar = NULL;
3884 getText.lpUsedDefChar = NULL;
3886 setText.flags = ST_SELECTION;
3887 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) bufACP);
3888 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
3889 ok(lstrcmpW(buf, TestItem1alt) == 0,
3890 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3891 " using ST_SELECTION and non-Unicode\n");
3893 /* Test setting text using rich text format */
3895 setText.codepage = CP_ACP;
3896 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3897 getText.codepage = CP_ACP;
3898 getText.cb = MAX_BUF_LEN;
3899 getText.flags = GT_DEFAULT;
3900 getText.lpDefaultChar = NULL;
3901 getText.lpUsedDefChar = NULL;
3902 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3903 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3906 setText.codepage = CP_ACP;
3907 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3908 getText.codepage = CP_ACP;
3909 getText.cb = MAX_BUF_LEN;
3910 getText.flags = GT_DEFAULT;
3911 getText.lpDefaultChar = NULL;
3912 getText.lpUsedDefChar = NULL;
3913 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) bufACP);
3914 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3916 DestroyWindow(hwndRichEdit);
3919 static void test_EM_LIMITTEXT(void)
3923 HWND hwndRichEdit = new_richedit(NULL);
3925 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3926 * about setting the length to -1 for multiline edit controls doesn't happen.
3929 /* Don't check default gettextlimit case. That's done in other tests */
3931 /* Set textlimit to 100 */
3932 SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0);
3933 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3935 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3937 /* Set textlimit to 0 */
3938 SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0);
3939 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3941 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3943 /* Set textlimit to -1 */
3944 SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0);
3945 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3947 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3949 /* Set textlimit to -2 */
3950 SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0);
3951 ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3953 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3955 DestroyWindow (hwndRichEdit);
3959 static void test_EM_EXLIMITTEXT(void)
3961 int i, selBegin, selEnd, len1, len2;
3963 char text[1024 + 1];
3964 char buffer[1024 + 1];
3965 int textlimit = 0; /* multiple of 100 */
3966 HWND hwndRichEdit = new_richedit(NULL);
3968 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3969 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
3972 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3973 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3975 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3978 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3979 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3981 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
3983 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
3984 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3985 /* default for WParam = 0 */
3986 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
3988 textlimit = sizeof(text)-1;
3989 memset(text, 'W', textlimit);
3990 text[sizeof(text)-1] = 0;
3991 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
3992 /* maxed out text */
3993 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
3995 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
3996 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
3997 len1 = selEnd - selBegin;
3999 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4000 SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4001 SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4002 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4003 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4004 len2 = selEnd - selBegin;
4007 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4010 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4011 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4012 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1);
4013 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4014 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4015 len1 = selEnd - selBegin;
4018 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4021 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4022 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4023 SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4024 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4025 SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4026 len2 = selEnd - selBegin;
4029 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4032 /* set text up to the limit, select all the text, then add a char */
4034 memset(text, 'W', textlimit);
4035 text[textlimit] = 0;
4036 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4037 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4038 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1);
4039 SendMessage(hwndRichEdit, WM_CHAR, 'A', 1);
4040 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4041 result = strcmp(buffer, "A");
4042 ok(0 == result, "got string = \"%s\"\n", buffer);
4044 /* WM_SETTEXT not limited */
4046 memset(text, 'W', textlimit);
4047 text[textlimit] = 0;
4048 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4049 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text);
4050 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4052 ok(10 == i, "expected 10 chars\n");
4053 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4054 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4056 /* try inserting more text at end */
4057 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4058 ok(0 == i, "WM_CHAR wasn't processed\n");
4059 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4061 ok(10 == i, "expected 10 chars, got %i\n", i);
4062 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4063 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4065 /* try inserting text at beginning */
4066 SendMessage(hwndRichEdit, EM_SETSEL, 0, 0);
4067 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4068 ok(0 == i, "WM_CHAR wasn't processed\n");
4069 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4071 ok(10 == i, "expected 10 chars, got %i\n", i);
4072 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4073 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4075 /* WM_CHAR is limited */
4077 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4078 SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4079 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4080 ok(0 == i, "WM_CHAR wasn't processed\n");
4081 i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4082 ok(0 == i, "WM_CHAR wasn't processed\n");
4083 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4085 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4087 DestroyWindow(hwndRichEdit);
4090 static void test_EM_GETLIMITTEXT(void)
4093 HWND hwndRichEdit = new_richedit(NULL);
4095 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4096 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4098 SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4099 i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4100 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4102 DestroyWindow(hwndRichEdit);
4105 static void test_WM_SETFONT(void)
4107 /* There is no invalid input or error conditions for this function.
4108 * NULL wParam and lParam just fall back to their default values
4109 * It should be noted that even if you use a gibberish name for your fonts
4110 * here, it will still work because the name is stored. They will display as
4111 * System, but will report their name to be whatever they were created as */
4113 HWND hwndRichEdit = new_richedit(NULL);
4114 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4115 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4116 FF_DONTCARE, "Marlett");
4117 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4118 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4119 FF_DONTCARE, "MS Sans Serif");
4120 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4121 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4122 FF_DONTCARE, "Courier");
4123 LOGFONTA sentLogFont;
4124 CHARFORMAT2A returnedCF2A;
4126 returnedCF2A.cbSize = sizeof(returnedCF2A);
4128 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x");
4129 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4130 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4132 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4133 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4134 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4135 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4137 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4138 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4139 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4140 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4141 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4142 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4144 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4145 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4146 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4147 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4148 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4149 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4151 /* This last test is special since we send in NULL. We clear the variables
4152 * and just compare to "System" instead of the sent in font name. */
4153 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4154 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4155 returnedCF2A.cbSize = sizeof(returnedCF2A);
4157 SendMessage(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4158 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A);
4159 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4160 ok (!strcmp("System",returnedCF2A.szFaceName),
4161 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4163 DestroyWindow(hwndRichEdit);
4167 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4172 const char** str = (const char**)dwCookie;
4173 int size = strlen(*str);
4174 if(size > 3) /* let's make it piecemeal for fun */
4181 memcpy(pbBuff, *str, *pcb);
4187 static void test_EM_GETMODIFY(void)
4189 HWND hwndRichEdit = new_richedit(NULL);
4192 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4194 'T', 'e', 'x', 't', 0};
4195 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4197 'O', 't', 'h', 'e', 'r',
4198 'T', 'e', 'x', 't', 0};
4199 const char* streamText = "hello world";
4204 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4205 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4206 FF_DONTCARE, "Courier");
4208 setText.codepage = 1200; /* no constant for unicode */
4209 setText.flags = ST_KEEPUNDO;
4212 /* modify flag shouldn't be set when richedit is first created */
4213 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4215 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4217 /* setting modify flag should actually set it */
4218 SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4219 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4221 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4223 /* clearing modify flag should actually clear it */
4224 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4225 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4227 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4229 /* setting font doesn't change modify flag */
4230 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4231 SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4232 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4234 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4236 /* setting text should set modify flag */
4237 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4238 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4239 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4241 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4243 /* undo previous text doesn't reset modify flag */
4244 SendMessage(hwndRichEdit, WM_UNDO, 0, 0);
4245 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4247 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4249 /* set text with no flag to keep undo stack should not set modify flag */
4250 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4252 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4253 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4255 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4257 /* WM_SETTEXT doesn't modify */
4258 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4259 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4260 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4262 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4264 /* clear the text */
4265 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4266 SendMessage(hwndRichEdit, WM_CLEAR, 0, 0);
4267 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4269 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4272 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4273 SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4274 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4275 SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4276 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4278 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4280 /* copy/paste text 1 */
4281 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4282 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4283 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4284 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4285 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4287 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4289 /* copy/paste text 2 */
4290 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4291 SendMessage(hwndRichEdit, EM_SETSEL, 0, 2);
4292 SendMessage(hwndRichEdit, WM_COPY, 0, 0);
4293 SendMessage(hwndRichEdit, EM_SETSEL, 0, 3);
4294 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4295 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4297 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4300 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4301 SendMessage(hwndRichEdit, EM_SETSEL, 0, 1);
4302 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4303 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4305 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4308 SendMessage(hwndRichEdit, WM_CHAR, 'A', 0);
4309 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4310 SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4311 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4313 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4315 /* set char format */
4316 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4317 cf2.cbSize = sizeof(CHARFORMAT2);
4318 SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4319 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4320 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4321 SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4322 result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4323 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4324 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4326 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4328 /* set para format */
4329 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4330 pf2.cbSize = sizeof(PARAFORMAT2);
4331 SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0,
4333 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4334 pf2.wAlignment = PFA_RIGHT;
4335 SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2);
4336 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4338 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4341 SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4342 es.dwCookie = (DWORD_PTR)&streamText;
4344 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4345 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4346 result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0);
4348 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4350 DestroyWindow(hwndRichEdit);
4356 LRESULT expected_retval;
4357 int expected_getsel_start;
4358 int expected_getsel_end;
4359 int _getsel_todo_wine;
4362 const struct exsetsel_s exsetsel_tests[] = {
4364 {5, 10, 10, 5, 10, 0},
4365 {15, 17, 17, 15, 17, 0},
4366 /* test cpMax > strlen() */
4367 {0, 100, 18, 0, 18, 1},
4368 /* test cpMin == cpMax */
4370 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4371 {-1, 0, 5, 5, 5, 0},
4372 {-1, 17, 5, 5, 5, 0},
4373 {-1, 18, 5, 5, 5, 0},
4374 /* test cpMin < 0 && cpMax < 0 */
4375 {-1, -1, 17, 17, 17, 0},
4376 {-4, -5, 17, 17, 17, 0},
4377 /* test cMin >=0 && cpMax < 0 (bug 6814) */
4378 {0, -1, 18, 0, 18, 1},
4379 {17, -5, 18, 17, 18, 1},
4380 {18, -3, 17, 17, 17, 0},
4381 /* test if cpMin > cpMax */
4382 {15, 19, 18, 15, 18, 1},
4383 {19, 15, 18, 15, 18, 1}
4386 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4391 cr.cpMin = setsel->min;
4392 cr.cpMax = setsel->max;
4393 result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr);
4395 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4397 SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
4399 if (setsel->_getsel_todo_wine) {
4401 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);
4404 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);
4408 static void test_EM_EXSETSEL(void)
4410 HWND hwndRichEdit = new_richedit(NULL);
4412 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4414 /* sending some text to the window */
4415 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4416 /* 01234567890123456*/
4419 for (i = 0; i < num_tests; i++) {
4420 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4423 DestroyWindow(hwndRichEdit);
4426 static void test_EM_REPLACESEL(int redraw)
4428 HWND hwndRichEdit = new_richedit(NULL);
4429 char buffer[1024] = {0};
4434 /* sending some text to the window */
4435 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection");
4436 /* 01234567890123456*/
4439 /* FIXME add more tests */
4440 SendMessage(hwndRichEdit, EM_SETSEL, 7, 17);
4441 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, 0);
4442 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4443 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4444 r = strcmp(buffer, "testing");
4445 ok(0 == r, "expected %d, got %d\n", 0, r);
4447 DestroyWindow(hwndRichEdit);
4449 hwndRichEdit = new_richedit(NULL);
4451 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4452 SendMessage(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4454 /* Test behavior with carriage returns and newlines */
4455 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4456 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1");
4457 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4458 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4459 r = strcmp(buffer, "RichEdit1");
4460 ok(0 == r, "expected %d, got %d\n", 0, r);
4462 getText.codepage = CP_ACP;
4463 getText.flags = GT_DEFAULT;
4464 getText.lpDefaultChar = NULL;
4465 getText.lpUsedDefChar = NULL;
4466 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4467 ok(strcmp(buffer, "RichEdit1") == 0,
4468 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4470 /* Test number of lines reported after EM_REPLACESEL */
4471 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4472 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4474 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4475 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r");
4476 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4477 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4478 r = strcmp(buffer, "RichEdit1\r\n");
4479 ok(0 == r, "expected %d, got %d\n", 0, r);
4481 getText.codepage = CP_ACP;
4482 getText.flags = GT_DEFAULT;
4483 getText.lpDefaultChar = NULL;
4484 getText.lpUsedDefChar = NULL;
4485 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4486 ok(strcmp(buffer, "RichEdit1\r") == 0,
4487 "EM_GETTEXTEX returned incorrect string\n");
4489 /* Test number of lines reported after EM_REPLACESEL */
4490 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4491 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4493 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4494 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n");
4495 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4497 /* Test number of lines reported after EM_REPLACESEL */
4498 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4499 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4501 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4502 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4503 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4504 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4506 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4507 r = strcmp(buffer, "RichEdit1\r\n");
4508 ok(0 == r, "expected %d, got %d\n", 0, r);
4510 getText.codepage = CP_ACP;
4511 getText.flags = GT_DEFAULT;
4512 getText.lpDefaultChar = NULL;
4513 getText.lpUsedDefChar = NULL;
4514 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4515 ok(strcmp(buffer, "RichEdit1\r") == 0,
4516 "EM_GETTEXTEX returned incorrect string\n");
4518 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4519 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4520 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4521 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4523 /* The following tests show that richedit should handle the special \r\r\n
4524 sequence by turning it into a single space on insertion. However,
4525 EM_REPLACESEL on WinXP returns the number of characters in the original
4529 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4530 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r");
4531 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4532 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4533 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4534 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4535 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4537 /* Test the actual string */
4539 getText.codepage = CP_ACP;
4540 getText.flags = GT_DEFAULT;
4541 getText.lpDefaultChar = NULL;
4542 getText.lpUsedDefChar = NULL;
4543 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4544 ok(strcmp(buffer, "\r\r") == 0,
4545 "EM_GETTEXTEX returned incorrect string\n");
4547 /* Test number of lines reported after EM_REPLACESEL */
4548 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4549 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4551 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4552 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n");
4553 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4554 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4555 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4556 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4557 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4559 /* Test the actual string */
4561 getText.codepage = CP_ACP;
4562 getText.flags = GT_DEFAULT;
4563 getText.lpDefaultChar = NULL;
4564 getText.lpUsedDefChar = NULL;
4565 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4566 ok(strcmp(buffer, " ") == 0,
4567 "EM_GETTEXTEX returned incorrect string\n");
4569 /* Test number of lines reported after EM_REPLACESEL */
4570 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4571 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4573 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4574 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r");
4575 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4576 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4577 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4578 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4579 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4581 /* Test the actual string */
4583 getText.codepage = CP_ACP;
4584 getText.flags = GT_DEFAULT;
4585 getText.lpDefaultChar = NULL;
4586 getText.lpUsedDefChar = NULL;
4587 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4588 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4589 "EM_GETTEXTEX returned incorrect string\n");
4591 /* Test number of lines reported after EM_REPLACESEL */
4592 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4593 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4595 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4596 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n");
4597 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4598 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4599 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4600 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4601 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4603 /* Test the actual string */
4605 getText.codepage = CP_ACP;
4606 getText.flags = GT_DEFAULT;
4607 getText.lpDefaultChar = NULL;
4608 getText.lpUsedDefChar = NULL;
4609 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4610 ok(strcmp(buffer, " \r") == 0,
4611 "EM_GETTEXTEX returned incorrect string\n");
4613 /* Test number of lines reported after EM_REPLACESEL */
4614 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4615 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4617 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4618 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r");
4619 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4620 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4621 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4622 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4623 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4625 /* Test the actual string */
4627 getText.codepage = CP_ACP;
4628 getText.flags = GT_DEFAULT;
4629 getText.lpDefaultChar = NULL;
4630 getText.lpUsedDefChar = NULL;
4631 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4632 ok(strcmp(buffer, " \r\r") == 0,
4633 "EM_GETTEXTEX returned incorrect string\n");
4635 /* Test number of lines reported after EM_REPLACESEL */
4636 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4637 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4639 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4640 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r");
4641 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4642 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4643 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4644 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4645 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4647 /* Test the actual string */
4649 getText.codepage = CP_ACP;
4650 getText.flags = GT_DEFAULT;
4651 getText.lpDefaultChar = NULL;
4652 getText.lpUsedDefChar = NULL;
4653 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4654 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4655 "EM_GETTEXTEX returned incorrect string\n");
4657 /* Test number of lines reported after EM_REPLACESEL */
4658 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4659 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4661 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4662 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n");
4663 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4664 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4665 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4666 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4667 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4669 /* Test the actual string */
4671 getText.codepage = CP_ACP;
4672 getText.flags = GT_DEFAULT;
4673 getText.lpDefaultChar = NULL;
4674 getText.lpUsedDefChar = NULL;
4675 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4676 ok(strcmp(buffer, "\r\r") == 0,
4677 "EM_GETTEXTEX returned incorrect string\n");
4679 /* Test number of lines reported after EM_REPLACESEL */
4680 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4681 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4683 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4684 r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n");
4685 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4686 r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4687 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4688 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4689 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4691 /* Test the actual string */
4693 getText.codepage = CP_ACP;
4694 getText.flags = GT_DEFAULT;
4695 getText.lpDefaultChar = NULL;
4696 getText.lpUsedDefChar = NULL;
4697 SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer);
4698 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4699 "EM_GETTEXTEX returned incorrect string\n");
4701 /* Test number of lines reported after EM_REPLACESEL */
4702 r = SendMessage(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4703 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4706 /* This is needed to avoid interferring with keybd_event calls
4707 * on other tests that simulate keyboard events. */
4708 SendMessage(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4710 DestroyWindow(hwndRichEdit);
4713 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4714 * to test the state of the modifiers (Ctrl/Alt/Shift).
4716 * Therefore Ctrl-<key> keystrokes need to be simulated with
4717 * keybd_event or by using SetKeyboardState to set the modifiers
4718 * and SendMessage to simulate the keystrokes.
4720 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4723 hold_key(VK_CONTROL);
4724 result = SendMessage(hwnd, WM_KEYDOWN, key, 1);
4725 release_key(VK_CONTROL);
4729 static void test_WM_PASTE(void)
4732 char buffer[1024] = {0};
4733 const char* text1 = "testing paste\r";
4734 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4735 const char* text1_after = "testing paste\r\n";
4736 const char* text2 = "testing paste\r\rtesting paste";
4737 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4738 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4739 HWND hwndRichEdit = new_richedit(NULL);
4741 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4742 SendMessage(hwndRichEdit, EM_SETSEL, 0, 14);
4744 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4745 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4746 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4747 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4748 /* Pasted text should be visible at this step */
4749 result = strcmp(text1_step1, buffer);
4751 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4753 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4754 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4755 /* Text should be the same as before (except for \r -> \r\n conversion) */
4756 result = strcmp(text1_after, buffer);
4758 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4760 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2);
4761 SendMessage(hwndRichEdit, EM_SETSEL, 8, 13);
4762 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4763 SendMessage(hwndRichEdit, EM_SETSEL, 14, 14);
4764 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4765 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4766 /* Pasted text should be visible at this step */
4767 result = strcmp(text3, buffer);
4769 "test paste: strcmp = %i\n", result);
4770 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4771 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4772 /* Text should be the same as before (except for \r -> \r\n conversion) */
4773 result = strcmp(text2_after, buffer);
4775 "test paste: strcmp = %i\n", result);
4776 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4777 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4778 /* Text should revert to post-paste state */
4779 result = strcmp(buffer,text3);
4781 "test paste: strcmp = %i\n", result);
4783 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4784 /* Send WM_CHAR to simulates Ctrl-V */
4785 SendMessage(hwndRichEdit, WM_CHAR, 22,
4786 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4787 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4788 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4789 result = strcmp(buffer,"");
4791 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4793 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4794 * with SetKeyboard state. */
4796 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4797 /* Simulates paste (Ctrl-V) */
4798 hold_key(VK_CONTROL);
4799 SendMessage(hwndRichEdit, WM_KEYDOWN, 'V',
4800 (MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16) | 1);
4801 release_key(VK_CONTROL);
4802 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4803 result = strcmp(buffer,"paste");
4805 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4807 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1);
4808 SendMessage(hwndRichEdit, EM_SETSEL, 0, 7);
4809 /* Simulates copy (Ctrl-C) */
4810 hold_key(VK_CONTROL);
4811 SendMessage(hwndRichEdit, WM_KEYDOWN, 'C',
4812 (MapVirtualKey('C', MAPVK_VK_TO_VSC) << 16) | 1);
4813 release_key(VK_CONTROL);
4814 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4815 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4816 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4817 result = strcmp(buffer,"testing");
4819 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4821 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4822 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "cut");
4823 /* Simulates select all (Ctrl-A) */
4824 hold_key(VK_CONTROL);
4825 SendMessage(hwndRichEdit, WM_KEYDOWN, 'A',
4826 (MapVirtualKey('A', MAPVK_VK_TO_VSC) << 16) | 1);
4827 /* Simulates select cut (Ctrl-X) */
4828 SendMessage(hwndRichEdit, WM_KEYDOWN, 'X',
4829 (MapVirtualKey('X', MAPVK_VK_TO_VSC) << 16) | 1);
4830 release_key(VK_CONTROL);
4831 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4832 result = strcmp(buffer,"");
4834 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4835 SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
4836 SendMessage(hwndRichEdit, WM_PASTE, 0, 0);
4837 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4838 result = strcmp(buffer,"cut\r\n");
4839 todo_wine ok(result == 0,
4840 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4841 /* Simulates undo (Ctrl-Z) */
4842 hold_key(VK_CONTROL);
4843 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Z',
4844 (MapVirtualKey('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4845 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4846 result = strcmp(buffer,"");
4848 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4849 /* Simulates redo (Ctrl-Y) */
4850 SendMessage(hwndRichEdit, WM_KEYDOWN, 'Y',
4851 (MapVirtualKey('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4852 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
4853 result = strcmp(buffer,"cut\r\n");
4854 todo_wine ok(result == 0,
4855 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4856 release_key(VK_CONTROL);
4858 DestroyWindow(hwndRichEdit);
4861 static void test_EM_FORMATRANGE(void)
4863 int r, i, tpp_x, tpp_y;
4865 HWND hwndRichEdit = new_richedit(NULL);
4867 BOOL skip_non_english;
4868 static const struct {
4869 const char *string; /* The string */
4870 int first; /* First 'pagebreak', 0 for don't care */
4871 int second; /* Second 'pagebreak', 0 for don't care */
4873 {"WINE wine", 0, 0},
4874 {"WINE wineWine", 0, 0},
4875 {"WINE\r\nwine\r\nwine", 5, 10},
4876 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4877 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4880 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4881 if (skip_non_english)
4882 skip("Skipping some tests on non-English platform\n");
4884 hdc = GetDC(hwndRichEdit);
4885 ok(hdc != NULL, "Could not get HDC\n");
4887 /* Calculate the twips per pixel */
4888 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4889 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4891 /* Test the simple case where all the text fits in the page rect. */
4892 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4893 fr.hdc = fr.hdcTarget = hdc;
4894 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4895 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4896 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4899 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4900 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4902 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4903 fr.rc.bottom = fr.rcPage.bottom;
4904 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4905 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4907 SendMessage(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4909 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4911 GETTEXTLENGTHEX gtl;
4915 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) fmtstrings[i].string);
4917 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4918 gtl.codepage = CP_ACP;
4919 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
4921 /* Get some size information for the string */
4922 GetTextExtentPoint32(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4924 /* Define the box to be half the width needed and a bit larger than the height.
4925 * Changes to the width means we have at least 2 pages. Changes to the height
4926 * is done so we can check the changing of fr.rc.bottom.
4928 fr.hdc = fr.hdcTarget = hdc;
4929 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4930 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4931 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4933 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4935 ok(r == len, "Expected %d, got %d\n", len, r);
4938 /* We know that the page can't hold the full string. See how many characters
4939 * are on the first one
4943 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4945 if (! skip_non_english)
4946 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4948 if (fmtstrings[i].first)
4950 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4953 ok(r < len, "Expected < %d, got %d\n", len, r);
4955 /* Do another page */
4957 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr);
4958 if (fmtstrings[i].second)
4960 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4962 else if (! skip_non_english)
4963 ok (r < len, "Expected < %d, got %d\n", len, r);
4965 /* There is at least on more page, but we don't care */
4967 r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4969 ok(r == len, "Expected %d, got %d\n", len, r);
4973 ReleaseDC(NULL, hdc);
4974 DestroyWindow(hwndRichEdit);
4977 static int nCallbackCount = 0;
4979 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
4982 const char text[] = {'t','e','s','t'};
4984 if (sizeof(text) <= cb)
4986 if ((int)dwCookie != nCallbackCount)
4992 memcpy (pbBuff, text, sizeof(text));
4993 *pcb = sizeof(text);
5000 return 1; /* indicates callback failed */
5003 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5008 const char** str = (const char**)dwCookie;
5009 int size = strlen(*str);
5015 memcpy(pbBuff, *str, *pcb);
5021 struct StringWithLength {
5026 /* This callback is used to handled the null characters in a string. */
5027 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5032 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5033 int size = str->length;
5039 memcpy(pbBuff, str->buffer, *pcb);
5040 str->buffer += *pcb;
5041 str->length -= *pcb;
5046 static void test_EM_STREAMIN(void)
5048 HWND hwndRichEdit = new_richedit(NULL);
5051 char buffer[1024] = {0};
5053 const char * streamText0 = "{\\rtf1 TestSomeText}";
5054 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5055 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5057 const char * streamText1 =
5058 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5059 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5062 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5063 const char * streamText2 =
5064 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5065 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5066 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5067 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5068 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5069 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5070 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5072 const char * streamText3 = "RichEdit1";
5074 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5076 const char * streamText4 =
5077 "This text just needs to be long enough to cause run to be split onto "
5078 "two separate lines and make sure the null terminating character is "
5079 "handled properly.\0";
5080 int length4 = strlen(streamText4) + 1;
5081 struct StringWithLength cookieForStream4 = {
5083 (char *)streamText4,
5086 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5087 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5088 struct StringWithLength cookieForStream5 = {
5089 sizeof(streamText5),
5090 (char *)streamText5,
5093 /* Minimal test without \par at the end */
5094 es.dwCookie = (DWORD_PTR)&streamText0;
5096 es.pfnCallback = test_EM_STREAMIN_esCallback;
5097 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5098 ok(result == 12, "got %ld, expected %d\n", result, 12);
5100 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5102 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5103 result = strcmp (buffer,"TestSomeText");
5105 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5106 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5108 /* Native richedit 2.0 ignores last \par */
5109 es.dwCookie = (DWORD_PTR)&streamText0a;
5111 es.pfnCallback = test_EM_STREAMIN_esCallback;
5112 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5113 ok(result == 12, "got %ld, expected %d\n", result, 12);
5115 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5117 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5118 result = strcmp (buffer,"TestSomeText");
5120 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5121 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5123 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5124 es.dwCookie = (DWORD_PTR)&streamText0b;
5126 es.pfnCallback = test_EM_STREAMIN_esCallback;
5127 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5128 ok(result == 13, "got %ld, expected %d\n", result, 13);
5130 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5132 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5133 result = strcmp (buffer,"TestSomeText\r\n");
5135 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5136 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5138 es.dwCookie = (DWORD_PTR)&streamText1;
5140 es.pfnCallback = test_EM_STREAMIN_esCallback;
5141 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5142 ok(result == 12, "got %ld, expected %d\n", result, 12);
5144 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5146 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5147 result = strcmp (buffer,"TestSomeText");
5149 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5150 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5152 es.dwCookie = (DWORD_PTR)&streamText2;
5154 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5155 ok(result == 0, "got %ld, expected %d\n", result, 0);
5157 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5159 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5160 ok (strlen(buffer) == 0,
5161 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5162 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5164 es.dwCookie = (DWORD_PTR)&streamText3;
5166 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5167 ok(result == 0, "got %ld, expected %d\n", result, 0);
5169 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5171 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5172 ok (strlen(buffer) == 0,
5173 "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5174 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5176 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5178 es.pfnCallback = test_EM_STREAMIN_esCallback;
5179 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5180 ok(result == 18, "got %ld, expected %d\n", result, 18);
5182 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5184 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5185 result = strcmp (buffer,"TestUTF8WithBOM");
5187 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5188 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5190 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5192 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5193 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5194 ok(result == length4, "got %ld, expected %d\n", result, length4);
5196 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5197 ok (result == length4,
5198 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5199 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5201 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5203 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5204 result = SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5205 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5207 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5208 ok (result == length5,
5209 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5210 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5212 DestroyWindow(hwndRichEdit);
5215 static void test_EM_StreamIn_Undo(void)
5217 /* The purpose of this test is to determine when a EM_StreamIn should be
5218 * undoable. This is important because WM_PASTE currently uses StreamIn and
5219 * pasting should always be undoable but streaming isn't always.
5222 * StreamIn plain text without SFF_SELECTION.
5223 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5224 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5225 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5226 * Feel free to add tests for other text modes or StreamIn things.
5230 HWND hwndRichEdit = new_richedit(NULL);
5233 char buffer[1024] = {0};
5234 const char randomtext[] = "Some text";
5236 es.pfnCallback = EditStreamCallback;
5238 /* StreamIn, no SFF_SELECTION */
5239 es.dwCookie = nCallbackCount;
5240 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5241 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5242 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5243 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5244 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5245 result = strcmp (buffer,"test");
5247 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5249 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5250 ok (result == FALSE,
5251 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5253 /* StreamIn, SFF_SELECTION, but nothing selected */
5254 es.dwCookie = nCallbackCount;
5255 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5256 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5257 SendMessage(hwndRichEdit, EM_SETSEL,0,0);
5258 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5259 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5260 result = strcmp (buffer,"testSome text");
5262 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5264 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5266 "EM_STREAMIN with SFF_SELECTION but no selection set "
5267 "should create an undo\n");
5269 /* StreamIn, SFF_SELECTION, with a selection */
5270 es.dwCookie = nCallbackCount;
5271 SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5272 SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext);
5273 SendMessage(hwndRichEdit, EM_SETSEL,4,5);
5274 SendMessage(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5275 SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
5276 result = strcmp (buffer,"Sometesttext");
5278 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5280 result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0);
5282 "EM_STREAMIN with SFF_SELECTION and selection set "
5283 "should create an undo\n");
5285 DestroyWindow(hwndRichEdit);
5288 static BOOL is_em_settextex_supported(HWND hwnd)
5290 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5291 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5294 static void test_unicode_conversions(void)
5296 static const WCHAR tW[] = {'t',0};
5297 static const WCHAR teW[] = {'t','e',0};
5298 static const WCHAR textW[] = {'t','e','s','t',0};
5299 static const char textA[] = "test";
5303 int em_settextex_supported, ret;
5305 #define set_textA(hwnd, wm_set_text, txt) \
5307 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5308 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5309 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5310 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5311 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5313 #define expect_textA(hwnd, wm_get_text, txt) \
5315 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5316 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5317 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5318 memset(bufA, 0xAA, sizeof(bufA)); \
5319 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5320 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5321 ret = lstrcmpA(bufA, txt); \
5322 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5325 #define set_textW(hwnd, wm_set_text, txt) \
5327 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5328 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5329 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5330 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5331 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5333 #define expect_textW(hwnd, wm_get_text, txt) \
5335 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5336 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5337 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5338 memset(bufW, 0xAA, sizeof(bufW)); \
5339 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5340 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5341 ret = lstrcmpW(bufW, txt); \
5342 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5344 #define expect_empty(hwnd, wm_get_text) \
5346 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5347 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \
5348 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5349 memset(bufA, 0xAA, sizeof(bufA)); \
5350 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5351 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5352 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5355 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5356 0, 0, 200, 60, 0, 0, 0, 0);
5357 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5359 ret = IsWindowUnicode(hwnd);
5360 ok(ret, "RichEdit20W should be unicode under NT\n");
5362 /* EM_SETTEXTEX is supported starting from version 3.0 */
5363 em_settextex_supported = is_em_settextex_supported(hwnd);
5364 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5365 em_settextex_supported ? "" : "NOT ");
5367 expect_empty(hwnd, WM_GETTEXT);
5368 expect_empty(hwnd, EM_GETTEXTEX);
5370 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5371 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5372 expect_textA(hwnd, WM_GETTEXT, "t");
5373 expect_textA(hwnd, EM_GETTEXTEX, "t");
5374 expect_textW(hwnd, EM_GETTEXTEX, tW);
5376 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5377 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5378 expect_textA(hwnd, WM_GETTEXT, "te");
5379 expect_textA(hwnd, EM_GETTEXTEX, "te");
5380 expect_textW(hwnd, EM_GETTEXTEX, teW);
5382 set_textA(hwnd, WM_SETTEXT, NULL);
5383 expect_empty(hwnd, WM_GETTEXT);
5384 expect_empty(hwnd, EM_GETTEXTEX);
5386 set_textA(hwnd, WM_SETTEXT, textA);
5387 expect_textA(hwnd, WM_GETTEXT, textA);
5388 expect_textA(hwnd, EM_GETTEXTEX, textA);
5389 expect_textW(hwnd, EM_GETTEXTEX, textW);
5391 if (em_settextex_supported)
5393 set_textA(hwnd, EM_SETTEXTEX, textA);
5394 expect_textA(hwnd, WM_GETTEXT, textA);
5395 expect_textA(hwnd, EM_GETTEXTEX, textA);
5396 expect_textW(hwnd, EM_GETTEXTEX, textW);
5399 set_textW(hwnd, WM_SETTEXT, textW);
5400 expect_textW(hwnd, WM_GETTEXT, textW);
5401 expect_textA(hwnd, WM_GETTEXT, textA);
5402 expect_textW(hwnd, EM_GETTEXTEX, textW);
5403 expect_textA(hwnd, EM_GETTEXTEX, textA);
5405 if (em_settextex_supported)
5407 set_textW(hwnd, EM_SETTEXTEX, textW);
5408 expect_textW(hwnd, WM_GETTEXT, textW);
5409 expect_textA(hwnd, WM_GETTEXT, textA);
5410 expect_textW(hwnd, EM_GETTEXTEX, textW);
5411 expect_textA(hwnd, EM_GETTEXTEX, textA);
5413 DestroyWindow(hwnd);
5415 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5416 0, 0, 200, 60, 0, 0, 0, 0);
5417 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5419 ret = IsWindowUnicode(hwnd);
5420 ok(!ret, "RichEdit20A should NOT be unicode\n");
5422 set_textA(hwnd, WM_SETTEXT, textA);
5423 expect_textA(hwnd, WM_GETTEXT, textA);
5424 expect_textA(hwnd, EM_GETTEXTEX, textA);
5425 expect_textW(hwnd, EM_GETTEXTEX, textW);
5427 if (em_settextex_supported)
5429 set_textA(hwnd, EM_SETTEXTEX, textA);
5430 expect_textA(hwnd, WM_GETTEXT, textA);
5431 expect_textA(hwnd, EM_GETTEXTEX, textA);
5432 expect_textW(hwnd, EM_GETTEXTEX, textW);
5435 set_textW(hwnd, WM_SETTEXT, textW);
5436 expect_textW(hwnd, WM_GETTEXT, textW);
5437 expect_textA(hwnd, WM_GETTEXT, textA);
5438 expect_textW(hwnd, EM_GETTEXTEX, textW);
5439 expect_textA(hwnd, EM_GETTEXTEX, textA);
5441 if (em_settextex_supported)
5443 set_textW(hwnd, EM_SETTEXTEX, textW);
5444 expect_textW(hwnd, WM_GETTEXT, textW);
5445 expect_textA(hwnd, WM_GETTEXT, textA);
5446 expect_textW(hwnd, EM_GETTEXTEX, textW);
5447 expect_textA(hwnd, EM_GETTEXTEX, textA);
5449 DestroyWindow(hwnd);
5452 static void test_WM_CHAR(void)
5456 const char * char_list = "abc\rabc\r";
5457 const char * expected_content_single = "abcabc";
5458 const char * expected_content_multi = "abc\r\nabc\r\n";
5459 char buffer[64] = {0};
5462 /* single-line control must IGNORE carriage returns */
5463 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5464 0, 0, 200, 60, 0, 0, 0, 0);
5465 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5468 while (*p != '\0') {
5469 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5470 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5471 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5472 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5476 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5477 ret = strcmp(buffer, expected_content_single);
5478 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5480 DestroyWindow(hwnd);
5482 /* multi-line control inserts CR normally */
5483 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5484 0, 0, 200, 60, 0, 0, 0, 0);
5485 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5488 while (*p != '\0') {
5489 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5490 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5491 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5492 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5496 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5497 ret = strcmp(buffer, expected_content_multi);
5498 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5500 DestroyWindow(hwnd);
5503 static void test_EM_GETTEXTLENGTHEX(void)
5506 GETTEXTLENGTHEX gtl;
5508 const char * base_string = "base string";
5509 const char * test_string = "a\nb\n\n\r\n";
5510 const char * test_string_after = "a";
5511 const char * test_string_2 = "a\rtest\rstring";
5512 char buffer[64] = {0};
5515 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5516 0, 0, 200, 60, 0, 0, 0, 0);
5517 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5519 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5520 gtl.codepage = CP_ACP;
5521 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5522 ok(ret == 0, "ret %d\n",ret);
5524 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5525 gtl.codepage = CP_ACP;
5526 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5527 ok(ret == 0, "ret %d\n",ret);
5529 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5531 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5532 gtl.codepage = CP_ACP;
5533 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5534 ok(ret == strlen(base_string), "ret %d\n",ret);
5536 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5537 gtl.codepage = CP_ACP;
5538 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5539 ok(ret == strlen(base_string), "ret %d\n",ret);
5541 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5543 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5544 gtl.codepage = CP_ACP;
5545 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5546 ok(ret == 1, "ret %d\n",ret);
5548 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5549 gtl.codepage = CP_ACP;
5550 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5551 ok(ret == 1, "ret %d\n",ret);
5553 SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5554 ret = strcmp(buffer, test_string_after);
5555 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5557 DestroyWindow(hwnd);
5560 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5561 0, 0, 200, 60, 0, 0, 0, 0);
5562 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5564 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5565 gtl.codepage = CP_ACP;
5566 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5567 ok(ret == 0, "ret %d\n",ret);
5569 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5570 gtl.codepage = CP_ACP;
5571 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5572 ok(ret == 0, "ret %d\n",ret);
5574 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) base_string);
5576 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5577 gtl.codepage = CP_ACP;
5578 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5579 ok(ret == strlen(base_string), "ret %d\n",ret);
5581 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5582 gtl.codepage = CP_ACP;
5583 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5584 ok(ret == strlen(base_string), "ret %d\n",ret);
5586 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5588 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5589 gtl.codepage = CP_ACP;
5590 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5591 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5593 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5594 gtl.codepage = CP_ACP;
5595 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5596 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5598 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string);
5600 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5601 gtl.codepage = CP_ACP;
5602 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5603 ok(ret == 10, "ret %d\n",ret);
5605 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5606 gtl.codepage = CP_ACP;
5607 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0);
5608 ok(ret == 6, "ret %d\n",ret);
5610 /* Unicode/NUMCHARS/NUMBYTES */
5611 SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string_2);
5613 gtl.flags = GTL_DEFAULT;
5614 gtl.codepage = 1200;
5615 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5616 ok(ret == lstrlen(test_string_2),
5617 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlen(test_string_2));
5619 gtl.flags = GTL_NUMCHARS;
5620 gtl.codepage = 1200;
5621 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5622 ok(ret == lstrlen(test_string_2),
5623 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlen(test_string_2));
5625 gtl.flags = GTL_NUMBYTES;
5626 gtl.codepage = 1200;
5627 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5628 ok(ret == lstrlen(test_string_2)*2,
5629 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5631 gtl.flags = GTL_PRECISE;
5632 gtl.codepage = 1200;
5633 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5634 ok(ret == lstrlen(test_string_2)*2,
5635 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2)*2);
5637 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5638 gtl.codepage = 1200;
5639 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5640 ok(ret == lstrlen(test_string_2),
5641 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlen(test_string_2));
5643 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5644 gtl.codepage = 1200;
5645 ret = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM) >l, 0);
5646 ok(ret == E_INVALIDARG,
5647 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5649 DestroyWindow(hwnd);
5653 /* globals that parent and child access when checking event masks & notifications */
5654 static HWND eventMaskEditHwnd = 0;
5655 static int queriedEventMask;
5656 static int watchForEventMask = 0;
5658 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5659 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5661 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5663 queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5665 return DefWindowProcA(hwnd, message, wParam, lParam);
5668 /* test event masks in combination with WM_COMMAND */
5669 static void test_eventMask(void)
5674 const char text[] = "foo bar\n";
5677 /* register class to capture WM_COMMAND */
5679 cls.lpfnWndProc = ParentMsgCheckProcA;
5682 cls.hInstance = GetModuleHandleA(0);
5684 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5685 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5686 cls.lpszMenuName = NULL;
5687 cls.lpszClassName = "EventMaskParentClass";
5688 if(!RegisterClassA(&cls)) assert(0);
5690 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5691 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5692 ok (parent != 0, "Failed to create parent window\n");
5694 eventMaskEditHwnd = new_richedit(parent);
5695 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5697 eventMask = ENM_CHANGE | ENM_UPDATE;
5698 ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5699 ok(ret == ENM_NONE, "wrong event mask\n");
5700 ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5701 ok(ret == eventMask, "failed to set event mask\n");
5703 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5704 queriedEventMask = 0; /* initialize to something other than we expect */
5705 watchForEventMask = EN_CHANGE;
5706 ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text);
5707 ok(ret == TRUE, "failed to set text\n");
5708 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5709 notification in response to WM_SETTEXT */
5710 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5711 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5713 /* check to see if EN_CHANGE is sent when redraw is turned off */
5714 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5715 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5716 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5717 /* redraw is disabled by making the window invisible. */
5718 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5719 queriedEventMask = 0; /* initialize to something other than we expect */
5720 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5721 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5722 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5723 SendMessage(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5724 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5726 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5727 SendMessage(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5728 style = GetWindowLong(eventMaskEditHwnd, GWL_STYLE);
5729 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5730 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5731 watchForEventMask = EN_UPDATE;
5732 queriedEventMask = 0; /* initialize to something other than we expect */
5733 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5734 ok(queriedEventMask == 0,
5735 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5736 SetWindowLong(eventMaskEditHwnd, GWL_STYLE, style);
5737 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5738 queriedEventMask = 0; /* initialize to something other than we expect */
5739 SendMessage(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM) text);
5740 ok(queriedEventMask == eventMask,
5741 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5744 DestroyWindow(parent);
5747 static int received_WM_NOTIFY = 0;
5748 static int modify_at_WM_NOTIFY = 0;
5749 static BOOL filter_on_WM_NOTIFY = FALSE;
5750 static HWND hwndRichedit_WM_NOTIFY;
5752 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5754 if(message == WM_NOTIFY)
5756 received_WM_NOTIFY = 1;
5757 modify_at_WM_NOTIFY = SendMessage(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5758 if (filter_on_WM_NOTIFY) return TRUE;
5760 return DefWindowProcA(hwnd, message, wParam, lParam);
5763 static void test_WM_NOTIFY(void)
5768 int sel_start, sel_end;
5770 /* register class to capture WM_NOTIFY */
5772 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5775 cls.hInstance = GetModuleHandleA(0);
5777 cls.hCursor = LoadCursorA(0, IDC_ARROW);
5778 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5779 cls.lpszMenuName = NULL;
5780 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5781 if(!RegisterClassA(&cls)) assert(0);
5783 parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5784 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5785 ok (parent != 0, "Failed to create parent window\n");
5787 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5788 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5790 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5792 /* Notifications for selection change should only be sent when selection
5793 actually changes. EM_SETCHARFORMAT is one message that calls
5794 ME_CommitUndo, which should check whether message should be sent */
5795 received_WM_NOTIFY = 0;
5796 cf2.cbSize = sizeof(CHARFORMAT2);
5797 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5798 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5799 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5800 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM) &cf2);
5801 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5803 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5805 received_WM_NOTIFY = 0;
5806 modify_at_WM_NOTIFY = 0;
5807 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5808 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5809 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5811 received_WM_NOTIFY = 0;
5812 modify_at_WM_NOTIFY = 0;
5813 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5814 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5816 received_WM_NOTIFY = 0;
5817 modify_at_WM_NOTIFY = 0;
5818 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5819 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5820 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5822 /* Test for WM_NOTIFY messages with redraw disabled. */
5823 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5824 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5825 received_WM_NOTIFY = 0;
5826 SendMessage(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5827 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5828 SendMessage(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5830 /* Test filtering key events. */
5831 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5832 SendMessage(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5833 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5834 received_WM_NOTIFY = 0;
5835 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5836 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5837 ok(sel_start == 1 && sel_end == 1,
5838 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5839 filter_on_WM_NOTIFY = TRUE;
5840 received_WM_NOTIFY = 0;
5841 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5842 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5843 ok(sel_start == 1 && sel_end == 1,
5844 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5846 /* test with owner set to NULL */
5847 SetWindowLongPtr(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5848 SendMessage(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5849 SendMessage(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5850 ok(sel_start == 1 && sel_end == 1,
5851 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5853 DestroyWindow(hwndRichedit_WM_NOTIFY);
5854 DestroyWindow(parent);
5857 static void test_undo_coalescing(void)
5861 char buffer[64] = {0};
5863 /* multi-line control inserts CR normally */
5864 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5865 0, 0, 200, 60, 0, 0, 0, 0);
5866 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5868 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5869 ok (result == FALSE, "Can undo after window creation.\n");
5870 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5871 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
5872 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5873 ok (result == FALSE, "Can redo after window creation.\n");
5874 result = SendMessage(hwnd, EM_REDO, 0, 0);
5875 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
5877 /* Test the effect of arrows keys during typing on undo transactions*/
5878 simulate_typing_characters(hwnd, "one two three");
5879 SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
5880 SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 1);
5881 simulate_typing_characters(hwnd, " four five six");
5883 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5884 ok (result == FALSE, "Can redo before anything is undone.\n");
5885 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5886 ok (result == TRUE, "Cannot undo typed characters.\n");
5887 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5888 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
5889 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5890 ok (result == TRUE, "Cannot redo after undo.\n");
5891 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5892 result = strcmp(buffer, "one two three");
5893 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5895 result = SendMessage(hwnd, EM_CANUNDO, 0, 0);
5896 ok (result == TRUE, "Cannot undo typed characters.\n");
5897 result = SendMessage(hwnd, WM_UNDO, 0, 0);
5898 ok (result == TRUE, "Failed to undo typed characters.\n");
5899 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5900 result = strcmp(buffer, "");
5901 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5903 /* Test the effect of focus changes during typing on undo transactions*/
5904 simulate_typing_characters(hwnd, "one two three");
5905 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5906 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5907 SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
5908 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
5909 simulate_typing_characters(hwnd, " four five six");
5910 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5911 ok (result == TRUE, "Failed to undo typed characters.\n");
5912 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5913 result = strcmp(buffer, "one two three");
5914 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5916 /* Test the effect of the back key during typing on undo transactions */
5917 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5918 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5919 ok (result == TRUE, "Failed to clear the text.\n");
5920 simulate_typing_characters(hwnd, "one two threa");
5921 result = SendMessage(hwnd, EM_CANREDO, 0, 0);
5922 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
5923 SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 1);
5924 SendMessage(hwnd, WM_KEYUP, VK_BACK, 1);
5925 simulate_typing_characters(hwnd, "e four five six");
5926 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5927 ok (result == TRUE, "Failed to undo typed characters.\n");
5928 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5929 result = strcmp(buffer, "");
5930 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5932 /* Test the effect of the delete key during typing on undo transactions */
5933 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5934 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
5935 ok(result == TRUE, "Failed to set the text.\n");
5936 SendMessage(hwnd, EM_SETSEL, 1, 1);
5937 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5938 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5939 SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);
5940 SendMessage(hwnd, WM_KEYUP, VK_DELETE, 1);
5941 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5942 ok (result == TRUE, "Failed to undo typed characters.\n");
5943 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5944 result = strcmp(buffer, "acd");
5945 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
5946 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5947 ok (result == TRUE, "Failed to undo typed characters.\n");
5948 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5949 result = strcmp(buffer, "abcd");
5950 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
5952 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
5953 SendMessage(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
5954 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
5955 ok (result == TRUE, "Failed to clear the text.\n");
5956 simulate_typing_characters(hwnd, "one two three");
5957 result = SendMessage(hwnd, EM_STOPGROUPTYPING, 0, 0);
5958 ok (result == 0, "expected %d but got %d\n", 0, result);
5959 simulate_typing_characters(hwnd, " four five six");
5960 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5961 ok (result == TRUE, "Failed to undo typed characters.\n");
5962 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5963 result = strcmp(buffer, "one two three");
5964 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
5965 result = SendMessage(hwnd, EM_UNDO, 0, 0);
5966 ok (result == TRUE, "Failed to undo typed characters.\n");
5967 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5968 result = strcmp(buffer, "");
5969 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
5971 DestroyWindow(hwnd);
5974 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
5978 /* MSDN lied, length is actually the number of bytes. */
5979 length = bytes / sizeof(WCHAR);
5982 case WB_ISDELIMITER:
5983 return text[pos] == 'X';
5985 case WB_MOVEWORDLEFT:
5986 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5988 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
5991 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5995 case WB_MOVEWORDRIGHT:
5996 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
5998 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6001 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6005 ok(FALSE, "Unexpected code %d\n", code);
6011 static void test_word_movement(void)
6015 int sel_start, sel_end;
6016 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6018 /* multi-line control inserts CR normally */
6019 hwnd = new_richedit(NULL);
6021 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6022 ok (result == TRUE, "Failed to clear the text.\n");
6023 SendMessage(hwnd, EM_SETSEL, 0, 0);
6024 /* |one two three */
6026 send_ctrl_key(hwnd, VK_RIGHT);
6027 /* one |two three */
6028 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6029 ok(sel_start == sel_end, "Selection should be empty\n");
6030 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6032 send_ctrl_key(hwnd, VK_RIGHT);
6033 /* one two |three */
6034 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6035 ok(sel_start == sel_end, "Selection should be empty\n");
6036 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6038 send_ctrl_key(hwnd, VK_LEFT);
6039 /* one |two three */
6040 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6041 ok(sel_start == sel_end, "Selection should be empty\n");
6042 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6044 send_ctrl_key(hwnd, VK_LEFT);
6045 /* |one two three */
6046 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6047 ok(sel_start == sel_end, "Selection should be empty\n");
6048 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6050 SendMessage(hwnd, EM_SETSEL, 8, 8);
6051 /* one two | three */
6052 send_ctrl_key(hwnd, VK_RIGHT);
6053 /* one two |three */
6054 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6055 ok(sel_start == sel_end, "Selection should be empty\n");
6056 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6058 SendMessage(hwnd, EM_SETSEL, 11, 11);
6059 /* one two th|ree */
6060 send_ctrl_key(hwnd, VK_LEFT);
6061 /* one two |three */
6062 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6063 ok(sel_start == sel_end, "Selection should be empty\n");
6064 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6066 /* Test with a custom word break procedure that uses X as the delimiter. */
6067 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6068 ok (result == TRUE, "Failed to clear the text.\n");
6069 SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6070 /* |one twoXthree */
6071 send_ctrl_key(hwnd, VK_RIGHT);
6072 /* one twoX|three */
6073 SendMessage(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6074 ok(sel_start == sel_end, "Selection should be empty\n");
6075 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6077 DestroyWindow(hwnd);
6079 /* Make sure the behaviour is the same with a unicode richedit window,
6080 * and using unicode functions. */
6082 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6083 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6084 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6086 /* Test with a custom word break procedure that uses X as the delimiter. */
6087 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6088 ok (result == TRUE, "Failed to clear the text.\n");
6089 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6090 /* |one twoXthree */
6091 send_ctrl_key(hwnd, VK_RIGHT);
6092 /* one twoX|three */
6093 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6094 ok(sel_start == sel_end, "Selection should be empty\n");
6095 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6097 DestroyWindow(hwnd);
6100 static void test_EM_CHARFROMPOS(void)
6109 /* multi-line control inserts CR normally */
6110 hwnd = new_richedit(NULL);
6111 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6112 (LPARAM)"one two three four five six seven\reight");
6113 ok(result == 1, "Expected 1, got %d\n", result);
6114 GetClientRect(hwnd, &rcClient);
6116 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6117 ok(result == 34, "expected character index of 34 but got %d\n", result);
6119 /* Test with points outside the bounds of the richedit control. */
6122 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6123 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6127 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6128 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6132 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6133 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6137 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6138 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6141 point.y = rcClient.bottom + 1;
6142 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6143 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6146 point.y = rcClient.bottom;
6147 result = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6148 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6150 DestroyWindow(hwnd);
6153 static void test_word_wrap(void)
6156 POINTL point = {0, 60}; /* This point must be below the first line */
6157 const char *text = "Must be long enough to test line wrapping";
6158 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6159 int res, pos, lines;
6161 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6162 * when specified on window creation and set later. */
6163 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6164 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6165 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6166 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6167 ok(res, "WM_SETTEXT failed.\n");
6168 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6169 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6170 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6171 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6173 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6174 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6175 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6176 DestroyWindow(hwnd);
6178 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|WS_HSCROLL,
6179 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6180 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6182 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6183 ok(res, "WM_SETTEXT failed.\n");
6184 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6185 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6186 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6187 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6189 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6190 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6191 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6192 DestroyWindow(hwnd);
6194 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6195 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6196 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6197 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6198 ok(res, "WM_SETTEXT failed.\n");
6199 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6200 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6202 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6203 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6204 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6205 DestroyWindow(hwnd);
6207 hwnd = CreateWindow(RICHEDIT_CLASS, NULL,
6208 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6209 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6210 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6211 res = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text);
6212 ok(res, "WM_SETTEXT failed.\n");
6213 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6214 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6216 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6217 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6218 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6220 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6221 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 1);
6222 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6223 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6224 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6226 res = SendMessage(hwnd, EM_SETTARGETDEVICE, 0, 0);
6227 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6228 pos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &point);
6229 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6230 DestroyWindow(hwnd);
6232 /* Test to see if wrapping happens with redraw disabled. */
6233 hwnd = CreateWindow(RICHEDIT_CLASS, NULL, dwCommonStyle,
6234 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6235 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6236 SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
6237 res = SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM) text);
6238 ok(res, "EM_REPLACESEL failed.\n");
6239 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6240 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6241 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6242 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6243 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6245 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6246 DestroyWindow(hwnd);
6249 static void test_autoscroll(void)
6251 HWND hwnd = new_richedit(NULL);
6252 int lines, ret, redraw;
6255 for (redraw = 0; redraw <= 1; redraw++) {
6256 trace("testing with WM_SETREDRAW=%d\n", redraw);
6257 SendMessage(hwnd, WM_SETREDRAW, redraw, 0);
6258 SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6259 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6260 ok(lines == 8, "%d lines instead of 8\n", lines);
6261 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6262 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6263 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6264 ret = GetWindowLong(hwnd, GWL_STYLE);
6265 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6267 SendMessage(hwnd, WM_SETTEXT, 0, 0);
6268 lines = SendMessage(hwnd, EM_GETLINECOUNT, 0, 0);
6269 ok(lines == 1, "%d lines instead of 1\n", lines);
6270 ret = SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6271 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6272 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6273 ret = GetWindowLong(hwnd, GWL_STYLE);
6274 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6277 SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
6278 DestroyWindow(hwnd);
6280 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6281 * auto vertical/horizontal scrolling options. */
6282 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6283 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6284 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6285 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6286 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6287 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6288 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6289 ret = GetWindowLong(hwnd, GWL_STYLE);
6290 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6291 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6292 DestroyWindow(hwnd);
6294 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6295 WS_POPUP|ES_MULTILINE,
6296 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6297 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6298 ret = SendMessage(hwnd, EM_GETOPTIONS, 0, 0);
6299 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6300 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6301 ret = GetWindowLong(hwnd, GWL_STYLE);
6302 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6303 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6304 DestroyWindow(hwnd);
6308 static void test_format_rect(void)
6311 RECT rc, expected, clientRect;
6315 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6316 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6317 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6318 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6320 GetClientRect(hwnd, &clientRect);
6322 expected = clientRect;
6324 expected.right -= 1;
6325 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6326 ok(rc.top == expected.top && rc.left == expected.left &&
6327 rc.bottom == expected.bottom && rc.right == expected.right,
6328 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6329 rc.top, rc.left, rc.bottom, rc.right,
6330 expected.top, expected.left, expected.bottom, expected.right);
6332 for (n = -3; n <= 3; n++)
6339 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6342 expected.top = max(0, rc.top);
6343 expected.left = max(0, rc.left);
6344 expected.bottom = min(clientRect.bottom, rc.bottom);
6345 expected.right = min(clientRect.right, rc.right);
6346 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6347 ok(rc.top == expected.top && rc.left == expected.left &&
6348 rc.bottom == expected.bottom && rc.right == expected.right,
6349 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6350 n, rc.top, rc.left, rc.bottom, rc.right,
6351 expected.top, expected.left, expected.bottom, expected.right);
6355 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6356 expected = clientRect;
6357 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6358 ok(rc.top == expected.top && rc.left == expected.left &&
6359 rc.bottom == expected.bottom && rc.right == expected.right,
6360 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6361 rc.top, rc.left, rc.bottom, rc.right,
6362 expected.top, expected.left, expected.bottom, expected.right);
6364 /* Adding the selectionbar adds the selectionbar width to the left side. */
6365 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6366 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6367 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6368 expected.left += 8; /* selection bar width */
6369 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6370 ok(rc.top == expected.top && rc.left == expected.left &&
6371 rc.bottom == expected.bottom && rc.right == expected.right,
6372 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6373 rc.top, rc.left, rc.bottom, rc.right,
6374 expected.top, expected.left, expected.bottom, expected.right);
6377 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6378 expected = clientRect;
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 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6387 * even if the left side is already 0. */
6388 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6389 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6390 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6391 expected.left -= 8; /* selection bar width */
6392 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6393 ok(rc.top == expected.top && rc.left == expected.left &&
6394 rc.bottom == expected.bottom && rc.right == expected.right,
6395 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6396 rc.top, rc.left, rc.bottom, rc.right,
6397 expected.top, expected.left, expected.bottom, expected.right);
6399 /* Set the absolute value of the formatting rectangle. */
6401 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6402 expected = clientRect;
6403 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6404 ok(rc.top == expected.top && rc.left == expected.left &&
6405 rc.bottom == expected.bottom && rc.right == expected.right,
6406 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6407 n, rc.top, rc.left, rc.bottom, rc.right,
6408 expected.top, expected.left, expected.bottom, expected.right);
6410 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6411 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6412 * tests show that this isn't true. */
6415 rc.bottom = clientRect.bottom - 15;
6416 rc.right = clientRect.right - 15;
6418 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6419 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6420 ok(rc.top == expected.top && rc.left == expected.left &&
6421 rc.bottom == expected.bottom && rc.right == expected.right,
6422 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6423 rc.top, rc.left, rc.bottom, rc.right,
6424 expected.top, expected.left, expected.bottom, expected.right);
6426 /* For some reason it does not limit the values to the client rect with
6427 * a WPARAM value of 1. */
6430 rc.bottom = clientRect.bottom + 15;
6431 rc.right = clientRect.right + 15;
6433 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6434 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6435 ok(rc.top == expected.top && rc.left == expected.left &&
6436 rc.bottom == expected.bottom && rc.right == expected.right,
6437 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6438 rc.top, rc.left, rc.bottom, rc.right,
6439 expected.top, expected.left, expected.bottom, expected.right);
6441 /* Reset to default rect and check how the format rect adjusts to window
6442 * resize and how it copes with very small windows */
6443 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6445 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6446 GetClientRect(hwnd, &clientRect);
6448 expected = clientRect;
6450 expected.right -= 1;
6451 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6452 ok(rc.top == expected.top && rc.left == expected.left &&
6453 rc.bottom == expected.bottom && rc.right == expected.right,
6454 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6455 rc.top, rc.left, rc.bottom, rc.right,
6456 expected.top, expected.left, expected.bottom, expected.right);
6458 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6459 GetClientRect(hwnd, &clientRect);
6461 expected = clientRect;
6463 expected.right -= 1;
6464 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6465 ok(rc.top == expected.top && rc.left == expected.left &&
6466 rc.bottom == expected.bottom && rc.right == expected.right,
6467 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6468 rc.top, rc.left, rc.bottom, rc.right,
6469 expected.top, expected.left, expected.bottom, expected.right);
6471 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6472 GetClientRect(hwnd, &clientRect);
6474 expected = clientRect;
6476 expected.right -= 1;
6477 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6478 ok(rc.top == expected.top && rc.left == expected.left &&
6479 rc.bottom == expected.bottom && rc.right == expected.right,
6480 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6481 rc.top, rc.left, rc.bottom, rc.right,
6482 expected.top, expected.left, expected.bottom, expected.right);
6484 DestroyWindow(hwnd);
6486 /* The extended window style affects the formatting rectangle. */
6487 hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
6488 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6489 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6490 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6492 GetClientRect(hwnd, &clientRect);
6494 expected = clientRect;
6497 expected.right -= 1;
6498 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6499 ok(rc.top == expected.top && rc.left == expected.left &&
6500 rc.bottom == expected.bottom && rc.right == expected.right,
6501 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6502 rc.top, rc.left, rc.bottom, rc.right,
6503 expected.top, expected.left, expected.bottom, expected.right);
6513 expected.right += 1;
6514 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6515 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6516 ok(rc.top == expected.top && rc.left == expected.left &&
6517 rc.bottom == expected.bottom && rc.right == expected.right,
6518 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6519 rc.top, rc.left, rc.bottom, rc.right,
6520 expected.top, expected.left, expected.bottom, expected.right);
6522 DestroyWindow(hwnd);
6525 static void test_WM_GETDLGCODE(void)
6531 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6533 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6534 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6535 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6536 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6538 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6539 expected = expected | DLGC_WANTMESSAGE;
6540 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6542 DestroyWindow(hwnd);
6544 msg.message = WM_KEYDOWN;
6545 msg.wParam = VK_RETURN;
6546 msg.lParam = (MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6549 msg.time = GetTickCount();
6551 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6552 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6553 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6554 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6556 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6557 expected = expected | DLGC_WANTMESSAGE;
6558 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6560 DestroyWindow(hwnd);
6562 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6563 ES_MULTILINE|WS_POPUP,
6564 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6565 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6567 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6568 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6569 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6571 DestroyWindow(hwnd);
6573 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6574 ES_WANTRETURN|WS_POPUP,
6575 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6576 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6578 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6579 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6580 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6582 DestroyWindow(hwnd);
6584 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6586 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6587 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6589 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6590 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6591 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6593 DestroyWindow(hwnd);
6595 msg.wParam = VK_TAB;
6596 msg.lParam = (MapVirtualKey(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6598 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6599 ES_MULTILINE|WS_POPUP,
6600 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6601 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6603 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6604 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6605 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6607 DestroyWindow(hwnd);
6609 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6611 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6612 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6614 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6615 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6616 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6618 DestroyWindow(hwnd);
6620 hold_key(VK_CONTROL);
6622 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6623 ES_MULTILINE|WS_POPUP,
6624 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6625 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6627 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6628 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6629 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6631 DestroyWindow(hwnd);
6633 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6635 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6636 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6638 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6639 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6640 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6642 DestroyWindow(hwnd);
6644 release_key(VK_CONTROL);
6647 msg.lParam = (MapVirtualKey('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6649 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6650 ES_MULTILINE|WS_POPUP,
6651 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6652 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6654 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6655 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6656 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6658 DestroyWindow(hwnd);
6660 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6662 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6663 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6665 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6666 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6667 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6669 DestroyWindow(hwnd);
6671 msg.message = WM_CHAR;
6673 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6674 ES_MULTILINE|WS_POPUP,
6675 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6676 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6678 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6679 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6680 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6682 DestroyWindow(hwnd);
6684 hwnd = CreateWindowEx(0, RICHEDIT_CLASS, NULL,
6686 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6687 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS, (int) GetLastError());
6689 res = SendMessage(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6690 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6691 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6693 DestroyWindow(hwnd);
6696 static void test_zoom(void)
6702 int numerator, denominator;
6704 hwnd = new_richedit(NULL);
6705 GetClientRect(hwnd, &rc);
6706 pt.x = (rc.right - rc.left) / 2;
6707 pt.y = (rc.bottom - rc.top) / 2;
6708 ClientToScreen(hwnd, &pt);
6710 /* Test initial zoom value */
6711 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6712 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6713 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6714 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6716 /* test scroll wheel */
6717 hold_key(VK_CONTROL);
6718 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6719 MAKELPARAM(pt.x, pt.y));
6720 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6721 release_key(VK_CONTROL);
6723 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6724 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6725 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6726 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6728 /* Test how much the mouse wheel can zoom in and out. */
6729 ret = SendMessage(hwnd, EM_SETZOOM, 490, 100);
6730 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6732 hold_key(VK_CONTROL);
6733 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6734 MAKELPARAM(pt.x, pt.y));
6735 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6736 release_key(VK_CONTROL);
6738 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6739 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6740 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6741 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6743 ret = SendMessage(hwnd, EM_SETZOOM, 491, 100);
6744 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6746 hold_key(VK_CONTROL);
6747 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6748 MAKELPARAM(pt.x, pt.y));
6749 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6750 release_key(VK_CONTROL);
6752 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6753 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6754 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6755 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6757 ret = SendMessage(hwnd, EM_SETZOOM, 20, 100);
6758 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6760 hold_key(VK_CONTROL);
6761 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6762 MAKELPARAM(pt.x, pt.y));
6763 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6764 release_key(VK_CONTROL);
6766 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6767 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6768 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6769 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6771 ret = SendMessage(hwnd, EM_SETZOOM, 19, 100);
6772 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6774 hold_key(VK_CONTROL);
6775 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6776 MAKELPARAM(pt.x, pt.y));
6777 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6778 release_key(VK_CONTROL);
6780 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6781 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6782 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6783 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6785 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6786 ret = SendMessage(hwnd, EM_SETZOOM, 50, 13);
6787 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6789 hold_key(VK_CONTROL);
6790 ret = SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6791 MAKELPARAM(pt.x, pt.y));
6792 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6793 release_key(VK_CONTROL);
6795 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6796 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6797 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6798 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6800 /* Test bounds checking on EM_SETZOOM */
6801 ret = SendMessage(hwnd, EM_SETZOOM, 2, 127);
6802 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6804 ret = SendMessage(hwnd, EM_SETZOOM, 127, 2);
6805 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6807 ret = SendMessage(hwnd, EM_SETZOOM, 2, 128);
6808 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6810 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6811 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6812 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6813 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6815 ret = SendMessage(hwnd, EM_SETZOOM, 128, 2);
6816 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6818 /* See if negative numbers are accepted. */
6819 ret = SendMessage(hwnd, EM_SETZOOM, -100, -100);
6820 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6822 /* See if negative numbers are accepted. */
6823 ret = SendMessage(hwnd, EM_SETZOOM, 0, 100);
6824 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
6826 ret = SendMessage(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6827 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6828 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6829 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6831 /* Reset the zoom value */
6832 ret = SendMessage(hwnd, EM_SETZOOM, 0, 0);
6833 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6835 DestroyWindow(hwnd);
6838 struct dialog_mode_messages
6840 int wm_getdefid, wm_close, wm_nextdlgctl;
6843 static struct dialog_mode_messages dm_messages;
6845 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
6846 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
6847 "got %d\n", wmclose, dm_messages.wm_close); \
6848 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
6849 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
6850 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
6851 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
6853 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
6858 dm_messages.wm_getdefid++;
6859 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
6861 dm_messages.wm_nextdlgctl++;
6864 dm_messages.wm_close++;
6868 return DefWindowProc(hwnd, iMsg, wParam, lParam);
6871 static void test_dialogmode(void)
6873 HWND hwRichEdit, hwParent, hwButton;
6879 cls.lpfnWndProc = dialog_mode_wnd_proc;
6882 cls.hInstance = GetModuleHandleA(0);
6884 cls.hCursor = LoadCursorA(0, IDC_ARROW);
6885 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6886 cls.lpszMenuName = NULL;
6887 cls.lpszClassName = "DialogModeParentClass";
6888 if(!RegisterClassA(&cls)) assert(0);
6890 hwParent = CreateWindow("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
6891 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
6893 /* Test richedit(ES_MULTILINE) */
6895 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6897 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6898 ok(0 == r, "expected 0, got %d\n", r);
6899 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6900 ok(2 == lcount, "expected 2, got %d\n", lcount);
6902 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, 0);
6903 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6905 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6906 ok(0 == r, "expected 0, got %d\n", r);
6907 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6908 ok(3 == lcount, "expected 3, got %d\n", lcount);
6910 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6911 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6912 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6913 ok(0 == r, "expected 0, got %d\n", r);
6914 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6915 ok(3 == lcount, "expected 3, got %d\n", lcount);
6917 DestroyWindow(hwRichEdit);
6919 /* Test standalone richedit(ES_MULTILINE) */
6921 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, NULL);
6923 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6924 ok(0 == r, "expected 0, got %d\n", r);
6925 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6926 ok(2 == lcount, "expected 2, got %d\n", lcount);
6928 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6929 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6931 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6932 ok(0 == r, "expected 0, got %d\n", r);
6933 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6934 ok(2 == lcount, "expected 2, got %d\n", lcount);
6936 DestroyWindow(hwRichEdit);
6938 /* Check a destination for messages */
6940 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6942 SetWindowLong(hwRichEdit, GWL_STYLE, GetWindowLong(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
6943 SetParent( hwRichEdit, NULL);
6945 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6946 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6948 memset(&dm_messages, 0, sizeof(dm_messages));
6949 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6950 ok(0 == r, "expected 0, got %d\n", r);
6951 test_dm_messages(0, 1, 0);
6953 memset(&dm_messages, 0, sizeof(dm_messages));
6954 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6955 ok(0 == r, "expected 0, got %d\n", r);
6956 test_dm_messages(0, 0, 1);
6958 DestroyWindow(hwRichEdit);
6960 /* Check messages from richedit(ES_MULTILINE) */
6962 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE, hwParent);
6964 memset(&dm_messages, 0, sizeof(dm_messages));
6965 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6966 ok(0 == r, "expected 0, got %d\n", r);
6967 test_dm_messages(0, 0, 0);
6969 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6970 ok(2 == lcount, "expected 2, got %d\n", lcount);
6972 memset(&dm_messages, 0, sizeof(dm_messages));
6973 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6974 ok(0 == r, "expected 0, got %d\n", r);
6975 test_dm_messages(0, 0, 0);
6977 memset(&dm_messages, 0, sizeof(dm_messages));
6978 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
6979 ok(0 == r, "expected 0, got %d\n", r);
6980 test_dm_messages(0, 0, 0);
6982 memset(&dm_messages, 0, sizeof(dm_messages));
6983 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
6984 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
6985 test_dm_messages(0, 0, 0);
6987 memset(&dm_messages, 0, sizeof(dm_messages));
6988 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
6989 ok(0 == r, "expected 0, got %d\n", r);
6990 test_dm_messages(0, 1, 0);
6992 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
6993 ok(2 == lcount, "expected 2, got %d\n", lcount);
6995 memset(&dm_messages, 0, sizeof(dm_messages));
6996 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
6997 ok(0 == r, "expected 0, got %d\n", r);
6998 test_dm_messages(0, 0, 0);
7000 memset(&dm_messages, 0, sizeof(dm_messages));
7001 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7002 ok(0 == r, "expected 0, got %d\n", r);
7003 test_dm_messages(0, 0, 1);
7005 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7006 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7007 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7009 memset(&dm_messages, 0, sizeof(dm_messages));
7010 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7011 ok(0 == r, "expected 0, got %d\n", r);
7012 test_dm_messages(0, 1, 1);
7014 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7015 ok(2 == lcount, "expected 2, got %d\n", lcount);
7017 DestroyWindow(hwButton);
7018 DestroyWindow(hwRichEdit);
7020 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7022 hwRichEdit = new_window(RICHEDIT_CLASS, ES_MULTILINE|ES_WANTRETURN, hwParent);
7024 memset(&dm_messages, 0, sizeof(dm_messages));
7025 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7026 ok(0 == r, "expected 0, got %d\n", r);
7027 test_dm_messages(0, 0, 0);
7029 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7030 ok(2 == lcount, "expected 2, got %d\n", lcount);
7032 memset(&dm_messages, 0, sizeof(dm_messages));
7033 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7034 ok(0 == r, "expected 0, got %d\n", r);
7035 test_dm_messages(0, 0, 0);
7037 memset(&dm_messages, 0, sizeof(dm_messages));
7038 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7039 ok(0 == r, "expected 0, got %d\n", r);
7040 test_dm_messages(0, 0, 0);
7042 memset(&dm_messages, 0, sizeof(dm_messages));
7043 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7044 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7045 test_dm_messages(0, 0, 0);
7047 memset(&dm_messages, 0, sizeof(dm_messages));
7048 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7049 ok(0 == r, "expected 0, got %d\n", r);
7050 test_dm_messages(0, 0, 0);
7052 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7053 ok(3 == lcount, "expected 3, got %d\n", lcount);
7055 memset(&dm_messages, 0, sizeof(dm_messages));
7056 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7057 ok(0 == r, "expected 0, got %d\n", r);
7058 test_dm_messages(0, 0, 0);
7060 memset(&dm_messages, 0, sizeof(dm_messages));
7061 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7062 ok(0 == r, "expected 0, got %d\n", r);
7063 test_dm_messages(0, 0, 1);
7065 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7066 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7067 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7069 memset(&dm_messages, 0, sizeof(dm_messages));
7070 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7071 ok(0 == r, "expected 0, got %d\n", r);
7072 test_dm_messages(0, 0, 0);
7074 lcount = SendMessage(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7075 ok(4 == lcount, "expected 4, got %d\n", lcount);
7077 DestroyWindow(hwButton);
7078 DestroyWindow(hwRichEdit);
7080 /* Check messages from richedit(0) */
7082 hwRichEdit = new_window(RICHEDIT_CLASS, 0, hwParent);
7084 memset(&dm_messages, 0, sizeof(dm_messages));
7085 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7086 ok(0 == r, "expected 0, got %d\n", r);
7087 test_dm_messages(0, 0, 0);
7089 memset(&dm_messages, 0, sizeof(dm_messages));
7090 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7091 ok(0 == r, "expected 0, got %d\n", r);
7092 test_dm_messages(0, 0, 0);
7094 memset(&dm_messages, 0, sizeof(dm_messages));
7095 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7096 ok(0 == r, "expected 0, got %d\n", r);
7097 test_dm_messages(0, 0, 0);
7099 memset(&dm_messages, 0, sizeof(dm_messages));
7100 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7101 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7102 test_dm_messages(0, 0, 0);
7104 memset(&dm_messages, 0, sizeof(dm_messages));
7105 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7106 ok(0 == r, "expected 0, got %d\n", r);
7107 test_dm_messages(0, 1, 0);
7109 memset(&dm_messages, 0, sizeof(dm_messages));
7110 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7111 ok(0 == r, "expected 0, got %d\n", r);
7112 test_dm_messages(0, 0, 0);
7114 memset(&dm_messages, 0, sizeof(dm_messages));
7115 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7116 ok(0 == r, "expected 0, got %d\n", r);
7117 test_dm_messages(0, 0, 1);
7119 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7120 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7121 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7123 memset(&dm_messages, 0, sizeof(dm_messages));
7124 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7125 ok(0 == r, "expected 0, got %d\n", r);
7126 test_dm_messages(0, 1, 1);
7128 DestroyWindow(hwRichEdit);
7130 /* Check messages from richedit(ES_WANTRETURN) */
7132 hwRichEdit = new_window(RICHEDIT_CLASS, ES_WANTRETURN, hwParent);
7134 memset(&dm_messages, 0, sizeof(dm_messages));
7135 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7136 ok(0 == r, "expected 0, got %d\n", r);
7137 test_dm_messages(0, 0, 0);
7139 memset(&dm_messages, 0, sizeof(dm_messages));
7140 r = SendMessage(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7141 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7142 test_dm_messages(0, 0, 0);
7144 memset(&dm_messages, 0, sizeof(dm_messages));
7145 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7146 ok(0 == r, "expected 0, got %d\n", r);
7147 test_dm_messages(0, 0, 0);
7149 hwButton = CreateWindow("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7150 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7151 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7153 memset(&dm_messages, 0, sizeof(dm_messages));
7154 r = SendMessage(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7155 ok(0 == r, "expected 0, got %d\n", r);
7156 test_dm_messages(0, 0, 0);
7158 DestroyWindow(hwRichEdit);
7159 DestroyWindow(hwParent);
7162 static void test_EM_FINDWORDBREAK_W(void)
7164 static const struct {
7166 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7167 } delimiter_tests[] = {
7168 {0x0a, FALSE}, /* newline */
7169 {0x0b, FALSE}, /* vertical tab */
7170 {0x0c, FALSE}, /* form feed */
7171 {0x0d, FALSE}, /* carriage return */
7172 {0x20, TRUE}, /* space */
7173 {0x61, FALSE}, /* capital letter a */
7174 {0xa0, FALSE}, /* no-break space */
7175 {0x2000, FALSE}, /* en quad */
7176 {0x3000, FALSE}, /* Ideographic space */
7177 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7178 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7179 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7180 {0xac00, FALSE}, /* Hangul character GA*/
7181 {0xd7af, FALSE}, /* End of Hangul character chart */
7182 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7183 {0xff20, FALSE}, /* fullwidth commercial @ */
7184 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7187 HWND hwndRichEdit = new_richeditW(NULL);
7188 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7189 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7194 wbuf[0] = delimiter_tests[i].c;
7196 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7197 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7198 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7200 ok(result == delimiter_tests[i].isdelimiter,
7201 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7202 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7204 ok(result == delimiter_tests[i].isdelimiter,
7205 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7206 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7208 DestroyWindow(hwndRichEdit);
7211 static void test_EM_FINDWORDBREAK_A(void)
7213 static const struct {
7215 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7216 } delimiter_tests[] = {
7217 {0x0a, FALSE}, /* newline */
7218 {0x0b, FALSE}, /* vertical tab */
7219 {0x0c, FALSE}, /* form feed */
7220 {0x0d, FALSE}, /* carriage return */
7221 {0x20, TRUE}, /* space */
7222 {0x61, FALSE}, /* capital letter a */
7225 HWND hwndRichEdit = new_richedit(NULL);
7227 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7228 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7232 buf[0] = delimiter_tests[i].c;
7234 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7235 result = SendMessage(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7238 ok(result == delimiter_tests[i].isdelimiter,
7239 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7240 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7242 ok(result == delimiter_tests[i].isdelimiter,
7243 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7244 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7246 DestroyWindow(hwndRichEdit);
7250 * This test attempts to show the effect of enter on a richedit
7251 * control v1.0 inserts CRLF whereas for higher versions it only
7252 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7253 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7254 * does for higher. The same test is cloned in riched32 and riched20.
7256 static void test_enter(void)
7258 static const struct {
7259 const char *initialtext;
7261 const char *expectedwmtext;
7262 const char *expectedemtext;
7263 const char *expectedemtextcrlf;
7264 } testenteritems[] = {
7265 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7266 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7267 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7268 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7269 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7272 char expectedbuf[1024];
7273 char resultbuf[1024];
7274 HWND hwndRichEdit = new_richedit(NULL);
7277 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7279 char buf[1024] = {0};
7282 const char *expected;
7284 /* Set the text to the initial text */
7285 result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) testenteritems[i].initialtext);
7286 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7289 SendMessage(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7290 simulate_typing_characters(hwndRichEdit, "\r");
7292 /* 1. Retrieve with WM_GETTEXT */
7294 result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf);
7295 expected = testenteritems[i].expectedwmtext;
7298 for (j = 0; j < (UINT)result; j++)
7299 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7300 expectedbuf[0] = '\0';
7301 for (j = 0; j < strlen(expected); j++)
7302 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7304 result = strcmp(expected, buf);
7306 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7307 i, resultbuf, expectedbuf);
7309 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7310 getText.cb = sizeof(buf);
7311 getText.flags = GT_DEFAULT;
7312 getText.codepage = CP_ACP;
7313 getText.lpDefaultChar = NULL;
7314 getText.lpUsedDefChar = NULL;
7316 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7317 expected = testenteritems[i].expectedemtext;
7320 for (j = 0; j < (UINT)result; j++)
7321 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7322 expectedbuf[0] = '\0';
7323 for (j = 0; j < strlen(expected); j++)
7324 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7326 result = strcmp(expected, buf);
7328 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7329 i, resultbuf, expectedbuf);
7331 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7332 getText.cb = sizeof(buf);
7333 getText.flags = GT_USECRLF;
7334 getText.codepage = CP_ACP;
7335 getText.lpDefaultChar = NULL;
7336 getText.lpUsedDefChar = NULL;
7338 result = SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf);
7339 expected = testenteritems[i].expectedemtextcrlf;
7342 for (j = 0; j < (UINT)result; j++)
7343 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7344 expectedbuf[0] = '\0';
7345 for (j = 0; j < strlen(expected); j++)
7346 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7348 result = strcmp(expected, buf);
7350 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7351 i, resultbuf, expectedbuf);
7354 DestroyWindow(hwndRichEdit);
7357 START_TEST( editor )
7360 /* Must explicitly LoadLibrary(). The test has no references to functions in
7361 * RICHED20.DLL, so the linker doesn't actually link to it. */
7362 hmoduleRichEdit = LoadLibrary("RICHED20.DLL");
7363 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7366 test_EM_FINDTEXT(FALSE);
7367 test_EM_FINDTEXT(TRUE);
7369 test_EM_POSFROMCHAR();
7370 test_EM_SCROLLCARET();
7372 test_scrollbar_visibility();
7374 test_EM_LINELENGTH();
7375 test_EM_SETCHARFORMAT();
7376 test_EM_SETTEXTMODE();
7377 test_TM_PLAINTEXT();
7378 test_EM_SETOPTIONS();
7380 test_EM_GETTEXTRANGE();
7381 test_EM_GETSELTEXT();
7382 test_EM_SETUNDOLIMIT();
7384 test_EM_SETTEXTEX();
7385 test_EM_LIMITTEXT();
7386 test_EM_EXLIMITTEXT();
7387 test_EM_GETLIMITTEXT();
7389 test_EM_GETMODIFY();
7393 test_EM_STREAMOUT();
7394 test_EM_STREAMOUT_FONTTBL();
7395 test_EM_StreamIn_Undo();
7396 test_EM_FORMATRANGE();
7397 test_unicode_conversions();
7398 test_EM_GETTEXTLENGTHEX();
7399 test_EM_REPLACESEL(1);
7400 test_EM_REPLACESEL(0);
7402 test_EM_AUTOURLDETECT();
7404 test_undo_coalescing();
7405 test_word_movement();
7406 test_EM_CHARFROMPOS();
7407 test_SETPARAFORMAT();
7411 test_WM_GETDLGCODE();
7414 test_EM_FINDWORDBREAK_W();
7415 test_EM_FINDWORDBREAK_A();
7418 /* Set the environment variable WINETEST_RICHED20 to keep windows
7419 * responsive and open for 30 seconds. This is useful for debugging.
7421 if (getenv( "WINETEST_RICHED20" )) {
7422 keep_responsive(30);
7425 OleFlushClipboard();
7426 ret = FreeLibrary(hmoduleRichEdit);
7427 ok(ret, "error: %d\n", (int) GetLastError());