server: Fixup input windows on thread_detach.
[wine/multimedia.git] / dlls / riched20 / tests / editor.c
blob09437ef894801f61625ea6791ddbfe90ca3c6189
1 /*
2 * Unit test suite for rich edit control
4 * Copyright 2006 Google (Thomas Kho)
5 * Copyright 2007 Matt Finnicum
6 * Copyright 2007 Dmitry Timoshkov
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <winnls.h>
31 #include <ole2.h>
32 #include <richedit.h>
33 #include <commdlg.h>
34 #include <time.h>
35 #include <wine/test.h>
37 #define ID_RICHEDITTESTDBUTTON 0x123
39 static CHAR string1[MAX_PATH], string2[MAX_PATH], string3[MAX_PATH];
41 #define ok_w3(format, szString1, szString2, szString3) \
42 WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
43 WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
44 WideCharToMultiByte(CP_ACP, 0, szString3, -1, string3, MAX_PATH, NULL, NULL); \
45 ok(!lstrcmpW(szString3, szString1) || !lstrcmpW(szString3, szString2), \
46 format, string1, string2, string3);
48 static HMODULE hmoduleRichEdit;
50 static HWND new_window(LPCSTR lpClassName, DWORD dwStyle, HWND parent) {
51 HWND hwnd;
52 hwnd = CreateWindowA(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
53 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
54 hmoduleRichEdit, NULL);
55 ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError());
56 return hwnd;
59 static HWND new_windowW(LPCWSTR lpClassName, DWORD dwStyle, HWND parent) {
60 HWND hwnd;
61 hwnd = CreateWindowW(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL
62 |WS_VISIBLE, 0, 0, 200, 60, parent, NULL,
63 hmoduleRichEdit, NULL);
64 ok(hwnd != NULL, "class: %s, error: %d\n", wine_dbgstr_w(lpClassName), (int) GetLastError());
65 return hwnd;
68 static HWND new_richedit(HWND parent) {
69 return new_window(RICHEDIT_CLASS20A, ES_MULTILINE, parent);
72 static HWND new_richeditW(HWND parent) {
73 return new_windowW(RICHEDIT_CLASS20W, ES_MULTILINE, parent);
76 /* Keeps the window reponsive for the deley_time in seconds.
77 * This is useful for debugging a test to see what is happening. */
78 static void keep_responsive(time_t delay_time)
80 MSG msg;
81 time_t end;
83 /* The message pump uses PeekMessage() to empty the queue and then
84 * sleeps for 50ms before retrying the queue. */
85 end = time(NULL) + delay_time;
86 while (time(NULL) < end) {
87 if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
88 TranslateMessage(&msg);
89 DispatchMessageA(&msg);
90 } else {
91 Sleep(50);
96 static void simulate_typing_characters(HWND hwnd, const char* szChars)
98 int ret;
100 while (*szChars != '\0') {
101 SendMessageA(hwnd, WM_KEYDOWN, *szChars, 1);
102 ret = SendMessageA(hwnd, WM_CHAR, *szChars, 1);
103 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *szChars, ret);
104 SendMessageA(hwnd, WM_KEYUP, *szChars, 1);
105 szChars++;
109 static BOOL hold_key(int vk)
111 BYTE key_state[256];
112 BOOL result;
114 result = GetKeyboardState(key_state);
115 ok(result, "GetKeyboardState failed.\n");
116 if (!result) return FALSE;
117 key_state[vk] |= 0x80;
118 result = SetKeyboardState(key_state);
119 ok(result, "SetKeyboardState failed.\n");
120 return result != 0;
123 static BOOL release_key(int vk)
125 BYTE key_state[256];
126 BOOL result;
128 result = GetKeyboardState(key_state);
129 ok(result, "GetKeyboardState failed.\n");
130 if (!result) return FALSE;
131 key_state[vk] &= ~0x80;
132 result = SetKeyboardState(key_state);
133 ok(result, "SetKeyboardState failed.\n");
134 return result != 0;
137 static const char haystack[] = "WINEWine wineWine wine WineWine";
138 /* ^0 ^10 ^20 ^30 */
140 struct find_s {
141 int start;
142 int end;
143 const char *needle;
144 int flags;
145 int expected_loc;
149 static struct find_s find_tests[] = {
150 /* Find in empty text */
151 {0, -1, "foo", FR_DOWN, -1},
152 {0, -1, "foo", 0, -1},
153 {0, -1, "", FR_DOWN, -1},
154 {20, 5, "foo", FR_DOWN, -1},
155 {5, 20, "foo", FR_DOWN, -1}
158 static struct find_s find_tests2[] = {
159 /* No-result find */
160 {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1},
161 {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1},
163 /* Subsequent finds */
164 {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4},
165 {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13},
166 {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
167 {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
169 /* Find backwards */
170 {19, 20, "Wine", FR_MATCHCASE, 13},
171 {10, 20, "Wine", FR_MATCHCASE, 4},
172 {20, 10, "Wine", FR_MATCHCASE, 13},
174 /* Case-insensitive */
175 {1, 31, "wInE", FR_DOWN, 4},
176 {1, 31, "Wine", FR_DOWN, 4},
178 /* High-to-low ranges */
179 {20, 5, "Wine", FR_DOWN, -1},
180 {2, 1, "Wine", FR_DOWN, -1},
181 {30, 29, "Wine", FR_DOWN, -1},
182 {20, 5, "Wine", 0, 13},
184 /* Find nothing */
185 {5, 10, "", FR_DOWN, -1},
186 {10, 5, "", FR_DOWN, -1},
187 {0, -1, "", FR_DOWN, -1},
188 {10, 5, "", 0, -1},
190 /* Whole-word search */
191 {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
192 {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1},
193 {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18},
194 {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0},
195 {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23},
196 {11, -1, "winewine", FR_WHOLEWORD, 0},
197 {31, -1, "winewine", FR_WHOLEWORD, 23},
199 /* Bad ranges */
200 {5, 200, "XXX", FR_DOWN, -1},
201 {-20, 20, "Wine", FR_DOWN, -1},
202 {-20, 20, "Wine", FR_DOWN, -1},
203 {-15, -20, "Wine", FR_DOWN, -1},
204 {1<<12, 1<<13, "Wine", FR_DOWN, -1},
206 /* Check the case noted in bug 4479 where matches at end aren't recognized */
207 {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23},
208 {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27},
209 {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27},
210 {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
211 {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23},
213 /* The backwards case of bug 4479; bounds look right
214 * Fails because backward find is wrong */
215 {19, 20, "WINE", FR_MATCHCASE, 0},
216 {0, 20, "WINE", FR_MATCHCASE, -1},
218 {0, -1, "wineWine wine", 0, -1},
221 static WCHAR *atowstr(const char *str)
223 WCHAR *ret;
224 DWORD len;
225 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
226 ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
227 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
228 return ret;
231 static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id, BOOL unicode)
233 int findloc;
235 if(unicode){
236 FINDTEXTW ftw;
237 memset(&ftw, 0, sizeof(ftw));
238 ftw.chrg.cpMin = f->start;
239 ftw.chrg.cpMax = f->end;
240 ftw.lpstrText = atowstr(f->needle);
242 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&ftw);
243 ok(findloc == f->expected_loc,
244 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
245 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
247 findloc = SendMessageA(hwnd, EM_FINDTEXTW, f->flags, (LPARAM)&ftw);
248 ok(findloc == f->expected_loc,
249 "EM_FINDTEXTW(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
250 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
252 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
253 }else{
254 FINDTEXTA fta;
255 memset(&fta, 0, sizeof(fta));
256 fta.chrg.cpMin = f->start;
257 fta.chrg.cpMax = f->end;
258 fta.lpstrText = f->needle;
260 findloc = SendMessageA(hwnd, EM_FINDTEXT, f->flags, (LPARAM)&fta);
261 ok(findloc == f->expected_loc,
262 "EM_FINDTEXT(%s,%d,%u) '%s' in range(%d,%d), flags %08x, got start at %d, expected %d\n",
263 name, id, unicode, f->needle, f->start, f->end, f->flags, findloc, f->expected_loc);
267 static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f,
268 int id, BOOL unicode)
270 int findloc;
271 int expected_end_loc;
273 if(unicode){
274 FINDTEXTEXW ftw;
275 memset(&ftw, 0, sizeof(ftw));
276 ftw.chrg.cpMin = f->start;
277 ftw.chrg.cpMax = f->end;
278 ftw.lpstrText = atowstr(f->needle);
279 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&ftw);
280 ok(findloc == f->expected_loc,
281 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
282 name, id, f->needle, f->start, f->end, f->flags, findloc);
283 ok(ftw.chrgText.cpMin == f->expected_loc,
284 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
285 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMin);
286 expected_end_loc = ((f->expected_loc == -1) ? -1
287 : f->expected_loc + strlen(f->needle));
288 ok(ftw.chrgText.cpMax == expected_end_loc,
289 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
290 name, id, f->needle, f->start, f->end, f->flags, ftw.chrgText.cpMax, expected_end_loc);
291 HeapFree(GetProcessHeap(), 0, (void*)ftw.lpstrText);
292 }else{
293 FINDTEXTEXA fta;
294 memset(&fta, 0, sizeof(fta));
295 fta.chrg.cpMin = f->start;
296 fta.chrg.cpMax = f->end;
297 fta.lpstrText = f->needle;
298 findloc = SendMessageA(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM)&fta);
299 ok(findloc == f->expected_loc,
300 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
301 name, id, f->needle, f->start, f->end, f->flags, findloc);
302 ok(fta.chrgText.cpMin == f->expected_loc,
303 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n",
304 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMin);
305 expected_end_loc = ((f->expected_loc == -1) ? -1
306 : f->expected_loc + strlen(f->needle));
307 ok(fta.chrgText.cpMax == expected_end_loc,
308 "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d, expected %d\n",
309 name, id, f->needle, f->start, f->end, f->flags, fta.chrgText.cpMax, expected_end_loc);
313 static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find,
314 int num_tests, BOOL unicode)
316 int i;
318 for (i = 0; i < num_tests; i++) {
319 check_EM_FINDTEXT(hwnd, name, &find[i], i, unicode);
320 check_EM_FINDTEXTEX(hwnd, name, &find[i], i, unicode);
324 static void test_EM_FINDTEXT(BOOL unicode)
326 HWND hwndRichEdit;
327 CHARFORMAT2A cf2;
329 if(unicode)
330 hwndRichEdit = new_richeditW(NULL);
331 else
332 hwndRichEdit = new_richedit(NULL);
334 /* Empty rich edit control */
335 run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests,
336 sizeof(find_tests)/sizeof(struct find_s), unicode);
338 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)haystack);
340 /* Haystack text */
341 run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2,
342 sizeof(find_tests2)/sizeof(struct find_s), unicode);
344 /* Setting a format on an arbitrary range should have no effect in search
345 results. This tests correct offset reporting across runs. */
346 cf2.cbSize = sizeof(CHARFORMAT2A);
347 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
348 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
349 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
350 SendMessageA(hwndRichEdit, EM_SETSEL, 6, 20);
351 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
353 /* Haystack text, again */
354 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bis", find_tests2,
355 sizeof(find_tests2)/sizeof(struct find_s), unicode);
357 /* Yet another range */
358 cf2.dwMask = CFM_BOLD | cf2.dwMask;
359 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
360 SendMessageA(hwndRichEdit, EM_SETSEL, 11, 15);
361 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
363 /* Haystack text, again */
364 run_tests_EM_FINDTEXT(hwndRichEdit, "2-bisbis", find_tests2,
365 sizeof(find_tests2)/sizeof(struct find_s), unicode);
367 DestroyWindow(hwndRichEdit);
370 static const struct getline_s {
371 int line;
372 size_t buffer_len;
373 const char *text;
374 } gl[] = {
375 {0, 10, "foo bar\r"},
376 {1, 10, "\r"},
377 {2, 10, "bar\r"},
378 {3, 10, "\r"},
380 /* Buffer smaller than line length */
381 {0, 2, "foo bar\r"},
382 {0, 1, "foo bar\r"},
383 {0, 0, "foo bar\r"}
386 static void test_EM_GETLINE(void)
388 int i;
389 HWND hwndRichEdit = new_richedit(NULL);
390 static const int nBuf = 1024;
391 char dest[1024], origdest[1024];
392 const char text[] = "foo bar\n"
393 "\n"
394 "bar\n";
396 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
398 memset(origdest, 0xBB, nBuf);
399 for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++)
401 int nCopied;
402 int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text));
403 int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text));
404 memset(dest, 0xBB, nBuf);
405 *(WORD *) dest = gl[i].buffer_len;
407 /* EM_GETLINE appends a "\r\0" to the end of the line
408 * nCopied counts up to and including the '\r' */
409 nCopied = SendMessageA(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM)dest);
410 ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied,
411 expected_nCopied);
412 /* two special cases since a parameter is passed via dest */
413 if (gl[i].buffer_len == 0)
414 ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2),
415 "buffer_len=0\n");
416 else if (gl[i].buffer_len == 1)
417 ok(dest[0] == gl[i].text[0] && !dest[1] &&
418 !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n");
419 else
421 /* Prepare hex strings of buffers to dump on failure. */
422 char expectedbuf[1024];
423 char resultbuf[1024];
424 int j;
425 resultbuf[0] = '\0';
426 for (j = 0; j < 32; j++)
427 sprintf(resultbuf+strlen(resultbuf), "%02x", dest[j] & 0xFF);
428 expectedbuf[0] = '\0';
429 for (j = 0; j < expected_bytes_written; j++) /* Written bytes */
430 sprintf(expectedbuf+strlen(expectedbuf), "%02x", gl[i].text[j] & 0xFF);
431 for (; j < gl[i].buffer_len; j++) /* Ignored bytes */
432 sprintf(expectedbuf+strlen(expectedbuf), "??");
433 for (; j < 32; j++) /* Bytes after declared buffer size */
434 sprintf(expectedbuf+strlen(expectedbuf), "%02x", origdest[j] & 0xFF);
436 /* Test the part of the buffer that is expected to be written according
437 * to the MSDN documentation fo EM_GETLINE, which does not state that
438 * a NULL terminating character will be added unless no text is copied.
440 * Windows NT does not append a NULL terminating character, but
441 * Windows 2000 and up do append a NULL terminating character if there
442 * is space in the buffer. The test will ignore this difference. */
443 ok(!strncmp(dest, gl[i].text, expected_bytes_written),
444 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
445 i, expected_bytes_written, expectedbuf, resultbuf);
446 /* Test the part of the buffer after the declared length to make sure
447 * there are no buffer overruns. */
448 ok(!strncmp(dest + gl[i].buffer_len, origdest + gl[i].buffer_len,
449 nBuf - gl[i].buffer_len),
450 "%d: expected_bytes_written=%d\n" "expected=0x%s\n" "but got= 0x%s\n",
451 i, expected_bytes_written, expectedbuf, resultbuf);
455 DestroyWindow(hwndRichEdit);
458 static void test_EM_LINELENGTH(void)
460 HWND hwndRichEdit = new_richedit(NULL);
461 const char * text =
462 "richedit1\r"
463 "richedit1\n"
464 "richedit1\r\n"
465 "richedit1";
466 int offset_test[10][2] = {
467 {0, 9},
468 {5, 9},
469 {10, 9},
470 {15, 9},
471 {20, 9},
472 {25, 9},
473 {30, 9},
474 {35, 9},
475 {40, 0},
476 {45, 0},
478 int i;
479 LRESULT result;
481 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
483 for (i = 0; i < 10; i++) {
484 result = SendMessageA(hwndRichEdit, EM_LINELENGTH, offset_test[i][0], 0);
485 ok(result == offset_test[i][1], "Length of line at offset %d is %ld, expected %d\n",
486 offset_test[i][0], result, offset_test[i][1]);
489 DestroyWindow(hwndRichEdit);
492 static int get_scroll_pos_y(HWND hwnd)
494 POINT p = {-1, -1};
495 SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&p);
496 ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y);
497 return p.y;
500 static void move_cursor(HWND hwnd, LONG charindex)
502 CHARRANGE cr;
503 cr.cpMax = charindex;
504 cr.cpMin = charindex;
505 SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
508 static void line_scroll(HWND hwnd, int amount)
510 SendMessageA(hwnd, EM_LINESCROLL, 0, amount);
513 static void test_EM_SCROLLCARET(void)
515 int prevY, curY;
516 const char text[] = "aa\n"
517 "this is a long line of text that should be longer than the "
518 "control's width\n"
519 "cc\n"
520 "dd\n"
521 "ee\n"
522 "ff\n"
523 "gg\n"
524 "hh\n";
525 /* The richedit window height needs to be large enough vertically to fit in
526 * more than two lines of text, so the new_richedit function can't be used
527 * since a height of 60 was not large enough on some systems.
529 HWND hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
530 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
531 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
532 ok(hwndRichEdit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
534 /* Can't verify this */
535 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
537 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
539 /* Caret above visible window */
540 line_scroll(hwndRichEdit, 3);
541 prevY = get_scroll_pos_y(hwndRichEdit);
542 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
543 curY = get_scroll_pos_y(hwndRichEdit);
544 ok(prevY != curY, "%d == %d\n", prevY, curY);
546 /* Caret below visible window */
547 move_cursor(hwndRichEdit, sizeof(text) - 1);
548 line_scroll(hwndRichEdit, -3);
549 prevY = get_scroll_pos_y(hwndRichEdit);
550 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
551 curY = get_scroll_pos_y(hwndRichEdit);
552 ok(prevY != curY, "%d == %d\n", prevY, curY);
554 /* Caret in visible window */
555 move_cursor(hwndRichEdit, sizeof(text) - 2);
556 prevY = get_scroll_pos_y(hwndRichEdit);
557 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
558 curY = get_scroll_pos_y(hwndRichEdit);
559 ok(prevY == curY, "%d != %d\n", prevY, curY);
561 /* Caret still in visible window */
562 line_scroll(hwndRichEdit, -1);
563 prevY = get_scroll_pos_y(hwndRichEdit);
564 SendMessageA(hwndRichEdit, EM_SCROLLCARET, 0, 0);
565 curY = get_scroll_pos_y(hwndRichEdit);
566 ok(prevY == curY, "%d != %d\n", prevY, curY);
568 DestroyWindow(hwndRichEdit);
571 static void test_EM_POSFROMCHAR(void)
573 HWND hwndRichEdit = new_richedit(NULL);
574 int i, expected;
575 LRESULT result;
576 unsigned int height = 0;
577 int xpos = 0;
578 POINTL pt;
579 LOCALESIGNATURE sig;
580 BOOL rtl;
581 static const char text[] = "aa\n"
582 "this is a long line of text that should be longer than the "
583 "control's width\n"
584 "cc\n"
585 "dd\n"
586 "ee\n"
587 "ff\n"
588 "gg\n"
589 "hh\n";
591 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
592 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
593 (sig.lsUsb[3] & 0x08000000) != 0);
595 /* Fill the control to lines to ensure that most of them are offscreen */
596 for (i = 0; i < 50; i++)
598 /* Do not modify the string; it is exactly 16 characters long. */
599 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
600 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"0123456789ABCDE\n");
604 Richedit 1.0 receives a POINTL* on wParam and character offset on lParam, returns void.
605 Richedit 2.0 receives character offset on wParam, ignores lParam, returns MAKELONG(x,y)
606 Richedit 3.0 accepts either of the above API conventions.
609 /* Testing Richedit 2.0 API format */
611 /* Testing start of lines. X-offset should be constant on all cases (native is 1).
612 Since all lines are identical and drawn with the same font,
613 they should have the same height... right?
615 for (i = 0; i < 50; i++)
617 /* All the lines are 16 characters long */
618 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
619 if (i == 0)
621 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
622 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
623 xpos = LOWORD(result);
625 else if (i == 1)
627 ok(HIWORD(result) > 0, "EM_POSFROMCHAR reports y=%d, expected > 0\n", HIWORD(result));
628 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
629 height = HIWORD(result);
631 else
633 ok(HIWORD(result) == i * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), i * height);
634 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
638 /* Testing position at end of text */
639 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
640 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
641 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
643 /* Testing position way past end of text */
644 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
645 ok(HIWORD(result) == 50 * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), 50 * height);
646 expected = (rtl ? 8 : 1);
647 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
649 /* Testing that vertical scrolling does, in fact, have an effect on EM_POSFROMCHAR */
650 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
651 for (i = 0; i < 50; i++)
653 /* All the lines are 16 characters long */
654 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, i * 16, 0);
655 ok((signed short)(HIWORD(result)) == (i - 1) * height,
656 "EM_POSFROMCHAR reports y=%hd, expected %d\n",
657 (signed short)(HIWORD(result)), (i - 1) * height);
658 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
661 /* Testing position at end of text */
662 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 50 * 16, 0);
663 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
664 ok(LOWORD(result) == xpos, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
666 /* Testing position way past end of text */
667 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 55 * 16, 0);
668 ok(HIWORD(result) == (50 - 1) * height, "EM_POSFROMCHAR reports y=%d, expected %d\n", HIWORD(result), (50 - 1) * height);
669 expected = (rtl ? 8 : 1);
670 ok(LOWORD(result) == expected, "EM_POSFROMCHAR reports x=%d, expected %d\n", LOWORD(result), expected);
672 /* Testing that horizontal scrolling does, in fact, have an effect on EM_POSFROMCHAR */
673 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
674 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
676 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
677 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
678 ok(LOWORD(result) == 1, "EM_POSFROMCHAR reports x=%d, expected 1\n", LOWORD(result));
679 xpos = LOWORD(result);
681 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINERIGHT, 0);
682 result = SendMessageA(hwndRichEdit, EM_POSFROMCHAR, 0, 0);
683 ok(HIWORD(result) == 0, "EM_POSFROMCHAR reports y=%d, expected 0\n", HIWORD(result));
684 ok((signed short)(LOWORD(result)) < xpos,
685 "EM_POSFROMCHAR reports x=%hd, expected value less than %d\n",
686 (signed short)(LOWORD(result)), xpos);
687 SendMessageA(hwndRichEdit, WM_HSCROLL, SB_LINELEFT, 0);
689 /* Test around end of text that doesn't end in a newline. */
690 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"12345678901234");
691 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
692 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)-1);
693 ok(pt.x > 1, "pt.x = %d\n", pt.x);
694 xpos = pt.x;
695 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
696 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0));
697 ok(pt.x > xpos, "pt.x = %d\n", pt.x);
698 xpos = (rtl ? pt.x + 7 : pt.x);
699 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt,
700 SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0)+1);
701 ok(pt.x == xpos, "pt.x = %d\n", pt.x);
703 /* Try a negative position. */
704 SendMessageA(hwndRichEdit, EM_POSFROMCHAR, (WPARAM)&pt, -1);
705 ok(pt.x == 1, "pt.x = %d\n", pt.x);
707 DestroyWindow(hwndRichEdit);
710 static void test_EM_SETCHARFORMAT(void)
712 HWND hwndRichEdit = new_richedit(NULL);
713 CHARFORMAT2A cf2;
714 int rc = 0;
715 int tested_effects[] = {
716 CFE_BOLD,
717 CFE_ITALIC,
718 CFE_UNDERLINE,
719 CFE_STRIKEOUT,
720 CFE_PROTECTED,
721 CFE_LINK,
722 CFE_SUBSCRIPT,
723 CFE_SUPERSCRIPT,
726 int i;
727 CHARRANGE cr;
728 LOCALESIGNATURE sig;
729 BOOL rtl;
731 rtl = (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_FONTSIGNATURE,
732 (LPSTR) &sig, sizeof(LOCALESIGNATURE)) &&
733 (sig.lsUsb[3] & 0x08000000) != 0);
735 /* Invalid flags, CHARFORMAT2 structure blanked out */
736 memset(&cf2, 0, sizeof(cf2));
737 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
738 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
740 /* A valid flag, CHARFORMAT2 structure blanked out */
741 memset(&cf2, 0, sizeof(cf2));
742 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
743 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
745 /* A valid flag, CHARFORMAT2 structure blanked out */
746 memset(&cf2, 0, sizeof(cf2));
747 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
748 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
750 /* A valid flag, CHARFORMAT2 structure blanked out */
751 memset(&cf2, 0, sizeof(cf2));
752 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
753 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
755 /* A valid flag, CHARFORMAT2 structure blanked out */
756 memset(&cf2, 0, sizeof(cf2));
757 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
758 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
760 /* Invalid flags, CHARFORMAT2 structure minimally filled */
761 memset(&cf2, 0, sizeof(cf2));
762 cf2.cbSize = sizeof(CHARFORMAT2A);
763 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)0xfffffff0, (LPARAM)&cf2);
764 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
765 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
766 ok(rc == FALSE, "Should not be able to undo here.\n");
767 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
769 /* A valid flag, CHARFORMAT2 structure minimally filled */
770 memset(&cf2, 0, sizeof(cf2));
771 cf2.cbSize = sizeof(CHARFORMAT2A);
772 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
773 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
774 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
775 ok(rc == FALSE, "Should not be able to undo here.\n");
776 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
778 /* A valid flag, CHARFORMAT2 structure minimally filled */
779 memset(&cf2, 0, sizeof(cf2));
780 cf2.cbSize = sizeof(CHARFORMAT2A);
781 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
782 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
783 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
784 ok(rc == FALSE, "Should not be able to undo here.\n");
785 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
787 /* A valid flag, CHARFORMAT2 structure minimally filled */
788 memset(&cf2, 0, sizeof(cf2));
789 cf2.cbSize = sizeof(CHARFORMAT2A);
790 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_WORD, (LPARAM)&cf2);
791 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
792 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
793 todo_wine ok(rc == TRUE, "Should not be able to undo here.\n");
794 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
796 /* A valid flag, CHARFORMAT2 structure minimally filled */
797 memset(&cf2, 0, sizeof(cf2));
798 cf2.cbSize = sizeof(CHARFORMAT2A);
799 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
800 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
801 rc = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
802 ok(rc == TRUE, "Should not be able to undo here.\n");
803 SendMessageA(hwndRichEdit, EM_EMPTYUNDOBUFFER, 0, 0);
805 cf2.cbSize = sizeof(CHARFORMAT2A);
806 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
808 /* Test state of modify flag before and after valid EM_SETCHARFORMAT */
809 cf2.cbSize = sizeof(CHARFORMAT2A);
810 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
811 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
812 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
814 /* wParam==0 is default char format, does not set modify */
815 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
816 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
817 ok(rc == 0, "Text marked as modified, expected not modified!\n");
818 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
819 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
820 if (! rtl)
822 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
823 ok(rc == 0, "Text marked as modified, expected not modified!\n");
825 else
826 skip("RTL language found\n");
828 /* wParam==SCF_SELECTION sets modify if nonempty selection */
829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
830 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
831 ok(rc == 0, "Text marked as modified, expected not modified!\n");
832 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
833 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
834 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
835 ok(rc == 0, "Text marked as modified, expected not modified!\n");
837 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
838 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
839 ok(rc == 0, "Text marked as modified, expected not modified!\n");
840 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
841 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
842 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
843 ok(rc == 0, "Text marked as modified, expected not modified!\n");
844 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
845 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
846 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
847 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
848 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
850 /* wParam==SCF_ALL sets modify regardless of whether text is present */
851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
852 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
853 ok(rc == 0, "Text marked as modified, expected not modified!\n");
854 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
855 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
856 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
857 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
859 DestroyWindow(hwndRichEdit);
861 /* EM_GETCHARFORMAT tests */
862 for (i = 0; tested_effects[i]; i++)
864 hwndRichEdit = new_richedit(NULL);
865 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
867 /* Need to set a TrueType font to get consistent CFM_BOLD results */
868 memset(&cf2, 0, sizeof(CHARFORMAT2A));
869 cf2.cbSize = sizeof(CHARFORMAT2A);
870 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
871 cf2.dwEffects = 0;
872 strcpy(cf2.szFaceName, "Courier New");
873 cf2.wWeight = FW_DONTCARE;
874 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
876 memset(&cf2, 0, sizeof(CHARFORMAT2A));
877 cf2.cbSize = sizeof(CHARFORMAT2A);
878 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 4);
879 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
880 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
881 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
883 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
884 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
885 ok((cf2.dwEffects & tested_effects[i]) == 0,
886 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
888 memset(&cf2, 0, sizeof(CHARFORMAT2A));
889 cf2.cbSize = sizeof(CHARFORMAT2A);
890 cf2.dwMask = tested_effects[i];
891 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
892 cf2.dwMask = CFM_SUPERSCRIPT;
893 cf2.dwEffects = tested_effects[i];
894 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
895 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
897 memset(&cf2, 0, sizeof(CHARFORMAT2A));
898 cf2.cbSize = sizeof(CHARFORMAT2A);
899 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
900 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
901 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
902 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
904 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
905 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
906 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
907 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
909 memset(&cf2, 0, sizeof(CHARFORMAT2A));
910 cf2.cbSize = sizeof(CHARFORMAT2A);
911 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
912 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
913 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
914 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
916 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
917 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
918 ok((cf2.dwEffects & tested_effects[i]) == 0,
919 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
921 memset(&cf2, 0, sizeof(CHARFORMAT2A));
922 cf2.cbSize = sizeof(CHARFORMAT2A);
923 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
924 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
925 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
926 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
928 (cf2.dwMask & tested_effects[i]) == 0),
929 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
931 DestroyWindow(hwndRichEdit);
934 for (i = 0; tested_effects[i]; i++)
936 hwndRichEdit = new_richedit(NULL);
937 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
939 /* Need to set a TrueType font to get consistent CFM_BOLD results */
940 memset(&cf2, 0, sizeof(CHARFORMAT2A));
941 cf2.cbSize = sizeof(CHARFORMAT2A);
942 cf2.dwMask = CFM_FACE|CFM_WEIGHT;
943 cf2.dwEffects = 0;
944 strcpy(cf2.szFaceName, "Courier New");
945 cf2.wWeight = FW_DONTCARE;
946 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
948 memset(&cf2, 0, sizeof(CHARFORMAT2A));
949 cf2.cbSize = sizeof(CHARFORMAT2A);
950 cf2.dwMask = tested_effects[i];
951 if (cf2.dwMask == CFE_SUBSCRIPT || cf2.dwMask == CFE_SUPERSCRIPT)
952 cf2.dwMask = CFM_SUPERSCRIPT;
953 cf2.dwEffects = tested_effects[i];
954 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
955 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
957 memset(&cf2, 0, sizeof(CHARFORMAT2A));
958 cf2.cbSize = sizeof(CHARFORMAT2A);
959 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
960 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
961 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
962 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
964 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
965 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
966 ok((cf2.dwEffects & tested_effects[i]) == 0,
967 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x clear\n", i, cf2.dwEffects, tested_effects[i]);
969 memset(&cf2, 0, sizeof(CHARFORMAT2A));
970 cf2.cbSize = sizeof(CHARFORMAT2A);
971 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 4);
972 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
973 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
974 (cf2.dwMask & CFM_SUPERSCRIPT) == CFM_SUPERSCRIPT)
976 (cf2.dwMask & tested_effects[i]) == tested_effects[i]),
977 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, tested_effects[i]);
978 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
979 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, tested_effects[i]);
981 memset(&cf2, 0, sizeof(CHARFORMAT2A));
982 cf2.cbSize = sizeof(CHARFORMAT2A);
983 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 3);
984 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
985 ok ((((tested_effects[i] == CFE_SUBSCRIPT || tested_effects[i] == CFE_SUPERSCRIPT) &&
986 (cf2.dwMask & CFM_SUPERSCRIPT) == 0)
988 (cf2.dwMask & tested_effects[i]) == 0),
989 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x clear\n", i, cf2.dwMask, tested_effects[i]);
990 ok((cf2.dwEffects & tested_effects[i]) == tested_effects[i],
991 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x set\n", i, cf2.dwEffects, tested_effects[i]);
993 DestroyWindow(hwndRichEdit);
996 /* Effects applied on an empty selection should take effect when selection is
997 replaced with text */
998 hwndRichEdit = new_richedit(NULL);
999 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1000 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1002 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1003 cf2.cbSize = sizeof(CHARFORMAT2A);
1004 cf2.dwMask = CFM_BOLD;
1005 cf2.dwEffects = CFE_BOLD;
1006 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1008 /* Selection is now nonempty */
1009 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1011 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1012 cf2.cbSize = sizeof(CHARFORMAT2A);
1013 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1014 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1016 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1017 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1018 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1019 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1022 /* Set two effects on an empty selection */
1023 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1024 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1026 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1027 cf2.cbSize = sizeof(CHARFORMAT2A);
1028 cf2.dwMask = CFM_BOLD;
1029 cf2.dwEffects = CFE_BOLD;
1030 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1031 cf2.dwMask = CFM_ITALIC;
1032 cf2.dwEffects = CFE_ITALIC;
1033 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1035 /* Selection is now nonempty */
1036 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1038 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1039 cf2.cbSize = sizeof(CHARFORMAT2A);
1040 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1041 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1043 ok (((cf2.dwMask & (CFM_BOLD|CFM_ITALIC)) == (CFM_BOLD|CFM_ITALIC)),
1044 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, (CFM_BOLD|CFM_ITALIC));
1045 ok((cf2.dwEffects & (CFE_BOLD|CFE_ITALIC)) == (CFE_BOLD|CFE_ITALIC),
1046 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, (CFE_BOLD|CFE_ITALIC));
1048 /* Setting the (empty) selection to exactly the same place as before should
1049 NOT clear the insertion style! */
1050 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1051 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2); /* Empty selection */
1053 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1054 cf2.cbSize = sizeof(CHARFORMAT2A);
1055 cf2.dwMask = CFM_BOLD;
1056 cf2.dwEffects = CFE_BOLD;
1057 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1059 /* Empty selection in same place, insert style should NOT be forgotten here. */
1060 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 2);
1062 /* Selection is now nonempty */
1063 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1065 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1066 cf2.cbSize = sizeof(CHARFORMAT2A);
1067 SendMessageA(hwndRichEdit, EM_SETSEL, 2, 6);
1068 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1070 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1071 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1072 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1073 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1075 /* Ditto with EM_EXSETSEL */
1076 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1077 cr.cpMin = 2; cr.cpMax = 2;
1078 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1080 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1081 cf2.cbSize = sizeof(CHARFORMAT2A);
1082 cf2.dwMask = CFM_BOLD;
1083 cf2.dwEffects = CFE_BOLD;
1084 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1086 /* Empty selection in same place, insert style should NOT be forgotten here. */
1087 cr.cpMin = 2; cr.cpMax = 2;
1088 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1090 /* Selection is now nonempty */
1091 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"newi");
1093 memset(&cf2, 0, sizeof(CHARFORMAT2A));
1094 cf2.cbSize = sizeof(CHARFORMAT2A);
1095 cr.cpMin = 2; cr.cpMax = 6;
1096 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr); /* Empty selection */
1097 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1099 ok (((cf2.dwMask & CFM_BOLD) == CFM_BOLD),
1100 "%d, cf2.dwMask == 0x%08x expected mask 0x%08x\n", i, cf2.dwMask, CFM_BOLD);
1101 ok((cf2.dwEffects & CFE_BOLD) == CFE_BOLD,
1102 "%d, cf2.dwEffects == 0x%08x expected effect 0x%08x\n", i, cf2.dwEffects, CFE_BOLD);
1104 DestroyWindow(hwndRichEdit);
1107 static void test_EM_SETTEXTMODE(void)
1109 HWND hwndRichEdit = new_richedit(NULL);
1110 CHARFORMAT2A cf2, cf2test;
1111 CHARRANGE cr;
1112 int rc = 0;
1114 /*Attempt to use mutually exclusive modes*/
1115 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT|TM_RICHTEXT, 0);
1116 ok(rc == E_INVALIDARG,
1117 "EM_SETTEXTMODE: using mutually exclusive mode flags - returned: %x\n", rc);
1119 /*Test that EM_SETTEXTMODE fails if text exists within the control*/
1120 /*Insert text into the control*/
1122 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1124 /*Attempt to change the control to plain text mode*/
1125 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1126 ok(rc == E_UNEXPECTED,
1127 "EM_SETTEXTMODE: changed text mode in control containing text - returned: %x\n", rc);
1129 /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted.
1130 If rich text is pasted, it should have the same formatting as the rest
1131 of the text in the control*/
1133 /*Italicize the text
1134 *NOTE: If the default text was already italicized, the test will simply
1135 reverse; in other words, it will copy a regular "wine" into a plain
1136 text window that uses an italicized format*/
1137 cf2.cbSize = sizeof(CHARFORMAT2A);
1138 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_DEFAULT, (LPARAM)&cf2);
1140 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
1141 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
1143 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1144 ok(rc == 0, "Text marked as modified, expected not modified!\n");
1146 /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine;
1147 however, SCF_ALL has been implemented*/
1148 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1149 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1151 rc = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
1152 ok(rc == -1, "Text not marked as modified, expected modified! (%d)\n", rc);
1154 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1156 /*Select the string "wine"*/
1157 cr.cpMin = 0;
1158 cr.cpMax = 4;
1159 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1161 /*Copy the italicized "wine" to the clipboard*/
1162 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1164 /*Reset the formatting to default*/
1165 cf2.dwEffects = CFE_ITALIC^cf2.dwEffects;
1166 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM)SCF_ALL, (LPARAM)&cf2);
1167 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1169 /*Clear the text in the control*/
1170 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1172 /*Switch to Plain Text Mode*/
1173 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
1174 ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc);
1176 /*Input "wine" again in normal format*/
1177 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1179 /*Paste the italicized "wine" into the control*/
1180 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1182 /*Select a character from the first "wine" string*/
1183 cr.cpMin = 2;
1184 cr.cpMax = 3;
1185 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1187 /*Retrieve its formatting*/
1188 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1190 /*Select a character from the second "wine" string*/
1191 cr.cpMin = 5;
1192 cr.cpMax = 6;
1193 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1195 /*Retrieve its formatting*/
1196 cf2test.cbSize = sizeof(CHARFORMAT2A);
1197 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1199 /*Compare the two formattings*/
1200 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1201 "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n",
1202 cf2.dwEffects, cf2test.dwEffects);
1203 /*Test TM_RICHTEXT by: switching back to Rich Text mode
1204 printing "wine" in the current format(normal)
1205 pasting "wine" from the clipboard(italicized)
1206 comparing the two formats(should differ)*/
1208 /*Attempt to switch with text in control*/
1209 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1210 ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc);
1212 /*Clear control*/
1213 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1215 /*Switch into Rich Text mode*/
1216 rc = SendMessageA(hwndRichEdit, EM_SETTEXTMODE, (WPARAM)TM_RICHTEXT, 0);
1217 ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc);
1219 /*Print "wine" in normal formatting into the control*/
1220 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1222 /*Paste italicized "wine" into the control*/
1223 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1225 /*Select text from the first "wine" string*/
1226 cr.cpMin = 1;
1227 cr.cpMax = 3;
1228 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1230 /*Retrieve its formatting*/
1231 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2);
1233 /*Select text from the second "wine" string*/
1234 cr.cpMin = 6;
1235 cr.cpMax = 7;
1236 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1238 /*Retrieve its formatting*/
1239 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM)SCF_SELECTION, (LPARAM)&cf2test);
1241 /*Test that the two formattings are not the same*/
1242 todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects),
1243 "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1244 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1246 DestroyWindow(hwndRichEdit);
1249 static void test_SETPARAFORMAT(void)
1251 HWND hwndRichEdit = new_richedit(NULL);
1252 PARAFORMAT2 fmt;
1253 HRESULT ret;
1254 LONG expectedMask = PFM_ALL2 & ~PFM_TABLEROWDELIMITER;
1255 fmt.cbSize = sizeof(PARAFORMAT2);
1256 fmt.dwMask = PFM_ALIGNMENT;
1257 fmt.wAlignment = PFA_LEFT;
1259 ret = SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt);
1260 ok(ret != 0, "expected non-zero got %d\n", ret);
1262 fmt.cbSize = sizeof(PARAFORMAT2);
1263 fmt.dwMask = -1;
1264 ret = SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt);
1265 /* Ignore the PFM_TABLEROWDELIMITER bit because it changes
1266 * between richedit different native builds of riched20.dll
1267 * used on different Windows versions. */
1268 ret &= ~PFM_TABLEROWDELIMITER;
1269 fmt.dwMask &= ~PFM_TABLEROWDELIMITER;
1271 ok(ret == expectedMask, "expected %x got %x\n", expectedMask, ret);
1272 ok(fmt.dwMask == expectedMask, "expected %x got %x\n", expectedMask, fmt.dwMask);
1274 DestroyWindow(hwndRichEdit);
1277 static void test_TM_PLAINTEXT(void)
1279 /*Tests plain text properties*/
1281 HWND hwndRichEdit = new_richedit(NULL);
1282 CHARFORMAT2A cf2, cf2test;
1283 CHARRANGE cr;
1284 int rc = 0;
1286 /*Switch to plain text mode*/
1288 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1289 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
1291 /*Fill control with text*/
1293 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Is Wine an emulator? No it's not");
1295 /*Select some text and bold it*/
1297 cr.cpMin = 10;
1298 cr.cpMax = 20;
1299 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1300 cf2.cbSize = sizeof(CHARFORMAT2A);
1301 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1303 cf2.dwMask = CFM_BOLD | cf2.dwMask;
1304 cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects;
1306 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1307 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1309 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_WORD | SCF_SELECTION, (LPARAM)&cf2);
1310 ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc);
1312 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1313 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1315 /*Get the formatting of those characters*/
1317 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1319 /*Get the formatting of some other characters*/
1320 cf2test.cbSize = sizeof(CHARFORMAT2A);
1321 cr.cpMin = 21;
1322 cr.cpMax = 30;
1323 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1324 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1326 /*Test that they are the same as plain text allows only one formatting*/
1328 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1329 "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1330 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1332 /*Fill the control with a "wine" string, which when inserted will be bold*/
1334 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1336 /*Copy the bolded "wine" string*/
1338 cr.cpMin = 0;
1339 cr.cpMax = 4;
1340 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1341 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
1343 /*Swap back to rich text*/
1345 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
1346 SendMessageA(hwndRichEdit, EM_SETTEXTMODE, TM_RICHTEXT, 0);
1348 /*Set the default formatting to bold italics*/
1350 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
1351 cf2.dwMask |= CFM_ITALIC;
1352 cf2.dwEffects ^= CFE_ITALIC;
1353 rc = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
1354 ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc);
1356 /*Set the text in the control to "wine", which will be bold and italicized*/
1358 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"wine");
1360 /*Paste the plain text "wine" string, which should take the insert
1361 formatting, which at the moment is bold italics*/
1363 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
1365 /*Select the first "wine" string and retrieve its formatting*/
1367 cr.cpMin = 1;
1368 cr.cpMax = 3;
1369 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1370 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
1372 /*Select the second "wine" string and retrieve its formatting*/
1374 cr.cpMin = 5;
1375 cr.cpMax = 7;
1376 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
1377 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2test);
1379 /*Compare the two formattings. They should be the same.*/
1381 ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects),
1382 "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n",
1383 cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects);
1384 DestroyWindow(hwndRichEdit);
1387 static void test_WM_GETTEXT(void)
1389 HWND hwndRichEdit = new_richedit(NULL);
1390 static const char text[] = "Hello. My name is RichEdit!";
1391 static const char text2[] = "Hello. My name is RichEdit!\r";
1392 static const char text2_after[] = "Hello. My name is RichEdit!\r\n";
1393 char buffer[1024] = {0};
1394 int result;
1396 /* Baseline test with normal-sized buffer */
1397 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1398 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1399 ok(result == lstrlenA(buffer),
1400 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1401 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1402 result = strcmp(buffer,text);
1403 ok(result == 0,
1404 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1406 /* Test for returned value of WM_GETTEXTLENGTH */
1407 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1408 ok(result == lstrlenA(text),
1409 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1410 result, lstrlenA(text));
1412 /* Test for behavior in overflow case */
1413 memset(buffer, 0, 1024);
1414 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text), (LPARAM)buffer);
1415 ok(result == 0 ||
1416 result == lstrlenA(text) - 1, /* XP, win2k3 */
1417 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text) - 1);
1418 result = strcmp(buffer,text);
1419 if (result)
1420 result = strncmp(buffer, text, lstrlenA(text) - 1); /* XP, win2k3 */
1421 ok(result == 0,
1422 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1424 /* Baseline test with normal-sized buffer and carriage return */
1425 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1426 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1427 ok(result == lstrlenA(buffer),
1428 "WM_GETTEXT returned %d, expected %d\n", result, lstrlenA(buffer));
1429 result = strcmp(buffer,text2_after);
1430 ok(result == 0,
1431 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1433 /* Test for returned value of WM_GETTEXTLENGTH */
1434 result = SendMessageA(hwndRichEdit, WM_GETTEXTLENGTH, 0, 0);
1435 ok(result == lstrlenA(text2_after),
1436 "WM_GETTEXTLENGTH reports incorrect length %d, expected %d\n",
1437 result, lstrlenA(text2_after));
1439 /* Test for behavior of CRLF conversion in case of overflow */
1440 memset(buffer, 0, 1024);
1441 result = SendMessageA(hwndRichEdit, WM_GETTEXT, strlen(text2), (LPARAM)buffer);
1442 ok(result == 0 ||
1443 result == lstrlenA(text2) - 1, /* XP, win2k3 */
1444 "WM_GETTEXT returned %d, expected 0 or %d\n", result, lstrlenA(text2) - 1);
1445 result = strcmp(buffer,text2);
1446 if (result)
1447 result = strncmp(buffer, text2, lstrlenA(text2) - 1); /* XP, win2k3 */
1448 ok(result == 0,
1449 "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result);
1451 DestroyWindow(hwndRichEdit);
1454 static void test_EM_GETTEXTRANGE(void)
1456 HWND hwndRichEdit = new_richedit(NULL);
1457 const char * text1 = "foo bar\r\nfoo bar";
1458 const char * text2 = "foo bar\rfoo bar";
1459 const char * expect = "bar\rfoo";
1460 char buffer[1024] = {0};
1461 LRESULT result;
1462 TEXTRANGEA textRange;
1464 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1466 textRange.lpstrText = buffer;
1467 textRange.chrg.cpMin = 4;
1468 textRange.chrg.cpMax = 11;
1469 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1470 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1471 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1473 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1475 textRange.lpstrText = buffer;
1476 textRange.chrg.cpMin = 4;
1477 textRange.chrg.cpMax = 11;
1478 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1479 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1480 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1482 /* cpMax of text length is used instead of -1 in this case */
1483 textRange.lpstrText = buffer;
1484 textRange.chrg.cpMin = 0;
1485 textRange.chrg.cpMax = -1;
1486 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1487 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1488 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1490 /* cpMin < 0 causes no text to be copied, and 0 to be returned */
1491 textRange.lpstrText = buffer;
1492 textRange.chrg.cpMin = -1;
1493 textRange.chrg.cpMax = 1;
1494 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1495 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1496 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1498 /* cpMax of -1 is not replaced with text length if cpMin != 0 */
1499 textRange.lpstrText = buffer;
1500 textRange.chrg.cpMin = 1;
1501 textRange.chrg.cpMax = -1;
1502 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1503 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1504 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1506 /* no end character is copied if cpMax - cpMin < 0 */
1507 textRange.lpstrText = buffer;
1508 textRange.chrg.cpMin = 5;
1509 textRange.chrg.cpMax = 5;
1510 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1511 ok(result == 0, "EM_GETTEXTRANGE returned %ld\n", result);
1512 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1514 /* cpMax of text length is used if cpMax > text length*/
1515 textRange.lpstrText = buffer;
1516 textRange.chrg.cpMin = 0;
1517 textRange.chrg.cpMax = 1000;
1518 result = SendMessageA(hwndRichEdit, EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
1519 ok(result == strlen(text2), "EM_GETTEXTRANGE returned %ld\n", result);
1520 ok(!strcmp(text2, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1522 DestroyWindow(hwndRichEdit);
1525 static void test_EM_GETSELTEXT(void)
1527 HWND hwndRichEdit = new_richedit(NULL);
1528 const char * text1 = "foo bar\r\nfoo bar";
1529 const char * text2 = "foo bar\rfoo bar";
1530 const char * expect = "bar\rfoo";
1531 char buffer[1024] = {0};
1532 LRESULT result;
1534 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
1536 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1537 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1538 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1539 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1541 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
1543 SendMessageA(hwndRichEdit, EM_SETSEL, 4, 11);
1544 result = SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffer);
1545 ok(result == 7, "EM_GETTEXTRANGE returned %ld\n", result);
1546 ok(!strcmp(expect, buffer), "EM_GETTEXTRANGE filled %s\n", buffer);
1548 DestroyWindow(hwndRichEdit);
1551 /* FIXME: need to test unimplemented options and robustly test wparam */
1552 static void test_EM_SETOPTIONS(void)
1554 HWND hwndRichEdit;
1555 static const char text[] = "Hello. My name is RichEdit!";
1556 char buffer[1024] = {0};
1557 DWORD dwStyle, options, oldOptions;
1558 DWORD optionStyles = ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_NOHIDESEL|
1559 ES_READONLY|ES_WANTRETURN|ES_SAVESEL|
1560 ES_SELECTIONBAR|ES_VERTICAL;
1562 /* Test initial options. */
1563 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL, WS_POPUP,
1564 0, 0, 200, 60, NULL, NULL,
1565 hmoduleRichEdit, NULL);
1566 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1567 RICHEDIT_CLASS20A, (int) GetLastError());
1568 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1569 ok(options == 0, "Incorrect initial options %x\n", options);
1570 DestroyWindow(hwndRichEdit);
1572 hwndRichEdit = CreateWindowA(RICHEDIT_CLASS20A, NULL,
1573 WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
1574 0, 0, 200, 60, NULL, NULL,
1575 hmoduleRichEdit, NULL);
1576 ok(hwndRichEdit != NULL, "class: %s, error: %d\n",
1577 RICHEDIT_CLASS20A, (int) GetLastError());
1578 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1579 /* WS_[VH]SCROLL cause the ECO_AUTO[VH]SCROLL options to be set */
1580 ok(options == (ECO_AUTOVSCROLL|ECO_AUTOHSCROLL),
1581 "Incorrect initial options %x\n", options);
1583 /* NEGATIVE TESTING - NO OPTIONS SET */
1584 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1585 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0);
1587 /* testing no readonly by sending 'a' to the control*/
1588 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1589 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1590 ok(buffer[0]=='a',
1591 "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer);
1592 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1594 /* READONLY - sending 'a' to the control */
1595 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
1596 SendMessageA(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY);
1597 SendMessageA(hwndRichEdit, WM_CHAR, 'a', 0x1E0001);
1598 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1599 ok(buffer[0]==text[0],
1600 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1602 /* EM_SETOPTIONS changes the window style, but changing the
1603 * window style does not change the options. */
1604 dwStyle = GetWindowLongA(hwndRichEdit, GWL_STYLE);
1605 ok(dwStyle & ES_READONLY, "Readonly style not set by EM_SETOPTIONS\n");
1606 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle & ~ES_READONLY);
1607 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1608 ok(options & ES_READONLY, "Readonly option set by SetWindowLong\n");
1609 /* Confirm that the text is still read only. */
1610 SendMessageA(hwndRichEdit, WM_CHAR, 'a', ('a' << 16) | 0x0001);
1611 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
1612 ok(buffer[0]==text[0],
1613 "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer);
1615 oldOptions = options;
1616 SetWindowLongA(hwndRichEdit, GWL_STYLE, dwStyle|optionStyles);
1617 options = SendMessageA(hwndRichEdit, EM_GETOPTIONS, 0, 0);
1618 ok(options == oldOptions,
1619 "Options set by SetWindowLong (%x -> %x)\n", oldOptions, options);
1621 DestroyWindow(hwndRichEdit);
1624 static BOOL check_CFE_LINK_selection(HWND hwnd, int sel_start, int sel_end)
1626 CHARFORMAT2A text_format;
1627 text_format.cbSize = sizeof(text_format);
1628 SendMessageA(hwnd, EM_SETSEL, sel_start, sel_end);
1629 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&text_format);
1630 return (text_format.dwEffects & CFE_LINK) != 0;
1633 static void check_CFE_LINK_rcvd(HWND hwnd, BOOL is_url, const char * url)
1635 BOOL link_present = FALSE;
1637 link_present = check_CFE_LINK_selection(hwnd, 0, 1);
1638 if (is_url)
1639 { /* control text is url; should get CFE_LINK */
1640 ok(link_present, "URL Case: CFE_LINK not set for [%s].\n", url);
1642 else
1644 ok(!link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url);
1648 static HWND new_static_wnd(HWND parent) {
1649 return new_window("Static", 0, parent);
1652 static void test_EM_AUTOURLDETECT(void)
1654 /* DO NOT change the properties of the first two elements. To shorten the
1655 tests, all tests after WM_SETTEXT test just the first two elements -
1656 one non-URL and one URL */
1657 struct urls_s {
1658 const char *text;
1659 BOOL is_url;
1660 } urls[12] = {
1661 {"winehq.org", FALSE},
1662 {"http://www.winehq.org", TRUE},
1663 {"http//winehq.org", FALSE},
1664 {"ww.winehq.org", FALSE},
1665 {"www.winehq.org", TRUE},
1666 {"ftp://192.168.1.1", TRUE},
1667 {"ftp//192.168.1.1", FALSE},
1668 {"mailto:your@email.com", TRUE},
1669 {"prospero:prosperoserver", TRUE},
1670 {"telnet:test", TRUE},
1671 {"news:newserver", TRUE},
1672 {"wais:waisserver", TRUE}
1675 int i, j;
1676 int urlRet=-1;
1677 HWND hwndRichEdit, parent;
1679 /* All of the following should cause the URL to be detected */
1680 const char * templates_delim[] = {
1681 "This is some text with X on it",
1682 "This is some text with (X) on it",
1683 "This is some text with X\r on it",
1684 "This is some text with ---X--- on it",
1685 "This is some text with \"X\" on it",
1686 "This is some text with 'X' on it",
1687 "This is some text with 'X' on it",
1688 "This is some text with :X: on it",
1690 "This text ends with X",
1692 "This is some text with X) on it",
1693 "This is some text with X--- on it",
1694 "This is some text with X\" on it",
1695 "This is some text with X' on it",
1696 "This is some text with X: on it",
1698 "This is some text with (X on it",
1699 "This is some text with \rX on it",
1700 "This is some text with ---X on it",
1701 "This is some text with \"X on it",
1702 "This is some text with 'X on it",
1703 "This is some text with :X on it",
1705 /* None of these should cause the URL to be detected */
1706 const char * templates_non_delim[] = {
1707 "This is some text with |X| on it",
1708 "This is some text with *X* on it",
1709 "This is some text with /X/ on it",
1710 "This is some text with +X+ on it",
1711 "This is some text with %X% on it",
1712 "This is some text with #X# on it",
1713 "This is some text with @X@ on it",
1714 "This is some text with \\X\\ on it",
1715 "This is some text with |X on it",
1716 "This is some text with *X on it",
1717 "This is some text with /X on it",
1718 "This is some text with +X on it",
1719 "This is some text with %X on it",
1720 "This is some text with #X on it",
1721 "This is some text with @X on it",
1722 "This is some text with \\X on it",
1724 /* All of these cause the URL detection to be extended by one more byte,
1725 thus demonstrating that the tested character is considered as part
1726 of the URL. */
1727 const char * templates_xten_delim[] = {
1728 "This is some text with X| on it",
1729 "This is some text with X* on it",
1730 "This is some text with X/ on it",
1731 "This is some text with X+ on it",
1732 "This is some text with X% on it",
1733 "This is some text with X# on it",
1734 "This is some text with X@ on it",
1735 "This is some text with X\\ on it",
1737 char buffer[1024];
1739 parent = new_static_wnd(NULL);
1740 hwndRichEdit = new_richedit(parent);
1741 /* Try and pass EM_AUTOURLDETECT some test wParam values */
1742 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1743 ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet);
1744 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 1, 0);
1745 ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet);
1746 /* Windows returns -2147024809 (0x80070057) on bad wParam values */
1747 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, 8, 0);
1748 ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet);
1749 urlRet=SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h");
1750 ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet);
1751 /* for each url, check the text to see if CFE_LINK effect is present */
1752 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1754 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0);
1755 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1756 check_CFE_LINK_rcvd(hwndRichEdit, FALSE, urls[i].text);
1758 /* Link detection should happen immediately upon WM_SETTEXT */
1759 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1760 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)urls[i].text);
1761 check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text);
1763 DestroyWindow(hwndRichEdit);
1765 /* Test detection of URLs within normal text - WM_SETTEXT case. */
1766 for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) {
1767 hwndRichEdit = new_richedit(parent);
1769 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
1770 char * at_pos;
1771 int at_offset;
1772 int end_offset;
1774 at_pos = strchr(templates_delim[j], 'X');
1775 at_offset = at_pos - templates_delim[j];
1776 memcpy(buffer, templates_delim[j], at_offset);
1777 buffer[at_offset] = '\0';
1778 strcat(buffer, urls[i].text);
1779 strcat(buffer, templates_delim[j] + at_offset + 1);
1780 end_offset = at_offset + strlen(urls[i].text);
1782 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1783 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1785 /* This assumes no templates start with the URL itself, and that they
1786 have at least two characters before the URL text */
1787 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1788 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1789 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1790 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1791 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1792 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1794 if (urls[i].is_url)
1796 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1797 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1798 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1799 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1801 else
1803 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1804 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1805 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1806 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1808 if (buffer[end_offset] != '\0')
1810 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1811 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1812 if (buffer[end_offset +1] != '\0')
1814 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1815 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1820 for (j = 0; j < sizeof(templates_non_delim) / sizeof(const char *); j++) {
1821 char * at_pos;
1822 int at_offset;
1823 int end_offset;
1825 at_pos = strchr(templates_non_delim[j], 'X');
1826 at_offset = at_pos - templates_non_delim[j];
1827 memcpy(buffer, templates_non_delim[j], at_offset);
1828 buffer[at_offset] = '\0';
1829 strcat(buffer, urls[i].text);
1830 strcat(buffer, templates_non_delim[j] + at_offset + 1);
1831 end_offset = at_offset + strlen(urls[i].text);
1833 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1834 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1836 /* This assumes no templates start with the URL itself, and that they
1837 have at least two characters before the URL text */
1838 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1839 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1840 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1841 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1842 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1843 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1845 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1846 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1847 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1848 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1849 if (buffer[end_offset] != '\0')
1851 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1852 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1853 if (buffer[end_offset +1] != '\0')
1855 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1856 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1861 for (j = 0; j < sizeof(templates_xten_delim) / sizeof(const char *); j++) {
1862 char * at_pos;
1863 int at_offset;
1864 int end_offset;
1866 at_pos = strchr(templates_xten_delim[j], 'X');
1867 at_offset = at_pos - templates_xten_delim[j];
1868 memcpy(buffer, templates_xten_delim[j], at_offset);
1869 buffer[at_offset] = '\0';
1870 strcat(buffer, urls[i].text);
1871 strcat(buffer, templates_xten_delim[j] + at_offset + 1);
1872 end_offset = at_offset + strlen(urls[i].text);
1874 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buffer);
1877 /* This assumes no templates start with the URL itself, and that they
1878 have at least two characters before the URL text */
1879 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1880 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1881 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1882 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1883 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1884 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1886 if (urls[i].is_url)
1888 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1889 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1890 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1891 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1892 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1893 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1895 else
1897 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1898 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1899 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1900 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1901 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1902 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset +1, buffer);
1904 if (buffer[end_offset +1] != '\0')
1906 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1907 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset + 2, buffer);
1908 if (buffer[end_offset +2] != '\0')
1910 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
1911 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
1916 DestroyWindow(hwndRichEdit);
1917 hwndRichEdit = NULL;
1920 /* Test detection of URLs within normal text - WM_CHAR case. */
1921 /* Test only the first two URL examples for brevity */
1922 for (i = 0; i < 2; i++) {
1923 hwndRichEdit = new_richedit(parent);
1925 /* Also for brevity, test only the first three delimiters */
1926 for (j = 0; j < 3; j++) {
1927 char * at_pos;
1928 int at_offset;
1929 int end_offset;
1930 int u, v;
1932 at_pos = strchr(templates_delim[j], 'X');
1933 at_offset = at_pos - templates_delim[j];
1934 end_offset = at_offset + strlen(urls[i].text);
1936 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
1937 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
1938 for (u = 0; templates_delim[j][u]; u++) {
1939 if (templates_delim[j][u] == '\r') {
1940 simulate_typing_characters(hwndRichEdit, "\r");
1941 } else if (templates_delim[j][u] != 'X') {
1942 SendMessageA(hwndRichEdit, WM_CHAR, templates_delim[j][u], 1);
1943 } else {
1944 for (v = 0; urls[i].text[v]; v++) {
1945 SendMessageA(hwndRichEdit, WM_CHAR, urls[i].text[v], 1);
1949 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1951 /* This assumes no templates start with the URL itself, and that they
1952 have at least two characters before the URL text */
1953 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1954 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1955 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1956 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1957 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1958 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1960 if (urls[i].is_url)
1962 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1963 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
1964 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1965 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1967 else
1969 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
1970 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
1971 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
1972 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
1974 if (buffer[end_offset] != '\0')
1976 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
1977 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
1978 if (buffer[end_offset +1] != '\0')
1980 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
1981 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
1985 /* The following will insert a paragraph break after the first character
1986 of the URL candidate, thus breaking the URL. It is expected that the
1987 CFE_LINK attribute should break across both pieces of the URL */
1988 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+1);
1989 simulate_typing_characters(hwndRichEdit, "\r");
1990 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1992 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
1993 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
1994 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
1995 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
1996 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
1997 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
1999 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2000 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2001 /* end_offset moved because of paragraph break */
2002 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2003 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset+1, buffer);
2004 ok(buffer[end_offset], "buffer \"%s\" ended prematurely. Is it missing a newline character?\n", buffer);
2005 if (buffer[end_offset] != 0 && buffer[end_offset+1] != '\0')
2007 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset+1, end_offset +2),
2008 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset+1, end_offset +2, buffer);
2009 if (buffer[end_offset +2] != '\0')
2011 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +2, end_offset +3),
2012 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +2, end_offset +3, buffer);
2016 /* The following will remove the just-inserted paragraph break, thus
2017 restoring the URL */
2018 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+2, at_offset+2);
2019 simulate_typing_characters(hwndRichEdit, "\b");
2020 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2022 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2023 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2024 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2025 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2026 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2027 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2029 if (urls[i].is_url)
2031 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2032 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2033 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2034 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2036 else
2038 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2039 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2040 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2041 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2043 if (buffer[end_offset] != '\0')
2045 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2046 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2047 if (buffer[end_offset +1] != '\0')
2049 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2050 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2054 DestroyWindow(hwndRichEdit);
2055 hwndRichEdit = NULL;
2058 /* Test detection of URLs within normal text - EM_SETTEXTEX case. */
2059 /* Test just the first two URL examples for brevity */
2060 for (i = 0; i < 2; i++) {
2061 SETTEXTEX st;
2063 hwndRichEdit = new_richedit(parent);
2065 /* There are at least three ways in which EM_SETTEXTEX must cause URLs to
2066 be detected:
2067 1) Set entire text, a la WM_SETTEXT
2068 2) Set a selection of the text to the URL
2069 3) Set a portion of the text at a time, which eventually results in
2070 an URL
2071 All of them should give equivalent results
2074 /* Set entire text in one go, like WM_SETTEXT */
2075 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2076 char * at_pos;
2077 int at_offset;
2078 int end_offset;
2080 st.codepage = CP_ACP;
2081 st.flags = ST_DEFAULT;
2083 at_pos = strchr(templates_delim[j], 'X');
2084 at_offset = at_pos - templates_delim[j];
2085 memcpy(buffer, templates_delim[j], at_offset);
2086 buffer[at_offset] = '\0';
2087 strcat(buffer, urls[i].text);
2088 strcat(buffer, templates_delim[j] + at_offset + 1);
2089 end_offset = at_offset + strlen(urls[i].text);
2091 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2092 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2094 /* This assumes no templates start with the URL itself, and that they
2095 have at least two characters before the URL text */
2096 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2097 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2098 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2099 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2100 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2101 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2103 if (urls[i].is_url)
2105 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2106 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2107 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2108 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2110 else
2112 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2113 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2114 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2115 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2117 if (buffer[end_offset] != '\0')
2119 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2120 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2121 if (buffer[end_offset +1] != '\0')
2123 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2124 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2129 /* Set selection with X to the URL */
2130 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2131 char * at_pos;
2132 int at_offset;
2133 int end_offset;
2135 at_pos = strchr(templates_delim[j], 'X');
2136 at_offset = at_pos - templates_delim[j];
2137 end_offset = at_offset + strlen(urls[i].text);
2139 st.codepage = CP_ACP;
2140 st.flags = ST_DEFAULT;
2141 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2142 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2143 st.flags = ST_SELECTION;
2144 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2145 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)urls[i].text);
2146 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2148 /* This assumes no templates start with the URL itself, and that they
2149 have at least two characters before the URL text */
2150 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2151 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2152 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2153 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2154 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2155 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2157 if (urls[i].is_url)
2159 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2160 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2161 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2162 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2164 else
2166 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2167 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2168 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2169 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2171 if (buffer[end_offset] != '\0')
2173 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2174 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2175 if (buffer[end_offset +1] != '\0')
2177 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2178 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2183 /* Set selection with X to the first character of the URL, then the rest */
2184 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2185 char * at_pos;
2186 int at_offset;
2187 int end_offset;
2189 at_pos = strchr(templates_delim[j], 'X');
2190 at_offset = at_pos - templates_delim[j];
2191 end_offset = at_offset + strlen(urls[i].text);
2193 strcpy(buffer, "YY");
2194 buffer[0] = urls[i].text[0];
2196 st.codepage = CP_ACP;
2197 st.flags = ST_DEFAULT;
2198 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2199 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)templates_delim[j]);
2200 st.flags = ST_SELECTION;
2201 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2202 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)buffer);
2203 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2204 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)(urls[i].text + 1));
2205 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2207 /* This assumes no templates start with the URL itself, and that they
2208 have at least two characters before the URL text */
2209 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2210 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2211 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2212 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2213 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2214 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2216 if (urls[i].is_url)
2218 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2219 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2220 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2221 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2223 else
2225 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2226 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2227 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2228 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2230 if (buffer[end_offset] != '\0')
2232 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2233 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2234 if (buffer[end_offset +1] != '\0')
2236 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2237 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2242 DestroyWindow(hwndRichEdit);
2243 hwndRichEdit = NULL;
2246 /* Test detection of URLs within normal text - EM_REPLACESEL case. */
2247 /* Test just the first two URL examples for brevity */
2248 for (i = 0; i < 2; i++) {
2249 hwndRichEdit = new_richedit(parent);
2251 /* Set selection with X to the URL */
2252 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2253 char * at_pos;
2254 int at_offset;
2255 int end_offset;
2257 at_pos = strchr(templates_delim[j], 'X');
2258 at_offset = at_pos - templates_delim[j];
2259 end_offset = at_offset + strlen(urls[i].text);
2261 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2262 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2263 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2264 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urls[i].text);
2265 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2267 /* This assumes no templates start with the URL itself, and that they
2268 have at least two characters before the URL text */
2269 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2270 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2271 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2272 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2273 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2274 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2276 if (urls[i].is_url)
2278 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2279 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2280 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2281 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2283 else
2285 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2286 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2287 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2288 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2290 if (buffer[end_offset] != '\0')
2292 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2293 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2294 if (buffer[end_offset +1] != '\0')
2296 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2297 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2302 /* Set selection with X to the first character of the URL, then the rest */
2303 for (j = 0; j < sizeof(templates_delim) / sizeof(const char *); j++) {
2304 char * at_pos;
2305 int at_offset;
2306 int end_offset;
2308 at_pos = strchr(templates_delim[j], 'X');
2309 at_offset = at_pos - templates_delim[j];
2310 end_offset = at_offset + strlen(urls[i].text);
2312 strcpy(buffer, "YY");
2313 buffer[0] = urls[i].text[0];
2315 SendMessageA(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0);
2316 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)templates_delim[j]);
2317 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset, at_offset+1);
2318 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)buffer);
2319 SendMessageA(hwndRichEdit, EM_SETSEL, at_offset+1, at_offset+2);
2320 SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)(urls[i].text + 1));
2321 SendMessageA(hwndRichEdit, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
2323 /* This assumes no templates start with the URL itself, and that they
2324 have at least two characters before the URL text */
2325 ok(!check_CFE_LINK_selection(hwndRichEdit, 0, 1),
2326 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", 0, 1, buffer);
2327 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -2, at_offset -1),
2328 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -2, at_offset -1, buffer);
2329 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset -1, at_offset),
2330 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset -1, at_offset, buffer);
2332 if (urls[i].is_url)
2334 ok(check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2335 "CFE_LINK not set in (%d-%d), text: %s\n", at_offset, at_offset +1, buffer);
2336 ok(check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2337 "CFE_LINK not set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2339 else
2341 ok(!check_CFE_LINK_selection(hwndRichEdit, at_offset, at_offset +1),
2342 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", at_offset, at_offset + 1, buffer);
2343 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset -1, end_offset),
2344 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset -1, end_offset, buffer);
2346 if (buffer[end_offset] != '\0')
2348 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset, end_offset +1),
2349 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset, end_offset + 1, buffer);
2350 if (buffer[end_offset +1] != '\0')
2352 ok(!check_CFE_LINK_selection(hwndRichEdit, end_offset +1, end_offset +2),
2353 "CFE_LINK incorrectly set in (%d-%d), text: %s\n", end_offset +1, end_offset +2, buffer);
2358 DestroyWindow(hwndRichEdit);
2359 hwndRichEdit = NULL;
2362 DestroyWindow(parent);
2365 static void test_EM_SCROLL(void)
2367 int i, j;
2368 int r; /* return value */
2369 int expr; /* expected return value */
2370 HWND hwndRichEdit = new_richedit(NULL);
2371 int y_before, y_after; /* units of lines of text */
2373 /* test a richedit box containing a single line of text */
2374 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");/* one line of text */
2375 expr = 0x00010000;
2376 for (i = 0; i < 4; i++) {
2377 static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP };
2379 r = SendMessageA(hwndRichEdit, EM_SCROLL, cmd[i], 0);
2380 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2381 ok(expr == r, "EM_SCROLL improper return value returned (i == %d). "
2382 "Got 0x%08x, expected 0x%08x\n", i, r, expr);
2383 ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 "
2384 "(i == %d)\n", y_after, i);
2388 * test a richedit box that will scroll. There are two general
2389 * cases: the case without any long lines and the case with a long
2390 * line.
2392 for (i = 0; i < 2; i++) { /* iterate through different bodies of text */
2393 if (i == 0)
2394 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\nb\nc\nd\ne");
2395 else
2396 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)
2397 "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2398 "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE "
2399 "LONG LINE \nb\nc\nd\ne");
2400 for (j = 0; j < 12; j++) /* reset scroll position to top */
2401 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0);
2403 /* get first visible line */
2404 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2405 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */
2407 /* get new current first visible line */
2408 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2410 ok(((r & 0xffffff00) == 0x00010000) &&
2411 ((r & 0x000000ff) != 0x00000000),
2412 "EM_SCROLL page down didn't scroll by a small positive number of "
2413 "lines (r == 0x%08x)\n", r);
2414 ok(y_after > y_before, "EM_SCROLL page down not functioning "
2415 "(line %d scrolled to line %d\n", y_before, y_after);
2417 y_before = y_after;
2419 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */
2420 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2421 ok(((r & 0xffffff00) == 0x0001ff00),
2422 "EM_SCROLL page up didn't scroll by a small negative number of lines "
2423 "(r == 0x%08x)\n", r);
2424 ok(y_after < y_before, "EM_SCROLL page up not functioning (line "
2425 "%d scrolled to line %d\n", y_before, y_after);
2427 y_before = y_after;
2429 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */
2431 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2433 ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line "
2434 "(r == 0x%08x)\n", r);
2435 ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by "
2436 "1 line (%d scrolled to %d)\n", y_before, y_after);
2438 y_before = y_after;
2440 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */
2442 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2444 ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line "
2445 "(r == 0x%08x)\n", r);
2446 ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 "
2447 "line (%d scrolled to %d)\n", y_before, y_after);
2449 y_before = y_after;
2451 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2452 SB_LINEUP, 0); /* lineup beyond top */
2454 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2456 ok(r == 0x00010000,
2457 "EM_SCROLL line up returned indicating movement (0x%08x)\n", r);
2458 ok(y_before == y_after,
2459 "EM_SCROLL line up beyond top worked (%d)\n", y_after);
2461 y_before = y_after;
2463 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2464 SB_PAGEUP, 0);/*page up beyond top */
2466 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2468 ok(r == 0x00010000,
2469 "EM_SCROLL page up returned indicating movement (0x%08x)\n", r);
2470 ok(y_before == y_after,
2471 "EM_SCROLL page up beyond top worked (%d)\n", y_after);
2473 for (j = 0; j < 12; j++) /* page down all the way to the bottom */
2474 SendMessageA(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0);
2475 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2476 r = SendMessageA(hwndRichEdit, EM_SCROLL,
2477 SB_PAGEDOWN, 0); /* page down beyond bot */
2478 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2480 ok(r == 0x00010000,
2481 "EM_SCROLL page down returned indicating movement (0x%08x)\n", r);
2482 ok(y_before == y_after,
2483 "EM_SCROLL page down beyond bottom worked (%d -> %d)\n",
2484 y_before, y_after);
2486 y_before = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2487 r = SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down beyond bot */
2488 y_after = SendMessageA(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0);
2490 ok(r == 0x00010000,
2491 "EM_SCROLL line down returned indicating movement (0x%08x)\n", r);
2492 ok(y_before == y_after,
2493 "EM_SCROLL line down beyond bottom worked (%d -> %d)\n",
2494 y_before, y_after);
2496 DestroyWindow(hwndRichEdit);
2499 static unsigned int recursionLevel = 0;
2500 static unsigned int WM_SIZE_recursionLevel = 0;
2501 static BOOL bailedOutOfRecursion = FALSE;
2502 static LRESULT (WINAPI *richeditProc)(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
2504 static LRESULT WINAPI RicheditStupidOverrideProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2506 LRESULT r;
2508 if (bailedOutOfRecursion) return 0;
2509 if (recursionLevel >= 32) {
2510 bailedOutOfRecursion = TRUE;
2511 return 0;
2514 recursionLevel++;
2515 switch (message) {
2516 case WM_SIZE:
2517 WM_SIZE_recursionLevel++;
2518 r = richeditProc(hwnd, message, wParam, lParam);
2519 /* Because, uhhhh... I never heard of ES_DISABLENOSCROLL */
2520 ShowScrollBar(hwnd, SB_VERT, TRUE);
2521 WM_SIZE_recursionLevel--;
2522 break;
2523 default:
2524 r = richeditProc(hwnd, message, wParam, lParam);
2525 break;
2527 recursionLevel--;
2528 return r;
2531 static void test_scrollbar_visibility(void)
2533 HWND hwndRichEdit;
2534 const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
2535 SCROLLINFO si;
2536 WNDCLASSA cls;
2537 BOOL r;
2539 /* These tests show that richedit should temporarily refrain from automatically
2540 hiding or showing its scrollbars (vertical at least) when an explicit request
2541 is made via ShowScrollBar() or similar, outside of standard richedit logic.
2542 Some applications depend on forced showing (when otherwise richedit would
2543 hide the vertical scrollbar) and are thrown on an endless recursive loop
2544 if richedit auto-hides the scrollbar again. Apparently they never heard of
2545 the ES_DISABLENOSCROLL style... */
2547 hwndRichEdit = new_richedit(NULL);
2549 /* Test default scrollbar visibility behavior */
2550 memset(&si, 0, sizeof(si));
2551 si.cbSize = sizeof(si);
2552 si.fMask = SIF_PAGE | SIF_RANGE;
2553 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2554 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2555 "Vertical scrollbar is visible, should be invisible.\n");
2556 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2557 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2558 si.nPage, si.nMin, si.nMax);
2560 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2561 memset(&si, 0, sizeof(si));
2562 si.cbSize = sizeof(si);
2563 si.fMask = SIF_PAGE | SIF_RANGE;
2564 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2565 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2566 "Vertical scrollbar is visible, should be invisible.\n");
2567 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2568 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2569 si.nPage, si.nMin, si.nMax);
2571 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2572 memset(&si, 0, sizeof(si));
2573 si.cbSize = sizeof(si);
2574 si.fMask = SIF_PAGE | SIF_RANGE;
2575 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2576 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2577 "Vertical scrollbar is invisible, should be visible.\n");
2578 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2579 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2580 si.nPage, si.nMin, si.nMax);
2582 /* Oddly, setting text to NULL does *not* reset the scrollbar range,
2583 even though it hides the scrollbar */
2584 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2585 memset(&si, 0, sizeof(si));
2586 si.cbSize = sizeof(si);
2587 si.fMask = SIF_PAGE | SIF_RANGE;
2588 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2589 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2590 "Vertical scrollbar is visible, should be invisible.\n");
2591 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2592 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2593 si.nPage, si.nMin, si.nMax);
2595 /* Setting non-scrolling text again does *not* reset scrollbar range */
2596 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2597 memset(&si, 0, sizeof(si));
2598 si.cbSize = sizeof(si);
2599 si.fMask = SIF_PAGE | SIF_RANGE;
2600 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2601 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2602 "Vertical scrollbar is visible, should be invisible.\n");
2603 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2604 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2605 si.nPage, si.nMin, si.nMax);
2607 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2608 memset(&si, 0, sizeof(si));
2609 si.cbSize = sizeof(si);
2610 si.fMask = SIF_PAGE | SIF_RANGE;
2611 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2612 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2613 "Vertical scrollbar is visible, should be invisible.\n");
2614 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2615 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2616 si.nPage, si.nMin, si.nMax);
2618 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2619 memset(&si, 0, sizeof(si));
2620 si.cbSize = sizeof(si);
2621 si.fMask = SIF_PAGE | SIF_RANGE;
2622 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2623 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2624 "Vertical scrollbar is visible, should be invisible.\n");
2625 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2626 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2627 si.nPage, si.nMin, si.nMax);
2629 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2630 memset(&si, 0, sizeof(si));
2631 si.cbSize = sizeof(si);
2632 si.fMask = SIF_PAGE | SIF_RANGE;
2633 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2634 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2635 "Vertical scrollbar is visible, should be invisible.\n");
2636 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2637 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2638 si.nPage, si.nMin, si.nMax);
2640 DestroyWindow(hwndRichEdit);
2642 /* Test again, with ES_DISABLENOSCROLL style */
2643 hwndRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_DISABLENOSCROLL, NULL);
2645 /* Test default scrollbar visibility behavior */
2646 memset(&si, 0, sizeof(si));
2647 si.cbSize = sizeof(si);
2648 si.fMask = SIF_PAGE | SIF_RANGE;
2649 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2650 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2651 "Vertical scrollbar is invisible, should be visible.\n");
2652 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2653 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2654 si.nPage, si.nMin, si.nMax);
2656 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2657 memset(&si, 0, sizeof(si));
2658 si.cbSize = sizeof(si);
2659 si.fMask = SIF_PAGE | SIF_RANGE;
2660 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2661 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2662 "Vertical scrollbar is invisible, should be visible.\n");
2663 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 1,
2664 "reported page/range is %d (%d..%d) expected 0 (0..1)\n",
2665 si.nPage, si.nMin, si.nMax);
2667 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2668 memset(&si, 0, sizeof(si));
2669 si.cbSize = sizeof(si);
2670 si.fMask = SIF_PAGE | SIF_RANGE;
2671 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2672 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2673 "Vertical scrollbar is invisible, should be visible.\n");
2674 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2675 "reported page/range is %d (%d..%d)\n",
2676 si.nPage, si.nMin, si.nMax);
2678 /* Oddly, setting text to NULL does *not* reset the scrollbar range */
2679 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2680 memset(&si, 0, sizeof(si));
2681 si.cbSize = sizeof(si);
2682 si.fMask = SIF_PAGE | SIF_RANGE;
2683 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2684 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2685 "Vertical scrollbar is invisible, should be visible.\n");
2686 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2687 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2688 si.nPage, si.nMin, si.nMax);
2690 /* Setting non-scrolling text again does *not* reset scrollbar range */
2691 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2692 memset(&si, 0, sizeof(si));
2693 si.cbSize = sizeof(si);
2694 si.fMask = SIF_PAGE | SIF_RANGE;
2695 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2696 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2697 "Vertical scrollbar is invisible, should be visible.\n");
2698 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2699 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2700 si.nPage, si.nMin, si.nMax);
2702 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2703 memset(&si, 0, sizeof(si));
2704 si.cbSize = sizeof(si);
2705 si.fMask = SIF_PAGE | SIF_RANGE;
2706 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2707 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2708 "Vertical scrollbar is invisible, should be visible.\n");
2709 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2710 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2711 si.nPage, si.nMin, si.nMax);
2713 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2714 memset(&si, 0, sizeof(si));
2715 si.cbSize = sizeof(si);
2716 si.fMask = SIF_PAGE | SIF_RANGE;
2717 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2718 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2719 "Vertical scrollbar is invisible, should be visible.\n");
2720 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2721 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2722 si.nPage, si.nMin, si.nMax);
2724 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"");
2725 memset(&si, 0, sizeof(si));
2726 si.cbSize = sizeof(si);
2727 si.fMask = SIF_PAGE | SIF_RANGE;
2728 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2729 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2730 "Vertical scrollbar is invisible, should be visible.\n");
2731 ok(si.nPage != 0 && si.nMin == 0 && si.nMax > 1,
2732 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2733 si.nPage, si.nMin, si.nMax);
2735 DestroyWindow(hwndRichEdit);
2737 /* Test behavior with explicit visibility request, using ShowScrollBar() */
2738 hwndRichEdit = new_richedit(NULL);
2740 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2741 ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
2742 memset(&si, 0, sizeof(si));
2743 si.cbSize = sizeof(si);
2744 si.fMask = SIF_PAGE | SIF_RANGE;
2745 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2746 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2747 "Vertical scrollbar is invisible, should be visible.\n");
2748 todo_wine {
2749 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2750 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2751 si.nPage, si.nMin, si.nMax);
2754 /* Ditto, see above */
2755 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2756 memset(&si, 0, sizeof(si));
2757 si.cbSize = sizeof(si);
2758 si.fMask = SIF_PAGE | SIF_RANGE;
2759 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2760 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2761 "Vertical scrollbar is invisible, should be visible.\n");
2762 todo_wine {
2763 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2764 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2765 si.nPage, si.nMin, si.nMax);
2768 /* Ditto, see above */
2769 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2770 memset(&si, 0, sizeof(si));
2771 si.cbSize = sizeof(si);
2772 si.fMask = SIF_PAGE | SIF_RANGE;
2773 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2774 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2775 "Vertical scrollbar is invisible, should be visible.\n");
2776 todo_wine {
2777 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2778 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2779 si.nPage, si.nMin, si.nMax);
2782 /* Ditto, see above */
2783 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
2784 memset(&si, 0, sizeof(si));
2785 si.cbSize = sizeof(si);
2786 si.fMask = SIF_PAGE | SIF_RANGE;
2787 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2788 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2789 "Vertical scrollbar is invisible, should be visible.\n");
2790 todo_wine {
2791 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2792 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2793 si.nPage, si.nMin, si.nMax);
2796 /* Ditto, see above */
2797 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2798 memset(&si, 0, sizeof(si));
2799 si.cbSize = sizeof(si);
2800 si.fMask = SIF_PAGE | SIF_RANGE;
2801 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2802 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2803 "Vertical scrollbar is invisible, should be visible.\n");
2804 todo_wine {
2805 ok(si.nPage == 0 && si.nMin == 0 && si.nMax == 100,
2806 "reported page/range is %d (%d..%d) expected 0 (0..100)\n",
2807 si.nPage, si.nMin, si.nMax);
2810 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2811 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2812 memset(&si, 0, sizeof(si));
2813 si.cbSize = sizeof(si);
2814 si.fMask = SIF_PAGE | SIF_RANGE;
2815 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2816 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2817 "Vertical scrollbar is visible, should be invisible.\n");
2818 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2819 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2820 si.nPage, si.nMin, si.nMax);
2822 DestroyWindow(hwndRichEdit);
2824 hwndRichEdit = new_richedit(NULL);
2826 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2827 memset(&si, 0, sizeof(si));
2828 si.cbSize = sizeof(si);
2829 si.fMask = SIF_PAGE | SIF_RANGE;
2830 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2831 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2832 "Vertical scrollbar is visible, should be invisible.\n");
2833 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2834 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2835 si.nPage, si.nMin, si.nMax);
2837 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2838 memset(&si, 0, sizeof(si));
2839 si.cbSize = sizeof(si);
2840 si.fMask = SIF_PAGE | SIF_RANGE;
2841 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2842 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2843 "Vertical scrollbar is visible, should be invisible.\n");
2844 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2845 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2846 si.nPage, si.nMin, si.nMax);
2848 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2849 memset(&si, 0, sizeof(si));
2850 si.cbSize = sizeof(si);
2851 si.fMask = SIF_PAGE | SIF_RANGE;
2852 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2853 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2854 "Vertical scrollbar is visible, should be invisible.\n");
2855 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2856 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2857 si.nPage, si.nMin, si.nMax);
2859 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2860 memset(&si, 0, sizeof(si));
2861 si.cbSize = sizeof(si);
2862 si.fMask = SIF_PAGE | SIF_RANGE;
2863 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2864 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2865 "Vertical scrollbar is visible, should be invisible.\n");
2866 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2867 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2868 si.nPage, si.nMin, si.nMax);
2870 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2871 memset(&si, 0, sizeof(si));
2872 si.cbSize = sizeof(si);
2873 si.fMask = SIF_PAGE | SIF_RANGE;
2874 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2875 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2876 "Vertical scrollbar is invisible, should be visible.\n");
2877 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2878 "reported page/range is %d (%d..%d)\n",
2879 si.nPage, si.nMin, si.nMax);
2881 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
2882 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2883 memset(&si, 0, sizeof(si));
2884 si.cbSize = sizeof(si);
2885 si.fMask = SIF_PAGE | SIF_RANGE;
2886 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2887 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2888 "Vertical scrollbar is visible, should be invisible.\n");
2889 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2890 "reported page/range is %d (%d..%d)\n",
2891 si.nPage, si.nMin, si.nMax);
2893 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2894 memset(&si, 0, sizeof(si));
2895 si.cbSize = sizeof(si);
2896 si.fMask = SIF_PAGE | SIF_RANGE;
2897 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2898 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2899 "Vertical scrollbar is visible, should be invisible.\n");
2900 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2901 "reported page/range is %d (%d..%d)\n",
2902 si.nPage, si.nMin, si.nMax);
2904 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
2905 EM_SCROLL will make visible any forcefully invisible scrollbar */
2906 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
2907 memset(&si, 0, sizeof(si));
2908 si.cbSize = sizeof(si);
2909 si.fMask = SIF_PAGE | SIF_RANGE;
2910 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2911 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2912 "Vertical scrollbar is invisible, should be visible.\n");
2913 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2914 "reported page/range is %d (%d..%d)\n",
2915 si.nPage, si.nMin, si.nMax);
2917 ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
2918 memset(&si, 0, sizeof(si));
2919 si.cbSize = sizeof(si);
2920 si.fMask = SIF_PAGE | SIF_RANGE;
2921 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2922 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2923 "Vertical scrollbar is visible, should be invisible.\n");
2924 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2925 "reported page/range is %d (%d..%d)\n",
2926 si.nPage, si.nMin, si.nMax);
2928 /* Again, EM_SCROLL, with SB_LINEUP */
2929 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
2930 memset(&si, 0, sizeof(si));
2931 si.cbSize = sizeof(si);
2932 si.fMask = SIF_PAGE | SIF_RANGE;
2933 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2934 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2935 "Vertical scrollbar is invisible, should be visible.\n");
2936 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2937 "reported page/range is %d (%d..%d)\n",
2938 si.nPage, si.nMin, si.nMax);
2940 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2941 memset(&si, 0, sizeof(si));
2942 si.cbSize = sizeof(si);
2943 si.fMask = SIF_PAGE | SIF_RANGE;
2944 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2945 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
2946 "Vertical scrollbar is visible, should be invisible.\n");
2947 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2948 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
2949 si.nPage, si.nMin, si.nMax);
2951 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
2952 memset(&si, 0, sizeof(si));
2953 si.cbSize = sizeof(si);
2954 si.fMask = SIF_PAGE | SIF_RANGE;
2955 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2956 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2957 "Vertical scrollbar is invisible, should be visible.\n");
2958 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
2959 "reported page/range is %d (%d..%d)\n",
2960 si.nPage, si.nMin, si.nMax);
2962 DestroyWindow(hwndRichEdit);
2965 /* Test behavior with explicit visibility request, using SetWindowLongA()() */
2966 hwndRichEdit = new_richedit(NULL);
2968 #define ENABLE_WS_VSCROLL(hwnd) \
2969 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) | WS_VSCROLL)
2970 #define DISABLE_WS_VSCROLL(hwnd) \
2971 SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE) & ~WS_VSCROLL)
2973 /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
2974 ENABLE_WS_VSCROLL(hwndRichEdit);
2975 memset(&si, 0, sizeof(si));
2976 si.cbSize = sizeof(si);
2977 si.fMask = SIF_PAGE | SIF_RANGE;
2978 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2979 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2980 "Vertical scrollbar is invisible, should be visible.\n");
2981 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2982 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2983 si.nPage, si.nMin, si.nMax);
2985 /* Ditto, see above */
2986 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
2987 memset(&si, 0, sizeof(si));
2988 si.cbSize = sizeof(si);
2989 si.fMask = SIF_PAGE | SIF_RANGE;
2990 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
2991 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
2992 "Vertical scrollbar is invisible, should be visible.\n");
2993 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
2994 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
2995 si.nPage, si.nMin, si.nMax);
2997 /* Ditto, see above */
2998 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
2999 memset(&si, 0, sizeof(si));
3000 si.cbSize = sizeof(si);
3001 si.fMask = SIF_PAGE | SIF_RANGE;
3002 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3003 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3004 "Vertical scrollbar is invisible, should be visible.\n");
3005 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3006 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3007 si.nPage, si.nMin, si.nMax);
3009 /* Ditto, see above */
3010 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
3011 memset(&si, 0, sizeof(si));
3012 si.cbSize = sizeof(si);
3013 si.fMask = SIF_PAGE | SIF_RANGE;
3014 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3015 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3016 "Vertical scrollbar is invisible, should be visible.\n");
3017 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3018 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3019 si.nPage, si.nMin, si.nMax);
3021 /* Ditto, see above */
3022 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3023 memset(&si, 0, sizeof(si));
3024 si.cbSize = sizeof(si);
3025 si.fMask = SIF_PAGE | SIF_RANGE;
3026 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3027 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3028 "Vertical scrollbar is invisible, should be visible.\n");
3029 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3030 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3031 si.nPage, si.nMin, si.nMax);
3033 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3034 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3035 memset(&si, 0, sizeof(si));
3036 si.cbSize = sizeof(si);
3037 si.fMask = SIF_PAGE | SIF_RANGE;
3038 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3039 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3040 "Vertical scrollbar is visible, should be invisible.\n");
3041 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3042 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3043 si.nPage, si.nMin, si.nMax);
3045 DestroyWindow(hwndRichEdit);
3047 hwndRichEdit = new_richedit(NULL);
3049 DISABLE_WS_VSCROLL(hwndRichEdit);
3050 memset(&si, 0, sizeof(si));
3051 si.cbSize = sizeof(si);
3052 si.fMask = SIF_PAGE | SIF_RANGE;
3053 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3054 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3055 "Vertical scrollbar is visible, should be invisible.\n");
3056 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3057 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3058 si.nPage, si.nMin, si.nMax);
3060 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3061 memset(&si, 0, sizeof(si));
3062 si.cbSize = sizeof(si);
3063 si.fMask = SIF_PAGE | SIF_RANGE;
3064 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3065 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3066 "Vertical scrollbar is visible, should be invisible.\n");
3067 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3068 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3069 si.nPage, si.nMin, si.nMax);
3071 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
3072 memset(&si, 0, sizeof(si));
3073 si.cbSize = sizeof(si);
3074 si.fMask = SIF_PAGE | SIF_RANGE;
3075 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3076 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3077 "Vertical scrollbar is visible, should be invisible.\n");
3078 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3079 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3080 si.nPage, si.nMin, si.nMax);
3082 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3083 memset(&si, 0, sizeof(si));
3084 si.cbSize = sizeof(si);
3085 si.fMask = SIF_PAGE | SIF_RANGE;
3086 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3087 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3088 "Vertical scrollbar is visible, should be invisible.\n");
3089 ok(si.nPage == 0 && si.nMin == 0 && (si.nMax == 0 || si.nMax == 100),
3090 "reported page/range is %d (%d..%d) expected all 0 or nMax=100\n",
3091 si.nPage, si.nMin, si.nMax);
3093 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3094 memset(&si, 0, sizeof(si));
3095 si.cbSize = sizeof(si);
3096 si.fMask = SIF_PAGE | SIF_RANGE;
3097 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3098 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3099 "Vertical scrollbar is invisible, should be visible.\n");
3100 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3101 "reported page/range is %d (%d..%d)\n",
3102 si.nPage, si.nMin, si.nMax);
3104 /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
3105 DISABLE_WS_VSCROLL(hwndRichEdit);
3106 memset(&si, 0, sizeof(si));
3107 si.cbSize = sizeof(si);
3108 si.fMask = SIF_PAGE | SIF_RANGE;
3109 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3110 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3111 "Vertical scrollbar is visible, should be invisible.\n");
3112 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3113 "reported page/range is %d (%d..%d)\n",
3114 si.nPage, si.nMin, si.nMax);
3116 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
3117 memset(&si, 0, sizeof(si));
3118 si.cbSize = sizeof(si);
3119 si.fMask = SIF_PAGE | SIF_RANGE;
3120 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3121 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3122 "Vertical scrollbar is visible, should be invisible.\n");
3123 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3124 "reported page/range is %d (%d..%d) expected nMax/nPage nonzero\n",
3125 si.nPage, si.nMin, si.nMax);
3127 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
3128 memset(&si, 0, sizeof(si));
3129 si.cbSize = sizeof(si);
3130 si.fMask = SIF_PAGE | SIF_RANGE;
3131 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3132 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3133 "Vertical scrollbar is invisible, should be visible.\n");
3134 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3135 "reported page/range is %d (%d..%d)\n",
3136 si.nPage, si.nMin, si.nMax);
3138 DISABLE_WS_VSCROLL(hwndRichEdit);
3139 memset(&si, 0, sizeof(si));
3140 si.cbSize = sizeof(si);
3141 si.fMask = SIF_PAGE | SIF_RANGE;
3142 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3143 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3144 "Vertical scrollbar is visible, should be invisible.\n");
3145 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3146 "reported page/range is %d (%d..%d)\n",
3147 si.nPage, si.nMin, si.nMax);
3149 /* Testing effect of EM_SCROLL on scrollbar visibility. It seems that
3150 EM_SCROLL will make visible any forcefully invisible scrollbar */
3151 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0);
3152 memset(&si, 0, sizeof(si));
3153 si.cbSize = sizeof(si);
3154 si.fMask = SIF_PAGE | SIF_RANGE;
3155 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3156 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3157 "Vertical scrollbar is invisible, should be visible.\n");
3158 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3159 "reported page/range is %d (%d..%d)\n",
3160 si.nPage, si.nMin, si.nMax);
3162 DISABLE_WS_VSCROLL(hwndRichEdit);
3163 memset(&si, 0, sizeof(si));
3164 si.cbSize = sizeof(si);
3165 si.fMask = SIF_PAGE | SIF_RANGE;
3166 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3167 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) == 0),
3168 "Vertical scrollbar is visible, should be invisible.\n");
3169 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3170 "reported page/range is %d (%d..%d)\n",
3171 si.nPage, si.nMin, si.nMax);
3173 /* Again, EM_SCROLL, with SB_LINEUP */
3174 SendMessageA(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0);
3175 memset(&si, 0, sizeof(si));
3176 si.cbSize = sizeof(si);
3177 si.fMask = SIF_PAGE | SIF_RANGE;
3178 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3179 ok (((GetWindowLongA(hwndRichEdit, GWL_STYLE) & WS_VSCROLL) != 0),
3180 "Vertical scrollbar is invisible, should be visible.\n");
3181 ok(si.nPage != 0 && si.nMin == 0 && si.nMax != 0,
3182 "reported page/range is %d (%d..%d)\n",
3183 si.nPage, si.nMin, si.nMax);
3185 DestroyWindow(hwndRichEdit);
3187 /* This window proc models what is going on with Corman Lisp 3.0.
3188 At WM_SIZE, this proc unconditionally calls ShowScrollBar() to
3189 force the scrollbar into visibility. Recursion should NOT happen
3190 as a result of this action.
3192 r = GetClassInfoA(NULL, RICHEDIT_CLASS20A, &cls);
3193 if (r) {
3194 richeditProc = cls.lpfnWndProc;
3195 cls.lpfnWndProc = RicheditStupidOverrideProcA;
3196 cls.lpszClassName = "RicheditStupidOverride";
3197 if(!RegisterClassA(&cls)) assert(0);
3199 recursionLevel = 0;
3200 WM_SIZE_recursionLevel = 0;
3201 bailedOutOfRecursion = FALSE;
3202 hwndRichEdit = new_window(cls.lpszClassName, ES_MULTILINE, NULL);
3203 ok(!bailedOutOfRecursion,
3204 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3206 recursionLevel = 0;
3207 WM_SIZE_recursionLevel = 0;
3208 bailedOutOfRecursion = FALSE;
3209 MoveWindow(hwndRichEdit, 0, 0, 250, 100, TRUE);
3210 ok(!bailedOutOfRecursion,
3211 "WM_SIZE/scrollbar mutual recursion detected, expected none!\n");
3213 /* Unblock window in order to process WM_DESTROY */
3214 recursionLevel = 0;
3215 bailedOutOfRecursion = FALSE;
3216 WM_SIZE_recursionLevel = 0;
3217 DestroyWindow(hwndRichEdit);
3221 static void test_EM_SETUNDOLIMIT(void)
3223 /* cases we test for:
3224 * default behaviour - limiting at 100 undo's
3225 * undo disabled - setting a limit of 0
3226 * undo limited - undo limit set to some to some number, like 2
3227 * bad input - sending a negative number should default to 100 undo's */
3229 HWND hwndRichEdit = new_richedit(NULL);
3230 CHARRANGE cr;
3231 int i;
3232 int result;
3234 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
3235 cr.cpMin = 0;
3236 cr.cpMax = 1;
3237 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
3238 /*Load "x" into the clipboard. Paste is an easy, undo'able operation.
3239 also, multiple pastes don't combine like WM_CHAR would */
3240 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3242 /* first case - check the default */
3243 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3244 for (i=0; i<101; i++) /* Put 101 undo's on the stack */
3245 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3246 for (i=0; i<100; i++) /* Undo 100 of them */
3247 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3248 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3249 "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n");
3251 /* second case - cannot undo */
3252 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3253 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0);
3254 SendMessageA(hwndRichEdit,
3255 WM_PASTE, 0, 0); /* Try to put something in the undo stack */
3256 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3257 "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n");
3259 /* third case - set it to an arbitrary number */
3260 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0);
3261 SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0);
3262 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3263 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3264 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
3265 /* If SETUNDOLIMIT is working, there should only be two undo's after this */
3266 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0,0),
3267 "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n");
3268 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3269 ok(SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3270 "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n");
3271 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
3272 ok(!SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0),
3273 "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n");
3275 /* fourth case - setting negative numbers should default to 100 undos */
3276 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
3277 result = SendMessageA(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0);
3278 ok (result == 100,
3279 "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result);
3281 DestroyWindow(hwndRichEdit);
3284 static void test_ES_PASSWORD(void)
3286 /* This isn't hugely testable, so we're just going to run it through its paces */
3288 HWND hwndRichEdit = new_richedit(NULL);
3289 WCHAR result;
3291 /* First, check the default of a regular control */
3292 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3293 ok (result == 0,
3294 "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result);
3296 /* Now, set it to something normal */
3297 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0);
3298 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3299 ok (result == 120,
3300 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3302 /* Now, set it to something odd */
3303 SendMessageA(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0);
3304 result = SendMessageA(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0);
3305 ok (result == 1234,
3306 "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result);
3307 DestroyWindow(hwndRichEdit);
3310 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3311 LPBYTE pbBuff,
3312 LONG cb,
3313 LONG *pcb)
3315 char** str = (char**)dwCookie;
3316 *pcb = cb;
3317 if (*pcb > 0) {
3318 memcpy(*str, pbBuff, *pcb);
3319 *str += *pcb;
3321 return 0;
3324 static void test_WM_SETTEXT(void)
3326 HWND hwndRichEdit = new_richedit(NULL);
3327 const char * TestItem1 = "TestSomeText";
3328 const char * TestItem2 = "TestSomeText\r";
3329 const char * TestItem2_after = "TestSomeText\r\n";
3330 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3331 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3332 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3333 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3334 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3335 const char * TestItem5_after = "TestSomeText TestSomeText";
3336 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3337 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3338 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3339 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3341 const char rtftextA[] = "{\\rtf sometext}";
3342 const char urtftextA[] = "{\\urtf sometext}";
3343 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3344 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3345 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3347 char buf[1024] = {0};
3348 WCHAR bufW[1024] = {0};
3349 LRESULT result;
3351 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3352 any solitary \r to be converted to \r\n on return. Properly paired
3353 \r\n are not affected. It also shows that the special sequence \r\r\n
3354 gets converted to a single space.
3357 #define TEST_SETTEXT(a, b) \
3358 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3359 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3360 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3361 ok (result == lstrlenA(buf), \
3362 "WM_GETTEXT returned %ld instead of expected %u\n", \
3363 result, lstrlenA(buf)); \
3364 result = strcmp(b, buf); \
3365 ok(result == 0, \
3366 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3368 TEST_SETTEXT(TestItem1, TestItem1)
3369 TEST_SETTEXT(TestItem2, TestItem2_after)
3370 TEST_SETTEXT(TestItem3, TestItem3_after)
3371 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3372 TEST_SETTEXT(TestItem4, TestItem4_after)
3373 TEST_SETTEXT(TestItem5, TestItem5_after)
3374 TEST_SETTEXT(TestItem6, TestItem6_after)
3375 TEST_SETTEXT(TestItem7, TestItem7_after)
3377 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3378 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3379 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3380 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3381 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3382 DestroyWindow(hwndRichEdit);
3383 #undef TEST_SETTEXT
3385 #define TEST_SETTEXTW(a, b) \
3386 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3387 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3388 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3389 ok (result == lstrlenW(bufW), \
3390 "WM_GETTEXT returned %ld instead of expected %u\n", \
3391 result, lstrlenW(bufW)); \
3392 result = lstrcmpW(b, bufW); \
3393 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3395 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3396 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3397 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3398 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3399 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3400 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3401 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3402 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3403 DestroyWindow(hwndRichEdit);
3404 #undef TEST_SETTEXTW
3407 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3408 resent to the callkack. */
3409 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3410 LPBYTE pbBuff,
3411 LONG cb,
3412 LONG *pcb)
3414 char** str = (char**)dwCookie;
3415 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3416 *pcb = 0;
3417 if (cb > 0) {
3418 memcpy(*str, pbBuff, cb);
3419 *str += cb;
3420 *pcb = 1;
3422 return 0;
3425 static 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 check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4458 LRESULT result;
4459 int start, end;
4461 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4463 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4465 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4467 if (setsel->_getsel_todo_wine) {
4468 todo_wine {
4469 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4471 } else {
4472 ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_SETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end);
4476 static void test_EM_SETSEL(void)
4478 HWND hwndRichEdit = new_richedit(NULL);
4479 int i;
4480 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4482 /* sending some text to the window */
4483 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4484 /* 01234567890123456*/
4485 /* 10 */
4487 for (i = 0; i < num_tests; i++) {
4488 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4491 DestroyWindow(hwndRichEdit);
4494 static void test_EM_REPLACESEL(int redraw)
4496 HWND hwndRichEdit = new_richedit(NULL);
4497 char buffer[1024] = {0};
4498 int r;
4499 GETTEXTEX getText;
4500 CHARRANGE cr;
4502 /* sending some text to the window */
4503 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4504 /* 01234567890123456*/
4505 /* 10 */
4507 /* FIXME add more tests */
4508 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4509 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4510 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4511 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4512 r = strcmp(buffer, "testing");
4513 ok(0 == r, "expected %d, got %d\n", 0, r);
4515 DestroyWindow(hwndRichEdit);
4517 hwndRichEdit = new_richedit(NULL);
4519 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4520 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4522 /* Test behavior with carriage returns and newlines */
4523 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4524 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4525 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4526 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4527 r = strcmp(buffer, "RichEdit1");
4528 ok(0 == r, "expected %d, got %d\n", 0, r);
4529 getText.cb = 1024;
4530 getText.codepage = CP_ACP;
4531 getText.flags = GT_DEFAULT;
4532 getText.lpDefaultChar = NULL;
4533 getText.lpUsedDefChar = NULL;
4534 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4535 ok(strcmp(buffer, "RichEdit1") == 0,
4536 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4538 /* Test number of lines reported after EM_REPLACESEL */
4539 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4540 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4542 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4543 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4544 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4545 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4546 r = strcmp(buffer, "RichEdit1\r\n");
4547 ok(0 == r, "expected %d, got %d\n", 0, r);
4548 getText.cb = 1024;
4549 getText.codepage = CP_ACP;
4550 getText.flags = GT_DEFAULT;
4551 getText.lpDefaultChar = NULL;
4552 getText.lpUsedDefChar = NULL;
4553 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4554 ok(strcmp(buffer, "RichEdit1\r") == 0,
4555 "EM_GETTEXTEX returned incorrect string\n");
4557 /* Test number of lines reported after EM_REPLACESEL */
4558 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4559 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4561 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4562 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4563 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4565 /* Test number of lines reported after EM_REPLACESEL */
4566 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4567 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4569 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4570 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4571 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4572 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4574 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4575 r = strcmp(buffer, "RichEdit1\r\n");
4576 ok(0 == r, "expected %d, got %d\n", 0, r);
4577 getText.cb = 1024;
4578 getText.codepage = CP_ACP;
4579 getText.flags = GT_DEFAULT;
4580 getText.lpDefaultChar = NULL;
4581 getText.lpUsedDefChar = NULL;
4582 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4583 ok(strcmp(buffer, "RichEdit1\r") == 0,
4584 "EM_GETTEXTEX returned incorrect string\n");
4586 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4587 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4588 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4589 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4591 /* The following tests show that richedit should handle the special \r\r\n
4592 sequence by turning it into a single space on insertion. However,
4593 EM_REPLACESEL on WinXP returns the number of characters in the original
4594 string.
4597 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4598 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4599 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4600 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4601 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4602 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4603 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4605 /* Test the actual string */
4606 getText.cb = 1024;
4607 getText.codepage = CP_ACP;
4608 getText.flags = GT_DEFAULT;
4609 getText.lpDefaultChar = NULL;
4610 getText.lpUsedDefChar = NULL;
4611 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4612 ok(strcmp(buffer, "\r\r") == 0,
4613 "EM_GETTEXTEX returned incorrect string\n");
4615 /* Test number of lines reported after EM_REPLACESEL */
4616 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4617 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4619 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4620 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4621 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4622 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4623 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4624 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4625 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4627 /* Test the actual string */
4628 getText.cb = 1024;
4629 getText.codepage = CP_ACP;
4630 getText.flags = GT_DEFAULT;
4631 getText.lpDefaultChar = NULL;
4632 getText.lpUsedDefChar = NULL;
4633 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4634 ok(strcmp(buffer, " ") == 0,
4635 "EM_GETTEXTEX returned incorrect string\n");
4637 /* Test number of lines reported after EM_REPLACESEL */
4638 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4639 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4641 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4642 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4643 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4644 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4645 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4646 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4647 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4649 /* Test the actual string */
4650 getText.cb = 1024;
4651 getText.codepage = CP_ACP;
4652 getText.flags = GT_DEFAULT;
4653 getText.lpDefaultChar = NULL;
4654 getText.lpUsedDefChar = NULL;
4655 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4656 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4657 "EM_GETTEXTEX returned incorrect string\n");
4659 /* Test number of lines reported after EM_REPLACESEL */
4660 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4661 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4663 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4664 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4665 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4666 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4667 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4668 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4669 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4671 /* Test the actual string */
4672 getText.cb = 1024;
4673 getText.codepage = CP_ACP;
4674 getText.flags = GT_DEFAULT;
4675 getText.lpDefaultChar = NULL;
4676 getText.lpUsedDefChar = NULL;
4677 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4678 ok(strcmp(buffer, " \r") == 0,
4679 "EM_GETTEXTEX returned incorrect string\n");
4681 /* Test number of lines reported after EM_REPLACESEL */
4682 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4683 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4685 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4686 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4687 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4688 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4689 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4690 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4691 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4693 /* Test the actual string */
4694 getText.cb = 1024;
4695 getText.codepage = CP_ACP;
4696 getText.flags = GT_DEFAULT;
4697 getText.lpDefaultChar = NULL;
4698 getText.lpUsedDefChar = NULL;
4699 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4700 ok(strcmp(buffer, " \r\r") == 0,
4701 "EM_GETTEXTEX returned incorrect string\n");
4703 /* Test number of lines reported after EM_REPLACESEL */
4704 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4705 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4707 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4708 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4709 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4710 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4711 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4712 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4713 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4715 /* Test the actual string */
4716 getText.cb = 1024;
4717 getText.codepage = CP_ACP;
4718 getText.flags = GT_DEFAULT;
4719 getText.lpDefaultChar = NULL;
4720 getText.lpUsedDefChar = NULL;
4721 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4722 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4723 "EM_GETTEXTEX returned incorrect string\n");
4725 /* Test number of lines reported after EM_REPLACESEL */
4726 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4727 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4729 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4730 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4731 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4732 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4733 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4734 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4735 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4737 /* Test the actual string */
4738 getText.cb = 1024;
4739 getText.codepage = CP_ACP;
4740 getText.flags = GT_DEFAULT;
4741 getText.lpDefaultChar = NULL;
4742 getText.lpUsedDefChar = NULL;
4743 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4744 ok(strcmp(buffer, "\r\r") == 0,
4745 "EM_GETTEXTEX returned incorrect string\n");
4747 /* Test number of lines reported after EM_REPLACESEL */
4748 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4749 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4751 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4752 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4753 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4754 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4755 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4756 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4757 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4759 /* Test the actual string */
4760 getText.cb = 1024;
4761 getText.codepage = CP_ACP;
4762 getText.flags = GT_DEFAULT;
4763 getText.lpDefaultChar = NULL;
4764 getText.lpUsedDefChar = NULL;
4765 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4766 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4767 "EM_GETTEXTEX returned incorrect string\n");
4769 /* Test number of lines reported after EM_REPLACESEL */
4770 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4771 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4773 if (!redraw)
4774 /* This is needed to avoid interferring with keybd_event calls
4775 * on other tests that simulate keyboard events. */
4776 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4778 DestroyWindow(hwndRichEdit);
4781 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4782 * to test the state of the modifiers (Ctrl/Alt/Shift).
4784 * Therefore Ctrl-<key> keystrokes need to be simulated with
4785 * keybd_event or by using SetKeyboardState to set the modifiers
4786 * and SendMessage to simulate the keystrokes.
4788 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4790 LRESULT result;
4791 hold_key(VK_CONTROL);
4792 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
4793 release_key(VK_CONTROL);
4794 return result;
4797 static void test_WM_PASTE(void)
4799 int result;
4800 char buffer[1024] = {0};
4801 const char* text1 = "testing paste\r";
4802 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4803 const char* text1_after = "testing paste\r\n";
4804 const char* text2 = "testing paste\r\rtesting paste";
4805 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4806 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4807 HWND hwndRichEdit = new_richedit(NULL);
4809 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4810 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
4812 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4813 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4814 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4815 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4816 /* Pasted text should be visible at this step */
4817 result = strcmp(text1_step1, buffer);
4818 ok(result == 0,
4819 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4821 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4822 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4823 /* Text should be the same as before (except for \r -> \r\n conversion) */
4824 result = strcmp(text1_after, buffer);
4825 ok(result == 0,
4826 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4828 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
4829 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
4830 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4831 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4832 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4833 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4834 /* Pasted text should be visible at this step */
4835 result = strcmp(text3, buffer);
4836 ok(result == 0,
4837 "test paste: strcmp = %i\n", result);
4838 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4839 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4840 /* Text should be the same as before (except for \r -> \r\n conversion) */
4841 result = strcmp(text2_after, buffer);
4842 ok(result == 0,
4843 "test paste: strcmp = %i\n", result);
4844 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
4845 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4846 /* Text should revert to post-paste state */
4847 result = strcmp(buffer,text3);
4848 ok(result == 0,
4849 "test paste: strcmp = %i\n", result);
4851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4852 /* Send WM_CHAR to simulate Ctrl-V */
4853 SendMessageA(hwndRichEdit, WM_CHAR, 22,
4854 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4855 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4856 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
4857 result = strcmp(buffer,"");
4858 ok(result == 0,
4859 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4861 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
4862 * with SetKeyboard state. */
4864 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4865 /* Simulates paste (Ctrl-V) */
4866 hold_key(VK_CONTROL);
4867 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
4868 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
4869 release_key(VK_CONTROL);
4870 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4871 result = strcmp(buffer,"paste");
4872 ok(result == 0,
4873 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4875 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4876 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
4877 /* Simulates copy (Ctrl-C) */
4878 hold_key(VK_CONTROL);
4879 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
4880 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
4881 release_key(VK_CONTROL);
4882 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4883 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4884 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4885 result = strcmp(buffer,"testing");
4886 ok(result == 0,
4887 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4889 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
4890 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
4891 /* Simulates select all (Ctrl-A) */
4892 hold_key(VK_CONTROL);
4893 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
4894 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
4895 /* Simulates select cut (Ctrl-X) */
4896 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
4897 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
4898 release_key(VK_CONTROL);
4899 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4900 result = strcmp(buffer,"");
4901 ok(result == 0,
4902 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4903 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4904 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4905 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4906 result = strcmp(buffer,"cut\r\n");
4907 todo_wine ok(result == 0,
4908 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4909 /* Simulates undo (Ctrl-Z) */
4910 hold_key(VK_CONTROL);
4911 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
4912 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
4913 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4914 result = strcmp(buffer,"");
4915 ok(result == 0,
4916 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4917 /* Simulates redo (Ctrl-Y) */
4918 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
4919 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
4920 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4921 result = strcmp(buffer,"cut\r\n");
4922 todo_wine ok(result == 0,
4923 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
4924 release_key(VK_CONTROL);
4926 DestroyWindow(hwndRichEdit);
4929 static void test_EM_FORMATRANGE(void)
4931 int r, i, tpp_x, tpp_y;
4932 HDC hdc;
4933 HWND hwndRichEdit = new_richedit(NULL);
4934 FORMATRANGE fr;
4935 BOOL skip_non_english;
4936 static const struct {
4937 const char *string; /* The string */
4938 int first; /* First 'pagebreak', 0 for don't care */
4939 int second; /* Second 'pagebreak', 0 for don't care */
4940 } fmtstrings[] = {
4941 {"WINE wine", 0, 0},
4942 {"WINE wineWine", 0, 0},
4943 {"WINE\r\nwine\r\nwine", 5, 10},
4944 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
4945 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
4948 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
4949 if (skip_non_english)
4950 skip("Skipping some tests on non-English platform\n");
4952 hdc = GetDC(hwndRichEdit);
4953 ok(hdc != NULL, "Could not get HDC\n");
4955 /* Calculate the twips per pixel */
4956 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
4957 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
4959 /* Test the simple case where all the text fits in the page rect. */
4960 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
4961 fr.hdc = fr.hdcTarget = hdc;
4962 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4963 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
4964 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
4965 fr.chrg.cpMin = 0;
4966 fr.chrg.cpMax = -1;
4967 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4968 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
4970 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
4971 fr.rc.bottom = fr.rcPage.bottom;
4972 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
4973 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
4975 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
4977 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
4979 GETTEXTLENGTHEX gtl;
4980 SIZE stringsize;
4981 int len;
4983 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
4985 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
4986 gtl.codepage = CP_ACP;
4987 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
4989 /* Get some size information for the string */
4990 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
4992 /* Define the box to be half the width needed and a bit larger than the height.
4993 * Changes to the width means we have at least 2 pages. Changes to the height
4994 * is done so we can check the changing of fr.rc.bottom.
4996 fr.hdc = fr.hdcTarget = hdc;
4997 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
4998 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
4999 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5001 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5002 todo_wine {
5003 ok(r == len, "Expected %d, got %d\n", len, r);
5006 /* We know that the page can't hold the full string. See how many characters
5007 * are on the first one
5009 fr.chrg.cpMin = 0;
5010 fr.chrg.cpMax = -1;
5011 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5012 todo_wine {
5013 if (! skip_non_english)
5014 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5016 if (fmtstrings[i].first)
5017 todo_wine {
5018 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5020 else
5021 ok(r < len, "Expected < %d, got %d\n", len, r);
5023 /* Do another page */
5024 fr.chrg.cpMin = r;
5025 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5026 if (fmtstrings[i].second)
5027 todo_wine {
5028 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5030 else if (! skip_non_english)
5031 ok (r < len, "Expected < %d, got %d\n", len, r);
5033 /* There is at least on more page, but we don't care */
5035 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5036 todo_wine {
5037 ok(r == len, "Expected %d, got %d\n", len, r);
5041 ReleaseDC(NULL, hdc);
5042 DestroyWindow(hwndRichEdit);
5045 static int nCallbackCount = 0;
5047 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5048 LONG cb, LONG* pcb)
5050 const char text[] = {'t','e','s','t'};
5052 if (sizeof(text) <= cb)
5054 if ((int)dwCookie != nCallbackCount)
5056 *pcb = 0;
5057 return 0;
5060 memcpy (pbBuff, text, sizeof(text));
5061 *pcb = sizeof(text);
5063 nCallbackCount++;
5065 return 0;
5067 else
5068 return 1; /* indicates callback failed */
5071 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5072 LPBYTE pbBuff,
5073 LONG cb,
5074 LONG *pcb)
5076 const char** str = (const char**)dwCookie;
5077 int size = strlen(*str);
5078 *pcb = cb;
5079 if (*pcb > size) {
5080 *pcb = size;
5082 if (*pcb > 0) {
5083 memcpy(pbBuff, *str, *pcb);
5084 *str += *pcb;
5086 return 0;
5089 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5090 LPBYTE pbBuff,
5091 LONG cb,
5092 LONG *pcb)
5094 DWORD *phase = (DWORD *)dwCookie;
5096 if(*phase == 0){
5097 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5098 *pcb = sizeof(first) - 1;
5099 memcpy(pbBuff, first, *pcb);
5100 }else if(*phase == 1){
5101 static const char second[] = "\x8f\xc3\x8b";
5102 *pcb = sizeof(second) - 1;
5103 memcpy(pbBuff, second, *pcb);
5104 }else
5105 *pcb = 0;
5107 ++*phase;
5109 return 0;
5112 struct StringWithLength {
5113 int length;
5114 char *buffer;
5117 /* This callback is used to handled the null characters in a string. */
5118 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5119 LPBYTE pbBuff,
5120 LONG cb,
5121 LONG *pcb)
5123 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5124 int size = str->length;
5125 *pcb = cb;
5126 if (*pcb > size) {
5127 *pcb = size;
5129 if (*pcb > 0) {
5130 memcpy(pbBuff, str->buffer, *pcb);
5131 str->buffer += *pcb;
5132 str->length -= *pcb;
5134 return 0;
5137 static void test_EM_STREAMIN(void)
5139 HWND hwndRichEdit = new_richedit(NULL);
5140 DWORD phase;
5141 LRESULT result;
5142 EDITSTREAM es;
5143 char buffer[1024] = {0}, tmp[16];
5144 CHARRANGE range;
5146 const char * streamText0 = "{\\rtf1 TestSomeText}";
5147 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5148 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5149 const char * ptr;
5151 const char * streamText1 =
5152 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5153 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5154 "}\r\n";
5156 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5157 const char * streamText2 =
5158 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5159 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5160 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5161 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5162 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5163 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5164 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5166 const char * streamText3 = "RichEdit1";
5168 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5170 const char * streamText4 =
5171 "This text just needs to be long enough to cause run to be split onto "
5172 "two separate lines and make sure the null terminating character is "
5173 "handled properly.\0";
5175 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5177 int length4 = strlen(streamText4) + 1;
5178 struct StringWithLength cookieForStream4 = {
5179 length4,
5180 (char *)streamText4,
5183 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5184 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5185 struct StringWithLength cookieForStream5 = {
5186 sizeof(streamText5),
5187 (char *)streamText5,
5190 /* Minimal test without \par at the end */
5191 es.dwCookie = (DWORD_PTR)&streamText0;
5192 es.dwError = 0;
5193 es.pfnCallback = test_EM_STREAMIN_esCallback;
5194 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5195 ok(result == 12, "got %ld, expected %d\n", result, 12);
5197 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5198 ok (result == 12,
5199 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5200 result = strcmp (buffer,"TestSomeText");
5201 ok (result == 0,
5202 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5203 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5205 /* Native richedit 2.0 ignores last \par */
5206 ptr = streamText0a;
5207 es.dwCookie = (DWORD_PTR)&ptr;
5208 es.dwError = 0;
5209 es.pfnCallback = test_EM_STREAMIN_esCallback;
5210 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5211 ok(result == 12, "got %ld, expected %d\n", result, 12);
5213 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5214 ok (result == 12,
5215 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5216 result = strcmp (buffer,"TestSomeText");
5217 ok (result == 0,
5218 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5219 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5221 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5222 es.dwCookie = (DWORD_PTR)&streamText0b;
5223 es.dwError = 0;
5224 es.pfnCallback = test_EM_STREAMIN_esCallback;
5225 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5226 ok(result == 13, "got %ld, expected %d\n", result, 13);
5228 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5229 ok (result == 14,
5230 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5231 result = strcmp (buffer,"TestSomeText\r\n");
5232 ok (result == 0,
5233 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5234 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5236 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5237 ptr = streamText0a;
5238 es.dwCookie = (DWORD_PTR)&ptr;
5239 es.dwError = 0;
5240 es.pfnCallback = test_EM_STREAMIN_esCallback;
5241 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5242 ok(result == 12, "got %ld, expected %d\n", result, 12);
5244 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5245 ok (result == 12,
5246 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5247 result = strcmp (buffer,"TestSomeText");
5248 ok (result == 0,
5249 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5250 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5252 range.cpMin = 0;
5253 range.cpMax = -1;
5254 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5255 ok (result == 13, "got %ld\n", result);
5257 ptr = streamText0a;
5258 es.dwCookie = (DWORD_PTR)&ptr;
5259 es.dwError = 0;
5260 es.pfnCallback = test_EM_STREAMIN_esCallback;
5262 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5263 ok(result == 13, "got %ld, expected 13\n", result);
5265 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5266 ok (result == 14,
5267 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5268 result = strcmp (buffer,"TestSomeText\r\n");
5269 ok (result == 0,
5270 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5271 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5273 es.dwCookie = (DWORD_PTR)&streamText1;
5274 es.dwError = 0;
5275 es.pfnCallback = test_EM_STREAMIN_esCallback;
5276 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5277 ok(result == 12, "got %ld, expected %d\n", result, 12);
5279 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5280 ok (result == 12,
5281 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5282 result = strcmp (buffer,"TestSomeText");
5283 ok (result == 0,
5284 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5285 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5287 es.dwCookie = (DWORD_PTR)&streamText2;
5288 es.dwError = 0;
5289 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5290 ok(result == 0, "got %ld, expected %d\n", result, 0);
5292 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5293 ok (result == 0,
5294 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5295 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5296 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5298 es.dwCookie = (DWORD_PTR)&streamText3;
5299 es.dwError = 0;
5300 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5301 ok(result == 0, "got %ld, expected %d\n", result, 0);
5303 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5304 ok (result == 0,
5305 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5306 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5307 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5309 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5310 es.dwError = 0;
5311 es.pfnCallback = test_EM_STREAMIN_esCallback;
5312 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5313 ok(result == 18, "got %ld, expected %d\n", result, 18);
5315 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5316 ok(result == 15,
5317 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5318 result = strcmp (buffer,"TestUTF8WithBOM");
5319 ok(result == 0,
5320 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5321 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5323 phase = 0;
5324 es.dwCookie = (DWORD_PTR)&phase;
5325 es.dwError = 0;
5326 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5327 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5328 ok(result == 8, "got %ld\n", result);
5330 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5332 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5333 ok(result == 3,
5334 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5335 result = memcmp (buffer, tmp, 3);
5336 ok(result == 0,
5337 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5338 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5340 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5341 es.dwError = 0;
5342 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5343 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5344 ok(result == length4, "got %ld, expected %d\n", result, length4);
5346 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5347 ok (result == length4,
5348 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5349 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5351 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5352 es.dwError = 0;
5353 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5354 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5355 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5357 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5358 ok (result == length5,
5359 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5360 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5362 DestroyWindow(hwndRichEdit);
5365 static void test_EM_StreamIn_Undo(void)
5367 /* The purpose of this test is to determine when a EM_StreamIn should be
5368 * undoable. This is important because WM_PASTE currently uses StreamIn and
5369 * pasting should always be undoable but streaming isn't always.
5371 * cases to test:
5372 * StreamIn plain text without SFF_SELECTION.
5373 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5374 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5375 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5376 * Feel free to add tests for other text modes or StreamIn things.
5380 HWND hwndRichEdit = new_richedit(NULL);
5381 LRESULT result;
5382 EDITSTREAM es;
5383 char buffer[1024] = {0};
5384 const char randomtext[] = "Some text";
5386 es.pfnCallback = EditStreamCallback;
5388 /* StreamIn, no SFF_SELECTION */
5389 es.dwCookie = nCallbackCount;
5390 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5391 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5392 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5393 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5394 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5395 result = strcmp (buffer,"test");
5396 ok (result == 0,
5397 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5399 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5400 ok (result == FALSE,
5401 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5403 /* StreamIn, SFF_SELECTION, but nothing selected */
5404 es.dwCookie = nCallbackCount;
5405 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5406 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5407 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5408 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5409 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5410 result = strcmp (buffer,"testSome text");
5411 ok (result == 0,
5412 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5414 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5415 ok (result == TRUE,
5416 "EM_STREAMIN with SFF_SELECTION but no selection set "
5417 "should create an undo\n");
5419 /* StreamIn, SFF_SELECTION, with a selection */
5420 es.dwCookie = nCallbackCount;
5421 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5422 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5423 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5424 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5425 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5426 result = strcmp (buffer,"Sometesttext");
5427 ok (result == 0,
5428 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5430 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5431 ok (result == TRUE,
5432 "EM_STREAMIN with SFF_SELECTION and selection set "
5433 "should create an undo\n");
5435 DestroyWindow(hwndRichEdit);
5438 static BOOL is_em_settextex_supported(HWND hwnd)
5440 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5441 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5444 static void test_unicode_conversions(void)
5446 static const WCHAR tW[] = {'t',0};
5447 static const WCHAR teW[] = {'t','e',0};
5448 static const WCHAR textW[] = {'t','e','s','t',0};
5449 static const char textA[] = "test";
5450 char bufA[64];
5451 WCHAR bufW[64];
5452 HWND hwnd;
5453 int em_settextex_supported, ret;
5455 #define set_textA(hwnd, wm_set_text, txt) \
5456 do { \
5457 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5458 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5459 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5460 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5461 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5462 } while(0)
5463 #define expect_textA(hwnd, wm_get_text, txt) \
5464 do { \
5465 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5466 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5467 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5468 memset(bufA, 0xAA, sizeof(bufA)); \
5469 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5470 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5471 ret = lstrcmpA(bufA, txt); \
5472 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5473 } while(0)
5475 #define set_textW(hwnd, wm_set_text, txt) \
5476 do { \
5477 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5478 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5479 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5480 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5481 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5482 } while(0)
5483 #define expect_textW(hwnd, wm_get_text, txt) \
5484 do { \
5485 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5486 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5487 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5488 memset(bufW, 0xAA, sizeof(bufW)); \
5489 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5490 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5491 ret = lstrcmpW(bufW, txt); \
5492 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5493 } while(0)
5494 #define expect_empty(hwnd, wm_get_text) \
5495 do { \
5496 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5497 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5498 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5499 memset(bufA, 0xAA, sizeof(bufA)); \
5500 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5501 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5502 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5503 } while(0)
5505 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5506 0, 0, 200, 60, 0, 0, 0, 0);
5507 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5509 ret = IsWindowUnicode(hwnd);
5510 ok(ret, "RichEdit20W should be unicode under NT\n");
5512 /* EM_SETTEXTEX is supported starting from version 3.0 */
5513 em_settextex_supported = is_em_settextex_supported(hwnd);
5514 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5515 em_settextex_supported ? "" : "NOT ");
5517 expect_empty(hwnd, WM_GETTEXT);
5518 expect_empty(hwnd, EM_GETTEXTEX);
5520 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5521 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5522 expect_textA(hwnd, WM_GETTEXT, "t");
5523 expect_textA(hwnd, EM_GETTEXTEX, "t");
5524 expect_textW(hwnd, EM_GETTEXTEX, tW);
5526 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5527 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5528 expect_textA(hwnd, WM_GETTEXT, "te");
5529 expect_textA(hwnd, EM_GETTEXTEX, "te");
5530 expect_textW(hwnd, EM_GETTEXTEX, teW);
5532 set_textA(hwnd, WM_SETTEXT, NULL);
5533 expect_empty(hwnd, WM_GETTEXT);
5534 expect_empty(hwnd, EM_GETTEXTEX);
5536 set_textA(hwnd, WM_SETTEXT, textA);
5537 expect_textA(hwnd, WM_GETTEXT, textA);
5538 expect_textA(hwnd, EM_GETTEXTEX, textA);
5539 expect_textW(hwnd, EM_GETTEXTEX, textW);
5541 if (em_settextex_supported)
5543 set_textA(hwnd, EM_SETTEXTEX, textA);
5544 expect_textA(hwnd, WM_GETTEXT, textA);
5545 expect_textA(hwnd, EM_GETTEXTEX, textA);
5546 expect_textW(hwnd, EM_GETTEXTEX, textW);
5549 set_textW(hwnd, WM_SETTEXT, textW);
5550 expect_textW(hwnd, WM_GETTEXT, textW);
5551 expect_textA(hwnd, WM_GETTEXT, textA);
5552 expect_textW(hwnd, EM_GETTEXTEX, textW);
5553 expect_textA(hwnd, EM_GETTEXTEX, textA);
5555 if (em_settextex_supported)
5557 set_textW(hwnd, EM_SETTEXTEX, textW);
5558 expect_textW(hwnd, WM_GETTEXT, textW);
5559 expect_textA(hwnd, WM_GETTEXT, textA);
5560 expect_textW(hwnd, EM_GETTEXTEX, textW);
5561 expect_textA(hwnd, EM_GETTEXTEX, textA);
5563 DestroyWindow(hwnd);
5565 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5566 0, 0, 200, 60, 0, 0, 0, 0);
5567 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5569 ret = IsWindowUnicode(hwnd);
5570 ok(!ret, "RichEdit20A should NOT be unicode\n");
5572 set_textA(hwnd, WM_SETTEXT, textA);
5573 expect_textA(hwnd, WM_GETTEXT, textA);
5574 expect_textA(hwnd, EM_GETTEXTEX, textA);
5575 expect_textW(hwnd, EM_GETTEXTEX, textW);
5577 if (em_settextex_supported)
5579 set_textA(hwnd, EM_SETTEXTEX, textA);
5580 expect_textA(hwnd, WM_GETTEXT, textA);
5581 expect_textA(hwnd, EM_GETTEXTEX, textA);
5582 expect_textW(hwnd, EM_GETTEXTEX, textW);
5585 set_textW(hwnd, WM_SETTEXT, textW);
5586 expect_textW(hwnd, WM_GETTEXT, textW);
5587 expect_textA(hwnd, WM_GETTEXT, textA);
5588 expect_textW(hwnd, EM_GETTEXTEX, textW);
5589 expect_textA(hwnd, EM_GETTEXTEX, textA);
5591 if (em_settextex_supported)
5593 set_textW(hwnd, EM_SETTEXTEX, textW);
5594 expect_textW(hwnd, WM_GETTEXT, textW);
5595 expect_textA(hwnd, WM_GETTEXT, textA);
5596 expect_textW(hwnd, EM_GETTEXTEX, textW);
5597 expect_textA(hwnd, EM_GETTEXTEX, textA);
5599 DestroyWindow(hwnd);
5602 static void test_WM_CHAR(void)
5604 HWND hwnd;
5605 int ret;
5606 const char * char_list = "abc\rabc\r";
5607 const char * expected_content_single = "abcabc";
5608 const char * expected_content_multi = "abc\r\nabc\r\n";
5609 char buffer[64] = {0};
5610 const char * p;
5612 /* single-line control must IGNORE carriage returns */
5613 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5614 0, 0, 200, 60, 0, 0, 0, 0);
5615 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5617 p = char_list;
5618 while (*p != '\0') {
5619 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5620 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5621 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5622 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5623 p++;
5626 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5627 ret = strcmp(buffer, expected_content_single);
5628 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5630 DestroyWindow(hwnd);
5632 /* multi-line control inserts CR normally */
5633 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5634 0, 0, 200, 60, 0, 0, 0, 0);
5635 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5637 p = char_list;
5638 while (*p != '\0') {
5639 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5640 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5641 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5642 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5643 p++;
5646 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5647 ret = strcmp(buffer, expected_content_multi);
5648 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5650 DestroyWindow(hwnd);
5653 static void test_EM_GETTEXTLENGTHEX(void)
5655 HWND hwnd;
5656 GETTEXTLENGTHEX gtl;
5657 int ret;
5658 const char * base_string = "base string";
5659 const char * test_string = "a\nb\n\n\r\n";
5660 const char * test_string_after = "a";
5661 const char * test_string_2 = "a\rtest\rstring";
5662 char buffer[64] = {0};
5664 /* single line */
5665 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5666 0, 0, 200, 60, 0, 0, 0, 0);
5667 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5669 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5670 gtl.codepage = CP_ACP;
5671 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5672 ok(ret == 0, "ret %d\n",ret);
5674 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5675 gtl.codepage = CP_ACP;
5676 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5677 ok(ret == 0, "ret %d\n",ret);
5679 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5681 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5682 gtl.codepage = CP_ACP;
5683 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5684 ok(ret == strlen(base_string), "ret %d\n",ret);
5686 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5687 gtl.codepage = CP_ACP;
5688 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5689 ok(ret == strlen(base_string), "ret %d\n",ret);
5691 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5693 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5694 gtl.codepage = CP_ACP;
5695 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5696 ok(ret == 1, "ret %d\n",ret);
5698 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5699 gtl.codepage = CP_ACP;
5700 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5701 ok(ret == 1, "ret %d\n",ret);
5703 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5704 ret = strcmp(buffer, test_string_after);
5705 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5707 DestroyWindow(hwnd);
5709 /* multi line */
5710 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5711 0, 0, 200, 60, 0, 0, 0, 0);
5712 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5714 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5715 gtl.codepage = CP_ACP;
5716 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5717 ok(ret == 0, "ret %d\n",ret);
5719 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5720 gtl.codepage = CP_ACP;
5721 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5722 ok(ret == 0, "ret %d\n",ret);
5724 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5726 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5727 gtl.codepage = CP_ACP;
5728 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5729 ok(ret == strlen(base_string), "ret %d\n",ret);
5731 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5732 gtl.codepage = CP_ACP;
5733 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5734 ok(ret == strlen(base_string), "ret %d\n",ret);
5736 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5738 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5739 gtl.codepage = CP_ACP;
5740 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5741 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5743 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5744 gtl.codepage = CP_ACP;
5745 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5746 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5748 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5750 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5751 gtl.codepage = CP_ACP;
5752 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5753 ok(ret == 10, "ret %d\n",ret);
5755 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5756 gtl.codepage = CP_ACP;
5757 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5758 ok(ret == 6, "ret %d\n",ret);
5760 /* Unicode/NUMCHARS/NUMBYTES */
5761 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5763 gtl.flags = GTL_DEFAULT;
5764 gtl.codepage = 1200;
5765 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5766 ok(ret == lstrlenA(test_string_2),
5767 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5769 gtl.flags = GTL_NUMCHARS;
5770 gtl.codepage = 1200;
5771 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5772 ok(ret == lstrlenA(test_string_2),
5773 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5775 gtl.flags = GTL_NUMBYTES;
5776 gtl.codepage = 1200;
5777 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5778 ok(ret == lstrlenA(test_string_2)*2,
5779 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5781 gtl.flags = GTL_PRECISE;
5782 gtl.codepage = 1200;
5783 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5784 ok(ret == lstrlenA(test_string_2)*2,
5785 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5787 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5788 gtl.codepage = 1200;
5789 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5790 ok(ret == lstrlenA(test_string_2),
5791 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5793 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5794 gtl.codepage = 1200;
5795 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5796 ok(ret == E_INVALIDARG,
5797 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5799 DestroyWindow(hwnd);
5803 /* globals that parent and child access when checking event masks & notifications */
5804 static HWND eventMaskEditHwnd = 0;
5805 static int queriedEventMask;
5806 static int watchForEventMask = 0;
5808 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5809 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5811 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5813 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5815 return DefWindowProcA(hwnd, message, wParam, lParam);
5818 /* test event masks in combination with WM_COMMAND */
5819 static void test_eventMask(void)
5821 HWND parent;
5822 int ret, style;
5823 WNDCLASSA cls;
5824 const char text[] = "foo bar\n";
5825 int eventMask;
5827 /* register class to capture WM_COMMAND */
5828 cls.style = 0;
5829 cls.lpfnWndProc = ParentMsgCheckProcA;
5830 cls.cbClsExtra = 0;
5831 cls.cbWndExtra = 0;
5832 cls.hInstance = GetModuleHandleA(0);
5833 cls.hIcon = 0;
5834 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5835 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5836 cls.lpszMenuName = NULL;
5837 cls.lpszClassName = "EventMaskParentClass";
5838 if(!RegisterClassA(&cls)) assert(0);
5840 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5841 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5842 ok (parent != 0, "Failed to create parent window\n");
5844 eventMaskEditHwnd = new_richedit(parent);
5845 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
5847 eventMask = ENM_CHANGE | ENM_UPDATE;
5848 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
5849 ok(ret == ENM_NONE, "wrong event mask\n");
5850 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5851 ok(ret == eventMask, "failed to set event mask\n");
5853 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
5854 queriedEventMask = 0; /* initialize to something other than we expect */
5855 watchForEventMask = EN_CHANGE;
5856 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
5857 ok(ret == TRUE, "failed to set text\n");
5858 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
5859 notification in response to WM_SETTEXT */
5860 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5861 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5863 /* check to see if EN_CHANGE is sent when redraw is turned off */
5864 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5865 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5866 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
5867 /* redraw is disabled by making the window invisible. */
5868 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5869 queriedEventMask = 0; /* initialize to something other than we expect */
5870 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5871 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
5872 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5873 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
5874 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5876 /* check to see if EN_UPDATE is sent when the editor isn't visible */
5877 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
5878 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
5879 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
5880 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
5881 watchForEventMask = EN_UPDATE;
5882 queriedEventMask = 0; /* initialize to something other than we expect */
5883 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5884 ok(queriedEventMask == 0,
5885 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5886 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
5887 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
5888 queriedEventMask = 0; /* initialize to something other than we expect */
5889 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
5890 ok(queriedEventMask == eventMask,
5891 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
5894 DestroyWindow(parent);
5897 static int received_WM_NOTIFY = 0;
5898 static int modify_at_WM_NOTIFY = 0;
5899 static BOOL filter_on_WM_NOTIFY = FALSE;
5900 static HWND hwndRichedit_WM_NOTIFY;
5902 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5904 if(message == WM_NOTIFY)
5906 received_WM_NOTIFY = 1;
5907 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
5908 if (filter_on_WM_NOTIFY) return TRUE;
5910 return DefWindowProcA(hwnd, message, wParam, lParam);
5913 static void test_WM_NOTIFY(void)
5915 HWND parent;
5916 WNDCLASSA cls;
5917 CHARFORMAT2A cf2;
5918 int sel_start, sel_end;
5920 /* register class to capture WM_NOTIFY */
5921 cls.style = 0;
5922 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
5923 cls.cbClsExtra = 0;
5924 cls.cbWndExtra = 0;
5925 cls.hInstance = GetModuleHandleA(0);
5926 cls.hIcon = 0;
5927 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
5928 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
5929 cls.lpszMenuName = NULL;
5930 cls.lpszClassName = "WM_NOTIFY_ParentClass";
5931 if(!RegisterClassA(&cls)) assert(0);
5933 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
5934 0, 0, 200, 60, NULL, NULL, NULL, NULL);
5935 ok (parent != 0, "Failed to create parent window\n");
5937 hwndRichedit_WM_NOTIFY = new_richedit(parent);
5938 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
5940 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
5942 /* Notifications for selection change should only be sent when selection
5943 actually changes. EM_SETCHARFORMAT is one message that calls
5944 ME_CommitUndo, which should check whether message should be sent */
5945 received_WM_NOTIFY = 0;
5946 cf2.cbSize = sizeof(CHARFORMAT2A);
5947 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
5948 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
5949 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
5950 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
5951 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5953 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
5954 already at 0. */
5955 received_WM_NOTIFY = 0;
5956 modify_at_WM_NOTIFY = 0;
5957 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5958 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
5959 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5961 received_WM_NOTIFY = 0;
5962 modify_at_WM_NOTIFY = 0;
5963 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
5964 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5966 received_WM_NOTIFY = 0;
5967 modify_at_WM_NOTIFY = 0;
5968 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
5969 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5970 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
5972 /* Test for WM_NOTIFY messages with redraw disabled. */
5973 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5974 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
5975 received_WM_NOTIFY = 0;
5976 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
5977 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
5978 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
5980 /* Test filtering key events. */
5981 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
5982 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
5983 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5984 received_WM_NOTIFY = 0;
5985 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5986 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5987 ok(sel_start == 1 && sel_end == 1,
5988 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5989 filter_on_WM_NOTIFY = TRUE;
5990 received_WM_NOTIFY = 0;
5991 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5992 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
5993 ok(sel_start == 1 && sel_end == 1,
5994 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
5996 /* test with owner set to NULL */
5997 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
5998 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
5999 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6000 ok(sel_start == 1 && sel_end == 1,
6001 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6003 DestroyWindow(hwndRichedit_WM_NOTIFY);
6004 DestroyWindow(parent);
6007 static int cpMin_EN_LINK = -1;
6008 static int cpMax_EN_LINK = -1;
6010 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6012 ENLINK* enlink = (ENLINK*)lParam;
6013 if(message == WM_NOTIFY && enlink->nmhdr.code == EN_LINK)
6015 cpMin_EN_LINK = enlink->chrg.cpMin;
6016 cpMax_EN_LINK = enlink->chrg.cpMax;
6018 return DefWindowProcA(hwnd, message, wParam, lParam);
6021 static void test_EN_LINK(void)
6023 HWND parent;
6024 WNDCLASSA cls;
6025 HWND hwndRichedit_EN_LINK;
6026 CHARFORMAT2A cf2;
6028 /* register class to capture WM_NOTIFY */
6029 cls.style = 0;
6030 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6031 cls.cbClsExtra = 0;
6032 cls.cbWndExtra = 0;
6033 cls.hInstance = GetModuleHandleA(0);
6034 cls.hIcon = 0;
6035 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6036 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6037 cls.lpszMenuName = NULL;
6038 cls.lpszClassName = "EN_LINK_ParentClass";
6039 if(!RegisterClassA(&cls)) assert(0);
6041 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6042 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6043 ok(parent != 0, "Failed to create parent window\n");
6045 hwndRichedit_EN_LINK = new_richedit(parent);
6046 ok(hwndRichedit_EN_LINK != 0, "Failed to create edit window\n");
6048 SendMessageA(hwndRichedit_EN_LINK, EM_SETEVENTMASK, 0, ENM_LINK);
6050 cf2.cbSize = sizeof(CHARFORMAT2A);
6051 cf2.dwMask = CFM_LINK;
6052 cf2.dwEffects = CFE_LINK;
6053 SendMessageA(hwndRichedit_EN_LINK, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6054 /* mixing letters and numbers causes runs to be split */
6055 SendMessageA(hwndRichedit_EN_LINK, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6056 SendMessageA(hwndRichedit_EN_LINK, WM_LBUTTONDOWN, 0, MAKELPARAM(5, 5));
6057 ok(cpMin_EN_LINK == 0 && cpMax_EN_LINK == 31, "Expected link range [0,31) got [%i,%i)\n", cpMin_EN_LINK, cpMax_EN_LINK);
6059 DestroyWindow(hwndRichedit_EN_LINK);
6060 DestroyWindow(parent);
6063 static void test_undo_coalescing(void)
6065 HWND hwnd;
6066 int result;
6067 char buffer[64] = {0};
6069 /* multi-line control inserts CR normally */
6070 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6071 0, 0, 200, 60, 0, 0, 0, 0);
6072 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6074 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6075 ok (result == FALSE, "Can undo after window creation.\n");
6076 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6077 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6078 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6079 ok (result == FALSE, "Can redo after window creation.\n");
6080 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6081 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6083 /* Test the effect of arrows keys during typing on undo transactions*/
6084 simulate_typing_characters(hwnd, "one two three");
6085 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6086 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6087 simulate_typing_characters(hwnd, " four five six");
6089 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6090 ok (result == FALSE, "Can redo before anything is undone.\n");
6091 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6092 ok (result == TRUE, "Cannot undo typed characters.\n");
6093 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6094 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6095 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6096 ok (result == TRUE, "Cannot redo after undo.\n");
6097 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6098 result = strcmp(buffer, "one two three");
6099 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6101 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6102 ok (result == TRUE, "Cannot undo typed characters.\n");
6103 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6104 ok (result == TRUE, "Failed to undo typed characters.\n");
6105 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6106 result = strcmp(buffer, "");
6107 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6109 /* Test the effect of focus changes during typing on undo transactions*/
6110 simulate_typing_characters(hwnd, "one two three");
6111 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6112 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6113 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6114 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6115 simulate_typing_characters(hwnd, " four five six");
6116 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6117 ok (result == TRUE, "Failed to undo typed characters.\n");
6118 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6119 result = strcmp(buffer, "one two three");
6120 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6122 /* Test the effect of the back key during typing on undo transactions */
6123 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6124 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6125 ok (result == TRUE, "Failed to clear the text.\n");
6126 simulate_typing_characters(hwnd, "one two threa");
6127 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6128 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6129 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6130 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6131 simulate_typing_characters(hwnd, "e four five six");
6132 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6133 ok (result == TRUE, "Failed to undo typed characters.\n");
6134 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6135 result = strcmp(buffer, "");
6136 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6138 /* Test the effect of the delete key during typing on undo transactions */
6139 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6140 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6141 ok(result == TRUE, "Failed to set the text.\n");
6142 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6143 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6144 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6145 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6146 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6147 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6148 ok (result == TRUE, "Failed to undo typed characters.\n");
6149 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6150 result = strcmp(buffer, "acd");
6151 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6152 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6153 ok (result == TRUE, "Failed to undo typed characters.\n");
6154 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6155 result = strcmp(buffer, "abcd");
6156 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6158 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6159 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6160 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6161 ok (result == TRUE, "Failed to clear the text.\n");
6162 simulate_typing_characters(hwnd, "one two three");
6163 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6164 ok (result == 0, "expected %d but got %d\n", 0, result);
6165 simulate_typing_characters(hwnd, " four five six");
6166 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6167 ok (result == TRUE, "Failed to undo typed characters.\n");
6168 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6169 result = strcmp(buffer, "one two three");
6170 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6171 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6172 ok (result == TRUE, "Failed to undo typed characters.\n");
6173 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6174 result = strcmp(buffer, "");
6175 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6177 DestroyWindow(hwnd);
6180 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6182 int length;
6184 /* MSDN lied, length is actually the number of bytes. */
6185 length = bytes / sizeof(WCHAR);
6186 switch(code)
6188 case WB_ISDELIMITER:
6189 return text[pos] == 'X';
6190 case WB_LEFT:
6191 case WB_MOVEWORDLEFT:
6192 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6193 return pos-1;
6194 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6195 case WB_LEFTBREAK:
6196 pos--;
6197 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6198 pos--;
6199 return pos;
6200 case WB_RIGHT:
6201 case WB_MOVEWORDRIGHT:
6202 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6203 return pos+1;
6204 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6205 case WB_RIGHTBREAK:
6206 pos++;
6207 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6208 pos++;
6209 return pos;
6210 default:
6211 ok(FALSE, "Unexpected code %d\n", code);
6212 break;
6214 return 0;
6217 static void test_word_movement(void)
6219 HWND hwnd;
6220 int result;
6221 int sel_start, sel_end;
6222 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6224 /* multi-line control inserts CR normally */
6225 hwnd = new_richedit(NULL);
6227 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6228 ok (result == TRUE, "Failed to clear the text.\n");
6229 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6230 /* |one two three */
6232 send_ctrl_key(hwnd, VK_RIGHT);
6233 /* one |two three */
6234 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6235 ok(sel_start == sel_end, "Selection should be empty\n");
6236 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6238 send_ctrl_key(hwnd, VK_RIGHT);
6239 /* one two |three */
6240 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6241 ok(sel_start == sel_end, "Selection should be empty\n");
6242 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6244 send_ctrl_key(hwnd, VK_LEFT);
6245 /* one |two three */
6246 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6247 ok(sel_start == sel_end, "Selection should be empty\n");
6248 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6250 send_ctrl_key(hwnd, VK_LEFT);
6251 /* |one two three */
6252 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6253 ok(sel_start == sel_end, "Selection should be empty\n");
6254 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6256 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6257 /* one two | three */
6258 send_ctrl_key(hwnd, VK_RIGHT);
6259 /* one two |three */
6260 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6261 ok(sel_start == sel_end, "Selection should be empty\n");
6262 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6264 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6265 /* one two th|ree */
6266 send_ctrl_key(hwnd, VK_LEFT);
6267 /* one two |three */
6268 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6269 ok(sel_start == sel_end, "Selection should be empty\n");
6270 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6272 /* Test with a custom word break procedure that uses X as the delimiter. */
6273 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6274 ok (result == TRUE, "Failed to clear the text.\n");
6275 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6276 /* |one twoXthree */
6277 send_ctrl_key(hwnd, VK_RIGHT);
6278 /* one twoX|three */
6279 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6280 ok(sel_start == sel_end, "Selection should be empty\n");
6281 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6283 DestroyWindow(hwnd);
6285 /* Make sure the behaviour is the same with a unicode richedit window,
6286 * and using unicode functions. */
6288 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6289 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6290 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6292 /* Test with a custom word break procedure that uses X as the delimiter. */
6293 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6294 ok (result == TRUE, "Failed to clear the text.\n");
6295 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6296 /* |one twoXthree */
6297 send_ctrl_key(hwnd, VK_RIGHT);
6298 /* one twoX|three */
6299 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6300 ok(sel_start == sel_end, "Selection should be empty\n");
6301 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6303 DestroyWindow(hwnd);
6306 static void test_EM_CHARFROMPOS(void)
6308 HWND hwnd;
6309 int result;
6310 RECT rcClient;
6311 POINTL point;
6312 point.x = 0;
6313 point.y = 40;
6315 /* multi-line control inserts CR normally */
6316 hwnd = new_richedit(NULL);
6317 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6318 (LPARAM)"one two three four five six seven\reight");
6319 ok(result == 1, "Expected 1, got %d\n", result);
6320 GetClientRect(hwnd, &rcClient);
6322 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6323 ok(result == 34, "expected character index of 34 but got %d\n", result);
6325 /* Test with points outside the bounds of the richedit control. */
6326 point.x = -1;
6327 point.y = 40;
6328 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6329 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6331 point.x = 1000;
6332 point.y = 0;
6333 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6334 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6336 point.x = 1000;
6337 point.y = 36;
6338 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6339 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6341 point.x = 1000;
6342 point.y = -1;
6343 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6344 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6346 point.x = 1000;
6347 point.y = rcClient.bottom + 1;
6348 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6349 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6351 point.x = 1000;
6352 point.y = rcClient.bottom;
6353 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6354 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6356 DestroyWindow(hwnd);
6359 static void test_word_wrap(void)
6361 HWND hwnd;
6362 POINTL point = {0, 60}; /* This point must be below the first line */
6363 const char *text = "Must be long enough to test line wrapping";
6364 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6365 int res, pos, lines;
6367 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6368 * when specified on window creation and set later. */
6369 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6370 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6371 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6372 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6373 ok(res, "WM_SETTEXT failed.\n");
6374 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6375 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6376 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6377 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6379 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6380 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6381 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6382 DestroyWindow(hwnd);
6384 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6385 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6386 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6388 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6389 ok(res, "WM_SETTEXT failed.\n");
6390 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6391 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6392 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6393 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6395 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6396 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6397 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6398 DestroyWindow(hwnd);
6400 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6401 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6402 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6403 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6404 ok(res, "WM_SETTEXT failed.\n");
6405 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6406 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6408 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6409 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6410 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6411 DestroyWindow(hwnd);
6413 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6414 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6415 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6416 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6417 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6418 ok(res, "WM_SETTEXT failed.\n");
6419 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6420 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6422 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6423 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6424 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6426 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6427 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6428 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6429 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6430 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6432 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6433 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6434 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6435 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6436 DestroyWindow(hwnd);
6438 /* Test to see if wrapping happens with redraw disabled. */
6439 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6440 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6441 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6442 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6443 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6444 ok(res, "EM_REPLACESEL failed.\n");
6445 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6446 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6447 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6448 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6449 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6451 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6452 DestroyWindow(hwnd);
6455 static void test_autoscroll(void)
6457 HWND hwnd = new_richedit(NULL);
6458 int lines, ret, redraw;
6459 POINT pt;
6461 for (redraw = 0; redraw <= 1; redraw++) {
6462 trace("testing with WM_SETREDRAW=%d\n", redraw);
6463 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6464 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6465 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6466 ok(lines == 8, "%d lines instead of 8\n", lines);
6467 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6468 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6469 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6470 ret = GetWindowLongA(hwnd, GWL_STYLE);
6471 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6473 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6474 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6475 ok(lines == 1, "%d lines instead of 1\n", lines);
6476 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6477 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6478 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6479 ret = GetWindowLongA(hwnd, GWL_STYLE);
6480 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6483 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6484 DestroyWindow(hwnd);
6486 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6487 * auto vertical/horizontal scrolling options. */
6488 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6489 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6490 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6491 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6492 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6493 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6494 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6495 ret = GetWindowLongA(hwnd, GWL_STYLE);
6496 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6497 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6498 DestroyWindow(hwnd);
6500 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6501 WS_POPUP|ES_MULTILINE,
6502 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6503 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6504 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6505 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6506 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6507 ret = GetWindowLongA(hwnd, GWL_STYLE);
6508 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6509 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6510 DestroyWindow(hwnd);
6514 static void test_format_rect(void)
6516 HWND hwnd;
6517 RECT rc, expected, clientRect;
6518 int n;
6519 DWORD options;
6521 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6522 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6523 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6524 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6526 GetClientRect(hwnd, &clientRect);
6528 expected = clientRect;
6529 expected.left += 1;
6530 expected.right -= 1;
6531 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6532 ok(rc.top == expected.top && rc.left == expected.left &&
6533 rc.bottom == expected.bottom && rc.right == expected.right,
6534 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6535 rc.top, rc.left, rc.bottom, rc.right,
6536 expected.top, expected.left, expected.bottom, expected.right);
6538 for (n = -3; n <= 3; n++)
6540 rc = clientRect;
6541 rc.top += n;
6542 rc.left += n;
6543 rc.bottom -= n;
6544 rc.right -= n;
6545 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6547 expected = rc;
6548 expected.top = max(0, rc.top);
6549 expected.left = max(0, rc.left);
6550 expected.bottom = min(clientRect.bottom, rc.bottom);
6551 expected.right = min(clientRect.right, rc.right);
6552 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6553 ok(rc.top == expected.top && rc.left == expected.left &&
6554 rc.bottom == expected.bottom && rc.right == expected.right,
6555 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6556 n, rc.top, rc.left, rc.bottom, rc.right,
6557 expected.top, expected.left, expected.bottom, expected.right);
6560 rc = clientRect;
6561 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6562 expected = clientRect;
6563 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6564 ok(rc.top == expected.top && rc.left == expected.left &&
6565 rc.bottom == expected.bottom && rc.right == expected.right,
6566 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6567 rc.top, rc.left, rc.bottom, rc.right,
6568 expected.top, expected.left, expected.bottom, expected.right);
6570 /* Adding the selectionbar adds the selectionbar width to the left side. */
6571 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6572 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6573 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6574 expected.left += 8; /* selection bar width */
6575 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6576 ok(rc.top == expected.top && rc.left == expected.left &&
6577 rc.bottom == expected.bottom && rc.right == expected.right,
6578 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6579 rc.top, rc.left, rc.bottom, rc.right,
6580 expected.top, expected.left, expected.bottom, expected.right);
6582 rc = clientRect;
6583 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6584 expected = clientRect;
6585 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6586 ok(rc.top == expected.top && rc.left == expected.left &&
6587 rc.bottom == expected.bottom && rc.right == expected.right,
6588 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6589 rc.top, rc.left, rc.bottom, rc.right,
6590 expected.top, expected.left, expected.bottom, expected.right);
6592 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6593 * even if the left side is already 0. */
6594 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6595 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6596 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6597 expected.left -= 8; /* selection bar width */
6598 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6599 ok(rc.top == expected.top && rc.left == expected.left &&
6600 rc.bottom == expected.bottom && rc.right == expected.right,
6601 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6602 rc.top, rc.left, rc.bottom, rc.right,
6603 expected.top, expected.left, expected.bottom, expected.right);
6605 /* Set the absolute value of the formatting rectangle. */
6606 rc = clientRect;
6607 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6608 expected = clientRect;
6609 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6610 ok(rc.top == expected.top && rc.left == expected.left &&
6611 rc.bottom == expected.bottom && rc.right == expected.right,
6612 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6613 n, rc.top, rc.left, rc.bottom, rc.right,
6614 expected.top, expected.left, expected.bottom, expected.right);
6616 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6617 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6618 * tests show that this isn't true. */
6619 rc.top = 15;
6620 rc.left = 15;
6621 rc.bottom = clientRect.bottom - 15;
6622 rc.right = clientRect.right - 15;
6623 expected = rc;
6624 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6625 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6626 ok(rc.top == expected.top && rc.left == expected.left &&
6627 rc.bottom == expected.bottom && rc.right == expected.right,
6628 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6629 rc.top, rc.left, rc.bottom, rc.right,
6630 expected.top, expected.left, expected.bottom, expected.right);
6632 /* For some reason it does not limit the values to the client rect with
6633 * a WPARAM value of 1. */
6634 rc.top = -15;
6635 rc.left = -15;
6636 rc.bottom = clientRect.bottom + 15;
6637 rc.right = clientRect.right + 15;
6638 expected = rc;
6639 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6640 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6641 ok(rc.top == expected.top && rc.left == expected.left &&
6642 rc.bottom == expected.bottom && rc.right == expected.right,
6643 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6644 rc.top, rc.left, rc.bottom, rc.right,
6645 expected.top, expected.left, expected.bottom, expected.right);
6647 /* Reset to default rect and check how the format rect adjusts to window
6648 * resize and how it copes with very small windows */
6649 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6651 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6652 GetClientRect(hwnd, &clientRect);
6654 expected = clientRect;
6655 expected.left += 1;
6656 expected.right -= 1;
6657 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6658 ok(rc.top == expected.top && rc.left == expected.left &&
6659 rc.bottom == expected.bottom && rc.right == expected.right,
6660 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6661 rc.top, rc.left, rc.bottom, rc.right,
6662 expected.top, expected.left, expected.bottom, expected.right);
6664 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6665 GetClientRect(hwnd, &clientRect);
6667 expected = clientRect;
6668 expected.left += 1;
6669 expected.right -= 1;
6670 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6671 ok(rc.top == expected.top && rc.left == expected.left &&
6672 rc.bottom == expected.bottom && rc.right == expected.right,
6673 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6674 rc.top, rc.left, rc.bottom, rc.right,
6675 expected.top, expected.left, expected.bottom, expected.right);
6677 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6678 GetClientRect(hwnd, &clientRect);
6680 expected = clientRect;
6681 expected.left += 1;
6682 expected.right -= 1;
6683 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6684 ok(rc.top == expected.top && rc.left == expected.left &&
6685 rc.bottom == expected.bottom && rc.right == expected.right,
6686 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6687 rc.top, rc.left, rc.bottom, rc.right,
6688 expected.top, expected.left, expected.bottom, expected.right);
6690 DestroyWindow(hwnd);
6692 /* The extended window style affects the formatting rectangle. */
6693 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
6694 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6695 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6696 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6698 GetClientRect(hwnd, &clientRect);
6700 expected = clientRect;
6701 expected.left += 1;
6702 expected.top += 1;
6703 expected.right -= 1;
6704 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6705 ok(rc.top == expected.top && rc.left == expected.left &&
6706 rc.bottom == expected.bottom && rc.right == expected.right,
6707 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6708 rc.top, rc.left, rc.bottom, rc.right,
6709 expected.top, expected.left, expected.bottom, expected.right);
6711 rc = clientRect;
6712 rc.top += 5;
6713 rc.left += 5;
6714 rc.bottom -= 5;
6715 rc.right -= 5;
6716 expected = rc;
6717 expected.top -= 1;
6718 expected.left -= 1;
6719 expected.right += 1;
6720 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6721 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6722 ok(rc.top == expected.top && rc.left == expected.left &&
6723 rc.bottom == expected.bottom && rc.right == expected.right,
6724 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6725 rc.top, rc.left, rc.bottom, rc.right,
6726 expected.top, expected.left, expected.bottom, expected.right);
6728 DestroyWindow(hwnd);
6731 static void test_WM_GETDLGCODE(void)
6733 HWND hwnd;
6734 UINT res, expected;
6735 MSG msg;
6737 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6739 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6740 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6741 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6742 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6743 msg.hwnd = hwnd;
6744 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
6745 expected = expected | DLGC_WANTMESSAGE;
6746 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6747 res, expected);
6748 DestroyWindow(hwnd);
6750 msg.message = WM_KEYDOWN;
6751 msg.wParam = VK_RETURN;
6752 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6753 msg.pt.x = 0;
6754 msg.pt.y = 0;
6755 msg.time = GetTickCount();
6757 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6758 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
6759 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6760 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6761 msg.hwnd = hwnd;
6762 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6763 expected = expected | DLGC_WANTMESSAGE;
6764 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6765 res, expected);
6766 DestroyWindow(hwnd);
6768 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6769 ES_MULTILINE|WS_POPUP,
6770 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6771 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6772 msg.hwnd = hwnd;
6773 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6774 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6775 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6776 res, expected);
6777 DestroyWindow(hwnd);
6779 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6780 ES_WANTRETURN|WS_POPUP,
6781 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6782 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6783 msg.hwnd = hwnd;
6784 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6785 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6786 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6787 res, expected);
6788 DestroyWindow(hwnd);
6790 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6791 WS_POPUP,
6792 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6793 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6794 msg.hwnd = hwnd;
6795 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6796 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6797 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6798 res, expected);
6799 DestroyWindow(hwnd);
6801 msg.wParam = VK_TAB;
6802 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
6804 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6805 ES_MULTILINE|WS_POPUP,
6806 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6807 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6808 msg.hwnd = hwnd;
6809 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6810 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6811 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6812 res, expected);
6813 DestroyWindow(hwnd);
6815 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6816 WS_POPUP,
6817 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6818 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6819 msg.hwnd = hwnd;
6820 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6821 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6822 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6823 res, expected);
6824 DestroyWindow(hwnd);
6826 hold_key(VK_CONTROL);
6828 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6829 ES_MULTILINE|WS_POPUP,
6830 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6831 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6832 msg.hwnd = hwnd;
6833 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6834 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6835 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6836 res, expected);
6837 DestroyWindow(hwnd);
6839 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6840 WS_POPUP,
6841 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6842 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6843 msg.hwnd = hwnd;
6844 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6845 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6846 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6847 res, expected);
6848 DestroyWindow(hwnd);
6850 release_key(VK_CONTROL);
6852 msg.wParam = 'a';
6853 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
6855 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6856 ES_MULTILINE|WS_POPUP,
6857 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6858 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6859 msg.hwnd = hwnd;
6860 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6861 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6862 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6863 res, expected);
6864 DestroyWindow(hwnd);
6866 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6867 WS_POPUP,
6868 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6869 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6870 msg.hwnd = hwnd;
6871 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6872 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6873 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6874 res, expected);
6875 DestroyWindow(hwnd);
6877 msg.message = WM_CHAR;
6879 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6880 ES_MULTILINE|WS_POPUP,
6881 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6882 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6883 msg.hwnd = hwnd;
6884 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6885 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
6886 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6887 res, expected);
6888 DestroyWindow(hwnd);
6890 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6891 WS_POPUP,
6892 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6893 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6894 msg.hwnd = hwnd;
6895 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
6896 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
6897 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6898 res, expected);
6899 DestroyWindow(hwnd);
6901 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6902 WS_POPUP|ES_SAVESEL,
6903 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6904 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6905 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
6906 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
6907 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
6908 res, expected);
6909 DestroyWindow(hwnd);
6912 static void test_zoom(void)
6914 HWND hwnd;
6915 UINT ret;
6916 RECT rc;
6917 POINT pt;
6918 int numerator, denominator;
6920 hwnd = new_richedit(NULL);
6921 GetClientRect(hwnd, &rc);
6922 pt.x = (rc.right - rc.left) / 2;
6923 pt.y = (rc.bottom - rc.top) / 2;
6924 ClientToScreen(hwnd, &pt);
6926 /* Test initial zoom value */
6927 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6928 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
6929 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
6930 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6932 /* test scroll wheel */
6933 hold_key(VK_CONTROL);
6934 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6935 MAKELPARAM(pt.x, pt.y));
6936 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6937 release_key(VK_CONTROL);
6939 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6940 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
6941 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6942 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6944 /* Test how much the mouse wheel can zoom in and out. */
6945 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
6946 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6948 hold_key(VK_CONTROL);
6949 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6950 MAKELPARAM(pt.x, pt.y));
6951 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6952 release_key(VK_CONTROL);
6954 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6955 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
6956 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6957 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6959 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
6960 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6962 hold_key(VK_CONTROL);
6963 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
6964 MAKELPARAM(pt.x, pt.y));
6965 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6966 release_key(VK_CONTROL);
6968 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6969 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
6970 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6971 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6973 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
6974 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6976 hold_key(VK_CONTROL);
6977 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6978 MAKELPARAM(pt.x, pt.y));
6979 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6980 release_key(VK_CONTROL);
6982 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6983 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
6984 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6985 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
6987 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
6988 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
6990 hold_key(VK_CONTROL);
6991 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
6992 MAKELPARAM(pt.x, pt.y));
6993 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
6994 release_key(VK_CONTROL);
6996 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
6997 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
6998 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
6999 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7001 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7002 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7003 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7005 hold_key(VK_CONTROL);
7006 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7007 MAKELPARAM(pt.x, pt.y));
7008 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7009 release_key(VK_CONTROL);
7011 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7012 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7013 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7014 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7016 /* Test bounds checking on EM_SETZOOM */
7017 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7018 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7020 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7021 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7023 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7024 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7026 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7027 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7028 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7029 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7031 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7032 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7034 /* See if negative numbers are accepted. */
7035 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7036 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7038 /* See if negative numbers are accepted. */
7039 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7040 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7042 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7043 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7044 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7045 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7047 /* Reset the zoom value */
7048 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7049 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7051 DestroyWindow(hwnd);
7054 struct dialog_mode_messages
7056 int wm_getdefid, wm_close, wm_nextdlgctl;
7059 static struct dialog_mode_messages dm_messages;
7061 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7062 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7063 "got %d\n", wmclose, dm_messages.wm_close); \
7064 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7065 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7066 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7067 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7069 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7071 switch (iMsg)
7073 case DM_GETDEFID:
7074 dm_messages.wm_getdefid++;
7075 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7076 case WM_NEXTDLGCTL:
7077 dm_messages.wm_nextdlgctl++;
7078 break;
7079 case WM_CLOSE:
7080 dm_messages.wm_close++;
7081 break;
7084 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7087 static void test_dialogmode(void)
7089 HWND hwRichEdit, hwParent, hwButton;
7090 MSG msg= {0};
7091 int lcount, r;
7092 WNDCLASSA cls;
7094 cls.style = 0;
7095 cls.lpfnWndProc = dialog_mode_wnd_proc;
7096 cls.cbClsExtra = 0;
7097 cls.cbWndExtra = 0;
7098 cls.hInstance = GetModuleHandleA(0);
7099 cls.hIcon = 0;
7100 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7101 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7102 cls.lpszMenuName = NULL;
7103 cls.lpszClassName = "DialogModeParentClass";
7104 if(!RegisterClassA(&cls)) assert(0);
7106 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7107 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7109 /* Test richedit(ES_MULTILINE) */
7111 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7113 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7114 ok(0 == r, "expected 0, got %d\n", r);
7115 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7116 ok(2 == lcount, "expected 2, got %d\n", lcount);
7118 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7119 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7121 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7122 ok(0 == r, "expected 0, got %d\n", r);
7123 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7124 ok(3 == lcount, "expected 3, got %d\n", lcount);
7126 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7127 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7128 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7129 ok(0 == r, "expected 0, got %d\n", r);
7130 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7131 ok(3 == lcount, "expected 3, got %d\n", lcount);
7133 DestroyWindow(hwRichEdit);
7135 /* Test standalone richedit(ES_MULTILINE) */
7137 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7139 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7140 ok(0 == r, "expected 0, got %d\n", r);
7141 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7142 ok(2 == lcount, "expected 2, got %d\n", lcount);
7144 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7145 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7147 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7148 ok(0 == r, "expected 0, got %d\n", r);
7149 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7150 ok(2 == lcount, "expected 2, got %d\n", lcount);
7152 DestroyWindow(hwRichEdit);
7154 /* Check a destination for messages */
7156 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7158 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7159 SetParent( hwRichEdit, NULL);
7161 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7162 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7164 memset(&dm_messages, 0, sizeof(dm_messages));
7165 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7166 ok(0 == r, "expected 0, got %d\n", r);
7167 test_dm_messages(0, 1, 0);
7169 memset(&dm_messages, 0, sizeof(dm_messages));
7170 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7171 ok(0 == r, "expected 0, got %d\n", r);
7172 test_dm_messages(0, 0, 1);
7174 DestroyWindow(hwRichEdit);
7176 /* Check messages from richedit(ES_MULTILINE) */
7178 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7180 memset(&dm_messages, 0, sizeof(dm_messages));
7181 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7182 ok(0 == r, "expected 0, got %d\n", r);
7183 test_dm_messages(0, 0, 0);
7185 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7186 ok(2 == lcount, "expected 2, got %d\n", lcount);
7188 memset(&dm_messages, 0, sizeof(dm_messages));
7189 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7190 ok(0 == r, "expected 0, got %d\n", r);
7191 test_dm_messages(0, 0, 0);
7193 memset(&dm_messages, 0, sizeof(dm_messages));
7194 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7195 ok(0 == r, "expected 0, got %d\n", r);
7196 test_dm_messages(0, 0, 0);
7198 memset(&dm_messages, 0, sizeof(dm_messages));
7199 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7200 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7201 test_dm_messages(0, 0, 0);
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, 1, 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, 1);
7221 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7222 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7223 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7225 memset(&dm_messages, 0, sizeof(dm_messages));
7226 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7227 ok(0 == r, "expected 0, got %d\n", r);
7228 test_dm_messages(0, 1, 1);
7230 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7231 ok(2 == lcount, "expected 2, got %d\n", lcount);
7233 DestroyWindow(hwButton);
7234 DestroyWindow(hwRichEdit);
7236 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7238 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7240 memset(&dm_messages, 0, sizeof(dm_messages));
7241 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7242 ok(0 == r, "expected 0, got %d\n", r);
7243 test_dm_messages(0, 0, 0);
7245 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7246 ok(2 == lcount, "expected 2, got %d\n", lcount);
7248 memset(&dm_messages, 0, sizeof(dm_messages));
7249 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7250 ok(0 == r, "expected 0, got %d\n", r);
7251 test_dm_messages(0, 0, 0);
7253 memset(&dm_messages, 0, sizeof(dm_messages));
7254 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7255 ok(0 == r, "expected 0, got %d\n", r);
7256 test_dm_messages(0, 0, 0);
7258 memset(&dm_messages, 0, sizeof(dm_messages));
7259 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7260 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7261 test_dm_messages(0, 0, 0);
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 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7269 ok(3 == lcount, "expected 3, got %d\n", lcount);
7271 memset(&dm_messages, 0, sizeof(dm_messages));
7272 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7273 ok(0 == r, "expected 0, got %d\n", r);
7274 test_dm_messages(0, 0, 0);
7276 memset(&dm_messages, 0, sizeof(dm_messages));
7277 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7278 ok(0 == r, "expected 0, got %d\n", r);
7279 test_dm_messages(0, 0, 1);
7281 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7282 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7283 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7285 memset(&dm_messages, 0, sizeof(dm_messages));
7286 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7287 ok(0 == r, "expected 0, got %d\n", r);
7288 test_dm_messages(0, 0, 0);
7290 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7291 ok(4 == lcount, "expected 4, got %d\n", lcount);
7293 DestroyWindow(hwButton);
7294 DestroyWindow(hwRichEdit);
7296 /* Check messages from richedit(0) */
7298 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7300 memset(&dm_messages, 0, sizeof(dm_messages));
7301 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7302 ok(0 == r, "expected 0, got %d\n", r);
7303 test_dm_messages(0, 0, 0);
7305 memset(&dm_messages, 0, sizeof(dm_messages));
7306 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7307 ok(0 == r, "expected 0, got %d\n", r);
7308 test_dm_messages(0, 0, 0);
7310 memset(&dm_messages, 0, sizeof(dm_messages));
7311 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7312 ok(0 == r, "expected 0, got %d\n", r);
7313 test_dm_messages(0, 0, 0);
7315 memset(&dm_messages, 0, sizeof(dm_messages));
7316 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7317 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7318 test_dm_messages(0, 0, 0);
7320 memset(&dm_messages, 0, sizeof(dm_messages));
7321 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7322 ok(0 == r, "expected 0, got %d\n", r);
7323 test_dm_messages(0, 1, 0);
7325 memset(&dm_messages, 0, sizeof(dm_messages));
7326 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7327 ok(0 == r, "expected 0, got %d\n", r);
7328 test_dm_messages(0, 0, 0);
7330 memset(&dm_messages, 0, sizeof(dm_messages));
7331 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7332 ok(0 == r, "expected 0, got %d\n", r);
7333 test_dm_messages(0, 0, 1);
7335 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7336 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7337 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7339 memset(&dm_messages, 0, sizeof(dm_messages));
7340 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7341 ok(0 == r, "expected 0, got %d\n", r);
7342 test_dm_messages(0, 1, 1);
7344 DestroyWindow(hwRichEdit);
7346 /* Check messages from richedit(ES_WANTRETURN) */
7348 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7350 memset(&dm_messages, 0, sizeof(dm_messages));
7351 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7352 ok(0 == r, "expected 0, got %d\n", r);
7353 test_dm_messages(0, 0, 0);
7355 memset(&dm_messages, 0, sizeof(dm_messages));
7356 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7357 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7358 test_dm_messages(0, 0, 0);
7360 memset(&dm_messages, 0, sizeof(dm_messages));
7361 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7362 ok(0 == r, "expected 0, got %d\n", r);
7363 test_dm_messages(0, 0, 0);
7365 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7366 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7367 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7369 memset(&dm_messages, 0, sizeof(dm_messages));
7370 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7371 ok(0 == r, "expected 0, got %d\n", r);
7372 test_dm_messages(0, 0, 0);
7374 DestroyWindow(hwRichEdit);
7375 DestroyWindow(hwParent);
7378 static void test_EM_FINDWORDBREAK_W(void)
7380 static const struct {
7381 WCHAR c;
7382 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7383 } delimiter_tests[] = {
7384 {0x0a, FALSE}, /* newline */
7385 {0x0b, FALSE}, /* vertical tab */
7386 {0x0c, FALSE}, /* form feed */
7387 {0x0d, FALSE}, /* carriage return */
7388 {0x20, TRUE}, /* space */
7389 {0x61, FALSE}, /* capital letter a */
7390 {0xa0, FALSE}, /* no-break space */
7391 {0x2000, FALSE}, /* en quad */
7392 {0x3000, FALSE}, /* Ideographic space */
7393 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7394 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7395 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7396 {0xac00, FALSE}, /* Hangul character GA*/
7397 {0xd7af, FALSE}, /* End of Hangul character chart */
7398 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7399 {0xff20, FALSE}, /* fullwidth commercial @ */
7400 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7402 int i;
7403 HWND hwndRichEdit = new_richeditW(NULL);
7404 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7405 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7407 WCHAR wbuf[2];
7408 int result;
7410 wbuf[0] = delimiter_tests[i].c;
7411 wbuf[1] = 0;
7412 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7413 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7414 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7415 todo_wine
7416 ok(result == delimiter_tests[i].isdelimiter,
7417 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7418 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7419 else
7420 ok(result == delimiter_tests[i].isdelimiter,
7421 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7422 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7424 DestroyWindow(hwndRichEdit);
7427 static void test_EM_FINDWORDBREAK_A(void)
7429 static const struct {
7430 WCHAR c;
7431 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7432 } delimiter_tests[] = {
7433 {0x0a, FALSE}, /* newline */
7434 {0x0b, FALSE}, /* vertical tab */
7435 {0x0c, FALSE}, /* form feed */
7436 {0x0d, FALSE}, /* carriage return */
7437 {0x20, TRUE}, /* space */
7438 {0x61, FALSE}, /* capital letter a */
7440 int i;
7441 HWND hwndRichEdit = new_richedit(NULL);
7443 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7444 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7446 int result;
7447 char buf[2];
7448 buf[0] = delimiter_tests[i].c;
7449 buf[1] = 0;
7450 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7451 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7452 if (buf[0] == 0x20)
7453 todo_wine
7454 ok(result == delimiter_tests[i].isdelimiter,
7455 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7456 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7457 else
7458 ok(result == delimiter_tests[i].isdelimiter,
7459 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7460 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7462 DestroyWindow(hwndRichEdit);
7466 * This test attempts to show the effect of enter on a richedit
7467 * control v1.0 inserts CRLF whereas for higher versions it only
7468 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7469 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7470 * does for higher. The same test is cloned in riched32 and riched20.
7472 static void test_enter(void)
7474 static const struct {
7475 const char *initialtext;
7476 const int cursor;
7477 const char *expectedwmtext;
7478 const char *expectedemtext;
7479 const char *expectedemtextcrlf;
7480 } testenteritems[] = {
7481 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7482 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7483 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7484 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7485 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7488 char expectedbuf[1024];
7489 char resultbuf[1024];
7490 HWND hwndRichEdit = new_richedit(NULL);
7491 UINT i,j;
7493 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7495 char buf[1024] = {0};
7496 LRESULT result;
7497 GETTEXTEX getText;
7498 const char *expected;
7500 /* Set the text to the initial text */
7501 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7502 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7504 /* Send Enter */
7505 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7506 simulate_typing_characters(hwndRichEdit, "\r");
7508 /* 1. Retrieve with WM_GETTEXT */
7509 buf[0] = 0x00;
7510 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7511 expected = testenteritems[i].expectedwmtext;
7513 resultbuf[0]=0x00;
7514 for (j = 0; j < (UINT)result; j++)
7515 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7516 expectedbuf[0] = '\0';
7517 for (j = 0; j < strlen(expected); j++)
7518 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7520 result = strcmp(expected, buf);
7521 ok (result == 0,
7522 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7523 i, resultbuf, expectedbuf);
7525 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7526 getText.cb = sizeof(buf);
7527 getText.flags = GT_DEFAULT;
7528 getText.codepage = CP_ACP;
7529 getText.lpDefaultChar = NULL;
7530 getText.lpUsedDefChar = NULL;
7531 buf[0] = 0x00;
7532 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7533 expected = testenteritems[i].expectedemtext;
7535 resultbuf[0]=0x00;
7536 for (j = 0; j < (UINT)result; j++)
7537 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7538 expectedbuf[0] = '\0';
7539 for (j = 0; j < strlen(expected); j++)
7540 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7542 result = strcmp(expected, buf);
7543 ok (result == 0,
7544 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7545 i, resultbuf, expectedbuf);
7547 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7548 getText.cb = sizeof(buf);
7549 getText.flags = GT_USECRLF;
7550 getText.codepage = CP_ACP;
7551 getText.lpDefaultChar = NULL;
7552 getText.lpUsedDefChar = NULL;
7553 buf[0] = 0x00;
7554 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7555 expected = testenteritems[i].expectedemtextcrlf;
7557 resultbuf[0]=0x00;
7558 for (j = 0; j < (UINT)result; j++)
7559 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7560 expectedbuf[0] = '\0';
7561 for (j = 0; j < strlen(expected); j++)
7562 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7564 result = strcmp(expected, buf);
7565 ok (result == 0,
7566 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7567 i, resultbuf, expectedbuf);
7570 DestroyWindow(hwndRichEdit);
7573 static void test_WM_CREATE(void)
7575 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7576 static const char title[] = "line1\nline2";
7578 HWND rich_edit;
7579 LRESULT res;
7580 char buf[64];
7581 int len;
7583 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7584 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7585 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7587 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7588 ok(len == 5, "GetWindowText returned %d\n", len);
7589 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7591 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7592 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7594 DestroyWindow(rich_edit);
7596 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7597 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7598 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7600 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7601 ok(len == 12, "GetWindowText returned %d\n", len);
7602 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7604 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7605 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7607 DestroyWindow(rich_edit);
7610 /*******************************************************************
7611 * Test that after deleting all of the text, the first paragraph
7612 * format reverts to the default.
7614 static void test_reset_default_para_fmt( void )
7616 HWND richedit = new_richeditW( NULL );
7617 PARAFORMAT2 fmt;
7618 WORD def_align, new_align;
7620 memset( &fmt, 0, sizeof(fmt) );
7621 fmt.cbSize = sizeof(PARAFORMAT2);
7622 fmt.dwMask = -1;
7623 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7624 def_align = fmt.wAlignment;
7625 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7627 simulate_typing_characters( richedit, "123" );
7629 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7630 fmt.dwMask = PFM_ALIGNMENT;
7631 fmt.wAlignment = new_align;
7632 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7634 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7635 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7637 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7638 SendMessageA( richedit, WM_CUT, 0, 0 );
7640 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7641 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7643 DestroyWindow( richedit );
7646 static void test_EM_SETREADONLY(void)
7648 HWND richedit = new_richeditW(NULL);
7649 DWORD dwStyle;
7650 LRESULT res;
7652 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
7653 ok(res == 1, "EM_SETREADONLY\n");
7654 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7655 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
7657 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
7658 ok(res == 1, "EM_SETREADONLY\n");
7659 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7660 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
7662 DestroyWindow(richedit);
7665 START_TEST( editor )
7667 BOOL ret;
7668 /* Must explicitly LoadLibrary(). The test has no references to functions in
7669 * RICHED20.DLL, so the linker doesn't actually link to it. */
7670 hmoduleRichEdit = LoadLibraryA("riched20.dll");
7671 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
7673 test_WM_CHAR();
7674 test_EM_FINDTEXT(FALSE);
7675 test_EM_FINDTEXT(TRUE);
7676 test_EM_GETLINE();
7677 test_EM_POSFROMCHAR();
7678 test_EM_SCROLLCARET();
7679 test_EM_SCROLL();
7680 test_scrollbar_visibility();
7681 test_WM_SETTEXT();
7682 test_EM_LINELENGTH();
7683 test_EM_SETCHARFORMAT();
7684 test_EM_SETTEXTMODE();
7685 test_TM_PLAINTEXT();
7686 test_EM_SETOPTIONS();
7687 test_WM_GETTEXT();
7688 test_EM_GETTEXTRANGE();
7689 test_EM_GETSELTEXT();
7690 test_EM_SETUNDOLIMIT();
7691 test_ES_PASSWORD();
7692 test_EM_SETTEXTEX();
7693 test_EM_LIMITTEXT();
7694 test_EM_EXLIMITTEXT();
7695 test_EM_GETLIMITTEXT();
7696 test_WM_SETFONT();
7697 test_EM_GETMODIFY();
7698 test_EM_SETSEL();
7699 test_EM_EXSETSEL();
7700 test_WM_PASTE();
7701 test_EM_STREAMIN();
7702 test_EM_STREAMOUT();
7703 test_EM_STREAMOUT_FONTTBL();
7704 test_EM_StreamIn_Undo();
7705 test_EM_FORMATRANGE();
7706 test_unicode_conversions();
7707 test_EM_GETTEXTLENGTHEX();
7708 test_EM_REPLACESEL(1);
7709 test_EM_REPLACESEL(0);
7710 test_WM_NOTIFY();
7711 test_EN_LINK();
7712 test_EM_AUTOURLDETECT();
7713 test_eventMask();
7714 test_undo_coalescing();
7715 test_word_movement();
7716 test_EM_CHARFROMPOS();
7717 test_SETPARAFORMAT();
7718 test_word_wrap();
7719 test_autoscroll();
7720 test_format_rect();
7721 test_WM_GETDLGCODE();
7722 test_zoom();
7723 test_dialogmode();
7724 test_EM_FINDWORDBREAK_W();
7725 test_EM_FINDWORDBREAK_A();
7726 test_enter();
7727 test_WM_CREATE();
7728 test_reset_default_para_fmt();
7729 test_EM_SETREADONLY();
7731 /* Set the environment variable WINETEST_RICHED20 to keep windows
7732 * responsive and open for 30 seconds. This is useful for debugging.
7734 if (getenv( "WINETEST_RICHED20" )) {
7735 keep_responsive(30);
7738 OleFlushClipboard();
7739 ret = FreeLibrary(hmoduleRichEdit);
7740 ok(ret, "error: %d\n", (int) GetLastError());