riched20/tests: Print full flag values in traces.
[wine.git] / dlls / riched20 / tests / editor.c
blob151fcbae4aa0ad44ea3a1d15de490488480c0b44
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(cb == *pcb, "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 void test_EM_STREAMOUT(void)
3427 HWND hwndRichEdit = new_richedit(NULL);
3428 int r;
3429 EDITSTREAM es;
3430 char buf[1024] = {0};
3431 char * p;
3433 const char * TestItem1 = "TestSomeText";
3434 const char * TestItem2 = "TestSomeText\r";
3435 const char * TestItem3 = "TestSomeText\r\n";
3437 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3438 p = buf;
3439 es.dwCookie = (DWORD_PTR)&p;
3440 es.dwError = 0;
3441 es.pfnCallback = test_WM_SETTEXT_esCallback;
3442 memset(buf, 0, sizeof(buf));
3443 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3444 r = strlen(buf);
3445 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3446 ok(strcmp(buf, TestItem1) == 0,
3447 "streamed text different, got %s\n", buf);
3449 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3450 p = buf;
3451 es.dwCookie = (DWORD_PTR)&p;
3452 es.dwError = 0;
3453 es.pfnCallback = test_WM_SETTEXT_esCallback;
3454 memset(buf, 0, sizeof(buf));
3455 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3456 r = strlen(buf);
3457 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3458 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3459 ok(strcmp(buf, TestItem3) == 0,
3460 "streamed text different from, got %s\n", buf);
3461 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3462 p = buf;
3463 es.dwCookie = (DWORD_PTR)&p;
3464 es.dwError = 0;
3465 es.pfnCallback = test_WM_SETTEXT_esCallback;
3466 memset(buf, 0, sizeof(buf));
3467 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3468 r = strlen(buf);
3469 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3470 ok(strcmp(buf, TestItem3) == 0,
3471 "streamed text different, got %s\n", buf);
3473 /* Use a callback that sets *pcb to one */
3474 p = buf;
3475 es.dwCookie = (DWORD_PTR)&p;
3476 es.dwError = 0;
3477 es.pfnCallback = test_esCallback_written_1;
3478 memset(buf, 0, sizeof(buf));
3479 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3480 r = strlen(buf);
3481 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3482 ok(strcmp(buf, TestItem3) == 0,
3483 "streamed text different, got %s\n", buf);
3486 DestroyWindow(hwndRichEdit);
3489 static void test_EM_STREAMOUT_FONTTBL(void)
3491 HWND hwndRichEdit = new_richedit(NULL);
3492 EDITSTREAM es;
3493 char buf[1024] = {0};
3494 char * p;
3495 char * fontTbl;
3496 int brackCount;
3498 const char * TestItem = "TestSomeText";
3500 /* fills in the richedit control with some text */
3501 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3503 /* streams out the text in rtf format */
3504 p = buf;
3505 es.dwCookie = (DWORD_PTR)&p;
3506 es.dwError = 0;
3507 es.pfnCallback = test_WM_SETTEXT_esCallback;
3508 memset(buf, 0, sizeof(buf));
3509 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3511 /* scans for \fonttbl, error if not found */
3512 fontTbl = strstr(buf, "\\fonttbl");
3513 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3514 if(fontTbl)
3516 /* scans for terminating closing bracket */
3517 brackCount = 1;
3518 while(*fontTbl && brackCount)
3520 if(*fontTbl == '{')
3521 brackCount++;
3522 else if(*fontTbl == '}')
3523 brackCount--;
3524 fontTbl++;
3526 /* checks whether closing bracket is ok */
3527 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3528 if(!brackCount)
3530 /* char before closing fonttbl block should be a closed bracket */
3531 fontTbl -= 2;
3532 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3534 /* char after fonttbl block should be a crlf */
3535 fontTbl += 2;
3536 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3539 DestroyWindow(hwndRichEdit);
3543 static void test_EM_SETTEXTEX(void)
3545 HWND hwndRichEdit, parent;
3546 SCROLLINFO si;
3547 int sel_start, sel_end;
3548 SETTEXTEX setText;
3549 GETTEXTEX getText;
3550 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3551 'S', 'o', 'm', 'e',
3552 'T', 'e', 'x', 't', 0};
3553 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3554 't', 'S', 'o', 'm',
3555 'e', 'T', 'e', 'x',
3556 't', 't', 'S', 'o',
3557 'm', 'e', 'T', 'e',
3558 'x', 't', 0};
3559 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3560 '\r','t','S','o','m','e','T','e','x','t',0};
3561 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3562 'S', 'o', 'm', 'e',
3563 'T', 'e', 'x', 't',
3564 '\r', 0};
3565 const char * TestItem2_after = "TestSomeText\r\n";
3566 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3567 'S', 'o', 'm', 'e',
3568 'T', 'e', 'x', 't',
3569 '\r','\n','\r','\n', 0};
3570 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3571 'S', 'o', 'm', 'e',
3572 'T', 'e', 'x', 't',
3573 '\n','\n', 0};
3574 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3575 'S', 'o', 'm', 'e',
3576 'T', 'e', 'x', 't',
3577 '\r','\r', 0};
3578 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3579 'S', 'o', 'm', 'e',
3580 'T', 'e', 'x', 't',
3581 '\r','\r','\n','\r',
3582 '\n', 0};
3583 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3584 'S', 'o', 'm', 'e',
3585 'T', 'e', 'x', 't',
3586 ' ','\r', 0};
3587 #define MAX_BUF_LEN 1024
3588 WCHAR buf[MAX_BUF_LEN];
3589 char bufACP[MAX_BUF_LEN];
3590 char * p;
3591 int result;
3592 CHARRANGE cr;
3593 EDITSTREAM es;
3594 WNDCLASSA cls;
3596 /* Test the scroll position with and without a parent window.
3598 * For some reason the scroll position is 0 after EM_SETTEXTEX
3599 * with the ST_SELECTION flag only when the control has a parent
3600 * window, even though the selection is at the end. */
3601 cls.style = 0;
3602 cls.lpfnWndProc = DefWindowProcA;
3603 cls.cbClsExtra = 0;
3604 cls.cbWndExtra = 0;
3605 cls.hInstance = GetModuleHandleA(0);
3606 cls.hIcon = 0;
3607 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3608 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3609 cls.lpszMenuName = NULL;
3610 cls.lpszClassName = "ParentTestClass";
3611 if(!RegisterClassA(&cls)) assert(0);
3613 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3614 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3615 ok (parent != 0, "Failed to create parent window\n");
3617 hwndRichEdit = CreateWindowExA(0,
3618 RICHEDIT_CLASS20A, NULL,
3619 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3620 0, 0, 200, 60, parent, NULL,
3621 hmoduleRichEdit, NULL);
3623 setText.codepage = CP_ACP;
3624 setText.flags = ST_SELECTION;
3625 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3626 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3627 si.cbSize = sizeof(si);
3628 si.fMask = SIF_ALL;
3629 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3630 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3631 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3632 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3633 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3635 DestroyWindow(parent);
3637 /* Test without a parent window */
3638 hwndRichEdit = new_richedit(NULL);
3639 setText.codepage = CP_ACP;
3640 setText.flags = ST_SELECTION;
3641 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3642 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3643 si.cbSize = sizeof(si);
3644 si.fMask = SIF_ALL;
3645 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3646 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3647 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3648 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3649 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3651 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3652 * but this time it is because the selection is at the beginning. */
3653 setText.codepage = CP_ACP;
3654 setText.flags = ST_DEFAULT;
3655 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3656 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3657 si.cbSize = sizeof(si);
3658 si.fMask = SIF_ALL;
3659 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3660 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3661 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3662 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3663 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3665 setText.codepage = 1200; /* no constant for unicode */
3666 getText.codepage = 1200; /* no constant for unicode */
3667 getText.cb = MAX_BUF_LEN;
3668 getText.flags = GT_DEFAULT;
3669 getText.lpDefaultChar = NULL;
3670 getText.lpUsedDefChar = NULL;
3672 setText.flags = 0;
3673 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3674 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3675 ok(lstrcmpW(buf, TestItem1) == 0,
3676 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3678 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3679 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3681 setText.codepage = 1200; /* no constant for unicode */
3682 getText.codepage = 1200; /* no constant for unicode */
3683 getText.cb = MAX_BUF_LEN;
3684 getText.flags = GT_DEFAULT;
3685 getText.lpDefaultChar = NULL;
3686 getText.lpUsedDefChar = NULL;
3687 setText.flags = 0;
3688 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3689 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3690 ok(lstrcmpW(buf, TestItem2) == 0,
3691 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3693 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3694 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3695 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3696 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3698 /* Baseline test for just-enough buffer space for string */
3699 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3700 getText.codepage = 1200; /* no constant for unicode */
3701 getText.flags = GT_DEFAULT;
3702 getText.lpDefaultChar = NULL;
3703 getText.lpUsedDefChar = NULL;
3704 memset(buf, 0, MAX_BUF_LEN);
3705 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3706 ok(lstrcmpW(buf, TestItem2) == 0,
3707 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3709 /* When there is enough space for one character, but not both, of the CRLF
3710 pair at the end of the string, the CR is not copied at all. That is,
3711 the caller must not see CRLF pairs truncated to CR at the end of the
3712 string.
3714 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3715 getText.codepage = 1200; /* no constant for unicode */
3716 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3717 getText.lpDefaultChar = NULL;
3718 getText.lpUsedDefChar = NULL;
3719 memset(buf, 0, MAX_BUF_LEN);
3720 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3721 ok(lstrcmpW(buf, TestItem1) == 0,
3722 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3725 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3726 setText.codepage = 1200; /* no constant for unicode */
3727 getText.codepage = 1200; /* no constant for unicode */
3728 getText.cb = MAX_BUF_LEN;
3729 getText.flags = GT_DEFAULT;
3730 getText.lpDefaultChar = NULL;
3731 getText.lpUsedDefChar = NULL;
3732 setText.flags = 0;
3733 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3734 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3735 ok(lstrcmpW(buf, TestItem3_after) == 0,
3736 "EM_SETTEXTEX did not convert properly\n");
3738 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3739 setText.codepage = 1200; /* no constant for unicode */
3740 getText.codepage = 1200; /* no constant for unicode */
3741 getText.cb = MAX_BUF_LEN;
3742 getText.flags = GT_DEFAULT;
3743 getText.lpDefaultChar = NULL;
3744 getText.lpUsedDefChar = NULL;
3745 setText.flags = 0;
3746 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3747 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3748 ok(lstrcmpW(buf, TestItem3_after) == 0,
3749 "EM_SETTEXTEX did not convert properly\n");
3751 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3752 setText.codepage = 1200; /* no constant for unicode */
3753 getText.codepage = 1200; /* no constant for unicode */
3754 getText.cb = MAX_BUF_LEN;
3755 getText.flags = GT_DEFAULT;
3756 getText.lpDefaultChar = NULL;
3757 getText.lpUsedDefChar = NULL;
3758 setText.flags = 0;
3759 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3760 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3761 ok(lstrcmpW(buf, TestItem4_after) == 0,
3762 "EM_SETTEXTEX did not convert properly\n");
3764 /* !ST_SELECTION && Unicode && !\rtf */
3765 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3766 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3768 ok (result == 1,
3769 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3770 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3772 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3773 setText.flags = 0;
3774 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3775 /* select some text */
3776 cr.cpMax = 1;
3777 cr.cpMin = 3;
3778 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3779 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3780 setText.flags = ST_SELECTION;
3781 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3782 ok(result == 0,
3783 "EM_SETTEXTEX with NULL lParam to replace selection"
3784 " with no text should return 0. Got %i\n",
3785 result);
3787 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3788 setText.flags = 0;
3789 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3790 /* select some text */
3791 cr.cpMax = 1;
3792 cr.cpMin = 3;
3793 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3794 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3795 setText.flags = ST_SELECTION;
3796 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3797 /* get text */
3798 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3799 ok(result == lstrlenW(TestItem1),
3800 "EM_SETTEXTEX with NULL lParam to replace selection"
3801 " with no text should return 0. Got %i\n",
3802 result);
3803 ok(lstrlenW(buf) == 22,
3804 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3805 lstrlenW(buf) );
3807 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3808 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3809 p = (char *)buf;
3810 es.dwCookie = (DWORD_PTR)&p;
3811 es.dwError = 0;
3812 es.pfnCallback = test_WM_SETTEXT_esCallback;
3813 memset(buf, 0, sizeof(buf));
3814 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3815 (WPARAM)(SF_RTF), (LPARAM)&es);
3816 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3818 /* !ST_SELECTION && !Unicode && \rtf */
3819 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3820 getText.codepage = 1200; /* no constant for unicode */
3821 getText.cb = MAX_BUF_LEN;
3822 getText.flags = GT_DEFAULT;
3823 getText.lpDefaultChar = NULL;
3824 getText.lpUsedDefChar = NULL;
3826 setText.flags = 0;
3827 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3828 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3829 ok(lstrcmpW(buf, TestItem1) == 0,
3830 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3832 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3833 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3834 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3835 getText.codepage = CP_ACP;
3836 getText.cb = MAX_BUF_LEN;
3837 getText.flags = GT_DEFAULT;
3838 getText.lpDefaultChar = NULL;
3839 getText.lpUsedDefChar = NULL;
3841 setText.flags = ST_SELECTION;
3842 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3843 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
3844 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3845 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3846 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3848 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3849 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3850 p = (char *)buf;
3851 es.dwCookie = (DWORD_PTR)&p;
3852 es.dwError = 0;
3853 es.pfnCallback = test_WM_SETTEXT_esCallback;
3854 memset(buf, 0, sizeof(buf));
3855 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3856 (WPARAM)(SF_RTF), (LPARAM)&es);
3857 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3859 /* select some text */
3860 cr.cpMax = 1;
3861 cr.cpMin = 3;
3862 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3864 /* ST_SELECTION && !Unicode && \rtf */
3865 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3866 getText.codepage = 1200; /* no constant for unicode */
3867 getText.cb = MAX_BUF_LEN;
3868 getText.flags = GT_DEFAULT;
3869 getText.lpDefaultChar = NULL;
3870 getText.lpUsedDefChar = NULL;
3872 setText.flags = ST_SELECTION;
3873 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3874 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3875 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3877 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3878 setText.codepage = 1200; /* no constant for unicode */
3879 getText.codepage = CP_ACP;
3880 getText.cb = MAX_BUF_LEN;
3882 setText.flags = 0;
3883 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
3884 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3886 /* select some text */
3887 cr.cpMax = 1;
3888 cr.cpMin = 3;
3889 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3891 /* ST_SELECTION && !Unicode && !\rtf */
3892 setText.codepage = CP_ACP;
3893 getText.codepage = 1200; /* no constant for unicode */
3894 getText.cb = MAX_BUF_LEN;
3895 getText.flags = GT_DEFAULT;
3896 getText.lpDefaultChar = NULL;
3897 getText.lpUsedDefChar = NULL;
3899 setText.flags = ST_SELECTION;
3900 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
3901 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3902 ok(lstrcmpW(buf, TestItem1alt) == 0,
3903 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3904 " using ST_SELECTION and non-Unicode\n");
3906 /* Test setting text using rich text format */
3907 setText.flags = 0;
3908 setText.codepage = CP_ACP;
3909 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3910 getText.codepage = CP_ACP;
3911 getText.cb = MAX_BUF_LEN;
3912 getText.flags = GT_DEFAULT;
3913 getText.lpDefaultChar = NULL;
3914 getText.lpUsedDefChar = NULL;
3915 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3916 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3918 setText.flags = 0;
3919 setText.codepage = CP_ACP;
3920 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3921 getText.codepage = CP_ACP;
3922 getText.cb = MAX_BUF_LEN;
3923 getText.flags = GT_DEFAULT;
3924 getText.lpDefaultChar = NULL;
3925 getText.lpUsedDefChar = NULL;
3926 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3927 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3929 /* test for utf8 text with BOM */
3930 setText.flags = 0;
3931 setText.codepage = CP_ACP;
3932 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3933 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3934 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3935 result = strcmp(bufACP, "TestUTF8WithBOM");
3936 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3938 setText.flags = 0;
3939 setText.codepage = CP_UTF8;
3940 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3941 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3942 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3943 result = strcmp(bufACP, "TestUTF8WithBOM");
3944 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3946 DestroyWindow(hwndRichEdit);
3949 static void test_EM_LIMITTEXT(void)
3951 int ret;
3953 HWND hwndRichEdit = new_richedit(NULL);
3955 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
3956 * about setting the length to -1 for multiline edit controls doesn't happen.
3959 /* Don't check default gettextlimit case. That's done in other tests */
3961 /* Set textlimit to 100 */
3962 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
3963 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3964 ok (ret == 100,
3965 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
3967 /* Set textlimit to 0 */
3968 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
3969 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3970 ok (ret == 65536,
3971 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
3973 /* Set textlimit to -1 */
3974 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
3975 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3976 ok (ret == -1,
3977 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
3979 /* Set textlimit to -2 */
3980 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
3981 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3982 ok (ret == -2,
3983 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
3985 DestroyWindow (hwndRichEdit);
3989 static void test_EM_EXLIMITTEXT(void)
3991 int i, selBegin, selEnd, len1, len2;
3992 int result;
3993 char text[1024 + 1];
3994 char buffer[1024 + 1];
3995 int textlimit = 0; /* multiple of 100 */
3996 HWND hwndRichEdit = new_richedit(NULL);
3998 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
3999 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4001 textlimit = 256000;
4002 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4003 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4004 /* set higher */
4005 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4007 textlimit = 1000;
4008 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4009 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4010 /* set lower */
4011 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4013 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4014 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4015 /* default for WParam = 0 */
4016 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4018 textlimit = sizeof(text)-1;
4019 memset(text, 'W', textlimit);
4020 text[sizeof(text)-1] = 0;
4021 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4022 /* maxed out text */
4023 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4025 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4026 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4027 len1 = selEnd - selBegin;
4029 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4030 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4031 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4032 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4033 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4034 len2 = selEnd - selBegin;
4036 ok(len1 != len2,
4037 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4038 len1,len2,i);
4040 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4041 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4042 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4043 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4044 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4045 len1 = selEnd - selBegin;
4047 ok(len1 != len2,
4048 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4049 len1,len2,i);
4051 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4052 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4053 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4054 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4055 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4056 len2 = selEnd - selBegin;
4058 ok(len1 == len2,
4059 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4060 len1,len2,i);
4062 /* set text up to the limit, select all the text, then add a char */
4063 textlimit = 5;
4064 memset(text, 'W', textlimit);
4065 text[textlimit] = 0;
4066 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4067 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4068 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4069 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4070 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4071 result = strcmp(buffer, "A");
4072 ok(0 == result, "got string = \"%s\"\n", buffer);
4074 /* WM_SETTEXT not limited */
4075 textlimit = 10;
4076 memset(text, 'W', textlimit);
4077 text[textlimit] = 0;
4078 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4079 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4080 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4081 i = strlen(buffer);
4082 ok(10 == i, "expected 10 chars\n");
4083 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4084 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4086 /* try inserting more text at end */
4087 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4088 ok(0 == i, "WM_CHAR wasn't processed\n");
4089 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4090 i = strlen(buffer);
4091 ok(10 == i, "expected 10 chars, got %i\n", i);
4092 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4093 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4095 /* try inserting text at beginning */
4096 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4097 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4098 ok(0 == i, "WM_CHAR wasn't processed\n");
4099 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4100 i = strlen(buffer);
4101 ok(10 == i, "expected 10 chars, got %i\n", i);
4102 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4103 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4105 /* WM_CHAR is limited */
4106 textlimit = 1;
4107 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4108 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4109 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4110 ok(0 == i, "WM_CHAR wasn't processed\n");
4111 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4112 ok(0 == i, "WM_CHAR wasn't processed\n");
4113 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4114 i = strlen(buffer);
4115 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4117 DestroyWindow(hwndRichEdit);
4120 static void test_EM_GETLIMITTEXT(void)
4122 int i;
4123 HWND hwndRichEdit = new_richedit(NULL);
4125 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4126 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4128 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4129 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4130 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4132 DestroyWindow(hwndRichEdit);
4135 static void test_WM_SETFONT(void)
4137 /* There is no invalid input or error conditions for this function.
4138 * NULL wParam and lParam just fall back to their default values
4139 * It should be noted that even if you use a gibberish name for your fonts
4140 * here, it will still work because the name is stored. They will display as
4141 * System, but will report their name to be whatever they were created as */
4143 HWND hwndRichEdit = new_richedit(NULL);
4144 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4145 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4146 FF_DONTCARE, "Marlett");
4147 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4148 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4149 FF_DONTCARE, "MS Sans Serif");
4150 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4151 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4152 FF_DONTCARE, "Courier");
4153 LOGFONTA sentLogFont;
4154 CHARFORMAT2A returnedCF2A;
4156 returnedCF2A.cbSize = sizeof(returnedCF2A);
4158 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4159 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4160 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4162 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4163 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4164 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4165 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4167 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4168 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4169 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4170 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4171 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4172 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4174 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4175 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4176 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4177 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4178 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4179 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4181 /* This last test is special since we send in NULL. We clear the variables
4182 * and just compare to "System" instead of the sent in font name. */
4183 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4184 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4185 returnedCF2A.cbSize = sizeof(returnedCF2A);
4187 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4188 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4189 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4190 ok (!strcmp("System",returnedCF2A.szFaceName),
4191 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4193 DestroyWindow(hwndRichEdit);
4197 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4198 LPBYTE pbBuff,
4199 LONG cb,
4200 LONG *pcb)
4202 const char** str = (const char**)dwCookie;
4203 int size = strlen(*str);
4204 if(size > 3) /* let's make it piecemeal for fun */
4205 size = 3;
4206 *pcb = cb;
4207 if (*pcb > size) {
4208 *pcb = size;
4210 if (*pcb > 0) {
4211 memcpy(pbBuff, *str, *pcb);
4212 *str += *pcb;
4214 return 0;
4217 static void test_EM_GETMODIFY(void)
4219 HWND hwndRichEdit = new_richedit(NULL);
4220 LRESULT result;
4221 SETTEXTEX setText;
4222 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4223 'S', 'o', 'm', 'e',
4224 'T', 'e', 'x', 't', 0};
4225 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4226 'S', 'o', 'm', 'e',
4227 'O', 't', 'h', 'e', 'r',
4228 'T', 'e', 'x', 't', 0};
4229 const char* streamText = "hello world";
4230 CHARFORMAT2A cf2;
4231 PARAFORMAT2 pf2;
4232 EDITSTREAM es;
4234 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4235 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4236 FF_DONTCARE, "Courier");
4238 setText.codepage = 1200; /* no constant for unicode */
4239 setText.flags = ST_KEEPUNDO;
4242 /* modify flag shouldn't be set when richedit is first created */
4243 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4244 ok (result == 0,
4245 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4247 /* setting modify flag should actually set it */
4248 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4249 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4250 ok (result != 0,
4251 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4253 /* clearing modify flag should actually clear it */
4254 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4255 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4256 ok (result == 0,
4257 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4259 /* setting font doesn't change modify flag */
4260 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4261 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4262 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4263 ok (result == 0,
4264 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4266 /* setting text should set modify flag */
4267 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4268 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4269 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4270 ok (result != 0,
4271 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4273 /* undo previous text doesn't reset modify flag */
4274 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4275 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4276 ok (result != 0,
4277 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4279 /* set text with no flag to keep undo stack should not set modify flag */
4280 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4281 setText.flags = 0;
4282 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4283 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4284 ok (result == 0,
4285 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4287 /* WM_SETTEXT doesn't modify */
4288 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4289 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4290 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4291 ok (result == 0,
4292 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4294 /* clear the text */
4295 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4296 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4297 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4298 ok (result == 0,
4299 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4301 /* replace text */
4302 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4303 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4304 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4305 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4306 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4307 ok (result != 0,
4308 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4310 /* copy/paste text 1 */
4311 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4312 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4313 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4314 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4315 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4316 ok (result != 0,
4317 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4319 /* copy/paste text 2 */
4320 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4321 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4322 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4323 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4324 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4325 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4326 ok (result != 0,
4327 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4329 /* press char */
4330 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4331 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4332 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4333 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4334 ok (result != 0,
4335 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4337 /* press del */
4338 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4339 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4340 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4341 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4342 ok (result != 0,
4343 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4345 /* set char format */
4346 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4347 cf2.cbSize = sizeof(CHARFORMAT2A);
4348 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4349 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4350 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4351 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4352 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4353 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4354 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4355 ok (result != 0,
4356 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4358 /* set para format */
4359 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4360 pf2.cbSize = sizeof(PARAFORMAT2);
4361 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4362 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4363 pf2.wAlignment = PFA_RIGHT;
4364 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4365 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4366 ok (result == 0,
4367 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4369 /* EM_STREAM */
4370 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4371 es.dwCookie = (DWORD_PTR)&streamText;
4372 es.dwError = 0;
4373 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4374 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4375 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4376 ok (result != 0,
4377 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4379 DestroyWindow(hwndRichEdit);
4382 struct exsetsel_s {
4383 LONG min;
4384 LONG max;
4385 LRESULT expected_retval;
4386 int expected_getsel_start;
4387 int expected_getsel_end;
4388 int _getsel_todo_wine;
4391 const struct exsetsel_s exsetsel_tests[] = {
4392 /* sanity tests */
4393 {5, 10, 10, 5, 10, 0},
4394 {15, 17, 17, 15, 17, 0},
4395 /* test cpMax > strlen() */
4396 {0, 100, 18, 0, 18, 0},
4397 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4398 {-1, 1, 17, 17, 17, 0},
4399 /* test cpMin == cpMax */
4400 {5, 5, 5, 5, 5, 0},
4401 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4402 {-1, 0, 5, 5, 5, 0},
4403 {-1, 17, 5, 5, 5, 0},
4404 {-1, 18, 5, 5, 5, 0},
4405 /* test cpMin < 0 && cpMax < 0 */
4406 {-1, -1, 17, 17, 17, 0},
4407 {-4, -5, 17, 17, 17, 0},
4408 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4409 {0, -1, 18, 0, 18, 0},
4410 {17, -5, 18, 17, 18, 0},
4411 {18, -3, 17, 17, 17, 0},
4412 /* test if cpMin > cpMax */
4413 {15, 19, 18, 15, 18, 0},
4414 {19, 15, 18, 15, 18, 0},
4417 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4418 CHARRANGE cr;
4419 LRESULT result;
4420 int start, end;
4422 cr.cpMin = setsel->min;
4423 cr.cpMax = setsel->max;
4424 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4426 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4428 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4430 if (setsel->_getsel_todo_wine) {
4431 todo_wine {
4432 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);
4434 } else {
4435 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);
4439 static void test_EM_EXSETSEL(void)
4441 HWND hwndRichEdit = new_richedit(NULL);
4442 int i;
4443 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4445 /* sending some text to the window */
4446 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4447 /* 01234567890123456*/
4448 /* 10 */
4450 for (i = 0; i < num_tests; i++) {
4451 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4454 DestroyWindow(hwndRichEdit);
4457 static void test_EM_REPLACESEL(int redraw)
4459 HWND hwndRichEdit = new_richedit(NULL);
4460 char buffer[1024] = {0};
4461 int r;
4462 GETTEXTEX getText;
4463 CHARRANGE cr;
4465 /* sending some text to the window */
4466 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4467 /* 01234567890123456*/
4468 /* 10 */
4470 /* FIXME add more tests */
4471 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4472 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4473 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4474 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4475 r = strcmp(buffer, "testing");
4476 ok(0 == r, "expected %d, got %d\n", 0, r);
4478 DestroyWindow(hwndRichEdit);
4480 hwndRichEdit = new_richedit(NULL);
4482 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4483 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4485 /* Test behavior with carriage returns and newlines */
4486 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4487 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4488 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4489 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4490 r = strcmp(buffer, "RichEdit1");
4491 ok(0 == r, "expected %d, got %d\n", 0, r);
4492 getText.cb = 1024;
4493 getText.codepage = CP_ACP;
4494 getText.flags = GT_DEFAULT;
4495 getText.lpDefaultChar = NULL;
4496 getText.lpUsedDefChar = NULL;
4497 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4498 ok(strcmp(buffer, "RichEdit1") == 0,
4499 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4501 /* Test number of lines reported after EM_REPLACESEL */
4502 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4503 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4505 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4506 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4507 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4508 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4509 r = strcmp(buffer, "RichEdit1\r\n");
4510 ok(0 == r, "expected %d, got %d\n", 0, r);
4511 getText.cb = 1024;
4512 getText.codepage = CP_ACP;
4513 getText.flags = GT_DEFAULT;
4514 getText.lpDefaultChar = NULL;
4515 getText.lpUsedDefChar = NULL;
4516 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4517 ok(strcmp(buffer, "RichEdit1\r") == 0,
4518 "EM_GETTEXTEX returned incorrect string\n");
4520 /* Test number of lines reported after EM_REPLACESEL */
4521 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4522 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4524 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4525 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4526 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4528 /* Test number of lines reported after EM_REPLACESEL */
4529 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4530 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4532 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4533 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4534 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4535 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4537 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4538 r = strcmp(buffer, "RichEdit1\r\n");
4539 ok(0 == r, "expected %d, got %d\n", 0, r);
4540 getText.cb = 1024;
4541 getText.codepage = CP_ACP;
4542 getText.flags = GT_DEFAULT;
4543 getText.lpDefaultChar = NULL;
4544 getText.lpUsedDefChar = NULL;
4545 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4546 ok(strcmp(buffer, "RichEdit1\r") == 0,
4547 "EM_GETTEXTEX returned incorrect string\n");
4549 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4550 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4551 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4552 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4554 /* The following tests show that richedit should handle the special \r\r\n
4555 sequence by turning it into a single space on insertion. However,
4556 EM_REPLACESEL on WinXP returns the number of characters in the original
4557 string.
4560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4561 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4562 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4563 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4564 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4565 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4566 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4568 /* Test the actual string */
4569 getText.cb = 1024;
4570 getText.codepage = CP_ACP;
4571 getText.flags = GT_DEFAULT;
4572 getText.lpDefaultChar = NULL;
4573 getText.lpUsedDefChar = NULL;
4574 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4575 ok(strcmp(buffer, "\r\r") == 0,
4576 "EM_GETTEXTEX returned incorrect string\n");
4578 /* Test number of lines reported after EM_REPLACESEL */
4579 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4580 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4582 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4583 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4584 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4585 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4586 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4587 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4588 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4590 /* Test the actual string */
4591 getText.cb = 1024;
4592 getText.codepage = CP_ACP;
4593 getText.flags = GT_DEFAULT;
4594 getText.lpDefaultChar = NULL;
4595 getText.lpUsedDefChar = NULL;
4596 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4597 ok(strcmp(buffer, " ") == 0,
4598 "EM_GETTEXTEX returned incorrect string\n");
4600 /* Test number of lines reported after EM_REPLACESEL */
4601 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4602 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4604 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4605 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4606 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4607 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4608 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4609 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4610 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4612 /* Test the actual string */
4613 getText.cb = 1024;
4614 getText.codepage = CP_ACP;
4615 getText.flags = GT_DEFAULT;
4616 getText.lpDefaultChar = NULL;
4617 getText.lpUsedDefChar = NULL;
4618 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4619 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4620 "EM_GETTEXTEX returned incorrect string\n");
4622 /* Test number of lines reported after EM_REPLACESEL */
4623 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4624 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4626 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4627 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4628 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4629 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4630 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4631 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4632 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4634 /* Test the actual string */
4635 getText.cb = 1024;
4636 getText.codepage = CP_ACP;
4637 getText.flags = GT_DEFAULT;
4638 getText.lpDefaultChar = NULL;
4639 getText.lpUsedDefChar = NULL;
4640 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4641 ok(strcmp(buffer, " \r") == 0,
4642 "EM_GETTEXTEX returned incorrect string\n");
4644 /* Test number of lines reported after EM_REPLACESEL */
4645 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4646 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4648 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4649 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4650 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4651 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4652 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4653 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4654 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4656 /* Test the actual string */
4657 getText.cb = 1024;
4658 getText.codepage = CP_ACP;
4659 getText.flags = GT_DEFAULT;
4660 getText.lpDefaultChar = NULL;
4661 getText.lpUsedDefChar = NULL;
4662 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4663 ok(strcmp(buffer, " \r\r") == 0,
4664 "EM_GETTEXTEX returned incorrect string\n");
4666 /* Test number of lines reported after EM_REPLACESEL */
4667 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4668 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4670 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4671 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4672 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4673 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4674 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4675 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4676 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4678 /* Test the actual string */
4679 getText.cb = 1024;
4680 getText.codepage = CP_ACP;
4681 getText.flags = GT_DEFAULT;
4682 getText.lpDefaultChar = NULL;
4683 getText.lpUsedDefChar = NULL;
4684 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4685 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4686 "EM_GETTEXTEX returned incorrect string\n");
4688 /* Test number of lines reported after EM_REPLACESEL */
4689 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4690 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4692 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4693 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4694 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4695 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4696 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4697 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4698 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4700 /* Test the actual string */
4701 getText.cb = 1024;
4702 getText.codepage = CP_ACP;
4703 getText.flags = GT_DEFAULT;
4704 getText.lpDefaultChar = NULL;
4705 getText.lpUsedDefChar = NULL;
4706 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4707 ok(strcmp(buffer, "\r\r") == 0,
4708 "EM_GETTEXTEX returned incorrect string\n");
4710 /* Test number of lines reported after EM_REPLACESEL */
4711 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4712 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4714 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4715 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4716 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4717 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4718 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4719 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4720 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4722 /* Test the actual string */
4723 getText.cb = 1024;
4724 getText.codepage = CP_ACP;
4725 getText.flags = GT_DEFAULT;
4726 getText.lpDefaultChar = NULL;
4727 getText.lpUsedDefChar = NULL;
4728 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4729 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4730 "EM_GETTEXTEX returned incorrect string\n");
4732 /* Test number of lines reported after EM_REPLACESEL */
4733 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4734 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4736 if (!redraw)
4737 /* This is needed to avoid interferring with keybd_event calls
4738 * on other tests that simulate keyboard events. */
4739 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4741 DestroyWindow(hwndRichEdit);
4744 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4745 * to test the state of the modifiers (Ctrl/Alt/Shift).
4747 * Therefore Ctrl-<key> keystrokes need to be simulated with
4748 * keybd_event or by using SetKeyboardState to set the modifiers
4749 * and SendMessage to simulate the keystrokes.
4751 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4753 LRESULT result;
4754 hold_key(VK_CONTROL);
4755 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
4756 release_key(VK_CONTROL);
4757 return result;
4760 static void test_WM_PASTE(void)
4762 int result;
4763 char buffer[1024] = {0};
4764 const char* text1 = "testing paste\r";
4765 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4766 const char* text1_after = "testing paste\r\n";
4767 const char* text2 = "testing paste\r\rtesting paste";
4768 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4769 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4770 HWND hwndRichEdit = new_richedit(NULL);
4772 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4773 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
4775 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4776 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4777 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4778 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4779 /* Pasted text should be visible at this step */
4780 result = strcmp(text1_step1, buffer);
4781 ok(result == 0,
4782 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4784 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4785 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4786 /* Text should be the same as before (except for \r -> \r\n conversion) */
4787 result = strcmp(text1_after, buffer);
4788 ok(result == 0,
4789 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4791 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
4792 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
4793 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4794 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4795 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4796 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4797 /* Pasted text should be visible at this step */
4798 result = strcmp(text3, buffer);
4799 ok(result == 0,
4800 "test paste: strcmp = %i\n", result);
4801 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4802 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4803 /* Text should be the same as before (except for \r -> \r\n conversion) */
4804 result = strcmp(text2_after, buffer);
4805 ok(result == 0,
4806 "test paste: strcmp = %i\n", result);
4807 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4808 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4809 /* Text should revert to post-paste state */
4810 result = strcmp(buffer,text3);
4811 ok(result == 0,
4812 "test paste: strcmp = %i\n", result);
4814 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4815 /* Send WM_CHAR to simulate Ctrl-V */
4816 SendMessageA(hwndRichEdit, WM_CHAR, 22,
4817 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4818 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4819 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4820 result = strcmp(buffer,"");
4821 ok(result == 0,
4822 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4824 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4825 * with SetKeyboard state. */
4827 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4828 /* Simulates paste (Ctrl-V) */
4829 hold_key(VK_CONTROL);
4830 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
4831 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4832 release_key(VK_CONTROL);
4833 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4834 result = strcmp(buffer,"paste");
4835 ok(result == 0,
4836 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4838 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4839 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
4840 /* Simulates copy (Ctrl-C) */
4841 hold_key(VK_CONTROL);
4842 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
4843 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
4844 release_key(VK_CONTROL);
4845 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4846 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4847 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4848 result = strcmp(buffer,"testing");
4849 ok(result == 0,
4850 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4852 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4853 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
4854 /* Simulates select all (Ctrl-A) */
4855 hold_key(VK_CONTROL);
4856 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
4857 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
4858 /* Simulates select cut (Ctrl-X) */
4859 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
4860 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
4861 release_key(VK_CONTROL);
4862 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4863 result = strcmp(buffer,"");
4864 ok(result == 0,
4865 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4866 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4867 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4868 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4869 result = strcmp(buffer,"cut\r\n");
4870 todo_wine ok(result == 0,
4871 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4872 /* Simulates undo (Ctrl-Z) */
4873 hold_key(VK_CONTROL);
4874 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
4875 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4876 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4877 result = strcmp(buffer,"");
4878 ok(result == 0,
4879 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4880 /* Simulates redo (Ctrl-Y) */
4881 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
4882 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4883 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4884 result = strcmp(buffer,"cut\r\n");
4885 todo_wine ok(result == 0,
4886 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4887 release_key(VK_CONTROL);
4889 DestroyWindow(hwndRichEdit);
4892 static void test_EM_FORMATRANGE(void)
4894 int r, i, tpp_x, tpp_y;
4895 HDC hdc;
4896 HWND hwndRichEdit = new_richedit(NULL);
4897 FORMATRANGE fr;
4898 BOOL skip_non_english;
4899 static const struct {
4900 const char *string; /* The string */
4901 int first; /* First 'pagebreak', 0 for don't care */
4902 int second; /* Second 'pagebreak', 0 for don't care */
4903 } fmtstrings[] = {
4904 {"WINE wine", 0, 0},
4905 {"WINE wineWine", 0, 0},
4906 {"WINE\r\nwine\r\nwine", 5, 10},
4907 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4908 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4911 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4912 if (skip_non_english)
4913 skip("Skipping some tests on non-English platform\n");
4915 hdc = GetDC(hwndRichEdit);
4916 ok(hdc != NULL, "Could not get HDC\n");
4918 /* Calculate the twips per pixel */
4919 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4920 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4922 /* Test the simple case where all the text fits in the page rect. */
4923 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4924 fr.hdc = fr.hdcTarget = hdc;
4925 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4926 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4927 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4928 fr.chrg.cpMin = 0;
4929 fr.chrg.cpMax = -1;
4930 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4931 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4933 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4934 fr.rc.bottom = fr.rcPage.bottom;
4935 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4936 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4938 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4940 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4942 GETTEXTLENGTHEX gtl;
4943 SIZE stringsize;
4944 int len;
4946 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
4948 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4949 gtl.codepage = CP_ACP;
4950 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4952 /* Get some size information for the string */
4953 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4955 /* Define the box to be half the width needed and a bit larger than the height.
4956 * Changes to the width means we have at least 2 pages. Changes to the height
4957 * is done so we can check the changing of fr.rc.bottom.
4959 fr.hdc = fr.hdcTarget = hdc;
4960 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4961 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4962 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
4964 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4965 todo_wine {
4966 ok(r == len, "Expected %d, got %d\n", len, r);
4969 /* We know that the page can't hold the full string. See how many characters
4970 * are on the first one
4972 fr.chrg.cpMin = 0;
4973 fr.chrg.cpMax = -1;
4974 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
4975 todo_wine {
4976 if (! skip_non_english)
4977 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
4979 if (fmtstrings[i].first)
4980 todo_wine {
4981 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
4983 else
4984 ok(r < len, "Expected < %d, got %d\n", len, r);
4986 /* Do another page */
4987 fr.chrg.cpMin = r;
4988 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
4989 if (fmtstrings[i].second)
4990 todo_wine {
4991 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
4993 else if (! skip_non_english)
4994 ok (r < len, "Expected < %d, got %d\n", len, r);
4996 /* There is at least on more page, but we don't care */
4998 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
4999 todo_wine {
5000 ok(r == len, "Expected %d, got %d\n", len, r);
5004 ReleaseDC(NULL, hdc);
5005 DestroyWindow(hwndRichEdit);
5008 static int nCallbackCount = 0;
5010 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5011 LONG cb, LONG* pcb)
5013 const char text[] = {'t','e','s','t'};
5015 if (sizeof(text) <= cb)
5017 if ((int)dwCookie != nCallbackCount)
5019 *pcb = 0;
5020 return 0;
5023 memcpy (pbBuff, text, sizeof(text));
5024 *pcb = sizeof(text);
5026 nCallbackCount++;
5028 return 0;
5030 else
5031 return 1; /* indicates callback failed */
5034 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5035 LPBYTE pbBuff,
5036 LONG cb,
5037 LONG *pcb)
5039 const char** str = (const char**)dwCookie;
5040 int size = strlen(*str);
5041 *pcb = cb;
5042 if (*pcb > size) {
5043 *pcb = size;
5045 if (*pcb > 0) {
5046 memcpy(pbBuff, *str, *pcb);
5047 *str += *pcb;
5049 return 0;
5052 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5053 LPBYTE pbBuff,
5054 LONG cb,
5055 LONG *pcb)
5057 DWORD *phase = (DWORD *)dwCookie;
5059 if(*phase == 0){
5060 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5061 *pcb = sizeof(first) - 1;
5062 memcpy(pbBuff, first, *pcb);
5063 }else if(*phase == 1){
5064 static const char second[] = "\x8f\xc3\x8b";
5065 *pcb = sizeof(second) - 1;
5066 memcpy(pbBuff, second, *pcb);
5067 }else
5068 *pcb = 0;
5070 ++*phase;
5072 return 0;
5075 struct StringWithLength {
5076 int length;
5077 char *buffer;
5080 /* This callback is used to handled the null characters in a string. */
5081 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5082 LPBYTE pbBuff,
5083 LONG cb,
5084 LONG *pcb)
5086 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5087 int size = str->length;
5088 *pcb = cb;
5089 if (*pcb > size) {
5090 *pcb = size;
5092 if (*pcb > 0) {
5093 memcpy(pbBuff, str->buffer, *pcb);
5094 str->buffer += *pcb;
5095 str->length -= *pcb;
5097 return 0;
5100 static void test_EM_STREAMIN(void)
5102 HWND hwndRichEdit = new_richedit(NULL);
5103 DWORD phase;
5104 LRESULT result;
5105 EDITSTREAM es;
5106 char buffer[1024] = {0}, tmp[16];
5107 CHARRANGE range;
5109 const char * streamText0 = "{\\rtf1 TestSomeText}";
5110 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5111 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5112 const char * ptr;
5114 const char * streamText1 =
5115 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5116 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5117 "}\r\n";
5119 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5120 const char * streamText2 =
5121 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5122 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5123 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5124 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5125 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5126 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5127 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5129 const char * streamText3 = "RichEdit1";
5131 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5133 const char * streamText4 =
5134 "This text just needs to be long enough to cause run to be split onto "
5135 "two separate lines and make sure the null terminating character is "
5136 "handled properly.\0";
5138 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5140 int length4 = strlen(streamText4) + 1;
5141 struct StringWithLength cookieForStream4 = {
5142 length4,
5143 (char *)streamText4,
5146 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5147 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5148 struct StringWithLength cookieForStream5 = {
5149 sizeof(streamText5),
5150 (char *)streamText5,
5153 /* Minimal test without \par at the end */
5154 es.dwCookie = (DWORD_PTR)&streamText0;
5155 es.dwError = 0;
5156 es.pfnCallback = test_EM_STREAMIN_esCallback;
5157 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5158 ok(result == 12, "got %ld, expected %d\n", result, 12);
5160 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5161 ok (result == 12,
5162 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5163 result = strcmp (buffer,"TestSomeText");
5164 ok (result == 0,
5165 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5166 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5168 /* Native richedit 2.0 ignores last \par */
5169 ptr = streamText0a;
5170 es.dwCookie = (DWORD_PTR)&ptr;
5171 es.dwError = 0;
5172 es.pfnCallback = test_EM_STREAMIN_esCallback;
5173 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5174 ok(result == 12, "got %ld, expected %d\n", result, 12);
5176 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5177 ok (result == 12,
5178 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5179 result = strcmp (buffer,"TestSomeText");
5180 ok (result == 0,
5181 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5182 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5184 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5185 es.dwCookie = (DWORD_PTR)&streamText0b;
5186 es.dwError = 0;
5187 es.pfnCallback = test_EM_STREAMIN_esCallback;
5188 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5189 ok(result == 13, "got %ld, expected %d\n", result, 13);
5191 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5192 ok (result == 14,
5193 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5194 result = strcmp (buffer,"TestSomeText\r\n");
5195 ok (result == 0,
5196 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5197 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5199 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5200 ptr = streamText0a;
5201 es.dwCookie = (DWORD_PTR)&ptr;
5202 es.dwError = 0;
5203 es.pfnCallback = test_EM_STREAMIN_esCallback;
5204 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5205 ok(result == 12, "got %ld, expected %d\n", result, 12);
5207 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5208 ok (result == 12,
5209 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5210 result = strcmp (buffer,"TestSomeText");
5211 ok (result == 0,
5212 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5213 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5215 range.cpMin = 0;
5216 range.cpMax = -1;
5217 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5218 ok (result == 13, "got %ld\n", result);
5220 ptr = streamText0a;
5221 es.dwCookie = (DWORD_PTR)&ptr;
5222 es.dwError = 0;
5223 es.pfnCallback = test_EM_STREAMIN_esCallback;
5225 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5226 ok(result == 13, "got %ld, expected 13\n", result);
5228 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5229 ok (result == 14,
5230 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5231 result = strcmp (buffer,"TestSomeText\r\n");
5232 ok (result == 0,
5233 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5234 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5236 es.dwCookie = (DWORD_PTR)&streamText1;
5237 es.dwError = 0;
5238 es.pfnCallback = test_EM_STREAMIN_esCallback;
5239 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5240 ok(result == 12, "got %ld, expected %d\n", result, 12);
5242 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5243 ok (result == 12,
5244 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5245 result = strcmp (buffer,"TestSomeText");
5246 ok (result == 0,
5247 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5248 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5250 es.dwCookie = (DWORD_PTR)&streamText2;
5251 es.dwError = 0;
5252 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5253 ok(result == 0, "got %ld, expected %d\n", result, 0);
5255 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5256 ok (result == 0,
5257 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5258 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5259 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5261 es.dwCookie = (DWORD_PTR)&streamText3;
5262 es.dwError = 0;
5263 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5264 ok(result == 0, "got %ld, expected %d\n", result, 0);
5266 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5267 ok (result == 0,
5268 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5269 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5270 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5272 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5273 es.dwError = 0;
5274 es.pfnCallback = test_EM_STREAMIN_esCallback;
5275 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5276 ok(result == 18, "got %ld, expected %d\n", result, 18);
5278 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5279 ok(result == 15,
5280 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5281 result = strcmp (buffer,"TestUTF8WithBOM");
5282 ok(result == 0,
5283 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5284 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5286 phase = 0;
5287 es.dwCookie = (DWORD_PTR)&phase;
5288 es.dwError = 0;
5289 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5290 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5291 ok(result == 8, "got %ld\n", result);
5293 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5295 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5296 ok(result == 3,
5297 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5298 result = memcmp (buffer, tmp, 3);
5299 ok(result == 0,
5300 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5301 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5303 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5304 es.dwError = 0;
5305 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5306 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5307 ok(result == length4, "got %ld, expected %d\n", result, length4);
5309 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5310 ok (result == length4,
5311 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5312 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5314 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5315 es.dwError = 0;
5316 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5317 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5318 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5320 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5321 ok (result == length5,
5322 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5323 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5325 DestroyWindow(hwndRichEdit);
5328 static void test_EM_StreamIn_Undo(void)
5330 /* The purpose of this test is to determine when a EM_StreamIn should be
5331 * undoable. This is important because WM_PASTE currently uses StreamIn and
5332 * pasting should always be undoable but streaming isn't always.
5334 * cases to test:
5335 * StreamIn plain text without SFF_SELECTION.
5336 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5337 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5338 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5339 * Feel free to add tests for other text modes or StreamIn things.
5343 HWND hwndRichEdit = new_richedit(NULL);
5344 LRESULT result;
5345 EDITSTREAM es;
5346 char buffer[1024] = {0};
5347 const char randomtext[] = "Some text";
5349 es.pfnCallback = EditStreamCallback;
5351 /* StreamIn, no SFF_SELECTION */
5352 es.dwCookie = nCallbackCount;
5353 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5354 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5355 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5356 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5357 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5358 result = strcmp (buffer,"test");
5359 ok (result == 0,
5360 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5362 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5363 ok (result == FALSE,
5364 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5366 /* StreamIn, SFF_SELECTION, but nothing selected */
5367 es.dwCookie = nCallbackCount;
5368 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5369 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5370 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5371 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5372 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5373 result = strcmp (buffer,"testSome text");
5374 ok (result == 0,
5375 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5377 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5378 ok (result == TRUE,
5379 "EM_STREAMIN with SFF_SELECTION but no selection set "
5380 "should create an undo\n");
5382 /* StreamIn, SFF_SELECTION, with a selection */
5383 es.dwCookie = nCallbackCount;
5384 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5385 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5386 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5387 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5388 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5389 result = strcmp (buffer,"Sometesttext");
5390 ok (result == 0,
5391 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5393 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5394 ok (result == TRUE,
5395 "EM_STREAMIN with SFF_SELECTION and selection set "
5396 "should create an undo\n");
5398 DestroyWindow(hwndRichEdit);
5401 static BOOL is_em_settextex_supported(HWND hwnd)
5403 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5404 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5407 static void test_unicode_conversions(void)
5409 static const WCHAR tW[] = {'t',0};
5410 static const WCHAR teW[] = {'t','e',0};
5411 static const WCHAR textW[] = {'t','e','s','t',0};
5412 static const char textA[] = "test";
5413 char bufA[64];
5414 WCHAR bufW[64];
5415 HWND hwnd;
5416 int em_settextex_supported, ret;
5418 #define set_textA(hwnd, wm_set_text, txt) \
5419 do { \
5420 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5421 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5422 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5423 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5424 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5425 } while(0)
5426 #define expect_textA(hwnd, wm_get_text, txt) \
5427 do { \
5428 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5429 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5430 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5431 memset(bufA, 0xAA, sizeof(bufA)); \
5432 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5433 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5434 ret = lstrcmpA(bufA, txt); \
5435 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5436 } while(0)
5438 #define set_textW(hwnd, wm_set_text, txt) \
5439 do { \
5440 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5441 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5442 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5443 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5444 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5445 } while(0)
5446 #define expect_textW(hwnd, wm_get_text, txt) \
5447 do { \
5448 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5449 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5450 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5451 memset(bufW, 0xAA, sizeof(bufW)); \
5452 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5453 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5454 ret = lstrcmpW(bufW, txt); \
5455 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5456 } while(0)
5457 #define expect_empty(hwnd, wm_get_text) \
5458 do { \
5459 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5460 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5461 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5462 memset(bufA, 0xAA, sizeof(bufA)); \
5463 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5464 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5465 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5466 } while(0)
5468 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5469 0, 0, 200, 60, 0, 0, 0, 0);
5470 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5472 ret = IsWindowUnicode(hwnd);
5473 ok(ret, "RichEdit20W should be unicode under NT\n");
5475 /* EM_SETTEXTEX is supported starting from version 3.0 */
5476 em_settextex_supported = is_em_settextex_supported(hwnd);
5477 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5478 em_settextex_supported ? "" : "NOT ");
5480 expect_empty(hwnd, WM_GETTEXT);
5481 expect_empty(hwnd, EM_GETTEXTEX);
5483 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5484 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5485 expect_textA(hwnd, WM_GETTEXT, "t");
5486 expect_textA(hwnd, EM_GETTEXTEX, "t");
5487 expect_textW(hwnd, EM_GETTEXTEX, tW);
5489 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5490 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5491 expect_textA(hwnd, WM_GETTEXT, "te");
5492 expect_textA(hwnd, EM_GETTEXTEX, "te");
5493 expect_textW(hwnd, EM_GETTEXTEX, teW);
5495 set_textA(hwnd, WM_SETTEXT, NULL);
5496 expect_empty(hwnd, WM_GETTEXT);
5497 expect_empty(hwnd, EM_GETTEXTEX);
5499 set_textA(hwnd, WM_SETTEXT, textA);
5500 expect_textA(hwnd, WM_GETTEXT, textA);
5501 expect_textA(hwnd, EM_GETTEXTEX, textA);
5502 expect_textW(hwnd, EM_GETTEXTEX, textW);
5504 if (em_settextex_supported)
5506 set_textA(hwnd, EM_SETTEXTEX, textA);
5507 expect_textA(hwnd, WM_GETTEXT, textA);
5508 expect_textA(hwnd, EM_GETTEXTEX, textA);
5509 expect_textW(hwnd, EM_GETTEXTEX, textW);
5512 set_textW(hwnd, WM_SETTEXT, textW);
5513 expect_textW(hwnd, WM_GETTEXT, textW);
5514 expect_textA(hwnd, WM_GETTEXT, textA);
5515 expect_textW(hwnd, EM_GETTEXTEX, textW);
5516 expect_textA(hwnd, EM_GETTEXTEX, textA);
5518 if (em_settextex_supported)
5520 set_textW(hwnd, EM_SETTEXTEX, textW);
5521 expect_textW(hwnd, WM_GETTEXT, textW);
5522 expect_textA(hwnd, WM_GETTEXT, textA);
5523 expect_textW(hwnd, EM_GETTEXTEX, textW);
5524 expect_textA(hwnd, EM_GETTEXTEX, textA);
5526 DestroyWindow(hwnd);
5528 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5529 0, 0, 200, 60, 0, 0, 0, 0);
5530 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5532 ret = IsWindowUnicode(hwnd);
5533 ok(!ret, "RichEdit20A should NOT be unicode\n");
5535 set_textA(hwnd, WM_SETTEXT, textA);
5536 expect_textA(hwnd, WM_GETTEXT, textA);
5537 expect_textA(hwnd, EM_GETTEXTEX, textA);
5538 expect_textW(hwnd, EM_GETTEXTEX, textW);
5540 if (em_settextex_supported)
5542 set_textA(hwnd, EM_SETTEXTEX, textA);
5543 expect_textA(hwnd, WM_GETTEXT, textA);
5544 expect_textA(hwnd, EM_GETTEXTEX, textA);
5545 expect_textW(hwnd, EM_GETTEXTEX, textW);
5548 set_textW(hwnd, WM_SETTEXT, textW);
5549 expect_textW(hwnd, WM_GETTEXT, textW);
5550 expect_textA(hwnd, WM_GETTEXT, textA);
5551 expect_textW(hwnd, EM_GETTEXTEX, textW);
5552 expect_textA(hwnd, EM_GETTEXTEX, textA);
5554 if (em_settextex_supported)
5556 set_textW(hwnd, EM_SETTEXTEX, textW);
5557 expect_textW(hwnd, WM_GETTEXT, textW);
5558 expect_textA(hwnd, WM_GETTEXT, textA);
5559 expect_textW(hwnd, EM_GETTEXTEX, textW);
5560 expect_textA(hwnd, EM_GETTEXTEX, textA);
5562 DestroyWindow(hwnd);
5565 static void test_WM_CHAR(void)
5567 HWND hwnd;
5568 int ret;
5569 const char * char_list = "abc\rabc\r";
5570 const char * expected_content_single = "abcabc";
5571 const char * expected_content_multi = "abc\r\nabc\r\n";
5572 char buffer[64] = {0};
5573 const char * p;
5575 /* single-line control must IGNORE carriage returns */
5576 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5577 0, 0, 200, 60, 0, 0, 0, 0);
5578 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5580 p = char_list;
5581 while (*p != '\0') {
5582 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5583 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5584 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5585 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5586 p++;
5589 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5590 ret = strcmp(buffer, expected_content_single);
5591 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5593 DestroyWindow(hwnd);
5595 /* multi-line control inserts CR normally */
5596 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5597 0, 0, 200, 60, 0, 0, 0, 0);
5598 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5600 p = char_list;
5601 while (*p != '\0') {
5602 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5603 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5604 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5605 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5606 p++;
5609 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5610 ret = strcmp(buffer, expected_content_multi);
5611 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5613 DestroyWindow(hwnd);
5616 static void test_EM_GETTEXTLENGTHEX(void)
5618 HWND hwnd;
5619 GETTEXTLENGTHEX gtl;
5620 int ret;
5621 const char * base_string = "base string";
5622 const char * test_string = "a\nb\n\n\r\n";
5623 const char * test_string_after = "a";
5624 const char * test_string_2 = "a\rtest\rstring";
5625 char buffer[64] = {0};
5627 /* single line */
5628 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5629 0, 0, 200, 60, 0, 0, 0, 0);
5630 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5632 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5633 gtl.codepage = CP_ACP;
5634 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5635 ok(ret == 0, "ret %d\n",ret);
5637 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5638 gtl.codepage = CP_ACP;
5639 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5640 ok(ret == 0, "ret %d\n",ret);
5642 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5644 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5645 gtl.codepage = CP_ACP;
5646 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5647 ok(ret == strlen(base_string), "ret %d\n",ret);
5649 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5650 gtl.codepage = CP_ACP;
5651 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5652 ok(ret == strlen(base_string), "ret %d\n",ret);
5654 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5656 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5657 gtl.codepage = CP_ACP;
5658 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5659 ok(ret == 1, "ret %d\n",ret);
5661 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5662 gtl.codepage = CP_ACP;
5663 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5664 ok(ret == 1, "ret %d\n",ret);
5666 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5667 ret = strcmp(buffer, test_string_after);
5668 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5670 DestroyWindow(hwnd);
5672 /* multi line */
5673 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5674 0, 0, 200, 60, 0, 0, 0, 0);
5675 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5677 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5678 gtl.codepage = CP_ACP;
5679 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5680 ok(ret == 0, "ret %d\n",ret);
5682 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5683 gtl.codepage = CP_ACP;
5684 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5685 ok(ret == 0, "ret %d\n",ret);
5687 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5689 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5690 gtl.codepage = CP_ACP;
5691 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5692 ok(ret == strlen(base_string), "ret %d\n",ret);
5694 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5695 gtl.codepage = CP_ACP;
5696 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5697 ok(ret == strlen(base_string), "ret %d\n",ret);
5699 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5701 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5702 gtl.codepage = CP_ACP;
5703 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5704 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5706 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5707 gtl.codepage = CP_ACP;
5708 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5709 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5711 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5713 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5714 gtl.codepage = CP_ACP;
5715 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5716 ok(ret == 10, "ret %d\n",ret);
5718 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5719 gtl.codepage = CP_ACP;
5720 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5721 ok(ret == 6, "ret %d\n",ret);
5723 /* Unicode/NUMCHARS/NUMBYTES */
5724 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5726 gtl.flags = GTL_DEFAULT;
5727 gtl.codepage = 1200;
5728 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5729 ok(ret == lstrlenA(test_string_2),
5730 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5732 gtl.flags = GTL_NUMCHARS;
5733 gtl.codepage = 1200;
5734 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5735 ok(ret == lstrlenA(test_string_2),
5736 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5738 gtl.flags = GTL_NUMBYTES;
5739 gtl.codepage = 1200;
5740 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5741 ok(ret == lstrlenA(test_string_2)*2,
5742 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5744 gtl.flags = GTL_PRECISE;
5745 gtl.codepage = 1200;
5746 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5747 ok(ret == lstrlenA(test_string_2)*2,
5748 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5750 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5751 gtl.codepage = 1200;
5752 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5753 ok(ret == lstrlenA(test_string_2),
5754 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5756 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5757 gtl.codepage = 1200;
5758 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5759 ok(ret == E_INVALIDARG,
5760 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5762 DestroyWindow(hwnd);
5766 /* globals that parent and child access when checking event masks & notifications */
5767 static HWND eventMaskEditHwnd = 0;
5768 static int queriedEventMask;
5769 static int watchForEventMask = 0;
5771 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5772 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5774 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5776 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5778 return DefWindowProcA(hwnd, message, wParam, lParam);
5781 /* test event masks in combination with WM_COMMAND */
5782 static void test_eventMask(void)
5784 HWND parent;
5785 int ret, style;
5786 WNDCLASSA cls;
5787 const char text[] = "foo bar\n";
5788 int eventMask;
5790 /* register class to capture WM_COMMAND */
5791 cls.style = 0;
5792 cls.lpfnWndProc = ParentMsgCheckProcA;
5793 cls.cbClsExtra = 0;
5794 cls.cbWndExtra = 0;
5795 cls.hInstance = GetModuleHandleA(0);
5796 cls.hIcon = 0;
5797 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5798 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5799 cls.lpszMenuName = NULL;
5800 cls.lpszClassName = "EventMaskParentClass";
5801 if(!RegisterClassA(&cls)) assert(0);
5803 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5804 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5805 ok (parent != 0, "Failed to create parent window\n");
5807 eventMaskEditHwnd = new_richedit(parent);
5808 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5810 eventMask = ENM_CHANGE | ENM_UPDATE;
5811 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5812 ok(ret == ENM_NONE, "wrong event mask\n");
5813 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5814 ok(ret == eventMask, "failed to set event mask\n");
5816 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5817 queriedEventMask = 0; /* initialize to something other than we expect */
5818 watchForEventMask = EN_CHANGE;
5819 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
5820 ok(ret == TRUE, "failed to set text\n");
5821 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5822 notification in response to WM_SETTEXT */
5823 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5824 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5826 /* check to see if EN_CHANGE is sent when redraw is turned off */
5827 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5828 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5829 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5830 /* redraw is disabled by making the window invisible. */
5831 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5832 queriedEventMask = 0; /* initialize to something other than we expect */
5833 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5834 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5835 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5836 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5837 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5839 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5840 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5841 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
5842 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5843 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5844 watchForEventMask = EN_UPDATE;
5845 queriedEventMask = 0; /* initialize to something other than we expect */
5846 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5847 ok(queriedEventMask == 0,
5848 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5849 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
5850 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5851 queriedEventMask = 0; /* initialize to something other than we expect */
5852 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5853 ok(queriedEventMask == eventMask,
5854 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5857 DestroyWindow(parent);
5860 static int received_WM_NOTIFY = 0;
5861 static int modify_at_WM_NOTIFY = 0;
5862 static BOOL filter_on_WM_NOTIFY = FALSE;
5863 static HWND hwndRichedit_WM_NOTIFY;
5865 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5867 if(message == WM_NOTIFY)
5869 received_WM_NOTIFY = 1;
5870 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5871 if (filter_on_WM_NOTIFY) return TRUE;
5873 return DefWindowProcA(hwnd, message, wParam, lParam);
5876 static void test_WM_NOTIFY(void)
5878 HWND parent;
5879 WNDCLASSA cls;
5880 CHARFORMAT2A cf2;
5881 int sel_start, sel_end;
5883 /* register class to capture WM_NOTIFY */
5884 cls.style = 0;
5885 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5886 cls.cbClsExtra = 0;
5887 cls.cbWndExtra = 0;
5888 cls.hInstance = GetModuleHandleA(0);
5889 cls.hIcon = 0;
5890 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5891 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5892 cls.lpszMenuName = NULL;
5893 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5894 if(!RegisterClassA(&cls)) assert(0);
5896 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5897 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5898 ok (parent != 0, "Failed to create parent window\n");
5900 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5901 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5903 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5905 /* Notifications for selection change should only be sent when selection
5906 actually changes. EM_SETCHARFORMAT is one message that calls
5907 ME_CommitUndo, which should check whether message should be sent */
5908 received_WM_NOTIFY = 0;
5909 cf2.cbSize = sizeof(CHARFORMAT2A);
5910 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5911 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5912 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5913 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
5914 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5916 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5917 already at 0. */
5918 received_WM_NOTIFY = 0;
5919 modify_at_WM_NOTIFY = 0;
5920 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5921 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5922 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5924 received_WM_NOTIFY = 0;
5925 modify_at_WM_NOTIFY = 0;
5926 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5927 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5929 received_WM_NOTIFY = 0;
5930 modify_at_WM_NOTIFY = 0;
5931 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5932 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5933 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5935 /* Test for WM_NOTIFY messages with redraw disabled. */
5936 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5937 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5938 received_WM_NOTIFY = 0;
5939 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5940 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5941 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5943 /* Test filtering key events. */
5944 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5945 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5946 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5947 received_WM_NOTIFY = 0;
5948 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5949 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5950 ok(sel_start == 1 && sel_end == 1,
5951 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5952 filter_on_WM_NOTIFY = TRUE;
5953 received_WM_NOTIFY = 0;
5954 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5955 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5956 ok(sel_start == 1 && sel_end == 1,
5957 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5959 /* test with owner set to NULL */
5960 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5961 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5962 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5963 ok(sel_start == 1 && sel_end == 1,
5964 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5966 DestroyWindow(hwndRichedit_WM_NOTIFY);
5967 DestroyWindow(parent);
5970 static int cpMin_EN_LINK = -1;
5971 static int cpMax_EN_LINK = -1;
5973 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5975 ENLINK* enlink = (ENLINK*)lParam;
5976 if(message == WM_NOTIFY && enlink->nmhdr.code == EN_LINK)
5978 cpMin_EN_LINK = enlink->chrg.cpMin;
5979 cpMax_EN_LINK = enlink->chrg.cpMax;
5981 return DefWindowProcA(hwnd, message, wParam, lParam);
5984 static void test_EN_LINK(void)
5986 HWND parent;
5987 WNDCLASSA cls;
5988 HWND hwndRichedit_EN_LINK;
5989 CHARFORMAT2A cf2;
5991 /* register class to capture WM_NOTIFY */
5992 cls.style = 0;
5993 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
5994 cls.cbClsExtra = 0;
5995 cls.cbWndExtra = 0;
5996 cls.hInstance = GetModuleHandleA(0);
5997 cls.hIcon = 0;
5998 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5999 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6000 cls.lpszMenuName = NULL;
6001 cls.lpszClassName = "EN_LINK_ParentClass";
6002 if(!RegisterClassA(&cls)) assert(0);
6004 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6005 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6006 ok(parent != 0, "Failed to create parent window\n");
6008 hwndRichedit_EN_LINK = new_richedit(parent);
6009 ok(hwndRichedit_EN_LINK != 0, "Failed to create edit window\n");
6011 SendMessageA(hwndRichedit_EN_LINK, EM_SETEVENTMASK, 0, ENM_LINK);
6013 cf2.cbSize = sizeof(CHARFORMAT2A);
6014 cf2.dwMask = CFM_LINK;
6015 cf2.dwEffects = CFE_LINK;
6016 SendMessageA(hwndRichedit_EN_LINK, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6017 /* mixing letters and numbers causes runs to be split */
6018 SendMessageA(hwndRichedit_EN_LINK, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6019 SendMessageA(hwndRichedit_EN_LINK, WM_LBUTTONDOWN, 0, MAKELPARAM(5, 5));
6020 ok(cpMin_EN_LINK == 0 && cpMax_EN_LINK == 31, "Expected link range [0,31) got [%i,%i)\n", cpMin_EN_LINK, cpMax_EN_LINK);
6022 DestroyWindow(hwndRichedit_EN_LINK);
6023 DestroyWindow(parent);
6026 static void test_undo_coalescing(void)
6028 HWND hwnd;
6029 int result;
6030 char buffer[64] = {0};
6032 /* multi-line control inserts CR normally */
6033 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6034 0, 0, 200, 60, 0, 0, 0, 0);
6035 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6037 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6038 ok (result == FALSE, "Can undo after window creation.\n");
6039 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6040 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6041 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6042 ok (result == FALSE, "Can redo after window creation.\n");
6043 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6044 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6046 /* Test the effect of arrows keys during typing on undo transactions*/
6047 simulate_typing_characters(hwnd, "one two three");
6048 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6049 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6050 simulate_typing_characters(hwnd, " four five six");
6052 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6053 ok (result == FALSE, "Can redo before anything is undone.\n");
6054 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6055 ok (result == TRUE, "Cannot undo typed characters.\n");
6056 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6057 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6058 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6059 ok (result == TRUE, "Cannot redo after undo.\n");
6060 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6061 result = strcmp(buffer, "one two three");
6062 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6064 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6065 ok (result == TRUE, "Cannot undo typed characters.\n");
6066 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6067 ok (result == TRUE, "Failed to undo typed characters.\n");
6068 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6069 result = strcmp(buffer, "");
6070 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6072 /* Test the effect of focus changes during typing on undo transactions*/
6073 simulate_typing_characters(hwnd, "one two three");
6074 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6075 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6076 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6077 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6078 simulate_typing_characters(hwnd, " four five six");
6079 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6080 ok (result == TRUE, "Failed to undo typed characters.\n");
6081 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6082 result = strcmp(buffer, "one two three");
6083 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6085 /* Test the effect of the back key during typing on undo transactions */
6086 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6087 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6088 ok (result == TRUE, "Failed to clear the text.\n");
6089 simulate_typing_characters(hwnd, "one two threa");
6090 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6091 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6092 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6093 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6094 simulate_typing_characters(hwnd, "e four five six");
6095 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6096 ok (result == TRUE, "Failed to undo typed characters.\n");
6097 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6098 result = strcmp(buffer, "");
6099 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6101 /* Test the effect of the delete key during typing on undo transactions */
6102 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6103 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6104 ok(result == TRUE, "Failed to set the text.\n");
6105 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6106 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6107 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6108 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6109 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6110 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6111 ok (result == TRUE, "Failed to undo typed characters.\n");
6112 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6113 result = strcmp(buffer, "acd");
6114 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6115 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6116 ok (result == TRUE, "Failed to undo typed characters.\n");
6117 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6118 result = strcmp(buffer, "abcd");
6119 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6121 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6122 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6123 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6124 ok (result == TRUE, "Failed to clear the text.\n");
6125 simulate_typing_characters(hwnd, "one two three");
6126 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6127 ok (result == 0, "expected %d but got %d\n", 0, result);
6128 simulate_typing_characters(hwnd, " four five six");
6129 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6130 ok (result == TRUE, "Failed to undo typed characters.\n");
6131 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6132 result = strcmp(buffer, "one two three");
6133 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6134 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6135 ok (result == TRUE, "Failed to undo typed characters.\n");
6136 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6137 result = strcmp(buffer, "");
6138 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6140 DestroyWindow(hwnd);
6143 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6145 int length;
6147 /* MSDN lied, length is actually the number of bytes. */
6148 length = bytes / sizeof(WCHAR);
6149 switch(code)
6151 case WB_ISDELIMITER:
6152 return text[pos] == 'X';
6153 case WB_LEFT:
6154 case WB_MOVEWORDLEFT:
6155 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6156 return pos-1;
6157 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6158 case WB_LEFTBREAK:
6159 pos--;
6160 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6161 pos--;
6162 return pos;
6163 case WB_RIGHT:
6164 case WB_MOVEWORDRIGHT:
6165 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6166 return pos+1;
6167 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6168 case WB_RIGHTBREAK:
6169 pos++;
6170 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6171 pos++;
6172 return pos;
6173 default:
6174 ok(FALSE, "Unexpected code %d\n", code);
6175 break;
6177 return 0;
6180 static void test_word_movement(void)
6182 HWND hwnd;
6183 int result;
6184 int sel_start, sel_end;
6185 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6187 /* multi-line control inserts CR normally */
6188 hwnd = new_richedit(NULL);
6190 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6191 ok (result == TRUE, "Failed to clear the text.\n");
6192 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6193 /* |one two three */
6195 send_ctrl_key(hwnd, VK_RIGHT);
6196 /* one |two three */
6197 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6198 ok(sel_start == sel_end, "Selection should be empty\n");
6199 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6201 send_ctrl_key(hwnd, VK_RIGHT);
6202 /* one two |three */
6203 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6204 ok(sel_start == sel_end, "Selection should be empty\n");
6205 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6207 send_ctrl_key(hwnd, VK_LEFT);
6208 /* one |two three */
6209 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6210 ok(sel_start == sel_end, "Selection should be empty\n");
6211 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6213 send_ctrl_key(hwnd, VK_LEFT);
6214 /* |one two three */
6215 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6216 ok(sel_start == sel_end, "Selection should be empty\n");
6217 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6219 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6220 /* one two | three */
6221 send_ctrl_key(hwnd, VK_RIGHT);
6222 /* one two |three */
6223 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6224 ok(sel_start == sel_end, "Selection should be empty\n");
6225 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6227 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6228 /* one two th|ree */
6229 send_ctrl_key(hwnd, VK_LEFT);
6230 /* one two |three */
6231 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6232 ok(sel_start == sel_end, "Selection should be empty\n");
6233 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6235 /* Test with a custom word break procedure that uses X as the delimiter. */
6236 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6237 ok (result == TRUE, "Failed to clear the text.\n");
6238 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6239 /* |one twoXthree */
6240 send_ctrl_key(hwnd, VK_RIGHT);
6241 /* one twoX|three */
6242 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6243 ok(sel_start == sel_end, "Selection should be empty\n");
6244 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6246 DestroyWindow(hwnd);
6248 /* Make sure the behaviour is the same with a unicode richedit window,
6249 * and using unicode functions. */
6251 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6252 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6253 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6255 /* Test with a custom word break procedure that uses X as the delimiter. */
6256 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6257 ok (result == TRUE, "Failed to clear the text.\n");
6258 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6259 /* |one twoXthree */
6260 send_ctrl_key(hwnd, VK_RIGHT);
6261 /* one twoX|three */
6262 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6263 ok(sel_start == sel_end, "Selection should be empty\n");
6264 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6266 DestroyWindow(hwnd);
6269 static void test_EM_CHARFROMPOS(void)
6271 HWND hwnd;
6272 int result;
6273 RECT rcClient;
6274 POINTL point;
6275 point.x = 0;
6276 point.y = 40;
6278 /* multi-line control inserts CR normally */
6279 hwnd = new_richedit(NULL);
6280 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6281 (LPARAM)"one two three four five six seven\reight");
6282 ok(result == 1, "Expected 1, got %d\n", result);
6283 GetClientRect(hwnd, &rcClient);
6285 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6286 ok(result == 34, "expected character index of 34 but got %d\n", result);
6288 /* Test with points outside the bounds of the richedit control. */
6289 point.x = -1;
6290 point.y = 40;
6291 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6292 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6294 point.x = 1000;
6295 point.y = 0;
6296 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6297 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6299 point.x = 1000;
6300 point.y = 36;
6301 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6302 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6304 point.x = 1000;
6305 point.y = -1;
6306 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6307 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6309 point.x = 1000;
6310 point.y = rcClient.bottom + 1;
6311 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6312 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6314 point.x = 1000;
6315 point.y = rcClient.bottom;
6316 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6317 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6319 DestroyWindow(hwnd);
6322 static void test_word_wrap(void)
6324 HWND hwnd;
6325 POINTL point = {0, 60}; /* This point must be below the first line */
6326 const char *text = "Must be long enough to test line wrapping";
6327 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6328 int res, pos, lines;
6330 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6331 * when specified on window creation and set later. */
6332 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6333 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6334 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6335 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6336 ok(res, "WM_SETTEXT failed.\n");
6337 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6338 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6339 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6340 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6342 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6343 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6344 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6345 DestroyWindow(hwnd);
6347 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6348 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6349 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6351 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6352 ok(res, "WM_SETTEXT failed.\n");
6353 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6354 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6355 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6356 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6358 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6359 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6360 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6361 DestroyWindow(hwnd);
6363 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6364 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6365 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6366 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6367 ok(res, "WM_SETTEXT failed.\n");
6368 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6369 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6371 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6372 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6373 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6374 DestroyWindow(hwnd);
6376 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6377 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6378 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6379 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6380 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6381 ok(res, "WM_SETTEXT failed.\n");
6382 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6383 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6385 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6386 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6387 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6389 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6390 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6391 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6392 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6393 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6395 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6396 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6397 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6398 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6399 DestroyWindow(hwnd);
6401 /* Test to see if wrapping happens with redraw disabled. */
6402 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6403 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6404 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6405 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6406 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6407 ok(res, "EM_REPLACESEL failed.\n");
6408 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6409 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6410 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6411 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6412 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6414 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6415 DestroyWindow(hwnd);
6418 static void test_autoscroll(void)
6420 HWND hwnd = new_richedit(NULL);
6421 int lines, ret, redraw;
6422 POINT pt;
6424 for (redraw = 0; redraw <= 1; redraw++) {
6425 trace("testing with WM_SETREDRAW=%d\n", redraw);
6426 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6427 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6428 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6429 ok(lines == 8, "%d lines instead of 8\n", lines);
6430 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6431 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6432 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6433 ret = GetWindowLongA(hwnd, GWL_STYLE);
6434 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6436 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6437 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6438 ok(lines == 1, "%d lines instead of 1\n", lines);
6439 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6440 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6441 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6442 ret = GetWindowLongA(hwnd, GWL_STYLE);
6443 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6446 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6447 DestroyWindow(hwnd);
6449 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6450 * auto vertical/horizontal scrolling options. */
6451 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6452 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6453 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6454 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6455 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6456 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6457 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6458 ret = GetWindowLongA(hwnd, GWL_STYLE);
6459 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6460 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6461 DestroyWindow(hwnd);
6463 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6464 WS_POPUP|ES_MULTILINE,
6465 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6466 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6467 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6468 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6469 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6470 ret = GetWindowLongA(hwnd, GWL_STYLE);
6471 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6472 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6473 DestroyWindow(hwnd);
6477 static void test_format_rect(void)
6479 HWND hwnd;
6480 RECT rc, expected, clientRect;
6481 int n;
6482 DWORD options;
6484 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6485 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6486 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6487 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6489 GetClientRect(hwnd, &clientRect);
6491 expected = clientRect;
6492 expected.left += 1;
6493 expected.right -= 1;
6494 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6495 ok(rc.top == expected.top && rc.left == expected.left &&
6496 rc.bottom == expected.bottom && rc.right == expected.right,
6497 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6498 rc.top, rc.left, rc.bottom, rc.right,
6499 expected.top, expected.left, expected.bottom, expected.right);
6501 for (n = -3; n <= 3; n++)
6503 rc = clientRect;
6504 rc.top += n;
6505 rc.left += n;
6506 rc.bottom -= n;
6507 rc.right -= n;
6508 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6510 expected = rc;
6511 expected.top = max(0, rc.top);
6512 expected.left = max(0, rc.left);
6513 expected.bottom = min(clientRect.bottom, rc.bottom);
6514 expected.right = min(clientRect.right, rc.right);
6515 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6516 ok(rc.top == expected.top && rc.left == expected.left &&
6517 rc.bottom == expected.bottom && rc.right == expected.right,
6518 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6519 n, rc.top, rc.left, rc.bottom, rc.right,
6520 expected.top, expected.left, expected.bottom, expected.right);
6523 rc = clientRect;
6524 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6525 expected = clientRect;
6526 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6527 ok(rc.top == expected.top && rc.left == expected.left &&
6528 rc.bottom == expected.bottom && rc.right == expected.right,
6529 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6530 rc.top, rc.left, rc.bottom, rc.right,
6531 expected.top, expected.left, expected.bottom, expected.right);
6533 /* Adding the selectionbar adds the selectionbar width to the left side. */
6534 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6535 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6536 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6537 expected.left += 8; /* selection bar width */
6538 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6539 ok(rc.top == expected.top && rc.left == expected.left &&
6540 rc.bottom == expected.bottom && rc.right == expected.right,
6541 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6542 rc.top, rc.left, rc.bottom, rc.right,
6543 expected.top, expected.left, expected.bottom, expected.right);
6545 rc = clientRect;
6546 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6547 expected = clientRect;
6548 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6549 ok(rc.top == expected.top && rc.left == expected.left &&
6550 rc.bottom == expected.bottom && rc.right == expected.right,
6551 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6552 rc.top, rc.left, rc.bottom, rc.right,
6553 expected.top, expected.left, expected.bottom, expected.right);
6555 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6556 * even if the left side is already 0. */
6557 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6558 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6559 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6560 expected.left -= 8; /* selection bar width */
6561 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6562 ok(rc.top == expected.top && rc.left == expected.left &&
6563 rc.bottom == expected.bottom && rc.right == expected.right,
6564 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6565 rc.top, rc.left, rc.bottom, rc.right,
6566 expected.top, expected.left, expected.bottom, expected.right);
6568 /* Set the absolute value of the formatting rectangle. */
6569 rc = clientRect;
6570 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6571 expected = clientRect;
6572 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6573 ok(rc.top == expected.top && rc.left == expected.left &&
6574 rc.bottom == expected.bottom && rc.right == expected.right,
6575 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6576 n, rc.top, rc.left, rc.bottom, rc.right,
6577 expected.top, expected.left, expected.bottom, expected.right);
6579 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6580 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6581 * tests show that this isn't true. */
6582 rc.top = 15;
6583 rc.left = 15;
6584 rc.bottom = clientRect.bottom - 15;
6585 rc.right = clientRect.right - 15;
6586 expected = rc;
6587 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6588 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6589 ok(rc.top == expected.top && rc.left == expected.left &&
6590 rc.bottom == expected.bottom && rc.right == expected.right,
6591 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6592 rc.top, rc.left, rc.bottom, rc.right,
6593 expected.top, expected.left, expected.bottom, expected.right);
6595 /* For some reason it does not limit the values to the client rect with
6596 * a WPARAM value of 1. */
6597 rc.top = -15;
6598 rc.left = -15;
6599 rc.bottom = clientRect.bottom + 15;
6600 rc.right = clientRect.right + 15;
6601 expected = rc;
6602 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6603 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6604 ok(rc.top == expected.top && rc.left == expected.left &&
6605 rc.bottom == expected.bottom && rc.right == expected.right,
6606 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6607 rc.top, rc.left, rc.bottom, rc.right,
6608 expected.top, expected.left, expected.bottom, expected.right);
6610 /* Reset to default rect and check how the format rect adjusts to window
6611 * resize and how it copes with very small windows */
6612 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6614 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6615 GetClientRect(hwnd, &clientRect);
6617 expected = clientRect;
6618 expected.left += 1;
6619 expected.right -= 1;
6620 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6621 ok(rc.top == expected.top && rc.left == expected.left &&
6622 rc.bottom == expected.bottom && rc.right == expected.right,
6623 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6624 rc.top, rc.left, rc.bottom, rc.right,
6625 expected.top, expected.left, expected.bottom, expected.right);
6627 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6628 GetClientRect(hwnd, &clientRect);
6630 expected = clientRect;
6631 expected.left += 1;
6632 expected.right -= 1;
6633 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6634 ok(rc.top == expected.top && rc.left == expected.left &&
6635 rc.bottom == expected.bottom && rc.right == expected.right,
6636 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6637 rc.top, rc.left, rc.bottom, rc.right,
6638 expected.top, expected.left, expected.bottom, expected.right);
6640 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6641 GetClientRect(hwnd, &clientRect);
6643 expected = clientRect;
6644 expected.left += 1;
6645 expected.right -= 1;
6646 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6647 ok(rc.top == expected.top && rc.left == expected.left &&
6648 rc.bottom == expected.bottom && rc.right == expected.right,
6649 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6650 rc.top, rc.left, rc.bottom, rc.right,
6651 expected.top, expected.left, expected.bottom, expected.right);
6653 DestroyWindow(hwnd);
6655 /* The extended window style affects the formatting rectangle. */
6656 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
6657 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6658 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6659 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6661 GetClientRect(hwnd, &clientRect);
6663 expected = clientRect;
6664 expected.left += 1;
6665 expected.top += 1;
6666 expected.right -= 1;
6667 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6668 ok(rc.top == expected.top && rc.left == expected.left &&
6669 rc.bottom == expected.bottom && rc.right == expected.right,
6670 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6671 rc.top, rc.left, rc.bottom, rc.right,
6672 expected.top, expected.left, expected.bottom, expected.right);
6674 rc = clientRect;
6675 rc.top += 5;
6676 rc.left += 5;
6677 rc.bottom -= 5;
6678 rc.right -= 5;
6679 expected = rc;
6680 expected.top -= 1;
6681 expected.left -= 1;
6682 expected.right += 1;
6683 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6684 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6685 ok(rc.top == expected.top && rc.left == expected.left &&
6686 rc.bottom == expected.bottom && rc.right == expected.right,
6687 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6688 rc.top, rc.left, rc.bottom, rc.right,
6689 expected.top, expected.left, expected.bottom, expected.right);
6691 DestroyWindow(hwnd);
6694 static void test_WM_GETDLGCODE(void)
6696 HWND hwnd;
6697 UINT res, expected;
6698 MSG msg;
6700 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6702 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6703 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6704 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6705 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6706 msg.hwnd = hwnd;
6707 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6708 expected = expected | DLGC_WANTMESSAGE;
6709 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6710 res, expected);
6711 DestroyWindow(hwnd);
6713 msg.message = WM_KEYDOWN;
6714 msg.wParam = VK_RETURN;
6715 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6716 msg.pt.x = 0;
6717 msg.pt.y = 0;
6718 msg.time = GetTickCount();
6720 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6721 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6722 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6723 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6724 msg.hwnd = hwnd;
6725 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6726 expected = expected | DLGC_WANTMESSAGE;
6727 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6728 res, expected);
6729 DestroyWindow(hwnd);
6731 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6732 ES_MULTILINE|WS_POPUP,
6733 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6734 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6735 msg.hwnd = hwnd;
6736 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6737 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6738 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6739 res, expected);
6740 DestroyWindow(hwnd);
6742 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6743 ES_WANTRETURN|WS_POPUP,
6744 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6745 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6746 msg.hwnd = hwnd;
6747 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6748 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6749 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6750 res, expected);
6751 DestroyWindow(hwnd);
6753 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6754 WS_POPUP,
6755 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6756 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6757 msg.hwnd = hwnd;
6758 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6759 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6760 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6761 res, expected);
6762 DestroyWindow(hwnd);
6764 msg.wParam = VK_TAB;
6765 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6767 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6768 ES_MULTILINE|WS_POPUP,
6769 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6770 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6771 msg.hwnd = hwnd;
6772 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6773 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6774 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6775 res, expected);
6776 DestroyWindow(hwnd);
6778 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6779 WS_POPUP,
6780 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6781 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6782 msg.hwnd = hwnd;
6783 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6784 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6785 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6786 res, expected);
6787 DestroyWindow(hwnd);
6789 hold_key(VK_CONTROL);
6791 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6792 ES_MULTILINE|WS_POPUP,
6793 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6794 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6795 msg.hwnd = hwnd;
6796 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6797 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6798 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6799 res, expected);
6800 DestroyWindow(hwnd);
6802 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6803 WS_POPUP,
6804 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6805 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6806 msg.hwnd = hwnd;
6807 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6808 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6809 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6810 res, expected);
6811 DestroyWindow(hwnd);
6813 release_key(VK_CONTROL);
6815 msg.wParam = 'a';
6816 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6818 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6819 ES_MULTILINE|WS_POPUP,
6820 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6821 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6822 msg.hwnd = hwnd;
6823 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6824 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6825 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6826 res, expected);
6827 DestroyWindow(hwnd);
6829 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6830 WS_POPUP,
6831 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6832 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6833 msg.hwnd = hwnd;
6834 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6835 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6836 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6837 res, expected);
6838 DestroyWindow(hwnd);
6840 msg.message = WM_CHAR;
6842 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6843 ES_MULTILINE|WS_POPUP,
6844 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6845 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6846 msg.hwnd = hwnd;
6847 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6848 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6849 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6850 res, expected);
6851 DestroyWindow(hwnd);
6853 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6854 WS_POPUP,
6855 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6856 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6857 msg.hwnd = hwnd;
6858 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6859 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6860 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6861 res, expected);
6862 DestroyWindow(hwnd);
6864 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6865 WS_POPUP|ES_SAVESEL,
6866 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6867 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6868 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
6869 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
6870 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6871 res, expected);
6872 DestroyWindow(hwnd);
6875 static void test_zoom(void)
6877 HWND hwnd;
6878 UINT ret;
6879 RECT rc;
6880 POINT pt;
6881 int numerator, denominator;
6883 hwnd = new_richedit(NULL);
6884 GetClientRect(hwnd, &rc);
6885 pt.x = (rc.right - rc.left) / 2;
6886 pt.y = (rc.bottom - rc.top) / 2;
6887 ClientToScreen(hwnd, &pt);
6889 /* Test initial zoom value */
6890 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6891 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6892 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6893 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6895 /* test scroll wheel */
6896 hold_key(VK_CONTROL);
6897 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6898 MAKELPARAM(pt.x, pt.y));
6899 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6900 release_key(VK_CONTROL);
6902 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6903 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6904 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6905 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6907 /* Test how much the mouse wheel can zoom in and out. */
6908 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
6909 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6911 hold_key(VK_CONTROL);
6912 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6913 MAKELPARAM(pt.x, pt.y));
6914 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6915 release_key(VK_CONTROL);
6917 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6918 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6919 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6920 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6922 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
6923 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6925 hold_key(VK_CONTROL);
6926 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6927 MAKELPARAM(pt.x, pt.y));
6928 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6929 release_key(VK_CONTROL);
6931 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6932 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6933 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6934 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6936 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
6937 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6939 hold_key(VK_CONTROL);
6940 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6941 MAKELPARAM(pt.x, pt.y));
6942 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6943 release_key(VK_CONTROL);
6945 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6946 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6947 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6948 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6950 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
6951 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6953 hold_key(VK_CONTROL);
6954 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6955 MAKELPARAM(pt.x, pt.y));
6956 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6957 release_key(VK_CONTROL);
6959 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6960 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6961 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6962 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6964 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
6965 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
6966 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6968 hold_key(VK_CONTROL);
6969 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6970 MAKELPARAM(pt.x, pt.y));
6971 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6972 release_key(VK_CONTROL);
6974 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6975 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
6976 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6977 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6979 /* Test bounds checking on EM_SETZOOM */
6980 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
6981 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6983 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
6984 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
6986 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
6987 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6989 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6990 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
6991 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
6992 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6994 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
6995 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
6997 /* See if negative numbers are accepted. */
6998 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
6999 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7001 /* See if negative numbers are accepted. */
7002 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7003 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7005 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7006 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7007 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7008 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7010 /* Reset the zoom value */
7011 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7012 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7014 DestroyWindow(hwnd);
7017 struct dialog_mode_messages
7019 int wm_getdefid, wm_close, wm_nextdlgctl;
7022 static struct dialog_mode_messages dm_messages;
7024 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7025 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7026 "got %d\n", wmclose, dm_messages.wm_close); \
7027 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7028 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7029 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7030 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7032 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7034 switch (iMsg)
7036 case DM_GETDEFID:
7037 dm_messages.wm_getdefid++;
7038 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7039 case WM_NEXTDLGCTL:
7040 dm_messages.wm_nextdlgctl++;
7041 break;
7042 case WM_CLOSE:
7043 dm_messages.wm_close++;
7044 break;
7047 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7050 static void test_dialogmode(void)
7052 HWND hwRichEdit, hwParent, hwButton;
7053 MSG msg= {0};
7054 int lcount, r;
7055 WNDCLASSA cls;
7057 cls.style = 0;
7058 cls.lpfnWndProc = dialog_mode_wnd_proc;
7059 cls.cbClsExtra = 0;
7060 cls.cbWndExtra = 0;
7061 cls.hInstance = GetModuleHandleA(0);
7062 cls.hIcon = 0;
7063 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7064 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7065 cls.lpszMenuName = NULL;
7066 cls.lpszClassName = "DialogModeParentClass";
7067 if(!RegisterClassA(&cls)) assert(0);
7069 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7070 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7072 /* Test richedit(ES_MULTILINE) */
7074 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7076 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7077 ok(0 == r, "expected 0, got %d\n", r);
7078 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7079 ok(2 == lcount, "expected 2, got %d\n", lcount);
7081 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7082 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7084 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7085 ok(0 == r, "expected 0, got %d\n", r);
7086 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7087 ok(3 == lcount, "expected 3, got %d\n", lcount);
7089 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7090 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7091 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7092 ok(0 == r, "expected 0, got %d\n", r);
7093 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7094 ok(3 == lcount, "expected 3, got %d\n", lcount);
7096 DestroyWindow(hwRichEdit);
7098 /* Test standalone richedit(ES_MULTILINE) */
7100 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7102 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7103 ok(0 == r, "expected 0, got %d\n", r);
7104 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7105 ok(2 == lcount, "expected 2, got %d\n", lcount);
7107 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7108 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7110 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7111 ok(0 == r, "expected 0, got %d\n", r);
7112 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7113 ok(2 == lcount, "expected 2, got %d\n", lcount);
7115 DestroyWindow(hwRichEdit);
7117 /* Check a destination for messages */
7119 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7121 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7122 SetParent( hwRichEdit, NULL);
7124 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7125 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7127 memset(&dm_messages, 0, sizeof(dm_messages));
7128 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7129 ok(0 == r, "expected 0, got %d\n", r);
7130 test_dm_messages(0, 1, 0);
7132 memset(&dm_messages, 0, sizeof(dm_messages));
7133 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7134 ok(0 == r, "expected 0, got %d\n", r);
7135 test_dm_messages(0, 0, 1);
7137 DestroyWindow(hwRichEdit);
7139 /* Check messages from richedit(ES_MULTILINE) */
7141 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7143 memset(&dm_messages, 0, sizeof(dm_messages));
7144 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7145 ok(0 == r, "expected 0, got %d\n", r);
7146 test_dm_messages(0, 0, 0);
7148 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7149 ok(2 == lcount, "expected 2, got %d\n", lcount);
7151 memset(&dm_messages, 0, sizeof(dm_messages));
7152 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7153 ok(0 == r, "expected 0, got %d\n", r);
7154 test_dm_messages(0, 0, 0);
7156 memset(&dm_messages, 0, sizeof(dm_messages));
7157 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7158 ok(0 == r, "expected 0, got %d\n", r);
7159 test_dm_messages(0, 0, 0);
7161 memset(&dm_messages, 0, sizeof(dm_messages));
7162 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7163 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7164 test_dm_messages(0, 0, 0);
7166 memset(&dm_messages, 0, sizeof(dm_messages));
7167 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7168 ok(0 == r, "expected 0, got %d\n", r);
7169 test_dm_messages(0, 1, 0);
7171 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7172 ok(2 == lcount, "expected 2, got %d\n", lcount);
7174 memset(&dm_messages, 0, sizeof(dm_messages));
7175 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7176 ok(0 == r, "expected 0, got %d\n", r);
7177 test_dm_messages(0, 0, 0);
7179 memset(&dm_messages, 0, sizeof(dm_messages));
7180 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7181 ok(0 == r, "expected 0, got %d\n", r);
7182 test_dm_messages(0, 0, 1);
7184 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7185 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7186 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7188 memset(&dm_messages, 0, sizeof(dm_messages));
7189 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7190 ok(0 == r, "expected 0, got %d\n", r);
7191 test_dm_messages(0, 1, 1);
7193 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7194 ok(2 == lcount, "expected 2, got %d\n", lcount);
7196 DestroyWindow(hwButton);
7197 DestroyWindow(hwRichEdit);
7199 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7201 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7203 memset(&dm_messages, 0, sizeof(dm_messages));
7204 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7205 ok(0 == r, "expected 0, got %d\n", r);
7206 test_dm_messages(0, 0, 0);
7208 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7209 ok(2 == lcount, "expected 2, got %d\n", lcount);
7211 memset(&dm_messages, 0, sizeof(dm_messages));
7212 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7213 ok(0 == r, "expected 0, got %d\n", r);
7214 test_dm_messages(0, 0, 0);
7216 memset(&dm_messages, 0, sizeof(dm_messages));
7217 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7218 ok(0 == r, "expected 0, got %d\n", r);
7219 test_dm_messages(0, 0, 0);
7221 memset(&dm_messages, 0, sizeof(dm_messages));
7222 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7223 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7224 test_dm_messages(0, 0, 0);
7226 memset(&dm_messages, 0, sizeof(dm_messages));
7227 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7228 ok(0 == r, "expected 0, got %d\n", r);
7229 test_dm_messages(0, 0, 0);
7231 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7232 ok(3 == lcount, "expected 3, got %d\n", lcount);
7234 memset(&dm_messages, 0, sizeof(dm_messages));
7235 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7236 ok(0 == r, "expected 0, got %d\n", r);
7237 test_dm_messages(0, 0, 0);
7239 memset(&dm_messages, 0, sizeof(dm_messages));
7240 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7241 ok(0 == r, "expected 0, got %d\n", r);
7242 test_dm_messages(0, 0, 1);
7244 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7245 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7246 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7248 memset(&dm_messages, 0, sizeof(dm_messages));
7249 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7250 ok(0 == r, "expected 0, got %d\n", r);
7251 test_dm_messages(0, 0, 0);
7253 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7254 ok(4 == lcount, "expected 4, got %d\n", lcount);
7256 DestroyWindow(hwButton);
7257 DestroyWindow(hwRichEdit);
7259 /* Check messages from richedit(0) */
7261 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7263 memset(&dm_messages, 0, sizeof(dm_messages));
7264 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7265 ok(0 == r, "expected 0, got %d\n", r);
7266 test_dm_messages(0, 0, 0);
7268 memset(&dm_messages, 0, sizeof(dm_messages));
7269 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7270 ok(0 == r, "expected 0, got %d\n", r);
7271 test_dm_messages(0, 0, 0);
7273 memset(&dm_messages, 0, sizeof(dm_messages));
7274 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7275 ok(0 == r, "expected 0, got %d\n", r);
7276 test_dm_messages(0, 0, 0);
7278 memset(&dm_messages, 0, sizeof(dm_messages));
7279 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7280 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7281 test_dm_messages(0, 0, 0);
7283 memset(&dm_messages, 0, sizeof(dm_messages));
7284 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7285 ok(0 == r, "expected 0, got %d\n", r);
7286 test_dm_messages(0, 1, 0);
7288 memset(&dm_messages, 0, sizeof(dm_messages));
7289 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7290 ok(0 == r, "expected 0, got %d\n", r);
7291 test_dm_messages(0, 0, 0);
7293 memset(&dm_messages, 0, sizeof(dm_messages));
7294 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7295 ok(0 == r, "expected 0, got %d\n", r);
7296 test_dm_messages(0, 0, 1);
7298 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7299 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7300 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7302 memset(&dm_messages, 0, sizeof(dm_messages));
7303 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7304 ok(0 == r, "expected 0, got %d\n", r);
7305 test_dm_messages(0, 1, 1);
7307 DestroyWindow(hwRichEdit);
7309 /* Check messages from richedit(ES_WANTRETURN) */
7311 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7313 memset(&dm_messages, 0, sizeof(dm_messages));
7314 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7315 ok(0 == r, "expected 0, got %d\n", r);
7316 test_dm_messages(0, 0, 0);
7318 memset(&dm_messages, 0, sizeof(dm_messages));
7319 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7320 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7321 test_dm_messages(0, 0, 0);
7323 memset(&dm_messages, 0, sizeof(dm_messages));
7324 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7325 ok(0 == r, "expected 0, got %d\n", r);
7326 test_dm_messages(0, 0, 0);
7328 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7329 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7330 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7332 memset(&dm_messages, 0, sizeof(dm_messages));
7333 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7334 ok(0 == r, "expected 0, got %d\n", r);
7335 test_dm_messages(0, 0, 0);
7337 DestroyWindow(hwRichEdit);
7338 DestroyWindow(hwParent);
7341 static void test_EM_FINDWORDBREAK_W(void)
7343 static const struct {
7344 WCHAR c;
7345 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7346 } delimiter_tests[] = {
7347 {0x0a, FALSE}, /* newline */
7348 {0x0b, FALSE}, /* vertical tab */
7349 {0x0c, FALSE}, /* form feed */
7350 {0x0d, FALSE}, /* carriage return */
7351 {0x20, TRUE}, /* space */
7352 {0x61, FALSE}, /* capital letter a */
7353 {0xa0, FALSE}, /* no-break space */
7354 {0x2000, FALSE}, /* en quad */
7355 {0x3000, FALSE}, /* Ideographic space */
7356 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7357 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7358 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7359 {0xac00, FALSE}, /* Hangul character GA*/
7360 {0xd7af, FALSE}, /* End of Hangul character chart */
7361 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7362 {0xff20, FALSE}, /* fullwidth commercial @ */
7363 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7365 int i;
7366 HWND hwndRichEdit = new_richeditW(NULL);
7367 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7368 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7370 WCHAR wbuf[2];
7371 int result;
7373 wbuf[0] = delimiter_tests[i].c;
7374 wbuf[1] = 0;
7375 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7376 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7377 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7378 todo_wine
7379 ok(result == delimiter_tests[i].isdelimiter,
7380 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7381 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7382 else
7383 ok(result == delimiter_tests[i].isdelimiter,
7384 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7385 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7387 DestroyWindow(hwndRichEdit);
7390 static void test_EM_FINDWORDBREAK_A(void)
7392 static const struct {
7393 WCHAR c;
7394 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7395 } delimiter_tests[] = {
7396 {0x0a, FALSE}, /* newline */
7397 {0x0b, FALSE}, /* vertical tab */
7398 {0x0c, FALSE}, /* form feed */
7399 {0x0d, FALSE}, /* carriage return */
7400 {0x20, TRUE}, /* space */
7401 {0x61, FALSE}, /* capital letter a */
7403 int i;
7404 HWND hwndRichEdit = new_richedit(NULL);
7406 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7407 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7409 int result;
7410 char buf[2];
7411 buf[0] = delimiter_tests[i].c;
7412 buf[1] = 0;
7413 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7414 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7415 if (buf[0] == 0x20)
7416 todo_wine
7417 ok(result == delimiter_tests[i].isdelimiter,
7418 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7419 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7420 else
7421 ok(result == delimiter_tests[i].isdelimiter,
7422 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7423 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7425 DestroyWindow(hwndRichEdit);
7429 * This test attempts to show the effect of enter on a richedit
7430 * control v1.0 inserts CRLF whereas for higher versions it only
7431 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7432 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7433 * does for higher. The same test is cloned in riched32 and riched20.
7435 static void test_enter(void)
7437 static const struct {
7438 const char *initialtext;
7439 const int cursor;
7440 const char *expectedwmtext;
7441 const char *expectedemtext;
7442 const char *expectedemtextcrlf;
7443 } testenteritems[] = {
7444 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7445 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7446 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7447 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7448 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7451 char expectedbuf[1024];
7452 char resultbuf[1024];
7453 HWND hwndRichEdit = new_richedit(NULL);
7454 UINT i,j;
7456 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7458 char buf[1024] = {0};
7459 LRESULT result;
7460 GETTEXTEX getText;
7461 const char *expected;
7463 /* Set the text to the initial text */
7464 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7465 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7467 /* Send Enter */
7468 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7469 simulate_typing_characters(hwndRichEdit, "\r");
7471 /* 1. Retrieve with WM_GETTEXT */
7472 buf[0] = 0x00;
7473 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7474 expected = testenteritems[i].expectedwmtext;
7476 resultbuf[0]=0x00;
7477 for (j = 0; j < (UINT)result; j++)
7478 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7479 expectedbuf[0] = '\0';
7480 for (j = 0; j < strlen(expected); j++)
7481 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7483 result = strcmp(expected, buf);
7484 ok (result == 0,
7485 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7486 i, resultbuf, expectedbuf);
7488 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7489 getText.cb = sizeof(buf);
7490 getText.flags = GT_DEFAULT;
7491 getText.codepage = CP_ACP;
7492 getText.lpDefaultChar = NULL;
7493 getText.lpUsedDefChar = NULL;
7494 buf[0] = 0x00;
7495 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7496 expected = testenteritems[i].expectedemtext;
7498 resultbuf[0]=0x00;
7499 for (j = 0; j < (UINT)result; j++)
7500 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7501 expectedbuf[0] = '\0';
7502 for (j = 0; j < strlen(expected); j++)
7503 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7505 result = strcmp(expected, buf);
7506 ok (result == 0,
7507 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7508 i, resultbuf, expectedbuf);
7510 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7511 getText.cb = sizeof(buf);
7512 getText.flags = GT_USECRLF;
7513 getText.codepage = CP_ACP;
7514 getText.lpDefaultChar = NULL;
7515 getText.lpUsedDefChar = NULL;
7516 buf[0] = 0x00;
7517 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7518 expected = testenteritems[i].expectedemtextcrlf;
7520 resultbuf[0]=0x00;
7521 for (j = 0; j < (UINT)result; j++)
7522 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7523 expectedbuf[0] = '\0';
7524 for (j = 0; j < strlen(expected); j++)
7525 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7527 result = strcmp(expected, buf);
7528 ok (result == 0,
7529 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7530 i, resultbuf, expectedbuf);
7533 DestroyWindow(hwndRichEdit);
7536 static void test_WM_CREATE(void)
7538 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7539 static const char title[] = "line1\nline2";
7541 HWND rich_edit;
7542 LRESULT res;
7543 char buf[64];
7544 int len;
7546 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7547 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7548 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7550 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7551 ok(len == 5, "GetWindowText returned %d\n", len);
7552 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7554 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7555 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7557 DestroyWindow(rich_edit);
7559 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7560 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7561 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7563 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7564 ok(len == 12, "GetWindowText returned %d\n", len);
7565 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7567 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7568 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7570 DestroyWindow(rich_edit);
7573 /*******************************************************************
7574 * Test that after deleting all of the text, the first paragraph
7575 * format reverts to the default.
7577 static void test_reset_default_para_fmt( void )
7579 HWND richedit = new_richeditW( NULL );
7580 PARAFORMAT2 fmt;
7581 WORD def_align, new_align;
7583 memset( &fmt, 0, sizeof(fmt) );
7584 fmt.cbSize = sizeof(PARAFORMAT2);
7585 fmt.dwMask = -1;
7586 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7587 def_align = fmt.wAlignment;
7588 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7590 simulate_typing_characters( richedit, "123" );
7592 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7593 fmt.dwMask = PFM_ALIGNMENT;
7594 fmt.wAlignment = new_align;
7595 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7597 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7598 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7600 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7601 SendMessageA( richedit, WM_CUT, 0, 0 );
7603 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7604 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7606 DestroyWindow( richedit );
7609 static void test_EM_SETREADONLY(void)
7611 HWND richedit = new_richeditW(NULL);
7612 DWORD dwStyle;
7613 LRESULT res;
7615 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
7616 ok(res == 1, "EM_SETREADONLY\n");
7617 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7618 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
7620 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
7621 ok(res == 1, "EM_SETREADONLY\n");
7622 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7623 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
7625 DestroyWindow(richedit);
7628 START_TEST( editor )
7630 BOOL ret;
7631 /* Must explicitly LoadLibrary(). The test has no references to functions in
7632 * RICHED20.DLL, so the linker doesn't actually link to it. */
7633 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7634 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7636 test_WM_CHAR();
7637 test_EM_FINDTEXT(FALSE);
7638 test_EM_FINDTEXT(TRUE);
7639 test_EM_GETLINE();
7640 test_EM_POSFROMCHAR();
7641 test_EM_SCROLLCARET();
7642 test_EM_SCROLL();
7643 test_scrollbar_visibility();
7644 test_WM_SETTEXT();
7645 test_EM_LINELENGTH();
7646 test_EM_SETCHARFORMAT();
7647 test_EM_SETTEXTMODE();
7648 test_TM_PLAINTEXT();
7649 test_EM_SETOPTIONS();
7650 test_WM_GETTEXT();
7651 test_EM_GETTEXTRANGE();
7652 test_EM_GETSELTEXT();
7653 test_EM_SETUNDOLIMIT();
7654 test_ES_PASSWORD();
7655 test_EM_SETTEXTEX();
7656 test_EM_LIMITTEXT();
7657 test_EM_EXLIMITTEXT();
7658 test_EM_GETLIMITTEXT();
7659 test_WM_SETFONT();
7660 test_EM_GETMODIFY();
7661 test_EM_EXSETSEL();
7662 test_WM_PASTE();
7663 test_EM_STREAMIN();
7664 test_EM_STREAMOUT();
7665 test_EM_STREAMOUT_FONTTBL();
7666 test_EM_StreamIn_Undo();
7667 test_EM_FORMATRANGE();
7668 test_unicode_conversions();
7669 test_EM_GETTEXTLENGTHEX();
7670 test_EM_REPLACESEL(1);
7671 test_EM_REPLACESEL(0);
7672 test_WM_NOTIFY();
7673 test_EN_LINK();
7674 test_EM_AUTOURLDETECT();
7675 test_eventMask();
7676 test_undo_coalescing();
7677 test_word_movement();
7678 test_EM_CHARFROMPOS();
7679 test_SETPARAFORMAT();
7680 test_word_wrap();
7681 test_autoscroll();
7682 test_format_rect();
7683 test_WM_GETDLGCODE();
7684 test_zoom();
7685 test_dialogmode();
7686 test_EM_FINDWORDBREAK_W();
7687 test_EM_FINDWORDBREAK_A();
7688 test_enter();
7689 test_WM_CREATE();
7690 test_reset_default_para_fmt();
7691 test_EM_SETREADONLY();
7693 /* Set the environment variable WINETEST_RICHED20 to keep windows
7694 * responsive and open for 30 seconds. This is useful for debugging.
7696 if (getenv( "WINETEST_RICHED20" )) {
7697 keep_responsive(30);
7700 OleFlushClipboard();
7701 ret = FreeLibrary(hmoduleRichEdit);
7702 ok(ret, "error: %d\n", (int) GetLastError());