riched20: Fix link notification conditions and add tests.
[wine.git] / dlls / riched20 / tests / editor.c
blob29af470824ec7c1581c7449a1dfaa015c1e10a36
1 /*
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
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <commdlg.h>
34 #include <time.h>
35 #include <wine/test.h>
37 #define ID_RICHEDITTESTDBUTTON 0x123
39 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
41 #define ok_w3(format, szString1, szString2, szString3) \
42 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
44 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
45 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
46 format, string1, string2, string3);
48 static HMODULE hmoduleRichEdit;
50 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
51 HWND hwnd;
52 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
53 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
54 hmoduleRichEdit, NULL);
55 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
56 return hwnd;
59 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
60 HWND hwnd;
61 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
62 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
63 hmoduleRichEdit, NULL);
64 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
65 return hwnd;
68 static HWND new_richedit(HWND parent) {
69 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
72 static HWND new_richeditW(HWND parent) {
73 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
76 /* Keeps the window reponsive for the deley_time in seconds.
77 * This is useful for debugging a test to see what is happening. */
78 static void keep_responsive(time_t delay_time)
80 MSG msg;
81 time_t end;
83 /* The message pump uses PeekMessage() to empty the queue and then
84 * sleeps for 50ms before retrying the queue. */
85 end = time(NULL) + delay_time;
86 while (time(NULL) < end) {
87 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
88 TranslateMessage(&msg);
89 DispatchMessageA(&msg);
90 } else {
91 Sleep(50);
96 static void simulate_typing_characters(HWND hwnd, const char* szChars)
98 int ret;
100 while (*szChars != '\0') {
101 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
102 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
103 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
104 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
105 szChars++;
109 static BOOL hold_key(int vk)
111 BYTE key_state[256];
112 BOOL result;
114 result = GetKeyboardState(key_state);
115 ok(result, "GetKeyboardState failed.\n");
116 if (!result) return FALSE;
117 key_state[vk] |= 0x80;
118 result = SetKeyboardState(key_state);
119 ok(result, "SetKeyboardState failed.\n");
120 return result != 0;
123 static BOOL release_key(int vk)
125 BYTE key_state[256];
126 BOOL result;
128 result = GetKeyboardState(key_state);
129 ok(result, "GetKeyboardState failed.\n");
130 if (!result) return FALSE;
131 key_state[vk] &= ~0x80;
132 result = SetKeyboardState(key_state);
133 ok(result, "SetKeyboardState failed.\n");
134 return result != 0;
137 static const char haystack[] = "WINEWine wineWine wine WineWine";
138 /* ^0 ^10 ^20 ^30 */
140 struct find_s {
141 int start;
142 int end;
143 const char *needle;
144 int flags;
145 int expected_loc;
149 static struct find_s find_tests[] = {
150 /* Find in empty text */
151 {0, -1, "foo", FR_DOWN, -1},
152 {0, -1, "foo", 0, -1},
153 {0, -1, "", FR_DOWN, -1},
154 {20, 5, "foo", FR_DOWN, -1},
155 {5, 20, "foo", FR_DOWN, -1}
158 static struct find_s find_tests2[] = {
159 /* No-result find */
160 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
161 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
163 /* Subsequent finds */
164 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
165 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
166 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
167 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
169 /* Find backwards */
170 {19, 20, "Wine", FR_MATCHCASE, 13},
171 {10, 20, "Wine", FR_MATCHCASE, 4},
172 {20, 10, "Wine", FR_MATCHCASE, 13},
174 /* Case-insensitive */
175 {1, 31, "wInE", FR_DOWN, 4},
176 {1, 31, "Wine", FR_DOWN, 4},
178 /* High-to-low ranges */
179 {20, 5, "Wine", FR_DOWN, -1},
180 {2, 1, "Wine", FR_DOWN, -1},
181 {30, 29, "Wine", FR_DOWN, -1},
182 {20, 5, "Wine", 0, 13},
184 /* Find nothing */
185 {5, 10, "", FR_DOWN, -1},
186 {10, 5, "", FR_DOWN, -1},
187 {0, -1, "", FR_DOWN, -1},
188 {10, 5, "", 0, -1},
190 /* Whole-word search */
191 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
192 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
193 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
194 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
195 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
196 {11, -1, "winewine", FR_WHOLEWORD, 0},
197 {31, -1, "winewine", FR_WHOLEWORD, 23},
199 /* Bad ranges */
200 {5, 200, "XXX", FR_DOWN, -1},
201 {-20, 20, "Wine", FR_DOWN, -1},
202 {-20, 20, "Wine", FR_DOWN, -1},
203 {-15, -20, "Wine", FR_DOWN, -1},
204 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
206 /* Check the case noted in bug 4479 where matches at end aren't recognized */
207 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
208 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
210 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
211 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
213 /* The backwards case of bug 4479; bounds look right
214 * Fails because backward find is wrong */
215 {19, 20, "WINE", FR_MATCHCASE, 0},
216 {0, 20, "WINE", FR_MATCHCASE, -1},
218 {0, -1, "wineWine wine", 0, -1},
221 static WCHAR *atowstr(const char *str)
223 WCHAR *ret;
224 DWORD len;
225 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
226 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
227 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
228 return ret;
231 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
233 int findloc;
235 if(unicode){
236 FINDTEXTW ftw;
237 memset(&ftw, 0, sizeof(ftw));
238 ftw.chrg.cpMin = f->start;
239 ftw.chrg.cpMax = f->end;
240 ftw.lpstrText = atowstr(f->needle);
242 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
243 ok(findloc == f->expected_loc,
244 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
245 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
247 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
248 ok(findloc == f->expected_loc,
249 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
250 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
252 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
253 }else{
254 FINDTEXTA fta;
255 memset(&fta, 0, sizeof(fta));
256 fta.chrg.cpMin = f->start;
257 fta.chrg.cpMax = f->end;
258 fta.lpstrText = f->needle;
260 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
261 ok(findloc == f->expected_loc,
262 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
263 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
267 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
268 int id, BOOL unicode)
270 int findloc;
271 int expected_end_loc;
273 if(unicode){
274 FINDTEXTEXW ftw;
275 memset(&ftw, 0, sizeof(ftw));
276 ftw.chrg.cpMin = f->start;
277 ftw.chrg.cpMax = f->end;
278 ftw.lpstrText = atowstr(f->needle);
279 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
280 ok(findloc == f->expected_loc,
281 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
282 name, id, f->needle, f->start, f->end, f->flags, findloc);
283 ok(ftw.chrgText.cpMin == f->expected_loc,
284 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
285 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
286 expected_end_loc = ((f->expected_loc == -1) ? -1
287 : f->expected_loc + strlen(f->needle));
288 ok(ftw.chrgText.cpMax == expected_end_loc,
289 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
290 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
291 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
292 }else{
293 FINDTEXTEXA fta;
294 memset(&fta, 0, sizeof(fta));
295 fta.chrg.cpMin = f->start;
296 fta.chrg.cpMax = f->end;
297 fta.lpstrText = f->needle;
298 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
299 ok(findloc == f->expected_loc,
300 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
301 name, id, f->needle, f->start, f->end, f->flags, findloc);
302 ok(fta.chrgText.cpMin == f->expected_loc,
303 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
304 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
305 expected_end_loc = ((f->expected_loc == -1) ? -1
306 : f->expected_loc + strlen(f->needle));
307 ok(fta.chrgText.cpMax == expected_end_loc,
308 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
309 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
313 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
314 int num_tests, BOOL unicode)
316 int i;
318 for (i = 0; i < num_tests; i++) {
319 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
320 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
324 static void test_EM_FINDTEXT(BOOL unicode)
326 HWND hwndRichEdit;
327 CHARFORMAT2A cf2;
329 if(unicode)
330 hwndRichEdit = new_richeditW(NULL);
331 else
332 hwndRichEdit = new_richedit(NULL);
334 /* Empty rich edit control */
335 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
336 sizeof(find_tests)/sizeof(struct find_s), unicode);
338 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
340 /* Haystack text */
341 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
342 sizeof(find_tests2)/sizeof(struct find_s), unicode);
344 /* Setting a format on an arbitrary range should have no effect in search
345 results. This tests correct offset reporting across runs. */
346 cf2.cbSize = sizeof(CHARFORMAT2A);
347 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
348 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
349 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
350 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
351 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
353 /* Haystack text, again */
354 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
355 sizeof(find_tests2)/sizeof(struct find_s), unicode);
357 /* Yet another range */
358 cf2.dwMask = CFM_BOLD | cf2.dwMask;
359 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
360 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
361 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
363 /* Haystack text, again */
364 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
365 sizeof(find_tests2)/sizeof(struct find_s), unicode);
367 DestroyWindow(hwndRichEdit);
370 static const struct getline_s {
371 int line;
372 size_t buffer_len;
373 const char *text;
374 } gl[] = {
375 {0, 10, "foo bar\r"},
376 {1, 10, "\r"},
377 {2, 10, "bar\r"},
378 {3, 10, "\r"},
380 /* Buffer smaller than line length */
381 {0, 2, "foo bar\r"},
382 {0, 1, "foo bar\r"},
383 {0, 0, "foo bar\r"}
386 static void test_EM_GETLINE(void)
388 int i;
389 HWND hwndRichEdit = new_richedit(NULL);
390 static const int nBuf = 1024;
391 char dest[1024], origdest[1024];
392 const char text[] = "foo bar\n"
393 "\n"
394 "bar\n";
396 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
398 memset(origdest, 0xBB, nBuf);
399 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
401 int nCopied;
402 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
403 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
404 memset(dest, 0xBB, nBuf);
405 *(WORD *) dest = gl[i].buffer_len;
407 /* EM_GETLINE appends a "\r\0" to the end of the line
408 * nCopied counts up to and including the '\r' */
409 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
410 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
411 expected_nCopied);
412 /* two special cases since a parameter is passed via dest */
413 if (gl[i].buffer_len == 0)
414 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
415 "buffer_len=0\n");
416 else if (gl[i].buffer_len == 1)
417 ok(dest[0] == gl[i].text[0] && !dest[1] &&
418 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
419 else
421 /* Prepare hex strings of buffers to dump on failure. */
422 char expectedbuf[1024];
423 char resultbuf[1024];
424 int j;
425 resultbuf[0] = '\0';
426 for (j = 0; j < 32; j++)
427 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
428 expectedbuf[0] = '\0';
429 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
430 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
431 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
432 sprintf(expectedbuf+strlen(expectedbuf), "??");
433 for (; j < 32; j++) /* Bytes after declared buffer size */
434 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
436 /* Test the part of the buffer that is expected to be written according
437 * to the MSDN documentation fo EM_GETLINE, which does not state that
438 * a NULL terminating character will be added unless no text is copied.
440 * Windows NT does not append a NULL terminating character, but
441 * Windows 2000 and up do append a NULL terminating character if there
442 * is space in the buffer. The test will ignore this difference. */
443 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
444 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
445 i, expected_bytes_written, expectedbuf, resultbuf);
446 /* Test the part of the buffer after the declared length to make sure
447 * there are no buffer overruns. */
448 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
449 nBuf - gl[i].buffer_len),
450 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
451 i, expected_bytes_written, expectedbuf, resultbuf);
455 DestroyWindow(hwndRichEdit);
458 static void test_EM_LINELENGTH(void)
460 HWND hwndRichEdit = new_richedit(NULL);
461 const char * text =
462 "richedit1\r"
463 "richedit1\n"
464 "richedit1\r\n"
465 "richedit1";
466 int offset_test[10][2] = {
467 {0, 9},
468 {5, 9},
469 {10, 9},
470 {15, 9},
471 {20, 9},
472 {25, 9},
473 {30, 9},
474 {35, 9},
475 {40, 0},
476 {45, 0},
478 int i;
479 LRESULT result;
481 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
483 for (i = 0; i < 10; i++) {
484 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
485 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
486 offset_test[i][0], result, offset_test[i][1]);
489 DestroyWindow(hwndRichEdit);
492 static int get_scroll_pos_y(HWND hwnd)
494 POINT p = {-1, -1};
495 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
496 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
497 return p.y;
500 static void move_cursor(HWND hwnd, LONG charindex)
502 CHARRANGE cr;
503 cr.cpMax = charindex;
504 cr.cpMin = charindex;
505 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
508 static void line_scroll(HWND hwnd, int amount)
510 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
513 static void test_EM_SCROLLCARET(void)
515 int prevY, curY;
516 const char text[] = "aa\n"
517 "this is a long line of text that should be longer than the "
518 "control's width\n"
519 "cc\n"
520 "dd\n"
521 "ee\n"
522 "ff\n"
523 "gg\n"
524 "hh\n";
525 /* The richedit window height needs to be large enough vertically to fit in
526 * more than two lines of text, so the new_richedit function can't be used
527 * since a height of 60 was not large enough on some systems.
529 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
530 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
531 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
532 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
534 /* Can't verify this */
535 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
537 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
539 /* Caret above visible window */
540 line_scroll(hwndRichEdit, 3);
541 prevY = get_scroll_pos_y(hwndRichEdit);
542 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
543 curY = get_scroll_pos_y(hwndRichEdit);
544 ok(prevY != curY, "%d == %d\n", prevY, curY);
546 /* Caret below visible window */
547 move_cursor(hwndRichEdit, sizeof(text) - 1);
548 line_scroll(hwndRichEdit, -3);
549 prevY = get_scroll_pos_y(hwndRichEdit);
550 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
551 curY = get_scroll_pos_y(hwndRichEdit);
552 ok(prevY != curY, "%d == %d\n", prevY, curY);
554 /* Caret in visible window */
555 move_cursor(hwndRichEdit, sizeof(text) - 2);
556 prevY = get_scroll_pos_y(hwndRichEdit);
557 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
558 curY = get_scroll_pos_y(hwndRichEdit);
559 ok(prevY == curY, "%d != %d\n", prevY, curY);
561 /* Caret still in visible window */
562 line_scroll(hwndRichEdit, -1);
563 prevY = get_scroll_pos_y(hwndRichEdit);
564 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
565 curY = get_scroll_pos_y(hwndRichEdit);
566 ok(prevY == curY, "%d != %d\n", prevY, curY);
568 DestroyWindow(hwndRichEdit);
571 static void test_EM_POSFROMCHAR(void)
573 HWND hwndRichEdit = new_richedit(NULL);
574 int i, expected;
575 LRESULT result;
576 unsigned int height = 0;
577 int xpos = 0;
578 POINTL pt;
579 LOCALESIGNATURE sig;
580 BOOL rtl;
581 static const char text[] = "aa\n"
582 "this is a long line of text that should be longer than the "
583 "control's width\n"
584 "cc\n"
585 "dd\n"
586 "ee\n"
587 "ff\n"
588 "gg\n"
589 "hh\n";
591 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
592 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
593 (sig.lsUsb[3] & 0x08000000) != 0);
595 /* Fill the control to lines to ensure that most of them are offscreen */
596 for (i = 0; i < 50; i++)
598 /* Do not modify the string; it is exactly 16 characters long. */
599 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
600 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
604 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
605 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
606 Richedit 3.0 accepts either of the above API conventions.
609 /* Testing Richedit 2.0 API format */
611 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
612 Since all lines are identical and drawn with the same font,
613 they should have the same height... right?
615 for (i = 0; i < 50; i++)
617 /* All the lines are 16 characters long */
618 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
619 if (i == 0)
621 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
622 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
623 xpos = LOWORD(result);
625 else if (i == 1)
627 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
628 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
629 height = HIWORD(result);
631 else
633 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
634 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
638 /* Testing position at end of text */
639 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
640 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
641 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
643 /* Testing position way past end of text */
644 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
645 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
646 expected = (rtl ? 8 : 1);
647 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
649 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
650 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
651 for (i = 0; i < 50; i++)
653 /* All the lines are 16 characters long */
654 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
655 ok((signed short)(HIWORD(result)) == (i - 1) * height,
656 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
657 (signed short)(HIWORD(result)), (i - 1) * height);
658 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
661 /* Testing position at end of text */
662 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
663 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
664 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
666 /* Testing position way past end of text */
667 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
668 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
669 expected = (rtl ? 8 : 1);
670 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
672 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
673 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
674 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
676 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
677 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
678 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
679 xpos = LOWORD(result);
681 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
682 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
683 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
684 ok((signed short)(LOWORD(result)) < xpos,
685 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
686 (signed short)(LOWORD(result)), xpos);
687 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
689 /* Test around end of text that doesn't end in a newline. */
690 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
691 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
692 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
693 ok(pt.x > 1, "pt.x = %d\n", pt.x);
694 xpos = pt.x;
695 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
696 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
697 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
698 xpos = (rtl ? pt.x + 7 : pt.x);
699 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
700 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
701 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
703 /* Try a negative position. */
704 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
705 ok(pt.x == 1, "pt.x = %d\n", pt.x);
707 DestroyWindow(hwndRichEdit);
710 static void test_EM_SETCHARFORMAT(void)
712 HWND hwndRichEdit = new_richedit(NULL);
713 CHARFORMAT2A cf2;
714 int rc = 0;
715 int tested_effects[] = {
716 CFE_BOLD,
717 CFE_ITALIC,
718 CFE_UNDERLINE,
719 CFE_STRIKEOUT,
720 CFE_PROTECTED,
721 CFE_LINK,
722 CFE_SUBSCRIPT,
723 CFE_SUPERSCRIPT,
726 int i;
727 CHARRANGE cr;
728 LOCALESIGNATURE sig;
729 BOOL rtl;
731 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
732 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
733 (sig.lsUsb[3] & 0x08000000) != 0);
735 /* Invalid flags, CHARFORMAT2 structure blanked out */
736 memset(&cf2, 0, sizeof(cf2));
737 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
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 = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
743 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
745 /* A valid flag, CHARFORMAT2 structure blanked out */
746 memset(&cf2, 0, sizeof(cf2));
747 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
748 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
750 /* A valid flag, CHARFORMAT2 structure blanked out */
751 memset(&cf2, 0, sizeof(cf2));
752 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
753 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
755 /* A valid flag, CHARFORMAT2 structure blanked out */
756 memset(&cf2, 0, sizeof(cf2));
757 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
758 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
760 /* Invalid flags, CHARFORMAT2 structure minimally filled */
761 memset(&cf2, 0, sizeof(cf2));
762 cf2.cbSize = sizeof(CHARFORMAT2A);
763 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
764 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
765 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
766 ok(rc == FALSE, "Should not be able to undo here.\n");
767 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
769 /* A valid flag, CHARFORMAT2 structure minimally filled */
770 memset(&cf2, 0, sizeof(cf2));
771 cf2.cbSize = sizeof(CHARFORMAT2A);
772 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
773 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
774 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
775 ok(rc == FALSE, "Should not be able to undo here.\n");
776 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
778 /* A valid flag, CHARFORMAT2 structure minimally filled */
779 memset(&cf2, 0, sizeof(cf2));
780 cf2.cbSize = sizeof(CHARFORMAT2A);
781 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
784 ok(rc == FALSE, "Should not be able to undo here.\n");
785 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
787 /* A valid flag, CHARFORMAT2 structure minimally filled */
788 memset(&cf2, 0, sizeof(cf2));
789 cf2.cbSize = sizeof(CHARFORMAT2A);
790 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
792 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
793 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
794 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
796 /* A valid flag, CHARFORMAT2 structure minimally filled */
797 memset(&cf2, 0, sizeof(cf2));
798 cf2.cbSize = sizeof(CHARFORMAT2A);
799 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
800 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
801 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
802 ok(rc == TRUE, "Should not be able to undo here.\n");
803 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
805 cf2.cbSize = sizeof(CHARFORMAT2A);
806 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
808 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
809 cf2.cbSize = sizeof(CHARFORMAT2A);
810 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
811 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
812 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
814 /* wParam==0 is default char format, does not set modify */
815 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
816 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
817 ok(rc == 0, "Text marked as modified, expected not modified!\n");
818 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
819 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
820 if (! rtl)
822 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
823 ok(rc == 0, "Text marked as modified, expected not modified!\n");
825 else
826 skip("RTL language found\n");
828 /* wParam==SCF_SELECTION sets modify if nonempty selection */
829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
830 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
831 ok(rc == 0, "Text marked as modified, expected not modified!\n");
832 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
833 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
835 ok(rc == 0, "Text marked as modified, expected not modified!\n");
837 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
838 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
839 ok(rc == 0, "Text marked as modified, expected not modified!\n");
840 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
841 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
842 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
843 ok(rc == 0, "Text marked as modified, expected not modified!\n");
844 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
845 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
846 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
847 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
848 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
850 /* wParam==SCF_ALL sets modify regardless of whether text is present */
851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
852 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
853 ok(rc == 0, "Text marked as modified, expected not modified!\n");
854 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
855 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
856 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
857 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
859 DestroyWindow(hwndRichEdit);
861 /* EM_GETCHARFORMAT tests */
862 for (i = 0; tested_effects[i]; i++)
864 hwndRichEdit = new_richedit(NULL);
865 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
867 /* Need to set a TrueType font to get consistent CFM_BOLD results */
868 memset(&cf2, 0, sizeof(CHARFORMAT2A));
869 cf2.cbSize = sizeof(CHARFORMAT2A);
870 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
871 cf2.dwEffects = 0;
872 strcpy(cf2.szFaceName, "Courier New");
873 cf2.wWeight = FW_DONTCARE;
874 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
876 memset(&cf2, 0, sizeof(CHARFORMAT2A));
877 cf2.cbSize = sizeof(CHARFORMAT2A);
878 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
879 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
880 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
881 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
883 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
884 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
885 ok((cf2.dwEffects & tested_effects[i]) == 0,
886 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
888 memset(&cf2, 0, sizeof(CHARFORMAT2A));
889 cf2.cbSize = sizeof(CHARFORMAT2A);
890 cf2.dwMask = tested_effects[i];
891 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
892 cf2.dwMask = CFM_SUPERSCRIPT;
893 cf2.dwEffects = tested_effects[i];
894 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
895 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
897 memset(&cf2, 0, sizeof(CHARFORMAT2A));
898 cf2.cbSize = sizeof(CHARFORMAT2A);
899 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
900 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
902 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
904 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
905 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
906 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
907 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
909 memset(&cf2, 0, sizeof(CHARFORMAT2A));
910 cf2.cbSize = sizeof(CHARFORMAT2A);
911 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
912 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
913 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
914 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
916 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
917 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
918 ok((cf2.dwEffects & tested_effects[i]) == 0,
919 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
921 memset(&cf2, 0, sizeof(CHARFORMAT2A));
922 cf2.cbSize = sizeof(CHARFORMAT2A);
923 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
924 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
925 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
926 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
928 (cf2.dwMask & tested_effects[i]) == 0),
929 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 DestroyWindow(hwndRichEdit);
934 for (i = 0; tested_effects[i]; i++)
936 hwndRichEdit = new_richedit(NULL);
937 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
939 /* Need to set a TrueType font to get consistent CFM_BOLD results */
940 memset(&cf2, 0, sizeof(CHARFORMAT2A));
941 cf2.cbSize = sizeof(CHARFORMAT2A);
942 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
943 cf2.dwEffects = 0;
944 strcpy(cf2.szFaceName, "Courier New");
945 cf2.wWeight = FW_DONTCARE;
946 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
948 memset(&cf2, 0, sizeof(CHARFORMAT2A));
949 cf2.cbSize = sizeof(CHARFORMAT2A);
950 cf2.dwMask = tested_effects[i];
951 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952 cf2.dwMask = CFM_SUPERSCRIPT;
953 cf2.dwEffects = tested_effects[i];
954 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
955 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
964 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966 ok((cf2.dwEffects & tested_effects[i]) == 0,
967 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
969 memset(&cf2, 0, sizeof(CHARFORMAT2A));
970 cf2.cbSize = sizeof(CHARFORMAT2A);
971 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
976 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
979 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
981 memset(&cf2, 0, sizeof(CHARFORMAT2A));
982 cf2.cbSize = sizeof(CHARFORMAT2A);
983 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
988 (cf2.dwMask & tested_effects[i]) == 0),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
991 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
993 DestroyWindow(hwndRichEdit);
996 /* Effects applied on an empty selection should take effect when selection is
997 replaced with text */
998 hwndRichEdit = new_richedit(NULL);
999 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1000 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1002 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1003 cf2.cbSize = sizeof(CHARFORMAT2A);
1004 cf2.dwMask = CFM_BOLD;
1005 cf2.dwEffects = CFE_BOLD;
1006 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1008 /* Selection is now nonempty */
1009 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1011 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1012 cf2.cbSize = sizeof(CHARFORMAT2A);
1013 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1014 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1017 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1018 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1019 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1022 /* Set two effects on an empty selection */
1023 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1024 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1026 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1027 cf2.cbSize = sizeof(CHARFORMAT2A);
1028 cf2.dwMask = CFM_BOLD;
1029 cf2.dwEffects = CFE_BOLD;
1030 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1031 cf2.dwMask = CFM_ITALIC;
1032 cf2.dwEffects = CFE_ITALIC;
1033 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1035 /* Selection is now nonempty */
1036 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1038 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1039 cf2.cbSize = sizeof(CHARFORMAT2A);
1040 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1041 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1043 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1044 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1045 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1046 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1048 /* Setting the (empty) selection to exactly the same place as before should
1049 NOT clear the insertion style! */
1050 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1051 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1053 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1054 cf2.cbSize = sizeof(CHARFORMAT2A);
1055 cf2.dwMask = CFM_BOLD;
1056 cf2.dwEffects = CFE_BOLD;
1057 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1059 /* Empty selection in same place, insert style should NOT be forgotten here. */
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1062 /* Selection is now nonempty */
1063 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1065 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1066 cf2.cbSize = sizeof(CHARFORMAT2A);
1067 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1068 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1070 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1071 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1072 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1073 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1075 /* Ditto with EM_EXSETSEL */
1076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1077 cr.cpMin = 2; cr.cpMax = 2;
1078 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1080 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1081 cf2.cbSize = sizeof(CHARFORMAT2A);
1082 cf2.dwMask = CFM_BOLD;
1083 cf2.dwEffects = CFE_BOLD;
1084 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1086 /* Empty selection in same place, insert style should NOT be forgotten here. */
1087 cr.cpMin = 2; cr.cpMax = 2;
1088 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1090 /* Selection is now nonempty */
1091 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1093 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1094 cf2.cbSize = sizeof(CHARFORMAT2A);
1095 cr.cpMin = 2; cr.cpMax = 6;
1096 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1097 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1100 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1101 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1102 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1104 DestroyWindow(hwndRichEdit);
1107 static void test_EM_SETTEXTMODE(void)
1109 HWND hwndRichEdit = new_richedit(NULL);
1110 CHARFORMAT2A cf2, cf2test;
1111 CHARRANGE cr;
1112 int rc = 0;
1114 /*Attempt to use mutually exclusive modes*/
1115 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1116 ok(rc == E_INVALIDARG,
1117 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1119 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1120 /*Insert text into the control*/
1122 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1124 /*Attempt to change the control to plain text mode*/
1125 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1126 ok(rc == E_UNEXPECTED,
1127 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1129 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1130 If rich text is pasted, it should have the same formatting as the rest
1131 of the text in the control*/
1133 /*Italicize the text
1134 *NOTE: If the default text was already italicized, the test will simply
1135 reverse; in other words, it will copy a regular "wine" into a plain
1136 text window that uses an italicized format*/
1137 cf2.cbSize = sizeof(CHARFORMAT2A);
1138 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1140 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1141 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1143 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1144 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1146 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1147 however, SCF_ALL has been implemented*/
1148 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1149 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1151 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1152 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1154 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1156 /*Select the string "wine"*/
1157 cr.cpMin = 0;
1158 cr.cpMax = 4;
1159 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1161 /*Copy the italicized "wine" to the clipboard*/
1162 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1164 /*Reset the formatting to default*/
1165 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1166 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1167 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1169 /*Clear the text in the control*/
1170 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1172 /*Switch to Plain Text Mode*/
1173 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1174 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1176 /*Input "wine" again in normal format*/
1177 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1179 /*Paste the italicized "wine" into the control*/
1180 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1182 /*Select a character from the first "wine" string*/
1183 cr.cpMin = 2;
1184 cr.cpMax = 3;
1185 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1187 /*Retrieve its formatting*/
1188 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1190 /*Select a character from the second "wine" string*/
1191 cr.cpMin = 5;
1192 cr.cpMax = 6;
1193 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1195 /*Retrieve its formatting*/
1196 cf2test.cbSize = sizeof(CHARFORMAT2A);
1197 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1199 /*Compare the two formattings*/
1200 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1201 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1202 cf2.dwEffects, cf2test.dwEffects);
1203 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1204 printing "wine" in the current format(normal)
1205 pasting "wine" from the clipboard(italicized)
1206 comparing the two formats(should differ)*/
1208 /*Attempt to switch with text in control*/
1209 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1210 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1212 /*Clear control*/
1213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1215 /*Switch into Rich Text mode*/
1216 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1217 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1219 /*Print "wine" in normal formatting into the control*/
1220 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1222 /*Paste italicized "wine" into the control*/
1223 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1225 /*Select text from the first "wine" string*/
1226 cr.cpMin = 1;
1227 cr.cpMax = 3;
1228 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1230 /*Retrieve its formatting*/
1231 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1233 /*Select text from the second "wine" string*/
1234 cr.cpMin = 6;
1235 cr.cpMax = 7;
1236 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1238 /*Retrieve its formatting*/
1239 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1241 /*Test that the two formattings are not the same*/
1242 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1243 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1244 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1246 DestroyWindow(hwndRichEdit);
1249 static void test_SETPARAFORMAT(void)
1251 HWND hwndRichEdit = new_richedit(NULL);
1252 PARAFORMAT2 fmt;
1253 HRESULT ret;
1254 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1255 fmt.cbSize = sizeof(PARAFORMAT2);
1256 fmt.dwMask = PFM_ALIGNMENT;
1257 fmt.wAlignment = PFA_LEFT;
1259 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1260 ok(ret != 0, "expected non-zero got %d\n", ret);
1262 fmt.cbSize = sizeof(PARAFORMAT2);
1263 fmt.dwMask = -1;
1264 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1265 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1266 * between richedit different native builds of riched20.dll
1267 * used on different Windows versions. */
1268 ret &= ~PFM_TABLEROWDELIMITER;
1269 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1271 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1272 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1274 DestroyWindow(hwndRichEdit);
1277 static void test_TM_PLAINTEXT(void)
1279 /*Tests plain text properties*/
1281 HWND hwndRichEdit = new_richedit(NULL);
1282 CHARFORMAT2A cf2, cf2test;
1283 CHARRANGE cr;
1284 int rc = 0;
1286 /*Switch to plain text mode*/
1288 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1289 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1291 /*Fill control with text*/
1293 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1295 /*Select some text and bold it*/
1297 cr.cpMin = 10;
1298 cr.cpMax = 20;
1299 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1300 cf2.cbSize = sizeof(CHARFORMAT2A);
1301 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1303 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1304 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1306 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1307 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1309 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1310 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1312 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1313 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1315 /*Get the formatting of those characters*/
1317 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1319 /*Get the formatting of some other characters*/
1320 cf2test.cbSize = sizeof(CHARFORMAT2A);
1321 cr.cpMin = 21;
1322 cr.cpMax = 30;
1323 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1324 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1326 /*Test that they are the same as plain text allows only one formatting*/
1328 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1329 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1330 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1332 /*Fill the control with a "wine" string, which when inserted will be bold*/
1334 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1336 /*Copy the bolded "wine" string*/
1338 cr.cpMin = 0;
1339 cr.cpMax = 4;
1340 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1341 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1343 /*Swap back to rich text*/
1345 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1346 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1348 /*Set the default formatting to bold italics*/
1350 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1351 cf2.dwMask |= CFM_ITALIC;
1352 cf2.dwEffects ^= CFE_ITALIC;
1353 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1354 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1356 /*Set the text in the control to "wine", which will be bold and italicized*/
1358 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1360 /*Paste the plain text "wine" string, which should take the insert
1361 formatting, which at the moment is bold italics*/
1363 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1365 /*Select the first "wine" string and retrieve its formatting*/
1367 cr.cpMin = 1;
1368 cr.cpMax = 3;
1369 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1370 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1372 /*Select the second "wine" string and retrieve its formatting*/
1374 cr.cpMin = 5;
1375 cr.cpMax = 7;
1376 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1377 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1379 /*Compare the two formattings. They should be the same.*/
1381 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1382 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1383 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1384 DestroyWindow(hwndRichEdit);
1387 static void test_WM_GETTEXT(void)
1389 HWND hwndRichEdit = new_richedit(NULL);
1390 static const char text[] = "Hello. My name is RichEdit!";
1391 static const char text2[] = "Hello. My name is RichEdit!\r";
1392 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1393 char buffer[1024] = {0};
1394 int result;
1396 /* Baseline test with normal-sized buffer */
1397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1398 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1399 ok(result == lstrlenA(buffer),
1400 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1401 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1402 result = strcmp(buffer,text);
1403 ok(result == 0,
1404 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1406 /* Test for returned value of WM_GETTEXTLENGTH */
1407 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1408 ok(result == lstrlenA(text),
1409 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1410 result, lstrlenA(text));
1412 /* Test for behavior in overflow case */
1413 memset(buffer, 0, 1024);
1414 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1415 ok(result == 0 ||
1416 result == lstrlenA(text) - 1, /* XP, win2k3 */
1417 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1418 result = strcmp(buffer,text);
1419 if (result)
1420 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1421 ok(result == 0,
1422 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1424 /* Baseline test with normal-sized buffer and carriage return */
1425 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1426 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1427 ok(result == lstrlenA(buffer),
1428 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1429 result = strcmp(buffer,text2_after);
1430 ok(result == 0,
1431 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1433 /* Test for returned value of WM_GETTEXTLENGTH */
1434 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1435 ok(result == lstrlenA(text2_after),
1436 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1437 result, lstrlenA(text2_after));
1439 /* Test for behavior of CRLF conversion in case of overflow */
1440 memset(buffer, 0, 1024);
1441 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1442 ok(result == 0 ||
1443 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1444 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1445 result = strcmp(buffer,text2);
1446 if (result)
1447 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1448 ok(result == 0,
1449 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1451 DestroyWindow(hwndRichEdit);
1454 static void test_EM_GETTEXTRANGE(void)
1456 HWND hwndRichEdit = new_richedit(NULL);
1457 const char * text1 = "foo bar\r\nfoo bar";
1458 const char * text2 = "foo bar\rfoo bar";
1459 const char * expect = "bar\rfoo";
1460 char buffer[1024] = {0};
1461 LRESULT result;
1462 TEXTRANGEA textRange;
1464 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1466 textRange.lpstrText = buffer;
1467 textRange.chrg.cpMin = 4;
1468 textRange.chrg.cpMax = 11;
1469 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1470 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1471 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1473 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1475 textRange.lpstrText = buffer;
1476 textRange.chrg.cpMin = 4;
1477 textRange.chrg.cpMax = 11;
1478 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1479 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1482 /* cpMax of text length is used instead of -1 in this case */
1483 textRange.lpstrText = buffer;
1484 textRange.chrg.cpMin = 0;
1485 textRange.chrg.cpMax = -1;
1486 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1487 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1488 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1490 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1491 textRange.lpstrText = buffer;
1492 textRange.chrg.cpMin = -1;
1493 textRange.chrg.cpMax = 1;
1494 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1495 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1496 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1498 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1499 textRange.lpstrText = buffer;
1500 textRange.chrg.cpMin = 1;
1501 textRange.chrg.cpMax = -1;
1502 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1503 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1504 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1506 /* no end character is copied if cpMax - cpMin < 0 */
1507 textRange.lpstrText = buffer;
1508 textRange.chrg.cpMin = 5;
1509 textRange.chrg.cpMax = 5;
1510 result = SendMessageA(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 text length is used if cpMax > text length*/
1515 textRange.lpstrText = buffer;
1516 textRange.chrg.cpMin = 0;
1517 textRange.chrg.cpMax = 1000;
1518 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1519 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1520 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1522 DestroyWindow(hwndRichEdit);
1525 static void test_EM_GETSELTEXT(void)
1527 HWND hwndRichEdit = new_richedit(NULL);
1528 const char * text1 = "foo bar\r\nfoo bar";
1529 const char * text2 = "foo bar\rfoo bar";
1530 const char * expect = "bar\rfoo";
1531 char buffer[1024] = {0};
1532 LRESULT result;
1534 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1536 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1537 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1538 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1539 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1541 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1543 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1544 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1545 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1546 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1548 DestroyWindow(hwndRichEdit);
1551 /* FIXME: need to test unimplemented options and robustly test wparam */
1552 static void test_EM_SETOPTIONS(void)
1554 HWND hwndRichEdit;
1555 static const char text[] = "Hello. My name is RichEdit!";
1556 char buffer[1024] = {0};
1557 DWORD dwStyle, options, oldOptions;
1558 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1559 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1560 ES_SELECTIONBAR|ES_VERTICAL;
1562 /* Test initial options. */
1563 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1564 0, 0, 200, 60, NULL, NULL,
1565 hmoduleRichEdit, NULL);
1566 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1567 RICHEDIT_CLASS20A, (int) GetLastError());
1568 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1569 ok(options == 0, "Incorrect initial options %x\n", options);
1570 DestroyWindow(hwndRichEdit);
1572 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1573 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1574 0, 0, 200, 60, NULL, NULL,
1575 hmoduleRichEdit, NULL);
1576 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1577 RICHEDIT_CLASS20A, (int) GetLastError());
1578 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1579 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1580 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1581 "Incorrect initial options %x\n", options);
1583 /* NEGATIVE TESTING - NO OPTIONS SET */
1584 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1585 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1587 /* testing no readonly by sending 'a' to the control*/
1588 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1589 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1590 ok(buffer[0]=='a',
1591 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1592 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1594 /* READONLY - sending 'a' to the control */
1595 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1596 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1597 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1598 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1599 ok(buffer[0]==text[0],
1600 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1602 /* EM_SETOPTIONS changes the window style, but changing the
1603 * window style does not change the options. */
1604 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1605 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1606 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1607 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1608 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1609 /* Confirm that the text is still read only. */
1610 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1611 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1612 ok(buffer[0]==text[0],
1613 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1615 oldOptions = options;
1616 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1617 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1618 ok(options == oldOptions,
1619 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1621 DestroyWindow(hwndRichEdit);
1624 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1626 CHARFORMAT2A text_format;
1627 text_format.cbSize = sizeof(text_format);
1628 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1629 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1630 return (text_format.dwEffects & CFE_LINK) != 0;
1633 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1635 BOOL link_present = FALSE;
1637 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1638 if (is_url)
1639 { /* control text is url; should get CFE_LINK */
1640 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1642 else
1644 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1648 static HWND new_static_wnd(HWND parent) {
1649 return new_window("Static", 0, parent);
1652 static void test_EM_AUTOURLDETECT(void)
1654 /* DO NOT change the properties of the first two elements. To shorten the
1655 tests, all tests after WM_SETTEXT test just the first two elements -
1656 one non-URL and one URL */
1657 struct urls_s {
1658 const char *text;
1659 BOOL is_url;
1660 } urls[12] = {
1661 {"winehq.org", FALSE},
1662 {"http://www.winehq.org", TRUE},
1663 {"http//winehq.org", FALSE},
1664 {"ww.winehq.org", FALSE},
1665 {"www.winehq.org", TRUE},
1666 {"ftp://192.168.1.1", TRUE},
1667 {"ftp//192.168.1.1", FALSE},
1668 {"mailto:your@email.com", TRUE},
1669 {"prospero:prosperoserver", TRUE},
1670 {"telnet:test", TRUE},
1671 {"news:newserver", TRUE},
1672 {"wais:waisserver", TRUE}
1675 int i, j;
1676 int urlRet=-1;
1677 HWND hwndRichEdit, parent;
1679 /* All of the following should cause the URL to be detected */
1680 const char * templates_delim[] = {
1681 "This is some text with X on it",
1682 "This is some text with (X) on it",
1683 "This is some text with X\r on it",
1684 "This is some text with ---X--- on it",
1685 "This is some text with \"X\" on it",
1686 "This is some text with 'X' on it",
1687 "This is some text with 'X' on it",
1688 "This is some text with :X: on it",
1690 "This text ends with X",
1692 "This is some text with X) on it",
1693 "This is some text with X--- on it",
1694 "This is some text with X\" on it",
1695 "This is some text with X' on it",
1696 "This is some text with X: on it",
1698 "This is some text with (X on it",
1699 "This is some text with \rX 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",
1705 /* None of these should cause the URL to be detected */
1706 const char * templates_non_delim[] = {
1707 "This is some text with |X| on it",
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",
1713 "This is some text with @X@ on it",
1714 "This is some text with \\X\\ on it",
1715 "This is some text with |X 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",
1720 "This is some text with #X on it",
1721 "This is some text with @X on it",
1722 "This is some text with \\X on it",
1724 /* All of these cause the URL detection to be extended by one more byte,
1725 thus demonstrating that the tested character is considered as part
1726 of the URL. */
1727 const char * templates_xten_delim[] = {
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",
1737 char buffer[1024];
1739 parent = new_static_wnd(NULL);
1740 hwndRichEdit = new_richedit(parent);
1741 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1742 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1743 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1744 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1745 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1746 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1747 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1748 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1749 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1750 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1751 /* for each url, check the text to see if CFE_LINK effect is present */
1752 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1754 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1755 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1756 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1758 /* Link detection should happen immediately upon WM_SETTEXT */
1759 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1760 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1761 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1763 DestroyWindow(hwndRichEdit);
1765 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1766 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1767 hwndRichEdit = new_richedit(parent);
1769 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1770 char * at_pos;
1771 int at_offset;
1772 int end_offset;
1774 at_pos = strchr(templates_delim[j], 'X');
1775 at_offset = at_pos - templates_delim[j];
1776 memcpy(buffer, templates_delim[j], at_offset);
1777 buffer[at_offset] = '\0';
1778 strcat(buffer, urls[i].text);
1779 strcat(buffer, templates_delim[j] + at_offset + 1);
1780 end_offset = at_offset + strlen(urls[i].text);
1782 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1783 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1785 /* This assumes no templates start with the URL itself, and that they
1786 have at least two characters before the URL text */
1787 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1788 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1791 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1792 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1794 if (urls[i].is_url)
1796 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1797 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1798 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1799 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1801 else
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1805 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1806 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1808 if (buffer[end_offset] != '\0')
1810 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1811 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1812 if (buffer[end_offset +1] != '\0')
1814 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1815 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1820 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1821 char * at_pos;
1822 int at_offset;
1823 int end_offset;
1825 at_pos = strchr(templates_non_delim[j], 'X');
1826 at_offset = at_pos - templates_non_delim[j];
1827 memcpy(buffer, templates_non_delim[j], at_offset);
1828 buffer[at_offset] = '\0';
1829 strcat(buffer, urls[i].text);
1830 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1831 end_offset = at_offset + strlen(urls[i].text);
1833 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1834 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1836 /* This assumes no templates start with the URL itself, and that they
1837 have at least two characters before the URL text */
1838 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1839 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1845 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1846 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1849 if (buffer[end_offset] != '\0')
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1861 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1862 char * at_pos;
1863 int at_offset;
1864 int end_offset;
1866 at_pos = strchr(templates_xten_delim[j], 'X');
1867 at_offset = at_pos - templates_xten_delim[j];
1868 memcpy(buffer, templates_xten_delim[j], at_offset);
1869 buffer[at_offset] = '\0';
1870 strcat(buffer, urls[i].text);
1871 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1872 end_offset = at_offset + strlen(urls[i].text);
1874 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1877 /* This assumes no templates start with the URL itself, and that they
1878 have at least two characters before the URL text */
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1886 if (urls[i].is_url)
1888 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1889 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1890 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1891 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1892 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1893 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1895 else
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1901 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1902 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1904 if (buffer[end_offset +1] != '\0')
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1908 if (buffer[end_offset +2] != '\0')
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1916 DestroyWindow(hwndRichEdit);
1917 hwndRichEdit = NULL;
1920 /* Test detection of URLs within normal text - WM_CHAR case. */
1921 /* Test only the first two URL examples for brevity */
1922 for (i = 0; i < 2; i++) {
1923 hwndRichEdit = new_richedit(parent);
1925 /* Also for brevity, test only the first three delimiters */
1926 for (j = 0; j < 3; j++) {
1927 char * at_pos;
1928 int at_offset;
1929 int end_offset;
1930 int u, v;
1932 at_pos = strchr(templates_delim[j], 'X');
1933 at_offset = at_pos - templates_delim[j];
1934 end_offset = at_offset + strlen(urls[i].text);
1936 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1937 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
1938 for (u = 0; templates_delim[j][u]; u++) {
1939 if (templates_delim[j][u] == '\r') {
1940 simulate_typing_characters(hwndRichEdit, "\r");
1941 } else if (templates_delim[j][u] != 'X') {
1942 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1943 } else {
1944 for (v = 0; urls[i].text[v]; v++) {
1945 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1949 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1951 /* This assumes no templates start with the URL itself, and that they
1952 have at least two characters before the URL text */
1953 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1954 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1957 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1958 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1960 if (urls[i].is_url)
1962 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1963 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1964 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1965 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1967 else
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1974 if (buffer[end_offset] != '\0')
1976 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1977 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1978 if (buffer[end_offset +1] != '\0')
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1985 /* The following will insert a paragraph break after the first character
1986 of the URL candidate, thus breaking the URL. It is expected that the
1987 CFE_LINK attribute should break across both pieces of the URL */
1988 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1989 simulate_typing_characters(hwndRichEdit, "\r");
1990 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2001 /* end_offset moved because of paragraph break */
2002 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2003 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2004 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2005 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2007 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2008 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2009 if (buffer[end_offset +2] != '\0')
2011 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2012 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2016 /* The following will remove the just-inserted paragraph break, thus
2017 restoring the URL */
2018 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2019 simulate_typing_characters(hwndRichEdit, "\b");
2020 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2029 if (urls[i].is_url)
2031 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2032 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2033 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2034 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2036 else
2038 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2039 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2043 if (buffer[end_offset] != '\0')
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2047 if (buffer[end_offset +1] != '\0')
2049 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2050 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2054 DestroyWindow(hwndRichEdit);
2055 hwndRichEdit = NULL;
2058 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2059 /* Test just the first two URL examples for brevity */
2060 for (i = 0; i < 2; i++) {
2061 SETTEXTEX st;
2063 hwndRichEdit = new_richedit(parent);
2065 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2066 be detected:
2067 1) Set entire text, a la WM_SETTEXT
2068 2) Set a selection of the text to the URL
2069 3) Set a portion of the text at a time, which eventually results in
2070 an URL
2071 All of them should give equivalent results
2074 /* Set entire text in one go, like WM_SETTEXT */
2075 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2076 char * at_pos;
2077 int at_offset;
2078 int end_offset;
2080 st.codepage = CP_ACP;
2081 st.flags = ST_DEFAULT;
2083 at_pos = strchr(templates_delim[j], 'X');
2084 at_offset = at_pos - templates_delim[j];
2085 memcpy(buffer, templates_delim[j], at_offset);
2086 buffer[at_offset] = '\0';
2087 strcat(buffer, urls[i].text);
2088 strcat(buffer, templates_delim[j] + at_offset + 1);
2089 end_offset = at_offset + strlen(urls[i].text);
2091 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2092 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2094 /* This assumes no templates start with the URL itself, and that they
2095 have at least two characters before the URL text */
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2100 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2101 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2103 if (urls[i].is_url)
2105 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2106 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2107 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2108 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2110 else
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2117 if (buffer[end_offset] != '\0')
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2121 if (buffer[end_offset +1] != '\0')
2123 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2124 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2129 /* Set selection with X to the URL */
2130 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2131 char * at_pos;
2132 int at_offset;
2133 int end_offset;
2135 at_pos = strchr(templates_delim[j], 'X');
2136 at_offset = at_pos - templates_delim[j];
2137 end_offset = at_offset + strlen(urls[i].text);
2139 st.codepage = CP_ACP;
2140 st.flags = ST_DEFAULT;
2141 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2143 st.flags = ST_SELECTION;
2144 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2146 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2148 /* This assumes no templates start with the URL itself, and that they
2149 have at least two characters before the URL text */
2150 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2151 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2157 if (urls[i].is_url)
2159 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2160 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2161 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2162 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2164 else
2166 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2167 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2171 if (buffer[end_offset] != '\0')
2173 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2174 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2175 if (buffer[end_offset +1] != '\0')
2177 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2178 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2183 /* Set selection with X to the first character of the URL, then the rest */
2184 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2185 char * at_pos;
2186 int at_offset;
2187 int end_offset;
2189 at_pos = strchr(templates_delim[j], 'X');
2190 at_offset = at_pos - templates_delim[j];
2191 end_offset = at_offset + strlen(urls[i].text);
2193 strcpy(buffer, "YY");
2194 buffer[0] = urls[i].text[0];
2196 st.codepage = CP_ACP;
2197 st.flags = ST_DEFAULT;
2198 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2199 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2200 st.flags = ST_SELECTION;
2201 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2202 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2203 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2204 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2205 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2207 /* This assumes no templates start with the URL itself, and that they
2208 have at least two characters before the URL text */
2209 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2210 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2213 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2214 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2216 if (urls[i].is_url)
2218 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2219 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2220 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2221 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2223 else
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2227 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2228 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2230 if (buffer[end_offset] != '\0')
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2234 if (buffer[end_offset +1] != '\0')
2236 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2237 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2242 DestroyWindow(hwndRichEdit);
2243 hwndRichEdit = NULL;
2246 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2247 /* Test just the first two URL examples for brevity */
2248 for (i = 0; i < 2; i++) {
2249 hwndRichEdit = new_richedit(parent);
2251 /* Set selection with X to the URL */
2252 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2253 char * at_pos;
2254 int at_offset;
2255 int end_offset;
2257 at_pos = strchr(templates_delim[j], 'X');
2258 at_offset = at_pos - templates_delim[j];
2259 end_offset = at_offset + strlen(urls[i].text);
2261 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2262 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2263 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2264 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2265 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2267 /* This assumes no templates start with the URL itself, and that they
2268 have at least two characters before the URL text */
2269 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2270 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2271 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2272 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2273 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2274 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2276 if (urls[i].is_url)
2278 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2279 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2280 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2281 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2283 else
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2287 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2288 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2290 if (buffer[end_offset] != '\0')
2292 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2293 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2294 if (buffer[end_offset +1] != '\0')
2296 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2297 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2302 /* Set selection with X to the first character of the URL, then the rest */
2303 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2304 char * at_pos;
2305 int at_offset;
2306 int end_offset;
2308 at_pos = strchr(templates_delim[j], 'X');
2309 at_offset = at_pos - templates_delim[j];
2310 end_offset = at_offset + strlen(urls[i].text);
2312 strcpy(buffer, "YY");
2313 buffer[0] = urls[i].text[0];
2315 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2316 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2317 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2318 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2319 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2320 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2321 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2323 /* This assumes no templates start with the URL itself, and that they
2324 have at least two characters before the URL text */
2325 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2326 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2327 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2328 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2329 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2330 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2332 if (urls[i].is_url)
2334 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2336 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2339 else
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2343 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2344 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2346 if (buffer[end_offset] != '\0')
2348 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2349 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2350 if (buffer[end_offset +1] != '\0')
2352 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2353 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2358 DestroyWindow(hwndRichEdit);
2359 hwndRichEdit = NULL;
2362 DestroyWindow(parent);
2365 static void test_EM_SCROLL(void)
2367 int i, j;
2368 int r; /* return value */
2369 int expr; /* expected return value */
2370 HWND hwndRichEdit = new_richedit(NULL);
2371 int y_before, y_after; /* units of lines of text */
2373 /* test a richedit box containing a single line of text */
2374 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2375 expr = 0x00010000;
2376 for (i = 0; i < 4; i++) {
2377 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2379 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2380 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2381 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2382 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2383 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2384 "(i == %d)\n", y_after, i);
2388 * test a richedit box that will scroll. There are two general
2389 * cases: the case without any long lines and the case with a long
2390 * line.
2392 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2393 if (i == 0)
2394 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2395 else
2396 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2397 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2398 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2399 "LONG LINE \nb\nc\nd\ne");
2400 for (j = 0; j < 12; j++) /* reset scroll position to top */
2401 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2403 /* get first visible line */
2404 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2405 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2407 /* get new current first visible line */
2408 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2410 ok(((r & 0xffffff00) == 0x00010000) &&
2411 ((r & 0x000000ff) != 0x00000000),
2412 "EM_SCROLL page down didn't scroll by a small positive number of "
2413 "lines (r == 0x%08x)\n", r);
2414 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2415 "(line %d scrolled to line %d\n", y_before, y_after);
2417 y_before = y_after;
2419 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2420 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421 ok(((r & 0xffffff00) == 0x0001ff00),
2422 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2423 "(r == 0x%08x)\n", r);
2424 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2425 "%d scrolled to line %d\n", y_before, y_after);
2427 y_before = y_after;
2429 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2431 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2433 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2434 "(r == 0x%08x)\n", r);
2435 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2436 "1 line (%d scrolled to %d)\n", y_before, y_after);
2438 y_before = y_after;
2440 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2442 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2444 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2445 "(r == 0x%08x)\n", r);
2446 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2447 "line (%d scrolled to %d)\n", y_before, y_after);
2449 y_before = y_after;
2451 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2452 SB_LINEUP, 0); /* lineup beyond top */
2454 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2456 ok(r == 0x00010000,
2457 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2458 ok(y_before == y_after,
2459 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2461 y_before = y_after;
2463 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2464 SB_PAGEUP, 0);/*page up beyond top */
2466 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2468 ok(r == 0x00010000,
2469 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2470 ok(y_before == y_after,
2471 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2473 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2474 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2475 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2476 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2477 SB_PAGEDOWN, 0); /* page down beyond bot */
2478 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2480 ok(r == 0x00010000,
2481 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2482 ok(y_before == y_after,
2483 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2484 y_before, y_after);
2486 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2487 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2488 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2490 ok(r == 0x00010000,
2491 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2492 ok(y_before == y_after,
2493 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2494 y_before, y_after);
2496 DestroyWindow(hwndRichEdit);
2499 static unsigned int recursionLevel = 0;
2500 static unsigned int WM_SIZE_recursionLevel = 0;
2501 static BOOL bailedOutOfRecursion = FALSE;
2502 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2504 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2506 LRESULT r;
2508 if (bailedOutOfRecursion) return 0;
2509 if (recursionLevel >= 32) {
2510 bailedOutOfRecursion = TRUE;
2511 return 0;
2514 recursionLevel++;
2515 switch (message) {
2516 case WM_SIZE:
2517 WM_SIZE_recursionLevel++;
2518 r = richeditProc(hwnd, message, wParam, lParam);
2519 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2520 ShowScrollBar(hwnd, SB_VERT, TRUE);
2521 WM_SIZE_recursionLevel--;
2522 break;
2523 default:
2524 r = richeditProc(hwnd, message, wParam, lParam);
2525 break;
2527 recursionLevel--;
2528 return r;
2531 static void test_scrollbar_visibility(void)
2533 HWND hwndRichEdit;
2534 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2535 SCROLLINFO si;
2536 WNDCLASSA cls;
2537 BOOL r;
2539 /* These tests show that richedit should temporarily refrain from automatically
2540 hiding or showing its scrollbars (vertical at least) when an explicit request
2541 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2542 Some applications depend on forced showing (when otherwise richedit would
2543 hide the vertical scrollbar) and are thrown on an endless recursive loop
2544 if richedit auto-hides the scrollbar again. Apparently they never heard of
2545 the ES_DISABLENOSCROLL style... */
2547 hwndRichEdit = new_richedit(NULL);
2549 /* Test default scrollbar visibility behavior */
2550 memset(&si, 0, sizeof(si));
2551 si.cbSize = sizeof(si);
2552 si.fMask = SIF_PAGE | SIF_RANGE;
2553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2554 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2555 "Vertical scrollbar is visible, should be invisible.\n");
2556 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2557 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2558 si.nPage, si.nMin, si.nMax);
2560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2566 "Vertical scrollbar is visible, should be invisible.\n");
2567 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2568 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2569 si.nPage, si.nMin, si.nMax);
2571 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2579 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2580 si.nPage, si.nMin, si.nMax);
2582 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2583 even though it hides the scrollbar */
2584 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2585 memset(&si, 0, sizeof(si));
2586 si.cbSize = sizeof(si);
2587 si.fMask = SIF_PAGE | SIF_RANGE;
2588 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2589 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2590 "Vertical scrollbar is visible, should be invisible.\n");
2591 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2592 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2593 si.nPage, si.nMin, si.nMax);
2595 /* Setting non-scrolling text again does *not* reset scrollbar range */
2596 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2597 memset(&si, 0, sizeof(si));
2598 si.cbSize = sizeof(si);
2599 si.fMask = SIF_PAGE | SIF_RANGE;
2600 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2601 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2602 "Vertical scrollbar is visible, should be invisible.\n");
2603 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2604 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2605 si.nPage, si.nMin, si.nMax);
2607 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2608 memset(&si, 0, sizeof(si));
2609 si.cbSize = sizeof(si);
2610 si.fMask = SIF_PAGE | SIF_RANGE;
2611 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2612 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2613 "Vertical scrollbar is visible, should be invisible.\n");
2614 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2615 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2616 si.nPage, si.nMin, si.nMax);
2618 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2619 memset(&si, 0, sizeof(si));
2620 si.cbSize = sizeof(si);
2621 si.fMask = SIF_PAGE | SIF_RANGE;
2622 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2623 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2624 "Vertical scrollbar is visible, should be invisible.\n");
2625 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2626 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2627 si.nPage, si.nMin, si.nMax);
2629 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2630 memset(&si, 0, sizeof(si));
2631 si.cbSize = sizeof(si);
2632 si.fMask = SIF_PAGE | SIF_RANGE;
2633 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2634 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2635 "Vertical scrollbar is visible, should be invisible.\n");
2636 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2637 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2638 si.nPage, si.nMin, si.nMax);
2640 DestroyWindow(hwndRichEdit);
2642 /* Test again, with ES_DISABLENOSCROLL style */
2643 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2645 /* Test default scrollbar visibility behavior */
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2651 "Vertical scrollbar is invisible, should be visible.\n");
2652 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2653 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2654 si.nPage, si.nMin, si.nMax);
2656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2657 memset(&si, 0, sizeof(si));
2658 si.cbSize = sizeof(si);
2659 si.fMask = SIF_PAGE | SIF_RANGE;
2660 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2661 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2662 "Vertical scrollbar is invisible, should be visible.\n");
2663 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2664 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2665 si.nPage, si.nMin, si.nMax);
2667 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2668 memset(&si, 0, sizeof(si));
2669 si.cbSize = sizeof(si);
2670 si.fMask = SIF_PAGE | SIF_RANGE;
2671 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2672 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2673 "Vertical scrollbar is invisible, should be visible.\n");
2674 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2675 "reported page/range is %d (%d..%d)\n",
2676 si.nPage, si.nMin, si.nMax);
2678 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2679 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2680 memset(&si, 0, sizeof(si));
2681 si.cbSize = sizeof(si);
2682 si.fMask = SIF_PAGE | SIF_RANGE;
2683 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2684 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2685 "Vertical scrollbar is invisible, should be visible.\n");
2686 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2687 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2688 si.nPage, si.nMin, si.nMax);
2690 /* Setting non-scrolling text again does *not* reset scrollbar range */
2691 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2692 memset(&si, 0, sizeof(si));
2693 si.cbSize = sizeof(si);
2694 si.fMask = SIF_PAGE | SIF_RANGE;
2695 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2697 "Vertical scrollbar is invisible, should be visible.\n");
2698 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2699 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2700 si.nPage, si.nMin, si.nMax);
2702 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2703 memset(&si, 0, sizeof(si));
2704 si.cbSize = sizeof(si);
2705 si.fMask = SIF_PAGE | SIF_RANGE;
2706 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2707 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2708 "Vertical scrollbar is invisible, should be visible.\n");
2709 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2710 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2711 si.nPage, si.nMin, si.nMax);
2713 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719 "Vertical scrollbar is invisible, should be visible.\n");
2720 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2721 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2722 si.nPage, si.nMin, si.nMax);
2724 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2725 memset(&si, 0, sizeof(si));
2726 si.cbSize = sizeof(si);
2727 si.fMask = SIF_PAGE | SIF_RANGE;
2728 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2729 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2730 "Vertical scrollbar is invisible, should be visible.\n");
2731 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2732 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2733 si.nPage, si.nMin, si.nMax);
2735 DestroyWindow(hwndRichEdit);
2737 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2738 hwndRichEdit = new_richedit(NULL);
2740 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2741 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2747 "Vertical scrollbar is invisible, should be visible.\n");
2748 todo_wine {
2749 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751 si.nPage, si.nMin, si.nMax);
2754 /* Ditto, see above */
2755 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2761 "Vertical scrollbar is invisible, should be visible.\n");
2762 todo_wine {
2763 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2764 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2765 si.nPage, si.nMin, si.nMax);
2768 /* Ditto, see above */
2769 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2770 memset(&si, 0, sizeof(si));
2771 si.cbSize = sizeof(si);
2772 si.fMask = SIF_PAGE | SIF_RANGE;
2773 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2774 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2775 "Vertical scrollbar is invisible, should be visible.\n");
2776 todo_wine {
2777 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2778 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2779 si.nPage, si.nMin, si.nMax);
2782 /* Ditto, see above */
2783 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2784 memset(&si, 0, sizeof(si));
2785 si.cbSize = sizeof(si);
2786 si.fMask = SIF_PAGE | SIF_RANGE;
2787 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2788 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2789 "Vertical scrollbar is invisible, should be visible.\n");
2790 todo_wine {
2791 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2792 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2793 si.nPage, si.nMin, si.nMax);
2796 /* Ditto, see above */
2797 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2803 "Vertical scrollbar is invisible, should be visible.\n");
2804 todo_wine {
2805 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2806 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2807 si.nPage, si.nMin, si.nMax);
2810 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2811 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2812 memset(&si, 0, sizeof(si));
2813 si.cbSize = sizeof(si);
2814 si.fMask = SIF_PAGE | SIF_RANGE;
2815 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2816 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2817 "Vertical scrollbar is visible, should be invisible.\n");
2818 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2819 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2820 si.nPage, si.nMin, si.nMax);
2822 DestroyWindow(hwndRichEdit);
2824 hwndRichEdit = new_richedit(NULL);
2826 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2834 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843 "Vertical scrollbar is visible, should be invisible.\n");
2844 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2845 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2846 si.nPage, si.nMin, si.nMax);
2848 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2849 memset(&si, 0, sizeof(si));
2850 si.cbSize = sizeof(si);
2851 si.fMask = SIF_PAGE | SIF_RANGE;
2852 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2853 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2854 "Vertical scrollbar is visible, should be invisible.\n");
2855 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2856 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2857 si.nPage, si.nMin, si.nMax);
2859 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2860 memset(&si, 0, sizeof(si));
2861 si.cbSize = sizeof(si);
2862 si.fMask = SIF_PAGE | SIF_RANGE;
2863 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2864 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2865 "Vertical scrollbar is visible, should be invisible.\n");
2866 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2867 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2868 si.nPage, si.nMin, si.nMax);
2870 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2871 memset(&si, 0, sizeof(si));
2872 si.cbSize = sizeof(si);
2873 si.fMask = SIF_PAGE | SIF_RANGE;
2874 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2875 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2876 "Vertical scrollbar is invisible, should be visible.\n");
2877 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2878 "reported page/range is %d (%d..%d)\n",
2879 si.nPage, si.nMin, si.nMax);
2881 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2882 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2883 memset(&si, 0, sizeof(si));
2884 si.cbSize = sizeof(si);
2885 si.fMask = SIF_PAGE | SIF_RANGE;
2886 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2887 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2888 "Vertical scrollbar is visible, should be invisible.\n");
2889 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2890 "reported page/range is %d (%d..%d)\n",
2891 si.nPage, si.nMin, si.nMax);
2893 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2894 memset(&si, 0, sizeof(si));
2895 si.cbSize = sizeof(si);
2896 si.fMask = SIF_PAGE | SIF_RANGE;
2897 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2898 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2899 "Vertical scrollbar is visible, should be invisible.\n");
2900 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2901 "reported page/range is %d (%d..%d)\n",
2902 si.nPage, si.nMin, si.nMax);
2904 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2905 EM_SCROLL will make visible any forcefully invisible scrollbar */
2906 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2907 memset(&si, 0, sizeof(si));
2908 si.cbSize = sizeof(si);
2909 si.fMask = SIF_PAGE | SIF_RANGE;
2910 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2911 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2912 "Vertical scrollbar is invisible, should be visible.\n");
2913 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2914 "reported page/range is %d (%d..%d)\n",
2915 si.nPage, si.nMin, si.nMax);
2917 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2918 memset(&si, 0, sizeof(si));
2919 si.cbSize = sizeof(si);
2920 si.fMask = SIF_PAGE | SIF_RANGE;
2921 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2922 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2923 "Vertical scrollbar is visible, should be invisible.\n");
2924 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2925 "reported page/range is %d (%d..%d)\n",
2926 si.nPage, si.nMin, si.nMax);
2928 /* Again, EM_SCROLL, with SB_LINEUP */
2929 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2930 memset(&si, 0, sizeof(si));
2931 si.cbSize = sizeof(si);
2932 si.fMask = SIF_PAGE | SIF_RANGE;
2933 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2934 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2935 "Vertical scrollbar is invisible, should be visible.\n");
2936 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2937 "reported page/range is %d (%d..%d)\n",
2938 si.nPage, si.nMin, si.nMax);
2940 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2941 memset(&si, 0, sizeof(si));
2942 si.cbSize = sizeof(si);
2943 si.fMask = SIF_PAGE | SIF_RANGE;
2944 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2945 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2946 "Vertical scrollbar is visible, should be invisible.\n");
2947 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2948 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2949 si.nPage, si.nMin, si.nMax);
2951 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2952 memset(&si, 0, sizeof(si));
2953 si.cbSize = sizeof(si);
2954 si.fMask = SIF_PAGE | SIF_RANGE;
2955 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2956 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2957 "Vertical scrollbar is invisible, should be visible.\n");
2958 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2959 "reported page/range is %d (%d..%d)\n",
2960 si.nPage, si.nMin, si.nMax);
2962 DestroyWindow(hwndRichEdit);
2965 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
2966 hwndRichEdit = new_richedit(NULL);
2968 #define ENABLE_WS_VSCROLL(hwnd) \
2969 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2970 #define DISABLE_WS_VSCROLL(hwnd) \
2971 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2973 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2974 ENABLE_WS_VSCROLL(hwndRichEdit);
2975 memset(&si, 0, sizeof(si));
2976 si.cbSize = sizeof(si);
2977 si.fMask = SIF_PAGE | SIF_RANGE;
2978 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2979 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2980 "Vertical scrollbar is invisible, should be visible.\n");
2981 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2982 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2983 si.nPage, si.nMin, si.nMax);
2985 /* Ditto, see above */
2986 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2992 "Vertical scrollbar is invisible, should be visible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2997 /* Ditto, see above */
2998 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2999 memset(&si, 0, sizeof(si));
3000 si.cbSize = sizeof(si);
3001 si.fMask = SIF_PAGE | SIF_RANGE;
3002 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3003 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3004 "Vertical scrollbar is invisible, should be visible.\n");
3005 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3006 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3007 si.nPage, si.nMin, si.nMax);
3009 /* Ditto, see above */
3010 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3011 memset(&si, 0, sizeof(si));
3012 si.cbSize = sizeof(si);
3013 si.fMask = SIF_PAGE | SIF_RANGE;
3014 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016 "Vertical scrollbar is invisible, should be visible.\n");
3017 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3018 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3019 si.nPage, si.nMin, si.nMax);
3021 /* Ditto, see above */
3022 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3023 memset(&si, 0, sizeof(si));
3024 si.cbSize = sizeof(si);
3025 si.fMask = SIF_PAGE | SIF_RANGE;
3026 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3027 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3028 "Vertical scrollbar is invisible, should be visible.\n");
3029 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3030 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3031 si.nPage, si.nMin, si.nMax);
3033 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3034 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3035 memset(&si, 0, sizeof(si));
3036 si.cbSize = sizeof(si);
3037 si.fMask = SIF_PAGE | SIF_RANGE;
3038 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3039 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3040 "Vertical scrollbar is visible, should be invisible.\n");
3041 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3042 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3043 si.nPage, si.nMin, si.nMax);
3045 DestroyWindow(hwndRichEdit);
3047 hwndRichEdit = new_richedit(NULL);
3049 DISABLE_WS_VSCROLL(hwndRichEdit);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3057 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3058 si.nPage, si.nMin, si.nMax);
3060 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066 "Vertical scrollbar is visible, should be invisible.\n");
3067 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3068 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3069 si.nPage, si.nMin, si.nMax);
3071 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3072 memset(&si, 0, sizeof(si));
3073 si.cbSize = sizeof(si);
3074 si.fMask = SIF_PAGE | SIF_RANGE;
3075 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3077 "Vertical scrollbar is visible, should be invisible.\n");
3078 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3079 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3080 si.nPage, si.nMin, si.nMax);
3082 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3083 memset(&si, 0, sizeof(si));
3084 si.cbSize = sizeof(si);
3085 si.fMask = SIF_PAGE | SIF_RANGE;
3086 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088 "Vertical scrollbar is visible, should be invisible.\n");
3089 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3090 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3091 si.nPage, si.nMin, si.nMax);
3093 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3094 memset(&si, 0, sizeof(si));
3095 si.cbSize = sizeof(si);
3096 si.fMask = SIF_PAGE | SIF_RANGE;
3097 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3098 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3099 "Vertical scrollbar is invisible, should be visible.\n");
3100 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3101 "reported page/range is %d (%d..%d)\n",
3102 si.nPage, si.nMin, si.nMax);
3104 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3105 DISABLE_WS_VSCROLL(hwndRichEdit);
3106 memset(&si, 0, sizeof(si));
3107 si.cbSize = sizeof(si);
3108 si.fMask = SIF_PAGE | SIF_RANGE;
3109 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3110 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3111 "Vertical scrollbar is visible, should be invisible.\n");
3112 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3113 "reported page/range is %d (%d..%d)\n",
3114 si.nPage, si.nMin, si.nMax);
3116 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3117 memset(&si, 0, sizeof(si));
3118 si.cbSize = sizeof(si);
3119 si.fMask = SIF_PAGE | SIF_RANGE;
3120 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3121 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3122 "Vertical scrollbar is visible, should be invisible.\n");
3123 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3124 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3125 si.nPage, si.nMin, si.nMax);
3127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3128 memset(&si, 0, sizeof(si));
3129 si.cbSize = sizeof(si);
3130 si.fMask = SIF_PAGE | SIF_RANGE;
3131 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3132 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3133 "Vertical scrollbar is invisible, should be visible.\n");
3134 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3135 "reported page/range is %d (%d..%d)\n",
3136 si.nPage, si.nMin, si.nMax);
3138 DISABLE_WS_VSCROLL(hwndRichEdit);
3139 memset(&si, 0, sizeof(si));
3140 si.cbSize = sizeof(si);
3141 si.fMask = SIF_PAGE | SIF_RANGE;
3142 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3143 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3144 "Vertical scrollbar is visible, should be invisible.\n");
3145 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3146 "reported page/range is %d (%d..%d)\n",
3147 si.nPage, si.nMin, si.nMax);
3149 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3150 EM_SCROLL will make visible any forcefully invisible scrollbar */
3151 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3152 memset(&si, 0, sizeof(si));
3153 si.cbSize = sizeof(si);
3154 si.fMask = SIF_PAGE | SIF_RANGE;
3155 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3156 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3157 "Vertical scrollbar is invisible, should be visible.\n");
3158 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3159 "reported page/range is %d (%d..%d)\n",
3160 si.nPage, si.nMin, si.nMax);
3162 DISABLE_WS_VSCROLL(hwndRichEdit);
3163 memset(&si, 0, sizeof(si));
3164 si.cbSize = sizeof(si);
3165 si.fMask = SIF_PAGE | SIF_RANGE;
3166 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3168 "Vertical scrollbar is visible, should be invisible.\n");
3169 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3170 "reported page/range is %d (%d..%d)\n",
3171 si.nPage, si.nMin, si.nMax);
3173 /* Again, EM_SCROLL, with SB_LINEUP */
3174 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3175 memset(&si, 0, sizeof(si));
3176 si.cbSize = sizeof(si);
3177 si.fMask = SIF_PAGE | SIF_RANGE;
3178 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3179 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3180 "Vertical scrollbar is invisible, should be visible.\n");
3181 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3182 "reported page/range is %d (%d..%d)\n",
3183 si.nPage, si.nMin, si.nMax);
3185 DestroyWindow(hwndRichEdit);
3187 /* This window proc models what is going on with Corman Lisp 3.0.
3188 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3189 force the scrollbar into visibility. Recursion should NOT happen
3190 as a result of this action.
3192 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3193 if (r) {
3194 richeditProc = cls.lpfnWndProc;
3195 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3196 cls.lpszClassName = "RicheditStupidOverride";
3197 if(!RegisterClassA(&cls)) assert(0);
3199 recursionLevel = 0;
3200 WM_SIZE_recursionLevel = 0;
3201 bailedOutOfRecursion = FALSE;
3202 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3203 ok(!bailedOutOfRecursion,
3204 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3206 recursionLevel = 0;
3207 WM_SIZE_recursionLevel = 0;
3208 bailedOutOfRecursion = FALSE;
3209 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3210 ok(!bailedOutOfRecursion,
3211 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3213 /* Unblock window in order to process WM_DESTROY */
3214 recursionLevel = 0;
3215 bailedOutOfRecursion = FALSE;
3216 WM_SIZE_recursionLevel = 0;
3217 DestroyWindow(hwndRichEdit);
3221 static void test_EM_SETUNDOLIMIT(void)
3223 /* cases we test for:
3224 * default behaviour - limiting at 100 undo's
3225 * undo disabled - setting a limit of 0
3226 * undo limited - undo limit set to some to some number, like 2
3227 * bad input - sending a negative number should default to 100 undo's */
3229 HWND hwndRichEdit = new_richedit(NULL);
3230 CHARRANGE cr;
3231 int i;
3232 int result;
3234 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3235 cr.cpMin = 0;
3236 cr.cpMax = 1;
3237 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3238 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3239 also, multiple pastes don't combine like WM_CHAR would */
3240 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3242 /* first case - check the default */
3243 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3244 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3245 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3246 for (i=0; i<100; i++) /* Undo 100 of them */
3247 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3248 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3249 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3251 /* second case - cannot undo */
3252 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3253 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3254 SendMessageA(hwndRichEdit,
3255 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3256 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3257 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3259 /* third case - set it to an arbitrary number */
3260 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3261 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3262 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3263 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3264 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3265 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3266 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3267 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3268 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3269 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3270 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3271 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3272 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3273 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3275 /* fourth case - setting negative numbers should default to 100 undos */
3276 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3277 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3278 ok (result == 100,
3279 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3281 DestroyWindow(hwndRichEdit);
3284 static void test_ES_PASSWORD(void)
3286 /* This isn't hugely testable, so we're just going to run it through its paces */
3288 HWND hwndRichEdit = new_richedit(NULL);
3289 WCHAR result;
3291 /* First, check the default of a regular control */
3292 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3293 ok (result == 0,
3294 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3296 /* Now, set it to something normal */
3297 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3298 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3299 ok (result == 120,
3300 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3302 /* Now, set it to something odd */
3303 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3304 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3305 ok (result == 1234,
3306 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3307 DestroyWindow(hwndRichEdit);
3310 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3311 LPBYTE pbBuff,
3312 LONG cb,
3313 LONG *pcb)
3315 char** str = (char**)dwCookie;
3316 *pcb = cb;
3317 if (*pcb > 0) {
3318 memcpy(*str, pbBuff, *pcb);
3319 *str += *pcb;
3321 return 0;
3324 static void test_WM_SETTEXT(void)
3326 HWND hwndRichEdit = new_richedit(NULL);
3327 const char * TestItem1 = "TestSomeText";
3328 const char * TestItem2 = "TestSomeText\r";
3329 const char * TestItem2_after = "TestSomeText\r\n";
3330 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3331 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3332 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3333 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3334 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3335 const char * TestItem5_after = "TestSomeText TestSomeText";
3336 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3337 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3338 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3339 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3341 const char rtftextA[] = "{\\rtf sometext}";
3342 const char urtftextA[] = "{\\urtf sometext}";
3343 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3344 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3345 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3347 char buf[1024] = {0};
3348 WCHAR bufW[1024] = {0};
3349 LRESULT result;
3351 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3352 any solitary \r to be converted to \r\n on return. Properly paired
3353 \r\n are not affected. It also shows that the special sequence \r\r\n
3354 gets converted to a single space.
3357 #define TEST_SETTEXT(a, b) \
3358 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3359 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3360 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3361 ok (result == lstrlenA(buf), \
3362 "WM_GETTEXT returned %ld instead of expected %u\n", \
3363 result, lstrlenA(buf)); \
3364 result = strcmp(b, buf); \
3365 ok(result == 0, \
3366 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3368 TEST_SETTEXT(TestItem1, TestItem1)
3369 TEST_SETTEXT(TestItem2, TestItem2_after)
3370 TEST_SETTEXT(TestItem3, TestItem3_after)
3371 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3372 TEST_SETTEXT(TestItem4, TestItem4_after)
3373 TEST_SETTEXT(TestItem5, TestItem5_after)
3374 TEST_SETTEXT(TestItem6, TestItem6_after)
3375 TEST_SETTEXT(TestItem7, TestItem7_after)
3377 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3378 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3379 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3380 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3381 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3382 DestroyWindow(hwndRichEdit);
3383 #undef TEST_SETTEXT
3385 #define TEST_SETTEXTW(a, b) \
3386 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3387 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3388 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3389 ok (result == lstrlenW(bufW), \
3390 "WM_GETTEXT returned %ld instead of expected %u\n", \
3391 result, lstrlenW(bufW)); \
3392 result = lstrcmpW(b, bufW); \
3393 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3395 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3396 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3397 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3398 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3399 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3400 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3401 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3402 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3403 DestroyWindow(hwndRichEdit);
3404 #undef TEST_SETTEXTW
3407 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3408 resent to the callkack. */
3409 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3410 LPBYTE pbBuff,
3411 LONG cb,
3412 LONG *pcb)
3414 char** str = (char**)dwCookie;
3415 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3416 *pcb = 0;
3417 if (cb > 0) {
3418 memcpy(*str, pbBuff, cb);
3419 *str += cb;
3420 *pcb = 1;
3422 return 0;
3425 static int count_pars(const char *buf)
3427 const char *p = buf;
3428 int count = 0;
3429 while ((p = strstr( p, "\\par" )) != NULL)
3431 if (!isalpha( p[4] ))
3432 count++;
3433 p++;
3435 return count;
3438 static void test_EM_STREAMOUT(void)
3440 HWND hwndRichEdit = new_richedit(NULL);
3441 int r;
3442 EDITSTREAM es;
3443 char buf[1024] = {0};
3444 char * p;
3446 const char * TestItem1 = "TestSomeText";
3447 const char * TestItem2 = "TestSomeText\r";
3448 const char * TestItem3 = "TestSomeText\r\n";
3450 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3451 p = buf;
3452 es.dwCookie = (DWORD_PTR)&p;
3453 es.dwError = 0;
3454 es.pfnCallback = test_WM_SETTEXT_esCallback;
3455 memset(buf, 0, sizeof(buf));
3456 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3457 r = strlen(buf);
3458 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3459 ok(strcmp(buf, TestItem1) == 0,
3460 "streamed text different, got %s\n", buf);
3462 /* RTF mode writes the final end of para \r if it's part of the selection */
3463 p = buf;
3464 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3465 ok (count_pars(buf) == 1, "got %s\n", buf);
3466 p = buf;
3467 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3468 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3469 ok (count_pars(buf) == 0, "got %s\n", buf);
3470 p = buf;
3471 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3472 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3473 ok (count_pars(buf) == 1, "got %s\n", buf);
3475 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3476 p = buf;
3477 es.dwCookie = (DWORD_PTR)&p;
3478 es.dwError = 0;
3479 es.pfnCallback = test_WM_SETTEXT_esCallback;
3480 memset(buf, 0, sizeof(buf));
3481 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3482 r = strlen(buf);
3483 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3484 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3485 ok(strcmp(buf, TestItem3) == 0,
3486 "streamed text different from, got %s\n", buf);
3488 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3489 p = buf;
3490 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3491 ok (count_pars(buf) == 2, "got %s\n", buf);
3492 p = buf;
3493 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3494 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3495 ok (count_pars(buf) == 1, "got %s\n", buf);
3496 p = buf;
3497 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3498 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3499 ok (count_pars(buf) == 2, "got %s\n", buf);
3501 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3502 p = buf;
3503 es.dwCookie = (DWORD_PTR)&p;
3504 es.dwError = 0;
3505 es.pfnCallback = test_WM_SETTEXT_esCallback;
3506 memset(buf, 0, sizeof(buf));
3507 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3508 r = strlen(buf);
3509 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3510 ok(strcmp(buf, TestItem3) == 0,
3511 "streamed text different, got %s\n", buf);
3513 /* Use a callback that sets *pcb to one */
3514 p = buf;
3515 es.dwCookie = (DWORD_PTR)&p;
3516 es.dwError = 0;
3517 es.pfnCallback = test_esCallback_written_1;
3518 memset(buf, 0, sizeof(buf));
3519 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3520 r = strlen(buf);
3521 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3522 ok(strcmp(buf, TestItem3) == 0,
3523 "streamed text different, got %s\n", buf);
3526 DestroyWindow(hwndRichEdit);
3529 static void test_EM_STREAMOUT_FONTTBL(void)
3531 HWND hwndRichEdit = new_richedit(NULL);
3532 EDITSTREAM es;
3533 char buf[1024] = {0};
3534 char * p;
3535 char * fontTbl;
3536 int brackCount;
3538 const char * TestItem = "TestSomeText";
3540 /* fills in the richedit control with some text */
3541 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3543 /* streams out the text in rtf format */
3544 p = buf;
3545 es.dwCookie = (DWORD_PTR)&p;
3546 es.dwError = 0;
3547 es.pfnCallback = test_WM_SETTEXT_esCallback;
3548 memset(buf, 0, sizeof(buf));
3549 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3551 /* scans for \fonttbl, error if not found */
3552 fontTbl = strstr(buf, "\\fonttbl");
3553 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3554 if(fontTbl)
3556 /* scans for terminating closing bracket */
3557 brackCount = 1;
3558 while(*fontTbl && brackCount)
3560 if(*fontTbl == '{')
3561 brackCount++;
3562 else if(*fontTbl == '}')
3563 brackCount--;
3564 fontTbl++;
3566 /* checks whether closing bracket is ok */
3567 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3568 if(!brackCount)
3570 /* char before closing fonttbl block should be a closed bracket */
3571 fontTbl -= 2;
3572 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3574 /* char after fonttbl block should be a crlf */
3575 fontTbl += 2;
3576 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3579 DestroyWindow(hwndRichEdit);
3583 static void test_EM_SETTEXTEX(void)
3585 HWND hwndRichEdit, parent;
3586 SCROLLINFO si;
3587 int sel_start, sel_end;
3588 SETTEXTEX setText;
3589 GETTEXTEX getText;
3590 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3591 'S', 'o', 'm', 'e',
3592 'T', 'e', 'x', 't', 0};
3593 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3594 't', 'S', 'o', 'm',
3595 'e', 'T', 'e', 'x',
3596 't', 't', 'S', 'o',
3597 'm', 'e', 'T', 'e',
3598 'x', 't', 0};
3599 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3600 '\r','t','S','o','m','e','T','e','x','t',0};
3601 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3602 'S', 'o', 'm', 'e',
3603 'T', 'e', 'x', 't',
3604 '\r', 0};
3605 const char * TestItem2_after = "TestSomeText\r\n";
3606 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3607 'S', 'o', 'm', 'e',
3608 'T', 'e', 'x', 't',
3609 '\r','\n','\r','\n', 0};
3610 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3611 'S', 'o', 'm', 'e',
3612 'T', 'e', 'x', 't',
3613 '\n','\n', 0};
3614 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3615 'S', 'o', 'm', 'e',
3616 'T', 'e', 'x', 't',
3617 '\r','\r', 0};
3618 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3619 'S', 'o', 'm', 'e',
3620 'T', 'e', 'x', 't',
3621 '\r','\r','\n','\r',
3622 '\n', 0};
3623 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3624 'S', 'o', 'm', 'e',
3625 'T', 'e', 'x', 't',
3626 ' ','\r', 0};
3627 #define MAX_BUF_LEN 1024
3628 WCHAR buf[MAX_BUF_LEN];
3629 char bufACP[MAX_BUF_LEN];
3630 char * p;
3631 int result;
3632 CHARRANGE cr;
3633 EDITSTREAM es;
3634 WNDCLASSA cls;
3636 /* Test the scroll position with and without a parent window.
3638 * For some reason the scroll position is 0 after EM_SETTEXTEX
3639 * with the ST_SELECTION flag only when the control has a parent
3640 * window, even though the selection is at the end. */
3641 cls.style = 0;
3642 cls.lpfnWndProc = DefWindowProcA;
3643 cls.cbClsExtra = 0;
3644 cls.cbWndExtra = 0;
3645 cls.hInstance = GetModuleHandleA(0);
3646 cls.hIcon = 0;
3647 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3648 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3649 cls.lpszMenuName = NULL;
3650 cls.lpszClassName = "ParentTestClass";
3651 if(!RegisterClassA(&cls)) assert(0);
3653 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3654 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3655 ok (parent != 0, "Failed to create parent window\n");
3657 hwndRichEdit = CreateWindowExA(0,
3658 RICHEDIT_CLASS20A, NULL,
3659 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3660 0, 0, 200, 60, parent, NULL,
3661 hmoduleRichEdit, NULL);
3663 setText.codepage = CP_ACP;
3664 setText.flags = ST_SELECTION;
3665 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3666 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3667 si.cbSize = sizeof(si);
3668 si.fMask = SIF_ALL;
3669 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3670 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3671 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3672 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3673 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3675 DestroyWindow(parent);
3677 /* Test without a parent window */
3678 hwndRichEdit = new_richedit(NULL);
3679 setText.codepage = CP_ACP;
3680 setText.flags = ST_SELECTION;
3681 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3682 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3683 si.cbSize = sizeof(si);
3684 si.fMask = SIF_ALL;
3685 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3686 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3687 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3688 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3689 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3691 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3692 * but this time it is because the selection is at the beginning. */
3693 setText.codepage = CP_ACP;
3694 setText.flags = ST_DEFAULT;
3695 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3696 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3697 si.cbSize = sizeof(si);
3698 si.fMask = SIF_ALL;
3699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3700 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3701 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3702 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3703 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3705 setText.codepage = 1200; /* no constant for unicode */
3706 getText.codepage = 1200; /* no constant for unicode */
3707 getText.cb = MAX_BUF_LEN;
3708 getText.flags = GT_DEFAULT;
3709 getText.lpDefaultChar = NULL;
3710 getText.lpUsedDefChar = NULL;
3712 setText.flags = 0;
3713 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3714 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3715 ok(lstrcmpW(buf, TestItem1) == 0,
3716 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3718 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3719 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3721 setText.codepage = 1200; /* no constant for unicode */
3722 getText.codepage = 1200; /* no constant for unicode */
3723 getText.cb = MAX_BUF_LEN;
3724 getText.flags = GT_DEFAULT;
3725 getText.lpDefaultChar = NULL;
3726 getText.lpUsedDefChar = NULL;
3727 setText.flags = 0;
3728 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3729 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3730 ok(lstrcmpW(buf, TestItem2) == 0,
3731 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3733 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3734 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3735 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3736 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3738 /* Baseline test for just-enough buffer space for string */
3739 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3740 getText.codepage = 1200; /* no constant for unicode */
3741 getText.flags = GT_DEFAULT;
3742 getText.lpDefaultChar = NULL;
3743 getText.lpUsedDefChar = NULL;
3744 memset(buf, 0, MAX_BUF_LEN);
3745 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3746 ok(lstrcmpW(buf, TestItem2) == 0,
3747 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3749 /* When there is enough space for one character, but not both, of the CRLF
3750 pair at the end of the string, the CR is not copied at all. That is,
3751 the caller must not see CRLF pairs truncated to CR at the end of the
3752 string.
3754 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3755 getText.codepage = 1200; /* no constant for unicode */
3756 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3757 getText.lpDefaultChar = NULL;
3758 getText.lpUsedDefChar = NULL;
3759 memset(buf, 0, MAX_BUF_LEN);
3760 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3761 ok(lstrcmpW(buf, TestItem1) == 0,
3762 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3765 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3766 setText.codepage = 1200; /* no constant for unicode */
3767 getText.codepage = 1200; /* no constant for unicode */
3768 getText.cb = MAX_BUF_LEN;
3769 getText.flags = GT_DEFAULT;
3770 getText.lpDefaultChar = NULL;
3771 getText.lpUsedDefChar = NULL;
3772 setText.flags = 0;
3773 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3774 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3775 ok(lstrcmpW(buf, TestItem3_after) == 0,
3776 "EM_SETTEXTEX did not convert properly\n");
3778 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3779 setText.codepage = 1200; /* no constant for unicode */
3780 getText.codepage = 1200; /* no constant for unicode */
3781 getText.cb = MAX_BUF_LEN;
3782 getText.flags = GT_DEFAULT;
3783 getText.lpDefaultChar = NULL;
3784 getText.lpUsedDefChar = NULL;
3785 setText.flags = 0;
3786 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3787 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3788 ok(lstrcmpW(buf, TestItem3_after) == 0,
3789 "EM_SETTEXTEX did not convert properly\n");
3791 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3792 setText.codepage = 1200; /* no constant for unicode */
3793 getText.codepage = 1200; /* no constant for unicode */
3794 getText.cb = MAX_BUF_LEN;
3795 getText.flags = GT_DEFAULT;
3796 getText.lpDefaultChar = NULL;
3797 getText.lpUsedDefChar = NULL;
3798 setText.flags = 0;
3799 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3800 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3801 ok(lstrcmpW(buf, TestItem4_after) == 0,
3802 "EM_SETTEXTEX did not convert properly\n");
3804 /* !ST_SELECTION && Unicode && !\rtf */
3805 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3806 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3808 ok (result == 1,
3809 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3810 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3812 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3813 setText.flags = 0;
3814 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3815 /* select some text */
3816 cr.cpMax = 1;
3817 cr.cpMin = 3;
3818 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3819 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3820 setText.flags = ST_SELECTION;
3821 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3822 ok(result == 0,
3823 "EM_SETTEXTEX with NULL lParam to replace selection"
3824 " with no text should return 0. Got %i\n",
3825 result);
3827 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3828 setText.flags = 0;
3829 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3830 /* select some text */
3831 cr.cpMax = 1;
3832 cr.cpMin = 3;
3833 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3834 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3835 setText.flags = ST_SELECTION;
3836 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3837 /* get text */
3838 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3839 ok(result == lstrlenW(TestItem1),
3840 "EM_SETTEXTEX with NULL lParam to replace selection"
3841 " with no text should return 0. Got %i\n",
3842 result);
3843 ok(lstrlenW(buf) == 22,
3844 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3845 lstrlenW(buf) );
3847 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3848 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3849 p = (char *)buf;
3850 es.dwCookie = (DWORD_PTR)&p;
3851 es.dwError = 0;
3852 es.pfnCallback = test_WM_SETTEXT_esCallback;
3853 memset(buf, 0, sizeof(buf));
3854 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3855 (WPARAM)(SF_RTF), (LPARAM)&es);
3856 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3858 /* !ST_SELECTION && !Unicode && \rtf */
3859 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3860 getText.codepage = 1200; /* no constant for unicode */
3861 getText.cb = MAX_BUF_LEN;
3862 getText.flags = GT_DEFAULT;
3863 getText.lpDefaultChar = NULL;
3864 getText.lpUsedDefChar = NULL;
3866 setText.flags = 0;
3867 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3868 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3869 ok(lstrcmpW(buf, TestItem1) == 0,
3870 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3872 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3873 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3874 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3875 getText.codepage = CP_ACP;
3876 getText.cb = MAX_BUF_LEN;
3877 getText.flags = GT_DEFAULT;
3878 getText.lpDefaultChar = NULL;
3879 getText.lpUsedDefChar = NULL;
3881 setText.flags = ST_SELECTION;
3882 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3883 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
3884 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3885 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3886 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3888 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3889 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3890 p = (char *)buf;
3891 es.dwCookie = (DWORD_PTR)&p;
3892 es.dwError = 0;
3893 es.pfnCallback = test_WM_SETTEXT_esCallback;
3894 memset(buf, 0, sizeof(buf));
3895 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3896 (WPARAM)(SF_RTF), (LPARAM)&es);
3897 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3899 /* select some text */
3900 cr.cpMax = 1;
3901 cr.cpMin = 3;
3902 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3904 /* ST_SELECTION && !Unicode && \rtf */
3905 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3906 getText.codepage = 1200; /* no constant for unicode */
3907 getText.cb = MAX_BUF_LEN;
3908 getText.flags = GT_DEFAULT;
3909 getText.lpDefaultChar = NULL;
3910 getText.lpUsedDefChar = NULL;
3912 setText.flags = ST_SELECTION;
3913 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3914 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3915 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3917 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3918 setText.codepage = 1200; /* no constant for unicode */
3919 getText.codepage = CP_ACP;
3920 getText.cb = MAX_BUF_LEN;
3922 setText.flags = 0;
3923 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
3924 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3926 /* select some text */
3927 cr.cpMax = 1;
3928 cr.cpMin = 3;
3929 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3931 /* ST_SELECTION && !Unicode && !\rtf */
3932 setText.codepage = CP_ACP;
3933 getText.codepage = 1200; /* no constant for unicode */
3934 getText.cb = MAX_BUF_LEN;
3935 getText.flags = GT_DEFAULT;
3936 getText.lpDefaultChar = NULL;
3937 getText.lpUsedDefChar = NULL;
3939 setText.flags = ST_SELECTION;
3940 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
3941 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3942 ok(lstrcmpW(buf, TestItem1alt) == 0,
3943 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3944 " using ST_SELECTION and non-Unicode\n");
3946 /* Test setting text using rich text format */
3947 setText.flags = 0;
3948 setText.codepage = CP_ACP;
3949 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3950 getText.codepage = CP_ACP;
3951 getText.cb = MAX_BUF_LEN;
3952 getText.flags = GT_DEFAULT;
3953 getText.lpDefaultChar = NULL;
3954 getText.lpUsedDefChar = NULL;
3955 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3956 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3958 setText.flags = 0;
3959 setText.codepage = CP_ACP;
3960 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3961 getText.codepage = CP_ACP;
3962 getText.cb = MAX_BUF_LEN;
3963 getText.flags = GT_DEFAULT;
3964 getText.lpDefaultChar = NULL;
3965 getText.lpUsedDefChar = NULL;
3966 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3967 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3969 /* test for utf8 text with BOM */
3970 setText.flags = 0;
3971 setText.codepage = CP_ACP;
3972 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3973 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3974 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3975 result = strcmp(bufACP, "TestUTF8WithBOM");
3976 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3978 setText.flags = 0;
3979 setText.codepage = CP_UTF8;
3980 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3981 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3982 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3983 result = strcmp(bufACP, "TestUTF8WithBOM");
3984 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3986 DestroyWindow(hwndRichEdit);
3989 static void test_EM_LIMITTEXT(void)
3991 int ret;
3993 HWND hwndRichEdit = new_richedit(NULL);
3995 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3996 * about setting the length to -1 for multiline edit controls doesn't happen.
3999 /* Don't check default gettextlimit case. That's done in other tests */
4001 /* Set textlimit to 100 */
4002 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4003 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4004 ok (ret == 100,
4005 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4007 /* Set textlimit to 0 */
4008 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4009 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4010 ok (ret == 65536,
4011 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4013 /* Set textlimit to -1 */
4014 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4015 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4016 ok (ret == -1,
4017 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4019 /* Set textlimit to -2 */
4020 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4021 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4022 ok (ret == -2,
4023 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4025 DestroyWindow (hwndRichEdit);
4029 static void test_EM_EXLIMITTEXT(void)
4031 int i, selBegin, selEnd, len1, len2;
4032 int result;
4033 char text[1024 + 1];
4034 char buffer[1024 + 1];
4035 int textlimit = 0; /* multiple of 100 */
4036 HWND hwndRichEdit = new_richedit(NULL);
4038 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4039 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4041 textlimit = 256000;
4042 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4043 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4044 /* set higher */
4045 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4047 textlimit = 1000;
4048 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4049 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4050 /* set lower */
4051 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4053 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4054 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4055 /* default for WParam = 0 */
4056 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4058 textlimit = sizeof(text)-1;
4059 memset(text, 'W', textlimit);
4060 text[sizeof(text)-1] = 0;
4061 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4062 /* maxed out text */
4063 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4065 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4066 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4067 len1 = selEnd - selBegin;
4069 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4070 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4071 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4072 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4073 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4074 len2 = selEnd - selBegin;
4076 ok(len1 != len2,
4077 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4078 len1,len2,i);
4080 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4081 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4082 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4083 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4084 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4085 len1 = selEnd - selBegin;
4087 ok(len1 != len2,
4088 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4089 len1,len2,i);
4091 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4092 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4093 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4094 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4095 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4096 len2 = selEnd - selBegin;
4098 ok(len1 == len2,
4099 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4100 len1,len2,i);
4102 /* set text up to the limit, select all the text, then add a char */
4103 textlimit = 5;
4104 memset(text, 'W', textlimit);
4105 text[textlimit] = 0;
4106 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4107 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4108 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4109 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4110 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4111 result = strcmp(buffer, "A");
4112 ok(0 == result, "got string = \"%s\"\n", buffer);
4114 /* WM_SETTEXT not limited */
4115 textlimit = 10;
4116 memset(text, 'W', textlimit);
4117 text[textlimit] = 0;
4118 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4119 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4120 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4121 i = strlen(buffer);
4122 ok(10 == i, "expected 10 chars\n");
4123 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4124 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4126 /* try inserting more text at end */
4127 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4128 ok(0 == i, "WM_CHAR wasn't processed\n");
4129 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4130 i = strlen(buffer);
4131 ok(10 == i, "expected 10 chars, got %i\n", i);
4132 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4133 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4135 /* try inserting text at beginning */
4136 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4137 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4138 ok(0 == i, "WM_CHAR wasn't processed\n");
4139 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4140 i = strlen(buffer);
4141 ok(10 == i, "expected 10 chars, got %i\n", i);
4142 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4143 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4145 /* WM_CHAR is limited */
4146 textlimit = 1;
4147 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4148 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4149 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4150 ok(0 == i, "WM_CHAR wasn't processed\n");
4151 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4152 ok(0 == i, "WM_CHAR wasn't processed\n");
4153 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4154 i = strlen(buffer);
4155 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4157 DestroyWindow(hwndRichEdit);
4160 static void test_EM_GETLIMITTEXT(void)
4162 int i;
4163 HWND hwndRichEdit = new_richedit(NULL);
4165 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4166 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4168 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4169 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4170 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4172 DestroyWindow(hwndRichEdit);
4175 static void test_WM_SETFONT(void)
4177 /* There is no invalid input or error conditions for this function.
4178 * NULL wParam and lParam just fall back to their default values
4179 * It should be noted that even if you use a gibberish name for your fonts
4180 * here, it will still work because the name is stored. They will display as
4181 * System, but will report their name to be whatever they were created as */
4183 HWND hwndRichEdit = new_richedit(NULL);
4184 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4185 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4186 FF_DONTCARE, "Marlett");
4187 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4188 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4189 FF_DONTCARE, "MS Sans Serif");
4190 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4191 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4192 FF_DONTCARE, "Courier");
4193 LOGFONTA sentLogFont;
4194 CHARFORMAT2A returnedCF2A;
4196 returnedCF2A.cbSize = sizeof(returnedCF2A);
4198 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4199 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4200 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4202 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4203 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4204 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4205 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4207 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4208 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4209 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4210 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4211 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4212 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4214 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4215 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4216 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4217 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4218 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4219 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4221 /* This last test is special since we send in NULL. We clear the variables
4222 * and just compare to "System" instead of the sent in font name. */
4223 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4224 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4225 returnedCF2A.cbSize = sizeof(returnedCF2A);
4227 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4228 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4229 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4230 ok (!strcmp("System",returnedCF2A.szFaceName),
4231 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4233 DestroyWindow(hwndRichEdit);
4237 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4238 LPBYTE pbBuff,
4239 LONG cb,
4240 LONG *pcb)
4242 const char** str = (const char**)dwCookie;
4243 int size = strlen(*str);
4244 if(size > 3) /* let's make it piecemeal for fun */
4245 size = 3;
4246 *pcb = cb;
4247 if (*pcb > size) {
4248 *pcb = size;
4250 if (*pcb > 0) {
4251 memcpy(pbBuff, *str, *pcb);
4252 *str += *pcb;
4254 return 0;
4257 static void test_EM_GETMODIFY(void)
4259 HWND hwndRichEdit = new_richedit(NULL);
4260 LRESULT result;
4261 SETTEXTEX setText;
4262 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4263 'S', 'o', 'm', 'e',
4264 'T', 'e', 'x', 't', 0};
4265 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4266 'S', 'o', 'm', 'e',
4267 'O', 't', 'h', 'e', 'r',
4268 'T', 'e', 'x', 't', 0};
4269 const char* streamText = "hello world";
4270 CHARFORMAT2A cf2;
4271 PARAFORMAT2 pf2;
4272 EDITSTREAM es;
4274 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4275 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4276 FF_DONTCARE, "Courier");
4278 setText.codepage = 1200; /* no constant for unicode */
4279 setText.flags = ST_KEEPUNDO;
4282 /* modify flag shouldn't be set when richedit is first created */
4283 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4284 ok (result == 0,
4285 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4287 /* setting modify flag should actually set it */
4288 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4289 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4290 ok (result != 0,
4291 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4293 /* clearing modify flag should actually clear it */
4294 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4295 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4296 ok (result == 0,
4297 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4299 /* setting font doesn't change modify flag */
4300 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4301 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4302 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4303 ok (result == 0,
4304 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4306 /* setting text should set modify flag */
4307 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4308 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4309 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4310 ok (result != 0,
4311 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4313 /* undo previous text doesn't reset modify flag */
4314 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4315 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4316 ok (result != 0,
4317 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4319 /* set text with no flag to keep undo stack should not set modify flag */
4320 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4321 setText.flags = 0;
4322 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4323 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4324 ok (result == 0,
4325 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4327 /* WM_SETTEXT doesn't modify */
4328 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4329 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4330 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4331 ok (result == 0,
4332 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4334 /* clear the text */
4335 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4336 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4337 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4338 ok (result == 0,
4339 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4341 /* replace text */
4342 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4343 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4344 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4345 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4346 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4347 ok (result != 0,
4348 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4350 /* copy/paste text 1 */
4351 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4352 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4353 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4354 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4355 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4356 ok (result != 0,
4357 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4359 /* copy/paste text 2 */
4360 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4361 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4362 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4363 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4364 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4365 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4366 ok (result != 0,
4367 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4369 /* press char */
4370 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4371 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4372 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4373 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4374 ok (result != 0,
4375 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4377 /* press del */
4378 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4379 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4380 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4381 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4382 ok (result != 0,
4383 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4385 /* set char format */
4386 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4387 cf2.cbSize = sizeof(CHARFORMAT2A);
4388 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4389 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4390 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4391 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4392 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4393 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4394 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4395 ok (result != 0,
4396 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4398 /* set para format */
4399 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4400 pf2.cbSize = sizeof(PARAFORMAT2);
4401 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4402 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4403 pf2.wAlignment = PFA_RIGHT;
4404 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4405 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4406 ok (result == 0,
4407 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4409 /* EM_STREAM */
4410 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4411 es.dwCookie = (DWORD_PTR)&streamText;
4412 es.dwError = 0;
4413 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4414 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4415 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4416 ok (result != 0,
4417 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4419 DestroyWindow(hwndRichEdit);
4422 struct exsetsel_s {
4423 LONG min;
4424 LONG max;
4425 LRESULT expected_retval;
4426 int expected_getsel_start;
4427 int expected_getsel_end;
4428 BOOL todo;
4431 static const struct exsetsel_s exsetsel_tests[] = {
4432 /* sanity tests */
4433 {5, 10, 10, 5, 10 },
4434 {15, 17, 17, 15, 17 },
4435 /* test cpMax > strlen() */
4436 {0, 100, 18, 0, 18 },
4437 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4438 {-1, 1, 17, 17, 17 },
4439 /* test cpMin == cpMax */
4440 {5, 5, 5, 5, 5 },
4441 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4442 {-1, 0, 5, 5, 5 },
4443 {-1, 17, 5, 5, 5 },
4444 {-1, 18, 5, 5, 5 },
4445 /* test cpMin < 0 && cpMax < 0 */
4446 {-1, -1, 17, 17, 17 },
4447 {-4, -5, 17, 17, 17 },
4448 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4449 {0, -1, 18, 0, 18 },
4450 {17, -5, 18, 17, 18 },
4451 {18, -3, 17, 17, 17 },
4452 /* test if cpMin > cpMax */
4453 {15, 19, 18, 15, 18 },
4454 {19, 15, 18, 15, 18 },
4455 /* cpMin == strlen() && cpMax > cpMin */
4456 {17, 18, 18, 17, 18 },
4457 {17, 50, 18, 17, 18 },
4460 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4461 CHARRANGE cr;
4462 LRESULT result;
4463 int start, end;
4465 cr.cpMin = setsel->min;
4466 cr.cpMax = setsel->max;
4467 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4469 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4471 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4473 if (setsel->todo) {
4474 todo_wine {
4475 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);
4477 } else {
4478 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);
4482 static void test_EM_EXSETSEL(void)
4484 HWND hwndRichEdit = new_richedit(NULL);
4485 int i;
4486 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4488 /* sending some text to the window */
4489 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4490 /* 01234567890123456*/
4491 /* 10 */
4493 for (i = 0; i < num_tests; i++) {
4494 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4497 DestroyWindow(hwndRichEdit);
4500 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4501 LRESULT result;
4502 int start, end;
4504 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4506 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4508 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4510 if (setsel->todo) {
4511 todo_wine {
4512 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4514 } else {
4515 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4519 static void test_EM_SETSEL(void)
4521 char buffA[32];
4522 HWND hwndRichEdit = new_richedit(NULL);
4523 int i;
4524 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4526 /* sending some text to the window */
4527 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4528 /* 01234567890123456*/
4529 /* 10 */
4531 for (i = 0; i < num_tests; i++) {
4532 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4535 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
4536 buffA[0] = 123;
4537 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
4538 ok(buffA[0] == 0, "selection text %s\n", buffA);
4540 DestroyWindow(hwndRichEdit);
4543 static void test_EM_REPLACESEL(int redraw)
4545 HWND hwndRichEdit = new_richedit(NULL);
4546 char buffer[1024] = {0};
4547 int r;
4548 GETTEXTEX getText;
4549 CHARRANGE cr;
4551 /* sending some text to the window */
4552 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4553 /* 01234567890123456*/
4554 /* 10 */
4556 /* FIXME add more tests */
4557 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4558 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4559 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4560 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4561 r = strcmp(buffer, "testing");
4562 ok(0 == r, "expected %d, got %d\n", 0, r);
4564 DestroyWindow(hwndRichEdit);
4566 hwndRichEdit = new_richedit(NULL);
4568 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4569 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4571 /* Test behavior with carriage returns and newlines */
4572 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4573 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4574 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4575 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4576 r = strcmp(buffer, "RichEdit1");
4577 ok(0 == r, "expected %d, got %d\n", 0, r);
4578 getText.cb = 1024;
4579 getText.codepage = CP_ACP;
4580 getText.flags = GT_DEFAULT;
4581 getText.lpDefaultChar = NULL;
4582 getText.lpUsedDefChar = NULL;
4583 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4584 ok(strcmp(buffer, "RichEdit1") == 0,
4585 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4587 /* Test number of lines reported after EM_REPLACESEL */
4588 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4589 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4591 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4592 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4593 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4594 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4595 r = strcmp(buffer, "RichEdit1\r\n");
4596 ok(0 == r, "expected %d, got %d\n", 0, r);
4597 getText.cb = 1024;
4598 getText.codepage = CP_ACP;
4599 getText.flags = GT_DEFAULT;
4600 getText.lpDefaultChar = NULL;
4601 getText.lpUsedDefChar = NULL;
4602 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4603 ok(strcmp(buffer, "RichEdit1\r") == 0,
4604 "EM_GETTEXTEX returned incorrect string\n");
4606 /* Test number of lines reported after EM_REPLACESEL */
4607 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4608 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4610 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4611 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4612 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4614 /* Test number of lines reported after EM_REPLACESEL */
4615 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4616 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4618 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4619 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4620 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4621 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4623 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4624 r = strcmp(buffer, "RichEdit1\r\n");
4625 ok(0 == r, "expected %d, got %d\n", 0, r);
4626 getText.cb = 1024;
4627 getText.codepage = CP_ACP;
4628 getText.flags = GT_DEFAULT;
4629 getText.lpDefaultChar = NULL;
4630 getText.lpUsedDefChar = NULL;
4631 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4632 ok(strcmp(buffer, "RichEdit1\r") == 0,
4633 "EM_GETTEXTEX returned incorrect string\n");
4635 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4636 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4637 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4638 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4640 /* The following tests show that richedit should handle the special \r\r\n
4641 sequence by turning it into a single space on insertion. However,
4642 EM_REPLACESEL on WinXP returns the number of characters in the original
4643 string.
4646 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4647 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4648 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4649 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4650 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4651 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4652 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4654 /* Test the actual string */
4655 getText.cb = 1024;
4656 getText.codepage = CP_ACP;
4657 getText.flags = GT_DEFAULT;
4658 getText.lpDefaultChar = NULL;
4659 getText.lpUsedDefChar = NULL;
4660 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4661 ok(strcmp(buffer, "\r\r") == 0,
4662 "EM_GETTEXTEX returned incorrect string\n");
4664 /* Test number of lines reported after EM_REPLACESEL */
4665 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4666 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4668 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4669 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4670 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4671 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4672 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4673 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4674 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4676 /* Test the actual string */
4677 getText.cb = 1024;
4678 getText.codepage = CP_ACP;
4679 getText.flags = GT_DEFAULT;
4680 getText.lpDefaultChar = NULL;
4681 getText.lpUsedDefChar = NULL;
4682 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4683 ok(strcmp(buffer, " ") == 0,
4684 "EM_GETTEXTEX returned incorrect string\n");
4686 /* Test number of lines reported after EM_REPLACESEL */
4687 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4688 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4690 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4691 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4692 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4693 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4694 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4695 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4696 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4698 /* Test the actual string */
4699 getText.cb = 1024;
4700 getText.codepage = CP_ACP;
4701 getText.flags = GT_DEFAULT;
4702 getText.lpDefaultChar = NULL;
4703 getText.lpUsedDefChar = NULL;
4704 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4705 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4706 "EM_GETTEXTEX returned incorrect string\n");
4708 /* Test number of lines reported after EM_REPLACESEL */
4709 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4710 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4712 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4713 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4714 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4715 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4716 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4717 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4718 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4720 /* Test the actual string */
4721 getText.cb = 1024;
4722 getText.codepage = CP_ACP;
4723 getText.flags = GT_DEFAULT;
4724 getText.lpDefaultChar = NULL;
4725 getText.lpUsedDefChar = NULL;
4726 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4727 ok(strcmp(buffer, " \r") == 0,
4728 "EM_GETTEXTEX returned incorrect string\n");
4730 /* Test number of lines reported after EM_REPLACESEL */
4731 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4732 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4734 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4735 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4736 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4737 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4738 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4739 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4740 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4742 /* Test the actual string */
4743 getText.cb = 1024;
4744 getText.codepage = CP_ACP;
4745 getText.flags = GT_DEFAULT;
4746 getText.lpDefaultChar = NULL;
4747 getText.lpUsedDefChar = NULL;
4748 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4749 ok(strcmp(buffer, " \r\r") == 0,
4750 "EM_GETTEXTEX returned incorrect string\n");
4752 /* Test number of lines reported after EM_REPLACESEL */
4753 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4754 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4756 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4757 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4758 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4759 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4760 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4761 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4762 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4764 /* Test the actual string */
4765 getText.cb = 1024;
4766 getText.codepage = CP_ACP;
4767 getText.flags = GT_DEFAULT;
4768 getText.lpDefaultChar = NULL;
4769 getText.lpUsedDefChar = NULL;
4770 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4771 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4772 "EM_GETTEXTEX returned incorrect string\n");
4774 /* Test number of lines reported after EM_REPLACESEL */
4775 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4776 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4778 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4779 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4780 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4781 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4782 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4783 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4784 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4786 /* Test the actual string */
4787 getText.cb = 1024;
4788 getText.codepage = CP_ACP;
4789 getText.flags = GT_DEFAULT;
4790 getText.lpDefaultChar = NULL;
4791 getText.lpUsedDefChar = NULL;
4792 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4793 ok(strcmp(buffer, "\r\r") == 0,
4794 "EM_GETTEXTEX returned incorrect string\n");
4796 /* Test number of lines reported after EM_REPLACESEL */
4797 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4798 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4800 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4801 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4802 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4803 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4804 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4805 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4806 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4808 /* Test the actual string */
4809 getText.cb = 1024;
4810 getText.codepage = CP_ACP;
4811 getText.flags = GT_DEFAULT;
4812 getText.lpDefaultChar = NULL;
4813 getText.lpUsedDefChar = NULL;
4814 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4815 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4816 "EM_GETTEXTEX returned incorrect string\n");
4818 /* Test number of lines reported after EM_REPLACESEL */
4819 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4820 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4822 if (!redraw)
4823 /* This is needed to avoid interferring with keybd_event calls
4824 * on other tests that simulate keyboard events. */
4825 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4827 DestroyWindow(hwndRichEdit);
4830 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4831 * to test the state of the modifiers (Ctrl/Alt/Shift).
4833 * Therefore Ctrl-<key> keystrokes need to be simulated with
4834 * keybd_event or by using SetKeyboardState to set the modifiers
4835 * and SendMessage to simulate the keystrokes.
4837 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4839 LRESULT result;
4840 hold_key(VK_CONTROL);
4841 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
4842 release_key(VK_CONTROL);
4843 return result;
4846 static void test_WM_PASTE(void)
4848 int result;
4849 char buffer[1024] = {0};
4850 const char* text1 = "testing paste\r";
4851 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4852 const char* text1_after = "testing paste\r\n";
4853 const char* text2 = "testing paste\r\rtesting paste";
4854 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4855 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4856 HWND hwndRichEdit = new_richedit(NULL);
4858 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4859 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
4861 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4862 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4863 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4864 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4865 /* Pasted text should be visible at this step */
4866 result = strcmp(text1_step1, buffer);
4867 ok(result == 0,
4868 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4870 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4871 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4872 /* Text should be the same as before (except for \r -> \r\n conversion) */
4873 result = strcmp(text1_after, buffer);
4874 ok(result == 0,
4875 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4877 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
4878 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
4879 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4880 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4881 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4882 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4883 /* Pasted text should be visible at this step */
4884 result = strcmp(text3, buffer);
4885 ok(result == 0,
4886 "test paste: strcmp = %i\n", result);
4887 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4888 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4889 /* Text should be the same as before (except for \r -> \r\n conversion) */
4890 result = strcmp(text2_after, buffer);
4891 ok(result == 0,
4892 "test paste: strcmp = %i\n", result);
4893 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4894 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4895 /* Text should revert to post-paste state */
4896 result = strcmp(buffer,text3);
4897 ok(result == 0,
4898 "test paste: strcmp = %i\n", result);
4900 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4901 /* Send WM_CHAR to simulate Ctrl-V */
4902 SendMessageA(hwndRichEdit, WM_CHAR, 22,
4903 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4904 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4905 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4906 result = strcmp(buffer,"");
4907 ok(result == 0,
4908 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4910 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4911 * with SetKeyboard state. */
4913 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4914 /* Simulates paste (Ctrl-V) */
4915 hold_key(VK_CONTROL);
4916 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
4917 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4918 release_key(VK_CONTROL);
4919 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4920 result = strcmp(buffer,"paste");
4921 ok(result == 0,
4922 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4924 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4925 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
4926 /* Simulates copy (Ctrl-C) */
4927 hold_key(VK_CONTROL);
4928 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
4929 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
4930 release_key(VK_CONTROL);
4931 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4932 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4933 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4934 result = strcmp(buffer,"testing");
4935 ok(result == 0,
4936 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4938 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4939 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
4940 /* Simulates select all (Ctrl-A) */
4941 hold_key(VK_CONTROL);
4942 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
4943 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
4944 /* Simulates select cut (Ctrl-X) */
4945 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
4946 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
4947 release_key(VK_CONTROL);
4948 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4949 result = strcmp(buffer,"");
4950 ok(result == 0,
4951 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4952 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4953 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4954 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4955 result = strcmp(buffer,"cut\r\n");
4956 ok(result == 0,
4957 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4958 /* Simulates undo (Ctrl-Z) */
4959 hold_key(VK_CONTROL);
4960 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
4961 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4962 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4963 result = strcmp(buffer,"");
4964 ok(result == 0,
4965 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4966 /* Simulates redo (Ctrl-Y) */
4967 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
4968 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4969 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4970 result = strcmp(buffer,"cut\r\n");
4971 ok(result == 0,
4972 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4973 release_key(VK_CONTROL);
4975 DestroyWindow(hwndRichEdit);
4978 static void test_EM_FORMATRANGE(void)
4980 int r, i, tpp_x, tpp_y;
4981 HDC hdc;
4982 HWND hwndRichEdit = new_richedit(NULL);
4983 FORMATRANGE fr;
4984 BOOL skip_non_english;
4985 static const struct {
4986 const char *string; /* The string */
4987 int first; /* First 'pagebreak', 0 for don't care */
4988 int second; /* Second 'pagebreak', 0 for don't care */
4989 } fmtstrings[] = {
4990 {"WINE wine", 0, 0},
4991 {"WINE wineWine", 0, 0},
4992 {"WINE\r\nwine\r\nwine", 5, 10},
4993 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4994 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4997 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4998 if (skip_non_english)
4999 skip("Skipping some tests on non-English platform\n");
5001 hdc = GetDC(hwndRichEdit);
5002 ok(hdc != NULL, "Could not get HDC\n");
5004 /* Calculate the twips per pixel */
5005 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5006 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5008 /* Test the simple case where all the text fits in the page rect. */
5009 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5010 fr.hdc = fr.hdcTarget = hdc;
5011 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5012 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5013 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5014 fr.chrg.cpMin = 0;
5015 fr.chrg.cpMax = -1;
5016 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5017 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5019 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5020 fr.rc.bottom = fr.rcPage.bottom;
5021 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5022 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5024 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5026 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5028 GETTEXTLENGTHEX gtl;
5029 SIZE stringsize;
5030 int len;
5032 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5034 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5035 gtl.codepage = CP_ACP;
5036 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5038 /* Get some size information for the string */
5039 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5041 /* Define the box to be half the width needed and a bit larger than the height.
5042 * Changes to the width means we have at least 2 pages. Changes to the height
5043 * is done so we can check the changing of fr.rc.bottom.
5045 fr.hdc = fr.hdcTarget = hdc;
5046 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5047 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5048 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5050 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5051 todo_wine {
5052 ok(r == len, "Expected %d, got %d\n", len, r);
5055 /* We know that the page can't hold the full string. See how many characters
5056 * are on the first one
5058 fr.chrg.cpMin = 0;
5059 fr.chrg.cpMax = -1;
5060 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5061 todo_wine {
5062 if (! skip_non_english)
5063 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5065 if (fmtstrings[i].first)
5066 todo_wine {
5067 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5069 else
5070 ok(r < len, "Expected < %d, got %d\n", len, r);
5072 /* Do another page */
5073 fr.chrg.cpMin = r;
5074 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5075 if (fmtstrings[i].second)
5076 todo_wine {
5077 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5079 else if (! skip_non_english)
5080 ok (r < len, "Expected < %d, got %d\n", len, r);
5082 /* There is at least on more page, but we don't care */
5084 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5085 todo_wine {
5086 ok(r == len, "Expected %d, got %d\n", len, r);
5090 ReleaseDC(NULL, hdc);
5091 DestroyWindow(hwndRichEdit);
5094 static int nCallbackCount = 0;
5096 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5097 LONG cb, LONG* pcb)
5099 const char text[] = {'t','e','s','t'};
5101 if (sizeof(text) <= cb)
5103 if ((int)dwCookie != nCallbackCount)
5105 *pcb = 0;
5106 return 0;
5109 memcpy (pbBuff, text, sizeof(text));
5110 *pcb = sizeof(text);
5112 nCallbackCount++;
5114 return 0;
5116 else
5117 return 1; /* indicates callback failed */
5120 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5121 LPBYTE pbBuff,
5122 LONG cb,
5123 LONG *pcb)
5125 const char** str = (const char**)dwCookie;
5126 int size = strlen(*str);
5127 *pcb = cb;
5128 if (*pcb > size) {
5129 *pcb = size;
5131 if (*pcb > 0) {
5132 memcpy(pbBuff, *str, *pcb);
5133 *str += *pcb;
5135 return 0;
5138 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5139 LPBYTE pbBuff,
5140 LONG cb,
5141 LONG *pcb)
5143 DWORD *phase = (DWORD *)dwCookie;
5145 if(*phase == 0){
5146 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5147 *pcb = sizeof(first) - 1;
5148 memcpy(pbBuff, first, *pcb);
5149 }else if(*phase == 1){
5150 static const char second[] = "\x8f\xc3\x8b";
5151 *pcb = sizeof(second) - 1;
5152 memcpy(pbBuff, second, *pcb);
5153 }else
5154 *pcb = 0;
5156 ++*phase;
5158 return 0;
5161 struct StringWithLength {
5162 int length;
5163 char *buffer;
5166 /* This callback is used to handled the null characters in a string. */
5167 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5168 LPBYTE pbBuff,
5169 LONG cb,
5170 LONG *pcb)
5172 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5173 int size = str->length;
5174 *pcb = cb;
5175 if (*pcb > size) {
5176 *pcb = size;
5178 if (*pcb > 0) {
5179 memcpy(pbBuff, str->buffer, *pcb);
5180 str->buffer += *pcb;
5181 str->length -= *pcb;
5183 return 0;
5186 static void test_EM_STREAMIN(void)
5188 HWND hwndRichEdit = new_richedit(NULL);
5189 DWORD phase;
5190 LRESULT result;
5191 EDITSTREAM es;
5192 char buffer[1024] = {0}, tmp[16];
5193 CHARRANGE range;
5195 const char * streamText0 = "{\\rtf1 TestSomeText}";
5196 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5197 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5198 const char * ptr;
5200 const char * streamText1 =
5201 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5202 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5203 "}\r\n";
5205 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5206 const char * streamText2 =
5207 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5208 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5209 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5210 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5211 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5212 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5213 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5215 const char * streamText3 = "RichEdit1";
5217 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5219 const char * streamText4 =
5220 "This text just needs to be long enough to cause run to be split onto "
5221 "two separate lines and make sure the null terminating character is "
5222 "handled properly.\0";
5224 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5226 int length4 = strlen(streamText4) + 1;
5227 struct StringWithLength cookieForStream4 = {
5228 length4,
5229 (char *)streamText4,
5232 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5233 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5234 struct StringWithLength cookieForStream5 = {
5235 sizeof(streamText5),
5236 (char *)streamText5,
5239 /* Minimal test without \par at the end */
5240 es.dwCookie = (DWORD_PTR)&streamText0;
5241 es.dwError = 0;
5242 es.pfnCallback = test_EM_STREAMIN_esCallback;
5243 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5244 ok(result == 12, "got %ld, expected %d\n", result, 12);
5246 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5247 ok (result == 12,
5248 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5249 result = strcmp (buffer,"TestSomeText");
5250 ok (result == 0,
5251 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5252 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5254 /* Native richedit 2.0 ignores last \par */
5255 ptr = streamText0a;
5256 es.dwCookie = (DWORD_PTR)&ptr;
5257 es.dwError = 0;
5258 es.pfnCallback = test_EM_STREAMIN_esCallback;
5259 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5260 ok(result == 12, "got %ld, expected %d\n", result, 12);
5262 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5263 ok (result == 12,
5264 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5265 result = strcmp (buffer,"TestSomeText");
5266 ok (result == 0,
5267 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5268 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5270 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5271 es.dwCookie = (DWORD_PTR)&streamText0b;
5272 es.dwError = 0;
5273 es.pfnCallback = test_EM_STREAMIN_esCallback;
5274 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5275 ok(result == 13, "got %ld, expected %d\n", result, 13);
5277 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5278 ok (result == 14,
5279 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5280 result = strcmp (buffer,"TestSomeText\r\n");
5281 ok (result == 0,
5282 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5283 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5285 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5286 ptr = streamText0a;
5287 es.dwCookie = (DWORD_PTR)&ptr;
5288 es.dwError = 0;
5289 es.pfnCallback = test_EM_STREAMIN_esCallback;
5290 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5291 ok(result == 12, "got %ld, expected %d\n", result, 12);
5293 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5294 ok (result == 12,
5295 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5296 result = strcmp (buffer,"TestSomeText");
5297 ok (result == 0,
5298 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5299 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5301 range.cpMin = 0;
5302 range.cpMax = -1;
5303 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5304 ok (result == 13, "got %ld\n", result);
5306 ptr = streamText0a;
5307 es.dwCookie = (DWORD_PTR)&ptr;
5308 es.dwError = 0;
5309 es.pfnCallback = test_EM_STREAMIN_esCallback;
5311 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5312 ok(result == 13, "got %ld, expected 13\n", result);
5314 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5315 ok (result == 14,
5316 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5317 result = strcmp (buffer,"TestSomeText\r\n");
5318 ok (result == 0,
5319 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5320 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5322 es.dwCookie = (DWORD_PTR)&streamText1;
5323 es.dwError = 0;
5324 es.pfnCallback = test_EM_STREAMIN_esCallback;
5325 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5326 ok(result == 12, "got %ld, expected %d\n", result, 12);
5328 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5329 ok (result == 12,
5330 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5331 result = strcmp (buffer,"TestSomeText");
5332 ok (result == 0,
5333 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5334 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5336 es.dwCookie = (DWORD_PTR)&streamText2;
5337 es.dwError = 0;
5338 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5339 ok(result == 0, "got %ld, expected %d\n", result, 0);
5341 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5342 ok (result == 0,
5343 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5344 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5345 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5347 es.dwCookie = (DWORD_PTR)&streamText3;
5348 es.dwError = 0;
5349 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5350 ok(result == 0, "got %ld, expected %d\n", result, 0);
5352 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5353 ok (result == 0,
5354 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5355 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5356 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5358 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5359 es.dwError = 0;
5360 es.pfnCallback = test_EM_STREAMIN_esCallback;
5361 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5362 ok(result == 18, "got %ld, expected %d\n", result, 18);
5364 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5365 ok(result == 15,
5366 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5367 result = strcmp (buffer,"TestUTF8WithBOM");
5368 ok(result == 0,
5369 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5370 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5372 phase = 0;
5373 es.dwCookie = (DWORD_PTR)&phase;
5374 es.dwError = 0;
5375 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5376 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5377 ok(result == 8, "got %ld\n", result);
5379 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5381 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5382 ok(result == 3,
5383 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5384 result = memcmp (buffer, tmp, 3);
5385 ok(result == 0,
5386 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5387 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5389 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5390 es.dwError = 0;
5391 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5392 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5393 ok(result == length4, "got %ld, expected %d\n", result, length4);
5395 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5396 ok (result == length4,
5397 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5398 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5400 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5401 es.dwError = 0;
5402 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5403 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5404 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5406 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5407 ok (result == length5,
5408 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5409 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5411 DestroyWindow(hwndRichEdit);
5414 static void test_EM_StreamIn_Undo(void)
5416 /* The purpose of this test is to determine when a EM_StreamIn should be
5417 * undoable. This is important because WM_PASTE currently uses StreamIn and
5418 * pasting should always be undoable but streaming isn't always.
5420 * cases to test:
5421 * StreamIn plain text without SFF_SELECTION.
5422 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5423 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5424 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5425 * Feel free to add tests for other text modes or StreamIn things.
5429 HWND hwndRichEdit = new_richedit(NULL);
5430 LRESULT result;
5431 EDITSTREAM es;
5432 char buffer[1024] = {0};
5433 const char randomtext[] = "Some text";
5435 es.pfnCallback = EditStreamCallback;
5437 /* StreamIn, no SFF_SELECTION */
5438 es.dwCookie = nCallbackCount;
5439 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5440 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5441 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5442 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5443 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5444 result = strcmp (buffer,"test");
5445 ok (result == 0,
5446 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5448 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5449 ok (result == FALSE,
5450 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5452 /* StreamIn, SFF_SELECTION, but nothing selected */
5453 es.dwCookie = nCallbackCount;
5454 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5455 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5456 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5457 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5458 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5459 result = strcmp (buffer,"testSome text");
5460 ok (result == 0,
5461 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5463 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5464 ok (result == TRUE,
5465 "EM_STREAMIN with SFF_SELECTION but no selection set "
5466 "should create an undo\n");
5468 /* StreamIn, SFF_SELECTION, with a selection */
5469 es.dwCookie = nCallbackCount;
5470 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5471 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5472 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5473 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5474 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5475 result = strcmp (buffer,"Sometesttext");
5476 ok (result == 0,
5477 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5479 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5480 ok (result == TRUE,
5481 "EM_STREAMIN with SFF_SELECTION and selection set "
5482 "should create an undo\n");
5484 DestroyWindow(hwndRichEdit);
5487 static BOOL is_em_settextex_supported(HWND hwnd)
5489 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5490 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5493 static void test_unicode_conversions(void)
5495 static const WCHAR tW[] = {'t',0};
5496 static const WCHAR teW[] = {'t','e',0};
5497 static const WCHAR textW[] = {'t','e','s','t',0};
5498 static const char textA[] = "test";
5499 char bufA[64];
5500 WCHAR bufW[64];
5501 HWND hwnd;
5502 int em_settextex_supported, ret;
5504 #define set_textA(hwnd, wm_set_text, txt) \
5505 do { \
5506 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5507 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5508 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5509 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5510 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5511 } while(0)
5512 #define expect_textA(hwnd, wm_get_text, txt) \
5513 do { \
5514 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5515 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5516 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5517 memset(bufA, 0xAA, sizeof(bufA)); \
5518 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5519 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5520 ret = lstrcmpA(bufA, txt); \
5521 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5522 } while(0)
5524 #define set_textW(hwnd, wm_set_text, txt) \
5525 do { \
5526 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5527 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5528 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5529 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5530 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5531 } while(0)
5532 #define expect_textW(hwnd, wm_get_text, txt) \
5533 do { \
5534 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5535 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5536 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5537 memset(bufW, 0xAA, sizeof(bufW)); \
5538 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5539 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5540 ret = lstrcmpW(bufW, txt); \
5541 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5542 } while(0)
5543 #define expect_empty(hwnd, wm_get_text) \
5544 do { \
5545 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5546 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5547 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5548 memset(bufA, 0xAA, sizeof(bufA)); \
5549 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5550 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5551 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5552 } while(0)
5554 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5555 0, 0, 200, 60, 0, 0, 0, 0);
5556 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5558 ret = IsWindowUnicode(hwnd);
5559 ok(ret, "RichEdit20W should be unicode under NT\n");
5561 /* EM_SETTEXTEX is supported starting from version 3.0 */
5562 em_settextex_supported = is_em_settextex_supported(hwnd);
5563 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5564 em_settextex_supported ? "" : "NOT ");
5566 expect_empty(hwnd, WM_GETTEXT);
5567 expect_empty(hwnd, EM_GETTEXTEX);
5569 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5570 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5571 expect_textA(hwnd, WM_GETTEXT, "t");
5572 expect_textA(hwnd, EM_GETTEXTEX, "t");
5573 expect_textW(hwnd, EM_GETTEXTEX, tW);
5575 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5576 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5577 expect_textA(hwnd, WM_GETTEXT, "te");
5578 expect_textA(hwnd, EM_GETTEXTEX, "te");
5579 expect_textW(hwnd, EM_GETTEXTEX, teW);
5581 set_textA(hwnd, WM_SETTEXT, NULL);
5582 expect_empty(hwnd, WM_GETTEXT);
5583 expect_empty(hwnd, EM_GETTEXTEX);
5585 set_textA(hwnd, WM_SETTEXT, textA);
5586 expect_textA(hwnd, WM_GETTEXT, textA);
5587 expect_textA(hwnd, EM_GETTEXTEX, textA);
5588 expect_textW(hwnd, EM_GETTEXTEX, textW);
5590 if (em_settextex_supported)
5592 set_textA(hwnd, EM_SETTEXTEX, textA);
5593 expect_textA(hwnd, WM_GETTEXT, textA);
5594 expect_textA(hwnd, EM_GETTEXTEX, textA);
5595 expect_textW(hwnd, EM_GETTEXTEX, textW);
5598 set_textW(hwnd, WM_SETTEXT, textW);
5599 expect_textW(hwnd, WM_GETTEXT, textW);
5600 expect_textA(hwnd, WM_GETTEXT, textA);
5601 expect_textW(hwnd, EM_GETTEXTEX, textW);
5602 expect_textA(hwnd, EM_GETTEXTEX, textA);
5604 if (em_settextex_supported)
5606 set_textW(hwnd, EM_SETTEXTEX, textW);
5607 expect_textW(hwnd, WM_GETTEXT, textW);
5608 expect_textA(hwnd, WM_GETTEXT, textA);
5609 expect_textW(hwnd, EM_GETTEXTEX, textW);
5610 expect_textA(hwnd, EM_GETTEXTEX, textA);
5612 DestroyWindow(hwnd);
5614 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5615 0, 0, 200, 60, 0, 0, 0, 0);
5616 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5618 ret = IsWindowUnicode(hwnd);
5619 ok(!ret, "RichEdit20A should NOT be unicode\n");
5621 set_textA(hwnd, WM_SETTEXT, textA);
5622 expect_textA(hwnd, WM_GETTEXT, textA);
5623 expect_textA(hwnd, EM_GETTEXTEX, textA);
5624 expect_textW(hwnd, EM_GETTEXTEX, textW);
5626 if (em_settextex_supported)
5628 set_textA(hwnd, EM_SETTEXTEX, textA);
5629 expect_textA(hwnd, WM_GETTEXT, textA);
5630 expect_textA(hwnd, EM_GETTEXTEX, textA);
5631 expect_textW(hwnd, EM_GETTEXTEX, textW);
5634 set_textW(hwnd, WM_SETTEXT, textW);
5635 expect_textW(hwnd, WM_GETTEXT, textW);
5636 expect_textA(hwnd, WM_GETTEXT, textA);
5637 expect_textW(hwnd, EM_GETTEXTEX, textW);
5638 expect_textA(hwnd, EM_GETTEXTEX, textA);
5640 if (em_settextex_supported)
5642 set_textW(hwnd, EM_SETTEXTEX, textW);
5643 expect_textW(hwnd, WM_GETTEXT, textW);
5644 expect_textA(hwnd, WM_GETTEXT, textA);
5645 expect_textW(hwnd, EM_GETTEXTEX, textW);
5646 expect_textA(hwnd, EM_GETTEXTEX, textA);
5648 DestroyWindow(hwnd);
5651 static void test_WM_CHAR(void)
5653 HWND hwnd;
5654 int ret;
5655 const char * char_list = "abc\rabc\r";
5656 const char * expected_content_single = "abcabc";
5657 const char * expected_content_multi = "abc\r\nabc\r\n";
5658 char buffer[64] = {0};
5659 const char * p;
5661 /* single-line control must IGNORE carriage returns */
5662 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5663 0, 0, 200, 60, 0, 0, 0, 0);
5664 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5666 p = char_list;
5667 while (*p != '\0') {
5668 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5669 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5670 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5671 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5672 p++;
5675 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5676 ret = strcmp(buffer, expected_content_single);
5677 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5679 DestroyWindow(hwnd);
5681 /* multi-line control inserts CR normally */
5682 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5683 0, 0, 200, 60, 0, 0, 0, 0);
5684 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5686 p = char_list;
5687 while (*p != '\0') {
5688 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5689 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5690 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5691 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5692 p++;
5695 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5696 ret = strcmp(buffer, expected_content_multi);
5697 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5699 DestroyWindow(hwnd);
5702 static void test_EM_GETTEXTLENGTHEX(void)
5704 HWND hwnd;
5705 GETTEXTLENGTHEX gtl;
5706 int ret;
5707 const char * base_string = "base string";
5708 const char * test_string = "a\nb\n\n\r\n";
5709 const char * test_string_after = "a";
5710 const char * test_string_2 = "a\rtest\rstring";
5711 char buffer[64] = {0};
5713 /* single line */
5714 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5715 0, 0, 200, 60, 0, 0, 0, 0);
5716 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5718 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5719 gtl.codepage = CP_ACP;
5720 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5721 ok(ret == 0, "ret %d\n",ret);
5723 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5724 gtl.codepage = CP_ACP;
5725 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5726 ok(ret == 0, "ret %d\n",ret);
5728 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5730 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5731 gtl.codepage = CP_ACP;
5732 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5733 ok(ret == strlen(base_string), "ret %d\n",ret);
5735 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5736 gtl.codepage = CP_ACP;
5737 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5738 ok(ret == strlen(base_string), "ret %d\n",ret);
5740 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5742 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5743 gtl.codepage = CP_ACP;
5744 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5745 ok(ret == 1, "ret %d\n",ret);
5747 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5748 gtl.codepage = CP_ACP;
5749 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5750 ok(ret == 1, "ret %d\n",ret);
5752 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5753 ret = strcmp(buffer, test_string_after);
5754 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5756 DestroyWindow(hwnd);
5758 /* multi line */
5759 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5760 0, 0, 200, 60, 0, 0, 0, 0);
5761 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5763 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5764 gtl.codepage = CP_ACP;
5765 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5766 ok(ret == 0, "ret %d\n",ret);
5768 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5769 gtl.codepage = CP_ACP;
5770 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5771 ok(ret == 0, "ret %d\n",ret);
5773 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5775 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5776 gtl.codepage = CP_ACP;
5777 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5778 ok(ret == strlen(base_string), "ret %d\n",ret);
5780 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5781 gtl.codepage = CP_ACP;
5782 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5783 ok(ret == strlen(base_string), "ret %d\n",ret);
5785 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5787 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5788 gtl.codepage = CP_ACP;
5789 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5790 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5792 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5793 gtl.codepage = CP_ACP;
5794 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5795 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5797 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5799 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5800 gtl.codepage = CP_ACP;
5801 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5802 ok(ret == 10, "ret %d\n",ret);
5804 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5805 gtl.codepage = CP_ACP;
5806 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5807 ok(ret == 6, "ret %d\n",ret);
5809 /* Unicode/NUMCHARS/NUMBYTES */
5810 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5812 gtl.flags = GTL_DEFAULT;
5813 gtl.codepage = 1200;
5814 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5815 ok(ret == lstrlenA(test_string_2),
5816 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5818 gtl.flags = GTL_NUMCHARS;
5819 gtl.codepage = 1200;
5820 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5821 ok(ret == lstrlenA(test_string_2),
5822 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5824 gtl.flags = GTL_NUMBYTES;
5825 gtl.codepage = 1200;
5826 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5827 ok(ret == lstrlenA(test_string_2)*2,
5828 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5830 gtl.flags = GTL_PRECISE;
5831 gtl.codepage = 1200;
5832 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5833 ok(ret == lstrlenA(test_string_2)*2,
5834 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5836 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5837 gtl.codepage = 1200;
5838 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5839 ok(ret == lstrlenA(test_string_2),
5840 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5842 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5843 gtl.codepage = 1200;
5844 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5845 ok(ret == E_INVALIDARG,
5846 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5848 DestroyWindow(hwnd);
5852 /* globals that parent and child access when checking event masks & notifications */
5853 static HWND eventMaskEditHwnd = 0;
5854 static int queriedEventMask;
5855 static int watchForEventMask = 0;
5857 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5858 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5860 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5862 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5864 return DefWindowProcA(hwnd, message, wParam, lParam);
5867 /* test event masks in combination with WM_COMMAND */
5868 static void test_eventMask(void)
5870 HWND parent;
5871 int ret, style;
5872 WNDCLASSA cls;
5873 const char text[] = "foo bar\n";
5874 int eventMask;
5876 /* register class to capture WM_COMMAND */
5877 cls.style = 0;
5878 cls.lpfnWndProc = ParentMsgCheckProcA;
5879 cls.cbClsExtra = 0;
5880 cls.cbWndExtra = 0;
5881 cls.hInstance = GetModuleHandleA(0);
5882 cls.hIcon = 0;
5883 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5884 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5885 cls.lpszMenuName = NULL;
5886 cls.lpszClassName = "EventMaskParentClass";
5887 if(!RegisterClassA(&cls)) assert(0);
5889 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5890 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5891 ok (parent != 0, "Failed to create parent window\n");
5893 eventMaskEditHwnd = new_richedit(parent);
5894 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5896 eventMask = ENM_CHANGE | ENM_UPDATE;
5897 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5898 ok(ret == ENM_NONE, "wrong event mask\n");
5899 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5900 ok(ret == eventMask, "failed to set event mask\n");
5902 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5903 queriedEventMask = 0; /* initialize to something other than we expect */
5904 watchForEventMask = EN_CHANGE;
5905 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
5906 ok(ret == TRUE, "failed to set text\n");
5907 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5908 notification in response to WM_SETTEXT */
5909 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5910 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5912 /* check to see if EN_CHANGE is sent when redraw is turned off */
5913 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5914 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5915 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5916 /* redraw is disabled by making the window invisible. */
5917 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5918 queriedEventMask = 0; /* initialize to something other than we expect */
5919 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5920 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5921 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5922 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5923 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5925 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5926 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5927 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
5928 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5929 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5930 watchForEventMask = EN_UPDATE;
5931 queriedEventMask = 0; /* initialize to something other than we expect */
5932 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5933 ok(queriedEventMask == 0,
5934 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5935 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
5936 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5937 queriedEventMask = 0; /* initialize to something other than we expect */
5938 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5939 ok(queriedEventMask == eventMask,
5940 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5943 DestroyWindow(parent);
5946 static int received_WM_NOTIFY = 0;
5947 static int modify_at_WM_NOTIFY = 0;
5948 static BOOL filter_on_WM_NOTIFY = FALSE;
5949 static HWND hwndRichedit_WM_NOTIFY;
5951 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5953 if(message == WM_NOTIFY)
5955 received_WM_NOTIFY = 1;
5956 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5957 if (filter_on_WM_NOTIFY) return TRUE;
5959 return DefWindowProcA(hwnd, message, wParam, lParam);
5962 static void test_WM_NOTIFY(void)
5964 HWND parent;
5965 WNDCLASSA cls;
5966 CHARFORMAT2A cf2;
5967 int sel_start, sel_end;
5969 /* register class to capture WM_NOTIFY */
5970 cls.style = 0;
5971 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5972 cls.cbClsExtra = 0;
5973 cls.cbWndExtra = 0;
5974 cls.hInstance = GetModuleHandleA(0);
5975 cls.hIcon = 0;
5976 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5977 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5978 cls.lpszMenuName = NULL;
5979 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5980 if(!RegisterClassA(&cls)) assert(0);
5982 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5983 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5984 ok (parent != 0, "Failed to create parent window\n");
5986 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5987 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5989 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5991 /* Notifications for selection change should only be sent when selection
5992 actually changes. EM_SETCHARFORMAT is one message that calls
5993 ME_CommitUndo, which should check whether message should be sent */
5994 received_WM_NOTIFY = 0;
5995 cf2.cbSize = sizeof(CHARFORMAT2A);
5996 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5997 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5998 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5999 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6000 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6002 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6003 already at 0. */
6004 received_WM_NOTIFY = 0;
6005 modify_at_WM_NOTIFY = 0;
6006 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6007 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6008 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6010 received_WM_NOTIFY = 0;
6011 modify_at_WM_NOTIFY = 0;
6012 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6013 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6015 received_WM_NOTIFY = 0;
6016 modify_at_WM_NOTIFY = 0;
6017 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6018 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6019 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6021 /* Test for WM_NOTIFY messages with redraw disabled. */
6022 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6023 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6024 received_WM_NOTIFY = 0;
6025 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6026 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6027 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6029 /* Test filtering key events. */
6030 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6031 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6032 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6033 received_WM_NOTIFY = 0;
6034 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6035 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6036 ok(sel_start == 1 && sel_end == 1,
6037 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6038 filter_on_WM_NOTIFY = TRUE;
6039 received_WM_NOTIFY = 0;
6040 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6041 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6042 ok(sel_start == 1 && sel_end == 1,
6043 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6045 /* test with owner set to NULL */
6046 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6047 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6048 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6049 ok(sel_start == 1 && sel_end == 1,
6050 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6052 DestroyWindow(hwndRichedit_WM_NOTIFY);
6053 DestroyWindow(parent);
6056 static ENLINK enlink;
6057 #define CURSOR_CLIENT_X 5
6058 #define CURSOR_CLIENT_Y 5
6059 #define WP_PARENT 1
6060 #define WP_CHILD 2
6062 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6064 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6066 enlink = *(ENLINK*)lParam;
6068 return DefWindowProcA(hwnd, message, wParam, lParam);
6071 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6072 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6074 ENLINK junk_enlink;
6076 switch (msg)
6078 case WM_LBUTTONDBLCLK:
6079 case WM_LBUTTONDOWN:
6080 case WM_LBUTTONUP:
6081 case WM_MOUSEHOVER:
6082 case WM_MOUSEMOVE:
6083 case WM_MOUSEWHEEL:
6084 case WM_RBUTTONDBLCLK:
6085 case WM_RBUTTONDOWN:
6086 case WM_RBUTTONUP:
6087 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6088 break;
6089 case WM_SETCURSOR:
6090 if (wParam == WP_PARENT)
6091 wParam = (WPARAM)parent;
6092 else if (wParam == WP_CHILD)
6093 wParam = (WPARAM)hwnd;
6094 break;
6097 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6098 enlink = junk_enlink;
6100 SendMessageA(hwnd, msg, wParam, lParam);
6102 if (notifies)
6104 ok(enlink.nmhdr.hwndFrom == hwnd,
6105 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6106 ok(enlink.nmhdr.idFrom == 0,
6107 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6108 ok(enlink.msg == msg,
6109 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6110 if (msg == WM_SETCURSOR)
6112 ok(enlink.wParam == 0,
6113 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6115 else
6117 ok(enlink.wParam == wParam,
6118 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6120 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6121 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6122 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6123 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6124 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6126 else
6128 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6129 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6133 static void test_EN_LINK(void)
6135 HWND hwnd, parent;
6136 WNDCLASSA cls;
6137 CHARFORMAT2A cf2;
6138 POINT orig_cursor_pos;
6139 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6140 int i;
6142 static const struct
6144 UINT msg;
6145 WPARAM wParam;
6146 LPARAM lParam;
6147 BOOL notifies;
6149 link_notify_tests[] =
6151 /* hold down the left button and try some messages */
6152 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6153 { EM_LINESCROLL, 0, 1, FALSE },
6154 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6155 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6156 { WM_MOUSEHOVER, 0, 0, FALSE },
6157 { WM_MOUSEMOVE, 0, 0, FALSE },
6158 { WM_MOUSEWHEEL, 0, 0, FALSE },
6159 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6160 { WM_RBUTTONDOWN, 0, 0, TRUE },
6161 { WM_RBUTTONUP, 0, 0, TRUE },
6162 { WM_SETCURSOR, 0, 0, FALSE },
6163 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6164 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6165 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6166 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6167 { WM_LBUTTONUP, 0, 0, TRUE },
6168 /* hold down the right button and try some messages */
6169 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6170 { EM_LINESCROLL, 0, 1, FALSE },
6171 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6172 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6173 { WM_LBUTTONDOWN, 0, 0, TRUE },
6174 { WM_LBUTTONUP, 0, 0, TRUE },
6175 { WM_MOUSEHOVER, 0, 0, FALSE },
6176 { WM_MOUSEMOVE, 0, 0, TRUE },
6177 { WM_MOUSEWHEEL, 0, 0, FALSE },
6178 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6179 { WM_SETCURSOR, 0, 0, FALSE },
6180 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6181 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6182 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6183 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6184 { WM_RBUTTONUP, 0, 0, TRUE },
6185 /* try the messages with both buttons released */
6186 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6187 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6188 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6189 { WM_LBUTTONDOWN, 0, 0, TRUE },
6190 { WM_LBUTTONUP, 0, 0, TRUE },
6191 { WM_MOUSEHOVER, 0, 0, FALSE },
6192 { WM_MOUSEMOVE, 0, 0, TRUE },
6193 { WM_MOUSEWHEEL, 0, 0, FALSE },
6194 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6195 { WM_RBUTTONDOWN, 0, 0, TRUE },
6196 { WM_RBUTTONUP, 0, 0, TRUE },
6197 { WM_SETCURSOR, 0, 0, FALSE },
6198 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6199 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6200 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6201 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6204 /* register class to capture WM_NOTIFY */
6205 cls.style = 0;
6206 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6207 cls.cbClsExtra = 0;
6208 cls.cbWndExtra = 0;
6209 cls.hInstance = GetModuleHandleA(0);
6210 cls.hIcon = 0;
6211 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6212 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6213 cls.lpszMenuName = NULL;
6214 cls.lpszClassName = "EN_LINK_ParentClass";
6215 if(!RegisterClassA(&cls)) assert(0);
6217 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6218 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6219 ok(parent != 0, "Failed to create parent window\n");
6221 hwnd = new_richedit(parent);
6222 ok(hwnd != 0, "Failed to create edit window\n");
6224 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6226 cf2.cbSize = sizeof(CHARFORMAT2A);
6227 cf2.dwMask = CFM_LINK;
6228 cf2.dwEffects = CFE_LINK;
6229 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6230 /* mixing letters and numbers causes runs to be split */
6231 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6233 GetCursorPos(&orig_cursor_pos);
6234 SetCursorPos(0, 0);
6236 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6238 link_notify_test("cursor position simulated", i, hwnd, parent,
6239 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6240 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6243 ClientToScreen(hwnd, &cursor_screen_pos);
6244 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6246 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6248 link_notify_test("cursor position set", i, hwnd, parent,
6249 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6250 link_notify_tests[i].notifies);
6253 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6254 DestroyWindow(hwnd);
6255 DestroyWindow(parent);
6258 static void test_undo_coalescing(void)
6260 HWND hwnd;
6261 int result;
6262 char buffer[64] = {0};
6264 /* multi-line control inserts CR normally */
6265 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6266 0, 0, 200, 60, 0, 0, 0, 0);
6267 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6269 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6270 ok (result == FALSE, "Can undo after window creation.\n");
6271 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6272 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6273 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6274 ok (result == FALSE, "Can redo after window creation.\n");
6275 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6276 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6278 /* Test the effect of arrows keys during typing on undo transactions*/
6279 simulate_typing_characters(hwnd, "one two three");
6280 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6281 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6282 simulate_typing_characters(hwnd, " four five six");
6284 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6285 ok (result == FALSE, "Can redo before anything is undone.\n");
6286 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6287 ok (result == TRUE, "Cannot undo typed characters.\n");
6288 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6289 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6290 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6291 ok (result == TRUE, "Cannot redo after undo.\n");
6292 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6293 result = strcmp(buffer, "one two three");
6294 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6296 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6297 ok (result == TRUE, "Cannot undo typed characters.\n");
6298 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6299 ok (result == TRUE, "Failed to undo typed characters.\n");
6300 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6301 result = strcmp(buffer, "");
6302 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6304 /* Test the effect of focus changes during typing on undo transactions*/
6305 simulate_typing_characters(hwnd, "one two three");
6306 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6307 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6308 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6309 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6310 simulate_typing_characters(hwnd, " four five six");
6311 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6312 ok (result == TRUE, "Failed to undo typed characters.\n");
6313 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6314 result = strcmp(buffer, "one two three");
6315 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6317 /* Test the effect of the back key during typing on undo transactions */
6318 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6319 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6320 ok (result == TRUE, "Failed to clear the text.\n");
6321 simulate_typing_characters(hwnd, "one two threa");
6322 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6323 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6324 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6325 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6326 simulate_typing_characters(hwnd, "e four five six");
6327 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6328 ok (result == TRUE, "Failed to undo typed characters.\n");
6329 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6330 result = strcmp(buffer, "");
6331 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6333 /* Test the effect of the delete key during typing on undo transactions */
6334 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6335 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6336 ok(result == TRUE, "Failed to set the text.\n");
6337 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6338 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6339 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6340 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6341 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6342 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6343 ok (result == TRUE, "Failed to undo typed characters.\n");
6344 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6345 result = strcmp(buffer, "acd");
6346 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6347 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6348 ok (result == TRUE, "Failed to undo typed characters.\n");
6349 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6350 result = strcmp(buffer, "abcd");
6351 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6353 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6354 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6355 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6356 ok (result == TRUE, "Failed to clear the text.\n");
6357 simulate_typing_characters(hwnd, "one two three");
6358 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6359 ok (result == 0, "expected %d but got %d\n", 0, result);
6360 simulate_typing_characters(hwnd, " four five six");
6361 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6362 ok (result == TRUE, "Failed to undo typed characters.\n");
6363 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6364 result = strcmp(buffer, "one two three");
6365 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6366 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6367 ok (result == TRUE, "Failed to undo typed characters.\n");
6368 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6369 result = strcmp(buffer, "");
6370 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6372 DestroyWindow(hwnd);
6375 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6377 int length;
6379 /* MSDN lied, length is actually the number of bytes. */
6380 length = bytes / sizeof(WCHAR);
6381 switch(code)
6383 case WB_ISDELIMITER:
6384 return text[pos] == 'X';
6385 case WB_LEFT:
6386 case WB_MOVEWORDLEFT:
6387 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6388 return pos-1;
6389 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6390 case WB_LEFTBREAK:
6391 pos--;
6392 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6393 pos--;
6394 return pos;
6395 case WB_RIGHT:
6396 case WB_MOVEWORDRIGHT:
6397 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6398 return pos+1;
6399 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6400 case WB_RIGHTBREAK:
6401 pos++;
6402 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6403 pos++;
6404 return pos;
6405 default:
6406 ok(FALSE, "Unexpected code %d\n", code);
6407 break;
6409 return 0;
6412 static void test_word_movement(void)
6414 HWND hwnd;
6415 int result;
6416 int sel_start, sel_end;
6417 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6419 /* multi-line control inserts CR normally */
6420 hwnd = new_richedit(NULL);
6422 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6423 ok (result == TRUE, "Failed to clear the text.\n");
6424 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6425 /* |one two three */
6427 send_ctrl_key(hwnd, VK_RIGHT);
6428 /* one |two three */
6429 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6430 ok(sel_start == sel_end, "Selection should be empty\n");
6431 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6433 send_ctrl_key(hwnd, VK_RIGHT);
6434 /* one two |three */
6435 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6436 ok(sel_start == sel_end, "Selection should be empty\n");
6437 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6439 send_ctrl_key(hwnd, VK_LEFT);
6440 /* one |two three */
6441 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6442 ok(sel_start == sel_end, "Selection should be empty\n");
6443 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6445 send_ctrl_key(hwnd, VK_LEFT);
6446 /* |one two three */
6447 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6448 ok(sel_start == sel_end, "Selection should be empty\n");
6449 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6451 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6452 /* one two | three */
6453 send_ctrl_key(hwnd, VK_RIGHT);
6454 /* one two |three */
6455 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6456 ok(sel_start == sel_end, "Selection should be empty\n");
6457 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6459 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6460 /* one two th|ree */
6461 send_ctrl_key(hwnd, VK_LEFT);
6462 /* one two |three */
6463 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6464 ok(sel_start == sel_end, "Selection should be empty\n");
6465 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6467 /* Test with a custom word break procedure that uses X as the delimiter. */
6468 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6469 ok (result == TRUE, "Failed to clear the text.\n");
6470 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6471 /* |one twoXthree */
6472 send_ctrl_key(hwnd, VK_RIGHT);
6473 /* one twoX|three */
6474 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6475 ok(sel_start == sel_end, "Selection should be empty\n");
6476 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6478 DestroyWindow(hwnd);
6480 /* Make sure the behaviour is the same with a unicode richedit window,
6481 * and using unicode functions. */
6483 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6484 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6485 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6487 /* Test with a custom word break procedure that uses X as the delimiter. */
6488 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6489 ok (result == TRUE, "Failed to clear the text.\n");
6490 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6491 /* |one twoXthree */
6492 send_ctrl_key(hwnd, VK_RIGHT);
6493 /* one twoX|three */
6494 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6495 ok(sel_start == sel_end, "Selection should be empty\n");
6496 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6498 DestroyWindow(hwnd);
6501 static void test_EM_CHARFROMPOS(void)
6503 HWND hwnd;
6504 int result;
6505 RECT rcClient;
6506 POINTL point;
6507 point.x = 0;
6508 point.y = 40;
6510 /* multi-line control inserts CR normally */
6511 hwnd = new_richedit(NULL);
6512 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6513 (LPARAM)"one two three four five six seven\reight");
6514 ok(result == 1, "Expected 1, got %d\n", result);
6515 GetClientRect(hwnd, &rcClient);
6517 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6518 ok(result == 34, "expected character index of 34 but got %d\n", result);
6520 /* Test with points outside the bounds of the richedit control. */
6521 point.x = -1;
6522 point.y = 40;
6523 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6524 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6526 point.x = 1000;
6527 point.y = 0;
6528 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6529 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6531 point.x = 1000;
6532 point.y = 36;
6533 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6534 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6536 point.x = 1000;
6537 point.y = -1;
6538 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6539 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6541 point.x = 1000;
6542 point.y = rcClient.bottom + 1;
6543 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6544 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6546 point.x = 1000;
6547 point.y = rcClient.bottom;
6548 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6549 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6551 DestroyWindow(hwnd);
6554 static void test_word_wrap(void)
6556 HWND hwnd;
6557 POINTL point = {0, 60}; /* This point must be below the first line */
6558 const char *text = "Must be long enough to test line wrapping";
6559 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6560 int res, pos, lines;
6562 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6563 * when specified on window creation and set later. */
6564 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6565 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6566 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6567 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6568 ok(res, "WM_SETTEXT failed.\n");
6569 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6570 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6571 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6572 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6574 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6575 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6576 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6577 DestroyWindow(hwnd);
6579 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6580 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6581 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6583 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6584 ok(res, "WM_SETTEXT failed.\n");
6585 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6586 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6587 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6588 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6590 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6591 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6592 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6593 DestroyWindow(hwnd);
6595 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6596 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6597 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6598 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6599 ok(res, "WM_SETTEXT failed.\n");
6600 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6601 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6603 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6604 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6605 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6606 DestroyWindow(hwnd);
6608 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6609 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6610 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6611 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6612 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6613 ok(res, "WM_SETTEXT failed.\n");
6614 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6615 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6617 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6618 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6619 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6621 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6622 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6623 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6624 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6625 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6627 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6628 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6629 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6630 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6631 DestroyWindow(hwnd);
6633 /* Test to see if wrapping happens with redraw disabled. */
6634 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6635 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6636 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6637 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6638 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6639 ok(res, "EM_REPLACESEL failed.\n");
6640 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6641 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6642 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6643 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6644 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6646 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6647 DestroyWindow(hwnd);
6650 static void test_autoscroll(void)
6652 HWND hwnd = new_richedit(NULL);
6653 int lines, ret, redraw;
6654 POINT pt;
6656 for (redraw = 0; redraw <= 1; redraw++) {
6657 trace("testing with WM_SETREDRAW=%d\n", redraw);
6658 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6659 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6660 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6661 ok(lines == 8, "%d lines instead of 8\n", lines);
6662 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6663 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6664 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6665 ret = GetWindowLongA(hwnd, GWL_STYLE);
6666 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6668 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6669 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6670 ok(lines == 1, "%d lines instead of 1\n", lines);
6671 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6672 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6673 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6674 ret = GetWindowLongA(hwnd, GWL_STYLE);
6675 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6678 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6679 DestroyWindow(hwnd);
6681 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6682 * auto vertical/horizontal scrolling options. */
6683 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6684 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6685 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6686 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6687 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6688 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6689 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6690 ret = GetWindowLongA(hwnd, GWL_STYLE);
6691 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6692 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6693 DestroyWindow(hwnd);
6695 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6696 WS_POPUP|ES_MULTILINE,
6697 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6698 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6699 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6700 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6701 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6702 ret = GetWindowLongA(hwnd, GWL_STYLE);
6703 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6704 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6705 DestroyWindow(hwnd);
6709 static void test_format_rect(void)
6711 HWND hwnd;
6712 RECT rc, expected, clientRect;
6713 int n;
6714 DWORD options;
6716 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6717 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6718 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6719 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6721 GetClientRect(hwnd, &clientRect);
6723 expected = clientRect;
6724 expected.left += 1;
6725 expected.right -= 1;
6726 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6727 ok(rc.top == expected.top && rc.left == expected.left &&
6728 rc.bottom == expected.bottom && rc.right == expected.right,
6729 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6730 rc.top, rc.left, rc.bottom, rc.right,
6731 expected.top, expected.left, expected.bottom, expected.right);
6733 for (n = -3; n <= 3; n++)
6735 rc = clientRect;
6736 rc.top += n;
6737 rc.left += n;
6738 rc.bottom -= n;
6739 rc.right -= n;
6740 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6742 expected = rc;
6743 expected.top = max(0, rc.top);
6744 expected.left = max(0, rc.left);
6745 expected.bottom = min(clientRect.bottom, rc.bottom);
6746 expected.right = min(clientRect.right, rc.right);
6747 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6748 ok(rc.top == expected.top && rc.left == expected.left &&
6749 rc.bottom == expected.bottom && rc.right == expected.right,
6750 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6751 n, rc.top, rc.left, rc.bottom, rc.right,
6752 expected.top, expected.left, expected.bottom, expected.right);
6755 rc = clientRect;
6756 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6757 expected = clientRect;
6758 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6759 ok(rc.top == expected.top && rc.left == expected.left &&
6760 rc.bottom == expected.bottom && rc.right == expected.right,
6761 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6762 rc.top, rc.left, rc.bottom, rc.right,
6763 expected.top, expected.left, expected.bottom, expected.right);
6765 /* Adding the selectionbar adds the selectionbar width to the left side. */
6766 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6767 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6768 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6769 expected.left += 8; /* selection bar width */
6770 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6771 ok(rc.top == expected.top && rc.left == expected.left &&
6772 rc.bottom == expected.bottom && rc.right == expected.right,
6773 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6774 rc.top, rc.left, rc.bottom, rc.right,
6775 expected.top, expected.left, expected.bottom, expected.right);
6777 rc = clientRect;
6778 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6779 expected = clientRect;
6780 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6781 ok(rc.top == expected.top && rc.left == expected.left &&
6782 rc.bottom == expected.bottom && rc.right == expected.right,
6783 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6784 rc.top, rc.left, rc.bottom, rc.right,
6785 expected.top, expected.left, expected.bottom, expected.right);
6787 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6788 * even if the left side is already 0. */
6789 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6790 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6791 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6792 expected.left -= 8; /* selection bar width */
6793 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6794 ok(rc.top == expected.top && rc.left == expected.left &&
6795 rc.bottom == expected.bottom && rc.right == expected.right,
6796 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6797 rc.top, rc.left, rc.bottom, rc.right,
6798 expected.top, expected.left, expected.bottom, expected.right);
6800 /* Set the absolute value of the formatting rectangle. */
6801 rc = clientRect;
6802 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6803 expected = clientRect;
6804 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6805 ok(rc.top == expected.top && rc.left == expected.left &&
6806 rc.bottom == expected.bottom && rc.right == expected.right,
6807 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6808 n, rc.top, rc.left, rc.bottom, rc.right,
6809 expected.top, expected.left, expected.bottom, expected.right);
6811 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6812 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6813 * tests show that this isn't true. */
6814 rc.top = 15;
6815 rc.left = 15;
6816 rc.bottom = clientRect.bottom - 15;
6817 rc.right = clientRect.right - 15;
6818 expected = rc;
6819 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6820 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6821 ok(rc.top == expected.top && rc.left == expected.left &&
6822 rc.bottom == expected.bottom && rc.right == expected.right,
6823 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6824 rc.top, rc.left, rc.bottom, rc.right,
6825 expected.top, expected.left, expected.bottom, expected.right);
6827 /* For some reason it does not limit the values to the client rect with
6828 * a WPARAM value of 1. */
6829 rc.top = -15;
6830 rc.left = -15;
6831 rc.bottom = clientRect.bottom + 15;
6832 rc.right = clientRect.right + 15;
6833 expected = rc;
6834 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6835 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6836 ok(rc.top == expected.top && rc.left == expected.left &&
6837 rc.bottom == expected.bottom && rc.right == expected.right,
6838 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6839 rc.top, rc.left, rc.bottom, rc.right,
6840 expected.top, expected.left, expected.bottom, expected.right);
6842 /* Reset to default rect and check how the format rect adjusts to window
6843 * resize and how it copes with very small windows */
6844 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6846 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6847 GetClientRect(hwnd, &clientRect);
6849 expected = clientRect;
6850 expected.left += 1;
6851 expected.right -= 1;
6852 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6853 ok(rc.top == expected.top && rc.left == expected.left &&
6854 rc.bottom == expected.bottom && rc.right == expected.right,
6855 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6856 rc.top, rc.left, rc.bottom, rc.right,
6857 expected.top, expected.left, expected.bottom, expected.right);
6859 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6860 GetClientRect(hwnd, &clientRect);
6862 expected = clientRect;
6863 expected.left += 1;
6864 expected.right -= 1;
6865 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6866 ok(rc.top == expected.top && rc.left == expected.left &&
6867 rc.bottom == expected.bottom && rc.right == expected.right,
6868 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6869 rc.top, rc.left, rc.bottom, rc.right,
6870 expected.top, expected.left, expected.bottom, expected.right);
6872 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6873 GetClientRect(hwnd, &clientRect);
6875 expected = clientRect;
6876 expected.left += 1;
6877 expected.right -= 1;
6878 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6879 ok(rc.top == expected.top && rc.left == expected.left &&
6880 rc.bottom == expected.bottom && rc.right == expected.right,
6881 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6882 rc.top, rc.left, rc.bottom, rc.right,
6883 expected.top, expected.left, expected.bottom, expected.right);
6885 DestroyWindow(hwnd);
6887 /* The extended window style affects the formatting rectangle. */
6888 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
6889 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6890 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6891 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6893 GetClientRect(hwnd, &clientRect);
6895 expected = clientRect;
6896 expected.left += 1;
6897 expected.top += 1;
6898 expected.right -= 1;
6899 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6900 ok(rc.top == expected.top && rc.left == expected.left &&
6901 rc.bottom == expected.bottom && rc.right == expected.right,
6902 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6903 rc.top, rc.left, rc.bottom, rc.right,
6904 expected.top, expected.left, expected.bottom, expected.right);
6906 rc = clientRect;
6907 rc.top += 5;
6908 rc.left += 5;
6909 rc.bottom -= 5;
6910 rc.right -= 5;
6911 expected = rc;
6912 expected.top -= 1;
6913 expected.left -= 1;
6914 expected.right += 1;
6915 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6916 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6917 ok(rc.top == expected.top && rc.left == expected.left &&
6918 rc.bottom == expected.bottom && rc.right == expected.right,
6919 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6920 rc.top, rc.left, rc.bottom, rc.right,
6921 expected.top, expected.left, expected.bottom, expected.right);
6923 DestroyWindow(hwnd);
6926 static void test_WM_GETDLGCODE(void)
6928 HWND hwnd;
6929 UINT res, expected;
6930 MSG msg;
6932 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6934 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6935 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6936 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6937 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6938 msg.hwnd = hwnd;
6939 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6940 expected = expected | DLGC_WANTMESSAGE;
6941 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6942 res, expected);
6943 DestroyWindow(hwnd);
6945 msg.message = WM_KEYDOWN;
6946 msg.wParam = VK_RETURN;
6947 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6948 msg.pt.x = 0;
6949 msg.pt.y = 0;
6950 msg.time = GetTickCount();
6952 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6953 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6954 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6955 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6956 msg.hwnd = hwnd;
6957 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6958 expected = expected | DLGC_WANTMESSAGE;
6959 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6960 res, expected);
6961 DestroyWindow(hwnd);
6963 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6964 ES_MULTILINE|WS_POPUP,
6965 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6966 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6967 msg.hwnd = hwnd;
6968 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6969 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6970 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6971 res, expected);
6972 DestroyWindow(hwnd);
6974 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6975 ES_WANTRETURN|WS_POPUP,
6976 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6977 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6978 msg.hwnd = hwnd;
6979 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6980 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6981 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6982 res, expected);
6983 DestroyWindow(hwnd);
6985 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6986 WS_POPUP,
6987 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6988 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6989 msg.hwnd = hwnd;
6990 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6991 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6992 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6993 res, expected);
6994 DestroyWindow(hwnd);
6996 msg.wParam = VK_TAB;
6997 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6999 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7000 ES_MULTILINE|WS_POPUP,
7001 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7002 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7003 msg.hwnd = hwnd;
7004 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7005 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7006 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7007 res, expected);
7008 DestroyWindow(hwnd);
7010 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7011 WS_POPUP,
7012 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7013 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7014 msg.hwnd = hwnd;
7015 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7016 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7017 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7018 res, expected);
7019 DestroyWindow(hwnd);
7021 hold_key(VK_CONTROL);
7023 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7024 ES_MULTILINE|WS_POPUP,
7025 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7026 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7027 msg.hwnd = hwnd;
7028 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7029 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7030 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7031 res, expected);
7032 DestroyWindow(hwnd);
7034 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7035 WS_POPUP,
7036 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7037 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7038 msg.hwnd = hwnd;
7039 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7040 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7041 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7042 res, expected);
7043 DestroyWindow(hwnd);
7045 release_key(VK_CONTROL);
7047 msg.wParam = 'a';
7048 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7050 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7051 ES_MULTILINE|WS_POPUP,
7052 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7053 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7054 msg.hwnd = hwnd;
7055 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7056 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7057 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7058 res, expected);
7059 DestroyWindow(hwnd);
7061 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7062 WS_POPUP,
7063 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7064 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7065 msg.hwnd = hwnd;
7066 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7067 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7068 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7069 res, expected);
7070 DestroyWindow(hwnd);
7072 msg.message = WM_CHAR;
7074 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7075 ES_MULTILINE|WS_POPUP,
7076 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7077 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7078 msg.hwnd = hwnd;
7079 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7080 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7081 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7082 res, expected);
7083 DestroyWindow(hwnd);
7085 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7086 WS_POPUP,
7087 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7088 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7089 msg.hwnd = hwnd;
7090 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7091 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7092 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7093 res, expected);
7094 DestroyWindow(hwnd);
7096 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7097 WS_POPUP|ES_SAVESEL,
7098 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7099 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7100 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7101 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7102 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7103 res, expected);
7104 DestroyWindow(hwnd);
7107 static void test_zoom(void)
7109 HWND hwnd;
7110 UINT ret;
7111 RECT rc;
7112 POINT pt;
7113 int numerator, denominator;
7115 hwnd = new_richedit(NULL);
7116 GetClientRect(hwnd, &rc);
7117 pt.x = (rc.right - rc.left) / 2;
7118 pt.y = (rc.bottom - rc.top) / 2;
7119 ClientToScreen(hwnd, &pt);
7121 /* Test initial zoom value */
7122 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7123 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7124 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7125 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7127 /* test scroll wheel */
7128 hold_key(VK_CONTROL);
7129 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7130 MAKELPARAM(pt.x, pt.y));
7131 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7132 release_key(VK_CONTROL);
7134 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7135 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7136 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7137 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7139 /* Test how much the mouse wheel can zoom in and out. */
7140 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7141 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7143 hold_key(VK_CONTROL);
7144 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7145 MAKELPARAM(pt.x, pt.y));
7146 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7147 release_key(VK_CONTROL);
7149 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7150 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7151 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7152 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7154 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7155 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7157 hold_key(VK_CONTROL);
7158 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7159 MAKELPARAM(pt.x, pt.y));
7160 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7161 release_key(VK_CONTROL);
7163 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7164 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7165 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7166 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7168 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7169 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7171 hold_key(VK_CONTROL);
7172 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7173 MAKELPARAM(pt.x, pt.y));
7174 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7175 release_key(VK_CONTROL);
7177 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7178 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7179 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7180 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7182 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7183 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7185 hold_key(VK_CONTROL);
7186 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7187 MAKELPARAM(pt.x, pt.y));
7188 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7189 release_key(VK_CONTROL);
7191 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7192 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7193 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7194 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7196 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7197 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7198 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7200 hold_key(VK_CONTROL);
7201 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7202 MAKELPARAM(pt.x, pt.y));
7203 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7204 release_key(VK_CONTROL);
7206 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7207 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7208 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7209 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7211 /* Test bounds checking on EM_SETZOOM */
7212 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7213 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7215 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7216 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7218 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7219 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7221 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7222 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7223 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7224 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7226 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7227 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7229 /* See if negative numbers are accepted. */
7230 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7231 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7233 /* See if negative numbers are accepted. */
7234 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7235 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7237 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7238 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7239 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7240 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7242 /* Reset the zoom value */
7243 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7244 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7246 DestroyWindow(hwnd);
7249 struct dialog_mode_messages
7251 int wm_getdefid, wm_close, wm_nextdlgctl;
7254 static struct dialog_mode_messages dm_messages;
7256 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7257 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7258 "got %d\n", wmclose, dm_messages.wm_close); \
7259 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7260 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7261 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7262 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7264 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7266 switch (iMsg)
7268 case DM_GETDEFID:
7269 dm_messages.wm_getdefid++;
7270 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7271 case WM_NEXTDLGCTL:
7272 dm_messages.wm_nextdlgctl++;
7273 break;
7274 case WM_CLOSE:
7275 dm_messages.wm_close++;
7276 break;
7279 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7282 static void test_dialogmode(void)
7284 HWND hwRichEdit, hwParent, hwButton;
7285 MSG msg= {0};
7286 int lcount, r;
7287 WNDCLASSA cls;
7289 cls.style = 0;
7290 cls.lpfnWndProc = dialog_mode_wnd_proc;
7291 cls.cbClsExtra = 0;
7292 cls.cbWndExtra = 0;
7293 cls.hInstance = GetModuleHandleA(0);
7294 cls.hIcon = 0;
7295 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7296 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7297 cls.lpszMenuName = NULL;
7298 cls.lpszClassName = "DialogModeParentClass";
7299 if(!RegisterClassA(&cls)) assert(0);
7301 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7302 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7304 /* Test richedit(ES_MULTILINE) */
7306 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7308 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7309 ok(0 == r, "expected 0, got %d\n", r);
7310 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7311 ok(2 == lcount, "expected 2, got %d\n", lcount);
7313 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7314 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7316 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7317 ok(0 == r, "expected 0, got %d\n", r);
7318 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7319 ok(3 == lcount, "expected 3, got %d\n", lcount);
7321 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7322 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7323 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7324 ok(0 == r, "expected 0, got %d\n", r);
7325 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7326 ok(3 == lcount, "expected 3, got %d\n", lcount);
7328 DestroyWindow(hwRichEdit);
7330 /* Test standalone richedit(ES_MULTILINE) */
7332 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7334 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7335 ok(0 == r, "expected 0, got %d\n", r);
7336 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7337 ok(2 == lcount, "expected 2, got %d\n", lcount);
7339 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7340 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7342 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7343 ok(0 == r, "expected 0, got %d\n", r);
7344 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7345 ok(2 == lcount, "expected 2, got %d\n", lcount);
7347 DestroyWindow(hwRichEdit);
7349 /* Check a destination for messages */
7351 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7353 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7354 SetParent( hwRichEdit, NULL);
7356 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7357 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7359 memset(&dm_messages, 0, sizeof(dm_messages));
7360 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7361 ok(0 == r, "expected 0, got %d\n", r);
7362 test_dm_messages(0, 1, 0);
7364 memset(&dm_messages, 0, sizeof(dm_messages));
7365 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7366 ok(0 == r, "expected 0, got %d\n", r);
7367 test_dm_messages(0, 0, 1);
7369 DestroyWindow(hwRichEdit);
7371 /* Check messages from richedit(ES_MULTILINE) */
7373 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7375 memset(&dm_messages, 0, sizeof(dm_messages));
7376 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7377 ok(0 == r, "expected 0, got %d\n", r);
7378 test_dm_messages(0, 0, 0);
7380 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7381 ok(2 == lcount, "expected 2, got %d\n", lcount);
7383 memset(&dm_messages, 0, sizeof(dm_messages));
7384 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7385 ok(0 == r, "expected 0, got %d\n", r);
7386 test_dm_messages(0, 0, 0);
7388 memset(&dm_messages, 0, sizeof(dm_messages));
7389 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7390 ok(0 == r, "expected 0, got %d\n", r);
7391 test_dm_messages(0, 0, 0);
7393 memset(&dm_messages, 0, sizeof(dm_messages));
7394 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7395 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7396 test_dm_messages(0, 0, 0);
7398 memset(&dm_messages, 0, sizeof(dm_messages));
7399 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7400 ok(0 == r, "expected 0, got %d\n", r);
7401 test_dm_messages(0, 1, 0);
7403 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7404 ok(2 == lcount, "expected 2, got %d\n", lcount);
7406 memset(&dm_messages, 0, sizeof(dm_messages));
7407 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7408 ok(0 == r, "expected 0, got %d\n", r);
7409 test_dm_messages(0, 0, 0);
7411 memset(&dm_messages, 0, sizeof(dm_messages));
7412 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7413 ok(0 == r, "expected 0, got %d\n", r);
7414 test_dm_messages(0, 0, 1);
7416 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7417 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7418 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7420 memset(&dm_messages, 0, sizeof(dm_messages));
7421 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7422 ok(0 == r, "expected 0, got %d\n", r);
7423 test_dm_messages(0, 1, 1);
7425 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7426 ok(2 == lcount, "expected 2, got %d\n", lcount);
7428 DestroyWindow(hwButton);
7429 DestroyWindow(hwRichEdit);
7431 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7433 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7435 memset(&dm_messages, 0, sizeof(dm_messages));
7436 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7437 ok(0 == r, "expected 0, got %d\n", r);
7438 test_dm_messages(0, 0, 0);
7440 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7441 ok(2 == lcount, "expected 2, got %d\n", lcount);
7443 memset(&dm_messages, 0, sizeof(dm_messages));
7444 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7445 ok(0 == r, "expected 0, got %d\n", r);
7446 test_dm_messages(0, 0, 0);
7448 memset(&dm_messages, 0, sizeof(dm_messages));
7449 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7450 ok(0 == r, "expected 0, got %d\n", r);
7451 test_dm_messages(0, 0, 0);
7453 memset(&dm_messages, 0, sizeof(dm_messages));
7454 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7455 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7456 test_dm_messages(0, 0, 0);
7458 memset(&dm_messages, 0, sizeof(dm_messages));
7459 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7460 ok(0 == r, "expected 0, got %d\n", r);
7461 test_dm_messages(0, 0, 0);
7463 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7464 ok(3 == lcount, "expected 3, got %d\n", lcount);
7466 memset(&dm_messages, 0, sizeof(dm_messages));
7467 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7468 ok(0 == r, "expected 0, got %d\n", r);
7469 test_dm_messages(0, 0, 0);
7471 memset(&dm_messages, 0, sizeof(dm_messages));
7472 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7473 ok(0 == r, "expected 0, got %d\n", r);
7474 test_dm_messages(0, 0, 1);
7476 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7477 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7478 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7480 memset(&dm_messages, 0, sizeof(dm_messages));
7481 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7482 ok(0 == r, "expected 0, got %d\n", r);
7483 test_dm_messages(0, 0, 0);
7485 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7486 ok(4 == lcount, "expected 4, got %d\n", lcount);
7488 DestroyWindow(hwButton);
7489 DestroyWindow(hwRichEdit);
7491 /* Check messages from richedit(0) */
7493 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7495 memset(&dm_messages, 0, sizeof(dm_messages));
7496 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7497 ok(0 == r, "expected 0, got %d\n", r);
7498 test_dm_messages(0, 0, 0);
7500 memset(&dm_messages, 0, sizeof(dm_messages));
7501 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7502 ok(0 == r, "expected 0, got %d\n", r);
7503 test_dm_messages(0, 0, 0);
7505 memset(&dm_messages, 0, sizeof(dm_messages));
7506 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7507 ok(0 == r, "expected 0, got %d\n", r);
7508 test_dm_messages(0, 0, 0);
7510 memset(&dm_messages, 0, sizeof(dm_messages));
7511 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7512 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7513 test_dm_messages(0, 0, 0);
7515 memset(&dm_messages, 0, sizeof(dm_messages));
7516 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7517 ok(0 == r, "expected 0, got %d\n", r);
7518 test_dm_messages(0, 1, 0);
7520 memset(&dm_messages, 0, sizeof(dm_messages));
7521 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7522 ok(0 == r, "expected 0, got %d\n", r);
7523 test_dm_messages(0, 0, 0);
7525 memset(&dm_messages, 0, sizeof(dm_messages));
7526 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7527 ok(0 == r, "expected 0, got %d\n", r);
7528 test_dm_messages(0, 0, 1);
7530 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7531 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7532 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7534 memset(&dm_messages, 0, sizeof(dm_messages));
7535 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7536 ok(0 == r, "expected 0, got %d\n", r);
7537 test_dm_messages(0, 1, 1);
7539 DestroyWindow(hwRichEdit);
7541 /* Check messages from richedit(ES_WANTRETURN) */
7543 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7545 memset(&dm_messages, 0, sizeof(dm_messages));
7546 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7547 ok(0 == r, "expected 0, got %d\n", r);
7548 test_dm_messages(0, 0, 0);
7550 memset(&dm_messages, 0, sizeof(dm_messages));
7551 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7552 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7553 test_dm_messages(0, 0, 0);
7555 memset(&dm_messages, 0, sizeof(dm_messages));
7556 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7557 ok(0 == r, "expected 0, got %d\n", r);
7558 test_dm_messages(0, 0, 0);
7560 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7561 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7562 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7564 memset(&dm_messages, 0, sizeof(dm_messages));
7565 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7566 ok(0 == r, "expected 0, got %d\n", r);
7567 test_dm_messages(0, 0, 0);
7569 DestroyWindow(hwRichEdit);
7570 DestroyWindow(hwParent);
7573 static void test_EM_FINDWORDBREAK_W(void)
7575 static const struct {
7576 WCHAR c;
7577 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7578 } delimiter_tests[] = {
7579 {0x0a, FALSE}, /* newline */
7580 {0x0b, FALSE}, /* vertical tab */
7581 {0x0c, FALSE}, /* form feed */
7582 {0x0d, FALSE}, /* carriage return */
7583 {0x20, TRUE}, /* space */
7584 {0x61, FALSE}, /* capital letter a */
7585 {0xa0, FALSE}, /* no-break space */
7586 {0x2000, FALSE}, /* en quad */
7587 {0x3000, FALSE}, /* Ideographic space */
7588 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7589 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7590 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7591 {0xac00, FALSE}, /* Hangul character GA*/
7592 {0xd7af, FALSE}, /* End of Hangul character chart */
7593 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7594 {0xff20, FALSE}, /* fullwidth commercial @ */
7595 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7597 int i;
7598 HWND hwndRichEdit = new_richeditW(NULL);
7599 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7600 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7602 WCHAR wbuf[2];
7603 int result;
7605 wbuf[0] = delimiter_tests[i].c;
7606 wbuf[1] = 0;
7607 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7608 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7609 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7610 todo_wine
7611 ok(result == delimiter_tests[i].isdelimiter,
7612 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7613 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7614 else
7615 ok(result == delimiter_tests[i].isdelimiter,
7616 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7617 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7619 DestroyWindow(hwndRichEdit);
7622 static void test_EM_FINDWORDBREAK_A(void)
7624 static const struct {
7625 WCHAR c;
7626 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7627 } delimiter_tests[] = {
7628 {0x0a, FALSE}, /* newline */
7629 {0x0b, FALSE}, /* vertical tab */
7630 {0x0c, FALSE}, /* form feed */
7631 {0x0d, FALSE}, /* carriage return */
7632 {0x20, TRUE}, /* space */
7633 {0x61, FALSE}, /* capital letter a */
7635 int i;
7636 HWND hwndRichEdit = new_richedit(NULL);
7638 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7639 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7641 int result;
7642 char buf[2];
7643 buf[0] = delimiter_tests[i].c;
7644 buf[1] = 0;
7645 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7646 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7647 if (buf[0] == 0x20)
7648 todo_wine
7649 ok(result == delimiter_tests[i].isdelimiter,
7650 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7651 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7652 else
7653 ok(result == delimiter_tests[i].isdelimiter,
7654 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7655 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7657 DestroyWindow(hwndRichEdit);
7661 * This test attempts to show the effect of enter on a richedit
7662 * control v1.0 inserts CRLF whereas for higher versions it only
7663 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7664 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7665 * does for higher. The same test is cloned in riched32 and riched20.
7667 static void test_enter(void)
7669 static const struct {
7670 const char *initialtext;
7671 const int cursor;
7672 const char *expectedwmtext;
7673 const char *expectedemtext;
7674 const char *expectedemtextcrlf;
7675 } testenteritems[] = {
7676 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7677 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7678 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7679 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7680 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7683 char expectedbuf[1024];
7684 char resultbuf[1024];
7685 HWND hwndRichEdit = new_richedit(NULL);
7686 UINT i,j;
7688 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7690 char buf[1024] = {0};
7691 LRESULT result;
7692 GETTEXTEX getText;
7693 const char *expected;
7695 /* Set the text to the initial text */
7696 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7697 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7699 /* Send Enter */
7700 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7701 simulate_typing_characters(hwndRichEdit, "\r");
7703 /* 1. Retrieve with WM_GETTEXT */
7704 buf[0] = 0x00;
7705 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7706 expected = testenteritems[i].expectedwmtext;
7708 resultbuf[0]=0x00;
7709 for (j = 0; j < (UINT)result; j++)
7710 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7711 expectedbuf[0] = '\0';
7712 for (j = 0; j < strlen(expected); j++)
7713 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7715 result = strcmp(expected, buf);
7716 ok (result == 0,
7717 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7718 i, resultbuf, expectedbuf);
7720 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7721 getText.cb = sizeof(buf);
7722 getText.flags = GT_DEFAULT;
7723 getText.codepage = CP_ACP;
7724 getText.lpDefaultChar = NULL;
7725 getText.lpUsedDefChar = NULL;
7726 buf[0] = 0x00;
7727 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7728 expected = testenteritems[i].expectedemtext;
7730 resultbuf[0]=0x00;
7731 for (j = 0; j < (UINT)result; j++)
7732 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7733 expectedbuf[0] = '\0';
7734 for (j = 0; j < strlen(expected); j++)
7735 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7737 result = strcmp(expected, buf);
7738 ok (result == 0,
7739 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7740 i, resultbuf, expectedbuf);
7742 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7743 getText.cb = sizeof(buf);
7744 getText.flags = GT_USECRLF;
7745 getText.codepage = CP_ACP;
7746 getText.lpDefaultChar = NULL;
7747 getText.lpUsedDefChar = NULL;
7748 buf[0] = 0x00;
7749 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7750 expected = testenteritems[i].expectedemtextcrlf;
7752 resultbuf[0]=0x00;
7753 for (j = 0; j < (UINT)result; j++)
7754 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7755 expectedbuf[0] = '\0';
7756 for (j = 0; j < strlen(expected); j++)
7757 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7759 result = strcmp(expected, buf);
7760 ok (result == 0,
7761 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7762 i, resultbuf, expectedbuf);
7765 DestroyWindow(hwndRichEdit);
7768 static void test_WM_CREATE(void)
7770 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7771 static const char title[] = "line1\nline2";
7773 HWND rich_edit;
7774 LRESULT res;
7775 char buf[64];
7776 int len;
7778 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7779 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7780 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7782 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7783 ok(len == 5, "GetWindowText returned %d\n", len);
7784 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7786 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7787 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7789 DestroyWindow(rich_edit);
7791 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7792 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7793 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7795 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7796 ok(len == 12, "GetWindowText returned %d\n", len);
7797 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7799 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7800 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7802 DestroyWindow(rich_edit);
7805 /*******************************************************************
7806 * Test that after deleting all of the text, the first paragraph
7807 * format reverts to the default.
7809 static void test_reset_default_para_fmt( void )
7811 HWND richedit = new_richeditW( NULL );
7812 PARAFORMAT2 fmt;
7813 WORD def_align, new_align;
7815 memset( &fmt, 0, sizeof(fmt) );
7816 fmt.cbSize = sizeof(PARAFORMAT2);
7817 fmt.dwMask = -1;
7818 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7819 def_align = fmt.wAlignment;
7820 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7822 simulate_typing_characters( richedit, "123" );
7824 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7825 fmt.dwMask = PFM_ALIGNMENT;
7826 fmt.wAlignment = new_align;
7827 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7829 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7830 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7832 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7833 SendMessageA( richedit, WM_CUT, 0, 0 );
7835 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7836 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7838 DestroyWindow( richedit );
7841 static void test_EM_SETREADONLY(void)
7843 HWND richedit = new_richeditW(NULL);
7844 DWORD dwStyle;
7845 LRESULT res;
7847 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
7848 ok(res == 1, "EM_SETREADONLY\n");
7849 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7850 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
7852 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
7853 ok(res == 1, "EM_SETREADONLY\n");
7854 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7855 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
7857 DestroyWindow(richedit);
7860 START_TEST( editor )
7862 BOOL ret;
7863 /* Must explicitly LoadLibrary(). The test has no references to functions in
7864 * RICHED20.DLL, so the linker doesn't actually link to it. */
7865 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7866 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7868 test_WM_CHAR();
7869 test_EM_FINDTEXT(FALSE);
7870 test_EM_FINDTEXT(TRUE);
7871 test_EM_GETLINE();
7872 test_EM_POSFROMCHAR();
7873 test_EM_SCROLLCARET();
7874 test_EM_SCROLL();
7875 test_scrollbar_visibility();
7876 test_WM_SETTEXT();
7877 test_EM_LINELENGTH();
7878 test_EM_SETCHARFORMAT();
7879 test_EM_SETTEXTMODE();
7880 test_TM_PLAINTEXT();
7881 test_EM_SETOPTIONS();
7882 test_WM_GETTEXT();
7883 test_EM_GETTEXTRANGE();
7884 test_EM_GETSELTEXT();
7885 test_EM_SETUNDOLIMIT();
7886 test_ES_PASSWORD();
7887 test_EM_SETTEXTEX();
7888 test_EM_LIMITTEXT();
7889 test_EM_EXLIMITTEXT();
7890 test_EM_GETLIMITTEXT();
7891 test_WM_SETFONT();
7892 test_EM_GETMODIFY();
7893 test_EM_SETSEL();
7894 test_EM_EXSETSEL();
7895 test_WM_PASTE();
7896 test_EM_STREAMIN();
7897 test_EM_STREAMOUT();
7898 test_EM_STREAMOUT_FONTTBL();
7899 test_EM_StreamIn_Undo();
7900 test_EM_FORMATRANGE();
7901 test_unicode_conversions();
7902 test_EM_GETTEXTLENGTHEX();
7903 test_EM_REPLACESEL(1);
7904 test_EM_REPLACESEL(0);
7905 test_WM_NOTIFY();
7906 test_EN_LINK();
7907 test_EM_AUTOURLDETECT();
7908 test_eventMask();
7909 test_undo_coalescing();
7910 test_word_movement();
7911 test_EM_CHARFROMPOS();
7912 test_SETPARAFORMAT();
7913 test_word_wrap();
7914 test_autoscroll();
7915 test_format_rect();
7916 test_WM_GETDLGCODE();
7917 test_zoom();
7918 test_dialogmode();
7919 test_EM_FINDWORDBREAK_W();
7920 test_EM_FINDWORDBREAK_A();
7921 test_enter();
7922 test_WM_CREATE();
7923 test_reset_default_para_fmt();
7924 test_EM_SETREADONLY();
7926 /* Set the environment variable WINETEST_RICHED20 to keep windows
7927 * responsive and open for 30 seconds. This is useful for debugging.
7929 if (getenv( "WINETEST_RICHED20" )) {
7930 keep_responsive(30);
7933 OleFlushClipboard();
7934 ret = FreeLibrary(hmoduleRichEdit);
7935 ok(ret, "error: %d\n", (int) GetLastError());