riched20/tests: Test EM_SETTEXT and EM_REPLACESEL with multibyte character.
[wine.git] / dlls / riched20 / tests / editor.c
blob20cc924bad63e532a87195c5c7c1a2e650fb3de1
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 LONG streamout_written = 0;
3312 static DWORD CALLBACK test_WM_SETTEXT_esCallback(DWORD_PTR dwCookie,
3313 LPBYTE pbBuff,
3314 LONG cb,
3315 LONG *pcb)
3317 char** str = (char**)dwCookie;
3318 *pcb = cb;
3319 if (*pcb > 0) {
3320 memcpy(*str, pbBuff, *pcb);
3321 *str += *pcb;
3323 streamout_written = *pcb;
3324 return 0;
3327 static void test_WM_SETTEXT(void)
3329 HWND hwndRichEdit = new_richedit(NULL);
3330 const char * TestItem1 = "TestSomeText";
3331 const char * TestItem2 = "TestSomeText\r";
3332 const char * TestItem2_after = "TestSomeText\r\n";
3333 const char * TestItem3 = "TestSomeText\rSomeMoreText\r";
3334 const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n";
3335 const char * TestItem4 = "TestSomeText\n\nTestSomeText";
3336 const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText";
3337 const char * TestItem5 = "TestSomeText\r\r\nTestSomeText";
3338 const char * TestItem5_after = "TestSomeText TestSomeText";
3339 const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText";
3340 const char * TestItem6_after = "TestSomeText \r\nTestSomeText";
3341 const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText";
3342 const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText";
3344 const char rtftextA[] = "{\\rtf sometext}";
3345 const char urtftextA[] = "{\\urtf sometext}";
3346 const WCHAR rtftextW[] = {'{','\\','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3347 const WCHAR urtftextW[] = {'{','\\','u','r','t','f',' ','s','o','m','e','t','e','x','t','}',0};
3348 const WCHAR sometextW[] = {'s','o','m','e','t','e','x','t',0};
3350 char buf[1024] = {0};
3351 WCHAR bufW[1024] = {0};
3352 LRESULT result;
3354 /* This test attempts to show that WM_SETTEXT on a riched20 control causes
3355 any solitary \r to be converted to \r\n on return. Properly paired
3356 \r\n are not affected. It also shows that the special sequence \r\r\n
3357 gets converted to a single space.
3360 #define TEST_SETTEXT(a, b) \
3361 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3362 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3363 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf); \
3364 ok (result == lstrlenA(buf), \
3365 "WM_GETTEXT returned %ld instead of expected %u\n", \
3366 result, lstrlenA(buf)); \
3367 result = strcmp(b, buf); \
3368 ok(result == 0, \
3369 "WM_SETTEXT round trip: strcmp = %ld, text=\"%s\"\n", result, buf);
3371 TEST_SETTEXT(TestItem1, TestItem1)
3372 TEST_SETTEXT(TestItem2, TestItem2_after)
3373 TEST_SETTEXT(TestItem3, TestItem3_after)
3374 TEST_SETTEXT(TestItem3_after, TestItem3_after)
3375 TEST_SETTEXT(TestItem4, TestItem4_after)
3376 TEST_SETTEXT(TestItem5, TestItem5_after)
3377 TEST_SETTEXT(TestItem6, TestItem6_after)
3378 TEST_SETTEXT(TestItem7, TestItem7_after)
3380 /* The following tests demonstrate that WM_SETTEXT supports RTF strings */
3381 TEST_SETTEXT(rtftextA, "sometext") /* interpreted as ascii rtf */
3382 TEST_SETTEXT(urtftextA, "sometext") /* interpreted as ascii rtf */
3383 TEST_SETTEXT(rtftextW, "{") /* interpreted as ascii text */
3384 TEST_SETTEXT(urtftextW, "{") /* interpreted as ascii text */
3385 DestroyWindow(hwndRichEdit);
3386 #undef TEST_SETTEXT
3388 #define TEST_SETTEXTW(a, b) \
3389 result = SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)a); \
3390 ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \
3391 result = SendMessageW(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufW); \
3392 ok (result == lstrlenW(bufW), \
3393 "WM_GETTEXT returned %ld instead of expected %u\n", \
3394 result, lstrlenW(bufW)); \
3395 result = lstrcmpW(b, bufW); \
3396 ok(result == 0, "WM_SETTEXT round trip: strcmp = %ld\n", result);
3398 hwndRichEdit = CreateWindowW(RICHEDIT_CLASS20W, NULL,
3399 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
3400 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
3401 ok(hwndRichEdit != NULL, "class: RichEdit20W, error: %d\n", (int) GetLastError());
3402 TEST_SETTEXTW(rtftextA, sometextW) /* interpreted as ascii rtf */
3403 TEST_SETTEXTW(urtftextA, sometextW) /* interpreted as ascii rtf */
3404 TEST_SETTEXTW(rtftextW, rtftextW) /* interpreted as ascii text */
3405 TEST_SETTEXTW(urtftextW, urtftextW) /* interpreted as ascii text */
3406 DestroyWindow(hwndRichEdit);
3407 #undef TEST_SETTEXTW
3410 /* Set *pcb to one to show that the remaining cb-1 bytes are not
3411 resent to the callkack. */
3412 static DWORD CALLBACK test_esCallback_written_1(DWORD_PTR dwCookie,
3413 LPBYTE pbBuff,
3414 LONG cb,
3415 LONG *pcb)
3417 char** str = (char**)dwCookie;
3418 ok(*pcb == cb || *pcb == 0, "cb %d, *pcb %d\n", cb, *pcb);
3419 *pcb = 0;
3420 if (cb > 0) {
3421 memcpy(*str, pbBuff, cb);
3422 *str += cb;
3423 *pcb = 1;
3425 return 0;
3428 static int count_pars(const char *buf)
3430 const char *p = buf;
3431 int count = 0;
3432 while ((p = strstr( p, "\\par" )) != NULL)
3434 if (!isalpha( p[4] ))
3435 count++;
3436 p++;
3438 return count;
3441 static void test_EM_STREAMOUT(void)
3443 HWND hwndRichEdit = new_richedit(NULL);
3444 int r;
3445 EDITSTREAM es;
3446 char buf[1024] = {0};
3447 char * p;
3448 LRESULT result;
3450 const char * TestItem1 = "TestSomeText";
3451 const char * TestItem2 = "TestSomeText\r";
3452 const char * TestItem3 = "TestSomeText\r\n";
3454 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem1);
3455 p = buf;
3456 es.dwCookie = (DWORD_PTR)&p;
3457 es.dwError = 0;
3458 es.pfnCallback = test_WM_SETTEXT_esCallback;
3459 memset(buf, 0, sizeof(buf));
3460 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3461 r = strlen(buf);
3462 ok(r == 12, "streamed text length is %d, expecting 12\n", r);
3463 ok(strcmp(buf, TestItem1) == 0,
3464 "streamed text different, got %s\n", buf);
3465 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3467 /* RTF mode writes the final end of para \r if it's part of the selection */
3468 p = buf;
3469 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3470 ok (count_pars(buf) == 1, "got %s\n", buf);
3471 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3472 p = buf;
3473 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 12);
3474 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3475 ok (count_pars(buf) == 0, "got %s\n", buf);
3476 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3477 p = buf;
3478 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3479 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3480 ok (count_pars(buf) == 1, "got %s\n", buf);
3481 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3483 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
3484 p = buf;
3485 es.dwCookie = (DWORD_PTR)&p;
3486 es.dwError = 0;
3487 es.pfnCallback = test_WM_SETTEXT_esCallback;
3488 memset(buf, 0, sizeof(buf));
3489 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3490 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3491 r = strlen(buf);
3492 /* Here again, \r gets converted to \r\n, like WM_GETTEXT */
3493 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3494 ok(strcmp(buf, TestItem3) == 0,
3495 "streamed text different from, got %s\n", buf);
3497 /* And again RTF mode writes the final end of para \r if it's part of the selection */
3498 p = buf;
3499 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3500 ok (count_pars(buf) == 2, "got %s\n", buf);
3501 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3502 p = buf;
3503 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 13);
3504 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3505 ok (count_pars(buf) == 1, "got %s\n", buf);
3506 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3507 p = buf;
3508 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3509 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF|SFF_SELECTION, (LPARAM)&es);
3510 ok (count_pars(buf) == 2, "got %s\n", buf);
3511 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3513 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem3);
3514 p = buf;
3515 es.dwCookie = (DWORD_PTR)&p;
3516 es.dwError = 0;
3517 es.pfnCallback = test_WM_SETTEXT_esCallback;
3518 memset(buf, 0, sizeof(buf));
3519 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3520 ok(result == streamout_written, "got %ld expected %d\n", result, streamout_written);
3521 r = strlen(buf);
3522 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3523 ok(strcmp(buf, TestItem3) == 0,
3524 "streamed text different, got %s\n", buf);
3526 /* Use a callback that sets *pcb to one */
3527 p = buf;
3528 es.dwCookie = (DWORD_PTR)&p;
3529 es.dwError = 0;
3530 es.pfnCallback = test_esCallback_written_1;
3531 memset(buf, 0, sizeof(buf));
3532 result = SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es);
3533 r = strlen(buf);
3534 ok(r == 14, "streamed text length is %d, expecting 14\n", r);
3535 ok(strcmp(buf, TestItem3) == 0,
3536 "streamed text different, got %s\n", buf);
3537 ok(result == 0, "got %ld expected 0\n", result);
3540 DestroyWindow(hwndRichEdit);
3543 static void test_EM_STREAMOUT_FONTTBL(void)
3545 HWND hwndRichEdit = new_richedit(NULL);
3546 EDITSTREAM es;
3547 char buf[1024] = {0};
3548 char * p;
3549 char * fontTbl;
3550 int brackCount;
3552 const char * TestItem = "TestSomeText";
3554 /* fills in the richedit control with some text */
3555 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem);
3557 /* streams out the text in rtf format */
3558 p = buf;
3559 es.dwCookie = (DWORD_PTR)&p;
3560 es.dwError = 0;
3561 es.pfnCallback = test_WM_SETTEXT_esCallback;
3562 memset(buf, 0, sizeof(buf));
3563 SendMessageA(hwndRichEdit, EM_STREAMOUT, SF_RTF, (LPARAM)&es);
3565 /* scans for \fonttbl, error if not found */
3566 fontTbl = strstr(buf, "\\fonttbl");
3567 ok(fontTbl != NULL, "missing \\fonttbl section\n");
3568 if(fontTbl)
3570 /* scans for terminating closing bracket */
3571 brackCount = 1;
3572 while(*fontTbl && brackCount)
3574 if(*fontTbl == '{')
3575 brackCount++;
3576 else if(*fontTbl == '}')
3577 brackCount--;
3578 fontTbl++;
3580 /* checks whether closing bracket is ok */
3581 ok(brackCount == 0, "missing closing bracket in \\fonttbl block\n");
3582 if(!brackCount)
3584 /* char before closing fonttbl block should be a closed bracket */
3585 fontTbl -= 2;
3586 ok(*fontTbl == '}', "spurious character '%02x' before \\fonttbl closing bracket\n", *fontTbl);
3588 /* char after fonttbl block should be a crlf */
3589 fontTbl += 2;
3590 ok(*fontTbl == 0x0d && *(fontTbl+1) == 0x0a, "missing crlf after \\fonttbl block\n");
3593 DestroyWindow(hwndRichEdit);
3597 static void test_EM_SETTEXTEX(void)
3599 HWND hwndRichEdit, parent;
3600 SCROLLINFO si;
3601 int sel_start, sel_end;
3602 SETTEXTEX setText;
3603 GETTEXTEX getText;
3604 WCHAR TestItem1[] = {'T', 'e', 's', 't',
3605 'S', 'o', 'm', 'e',
3606 'T', 'e', 'x', 't', 0};
3607 WCHAR TestItem1alt[] = {'T', 'T', 'e', 's',
3608 't', 'S', 'o', 'm',
3609 'e', 'T', 'e', 'x',
3610 't', 't', 'S', 'o',
3611 'm', 'e', 'T', 'e',
3612 'x', 't', 0};
3613 WCHAR TestItem1altn[] = {'T','T','e','s','t','S','o','m','e','T','e','x','t',
3614 '\r','t','S','o','m','e','T','e','x','t',0};
3615 WCHAR TestItem2[] = {'T', 'e', 's', 't',
3616 'S', 'o', 'm', 'e',
3617 'T', 'e', 'x', 't',
3618 '\r', 0};
3619 const char * TestItem2_after = "TestSomeText\r\n";
3620 WCHAR TestItem3[] = {'T', 'e', 's', 't',
3621 'S', 'o', 'm', 'e',
3622 'T', 'e', 'x', 't',
3623 '\r','\n','\r','\n', 0};
3624 WCHAR TestItem3alt[] = {'T', 'e', 's', 't',
3625 'S', 'o', 'm', 'e',
3626 'T', 'e', 'x', 't',
3627 '\n','\n', 0};
3628 WCHAR TestItem3_after[] = {'T', 'e', 's', 't',
3629 'S', 'o', 'm', 'e',
3630 'T', 'e', 'x', 't',
3631 '\r','\r', 0};
3632 WCHAR TestItem4[] = {'T', 'e', 's', 't',
3633 'S', 'o', 'm', 'e',
3634 'T', 'e', 'x', 't',
3635 '\r','\r','\n','\r',
3636 '\n', 0};
3637 WCHAR TestItem4_after[] = {'T', 'e', 's', 't',
3638 'S', 'o', 'm', 'e',
3639 'T', 'e', 'x', 't',
3640 ' ','\r', 0};
3641 #define MAX_BUF_LEN 1024
3642 WCHAR buf[MAX_BUF_LEN];
3643 char bufACP[MAX_BUF_LEN];
3644 char * p;
3645 int result;
3646 CHARRANGE cr;
3647 EDITSTREAM es;
3648 WNDCLASSA cls;
3650 /* Test the scroll position with and without a parent window.
3652 * For some reason the scroll position is 0 after EM_SETTEXTEX
3653 * with the ST_SELECTION flag only when the control has a parent
3654 * window, even though the selection is at the end. */
3655 cls.style = 0;
3656 cls.lpfnWndProc = DefWindowProcA;
3657 cls.cbClsExtra = 0;
3658 cls.cbWndExtra = 0;
3659 cls.hInstance = GetModuleHandleA(0);
3660 cls.hIcon = 0;
3661 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
3662 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
3663 cls.lpszMenuName = NULL;
3664 cls.lpszClassName = "ParentTestClass";
3665 if(!RegisterClassA(&cls)) assert(0);
3667 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
3668 0, 0, 200, 60, NULL, NULL, NULL, NULL);
3669 ok (parent != 0, "Failed to create parent window\n");
3671 hwndRichEdit = CreateWindowExA(0,
3672 RICHEDIT_CLASS20A, NULL,
3673 ES_MULTILINE|WS_VSCROLL|WS_VISIBLE|WS_CHILD,
3674 0, 0, 200, 60, parent, NULL,
3675 hmoduleRichEdit, NULL);
3677 setText.codepage = CP_ACP;
3678 setText.flags = ST_SELECTION;
3679 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3680 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3681 si.cbSize = sizeof(si);
3682 si.fMask = SIF_ALL;
3683 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3684 todo_wine ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3685 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3686 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3687 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3689 DestroyWindow(parent);
3691 /* Test without a parent window */
3692 hwndRichEdit = new_richedit(NULL);
3693 setText.codepage = CP_ACP;
3694 setText.flags = ST_SELECTION;
3695 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3696 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3697 si.cbSize = sizeof(si);
3698 si.fMask = SIF_ALL;
3699 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3700 ok(si.nPos != 0, "Position is incorrectly at %d\n", si.nPos);
3701 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3702 ok(sel_start == 18, "Selection start incorrectly at %d\n", sel_start);
3703 ok(sel_end == 18, "Selection end incorrectly at %d\n", sel_end);
3705 /* The scroll position should also be 0 after EM_SETTEXTEX with ST_DEFAULT,
3706 * but this time it is because the selection is at the beginning. */
3707 setText.codepage = CP_ACP;
3708 setText.flags = ST_DEFAULT;
3709 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText,
3710 (LPARAM)"{\\rtf 1\\par 2\\par 3\\par 4\\par 5\\par 6\\par 7\\par 8\\par 9\\par}");
3711 si.cbSize = sizeof(si);
3712 si.fMask = SIF_ALL;
3713 GetScrollInfo(hwndRichEdit, SB_VERT, &si);
3714 ok(si.nPos == 0, "Position is incorrectly at %d\n", si.nPos);
3715 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
3716 ok(sel_start == 0, "Selection start incorrectly at %d\n", sel_start);
3717 ok(sel_end == 0, "Selection end incorrectly at %d\n", sel_end);
3719 setText.codepage = 1200; /* no constant for unicode */
3720 getText.codepage = 1200; /* no constant for unicode */
3721 getText.cb = MAX_BUF_LEN;
3722 getText.flags = GT_DEFAULT;
3723 getText.lpDefaultChar = NULL;
3724 getText.lpUsedDefChar = NULL;
3726 setText.flags = 0;
3727 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3728 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3729 ok(lstrcmpW(buf, TestItem1) == 0,
3730 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3732 /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not
3733 convert \r to \r\n on return: !ST_SELECTION && Unicode && !\rtf
3735 setText.codepage = 1200; /* no constant for unicode */
3736 getText.codepage = 1200; /* no constant for unicode */
3737 getText.cb = MAX_BUF_LEN;
3738 getText.flags = GT_DEFAULT;
3739 getText.lpDefaultChar = NULL;
3740 getText.lpUsedDefChar = NULL;
3741 setText.flags = 0;
3742 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem2);
3743 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3744 ok(lstrcmpW(buf, TestItem2) == 0,
3745 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3747 /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */
3748 SendMessageA(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf);
3749 ok(strcmp((const char *)buf, TestItem2_after) == 0,
3750 "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n");
3752 /* Baseline test for just-enough buffer space for string */
3753 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3754 getText.codepage = 1200; /* no constant for unicode */
3755 getText.flags = GT_DEFAULT;
3756 getText.lpDefaultChar = NULL;
3757 getText.lpUsedDefChar = NULL;
3758 memset(buf, 0, MAX_BUF_LEN);
3759 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3760 ok(lstrcmpW(buf, TestItem2) == 0,
3761 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3763 /* When there is enough space for one character, but not both, of the CRLF
3764 pair at the end of the string, the CR is not copied at all. That is,
3765 the caller must not see CRLF pairs truncated to CR at the end of the
3766 string.
3768 getText.cb = (lstrlenW(TestItem2) + 1) * sizeof(WCHAR);
3769 getText.codepage = 1200; /* no constant for unicode */
3770 getText.flags = GT_USECRLF; /* <-- asking for CR -> CRLF conversion */
3771 getText.lpDefaultChar = NULL;
3772 getText.lpUsedDefChar = NULL;
3773 memset(buf, 0, MAX_BUF_LEN);
3774 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3775 ok(lstrcmpW(buf, TestItem1) == 0,
3776 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3779 /* \r\n pairs get changed into \r: !ST_SELECTION && Unicode && !\rtf */
3780 setText.codepage = 1200; /* no constant for unicode */
3781 getText.codepage = 1200; /* no constant for unicode */
3782 getText.cb = MAX_BUF_LEN;
3783 getText.flags = GT_DEFAULT;
3784 getText.lpDefaultChar = NULL;
3785 getText.lpUsedDefChar = NULL;
3786 setText.flags = 0;
3787 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3);
3788 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3789 ok(lstrcmpW(buf, TestItem3_after) == 0,
3790 "EM_SETTEXTEX did not convert properly\n");
3792 /* \n also gets changed to \r: !ST_SELECTION && Unicode && !\rtf */
3793 setText.codepage = 1200; /* no constant for unicode */
3794 getText.codepage = 1200; /* no constant for unicode */
3795 getText.cb = MAX_BUF_LEN;
3796 getText.flags = GT_DEFAULT;
3797 getText.lpDefaultChar = NULL;
3798 getText.lpUsedDefChar = NULL;
3799 setText.flags = 0;
3800 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem3alt);
3801 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3802 ok(lstrcmpW(buf, TestItem3_after) == 0,
3803 "EM_SETTEXTEX did not convert properly\n");
3805 /* \r\r\n gets changed into single space: !ST_SELECTION && Unicode && !\rtf */
3806 setText.codepage = 1200; /* no constant for unicode */
3807 getText.codepage = 1200; /* no constant for unicode */
3808 getText.cb = MAX_BUF_LEN;
3809 getText.flags = GT_DEFAULT;
3810 getText.lpDefaultChar = NULL;
3811 getText.lpUsedDefChar = NULL;
3812 setText.flags = 0;
3813 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem4);
3814 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3815 ok(lstrcmpW(buf, TestItem4_after) == 0,
3816 "EM_SETTEXTEX did not convert properly\n");
3818 /* !ST_SELECTION && Unicode && !\rtf */
3819 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3820 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3822 ok (result == 1,
3823 "EM_SETTEXTEX returned %d, instead of 1\n",result);
3824 ok(!buf[0], "EM_SETTEXTEX with NULL lParam should clear rich edit.\n");
3826 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3827 setText.flags = 0;
3828 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3829 /* select some text */
3830 cr.cpMax = 1;
3831 cr.cpMin = 3;
3832 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3833 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3834 setText.flags = ST_SELECTION;
3835 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, 0);
3836 ok(result == 0,
3837 "EM_SETTEXTEX with NULL lParam to replace selection"
3838 " with no text should return 0. Got %i\n",
3839 result);
3841 /* put some text back: !ST_SELECTION && Unicode && !\rtf */
3842 setText.flags = 0;
3843 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3844 /* select some text */
3845 cr.cpMax = 1;
3846 cr.cpMin = 3;
3847 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3848 /* replace current selection: ST_SELECTION && Unicode && !\rtf */
3849 setText.flags = ST_SELECTION;
3850 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
3851 /* get text */
3852 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3853 ok(result == lstrlenW(TestItem1),
3854 "EM_SETTEXTEX with NULL lParam to replace selection"
3855 " with no text should return 0. Got %i\n",
3856 result);
3857 ok(lstrlenW(buf) == 22,
3858 "EM_SETTEXTEX to replace selection with more text failed: %i.\n",
3859 lstrlenW(buf) );
3861 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings */
3862 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3863 p = (char *)buf;
3864 es.dwCookie = (DWORD_PTR)&p;
3865 es.dwError = 0;
3866 es.pfnCallback = test_WM_SETTEXT_esCallback;
3867 memset(buf, 0, sizeof(buf));
3868 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3869 (WPARAM)(SF_RTF), (LPARAM)&es);
3870 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3872 /* !ST_SELECTION && !Unicode && \rtf */
3873 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3874 getText.codepage = 1200; /* no constant for unicode */
3875 getText.cb = MAX_BUF_LEN;
3876 getText.flags = GT_DEFAULT;
3877 getText.lpDefaultChar = NULL;
3878 getText.lpUsedDefChar = NULL;
3880 setText.flags = 0;
3881 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3882 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3883 ok(lstrcmpW(buf, TestItem1) == 0,
3884 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n");
3886 /* The following test demonstrates that EM_SETTEXTEX treats text as ASCII if it
3887 * starts with ASCII characters "{\rtf" even when the codepage is unicode. */
3888 setText.codepage = 1200; /* Lie about code page (actual ASCII) */
3889 getText.codepage = CP_ACP;
3890 getText.cb = MAX_BUF_LEN;
3891 getText.flags = GT_DEFAULT;
3892 getText.lpDefaultChar = NULL;
3893 getText.lpUsedDefChar = NULL;
3895 setText.flags = ST_SELECTION;
3896 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
3897 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf not unicode}");
3898 todo_wine ok(result == 11, "EM_SETTEXTEX incorrectly returned %d\n", result);
3899 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3900 ok(lstrcmpA(bufACP, "not unicode") == 0, "'%s' != 'not unicode'\n", bufACP);
3902 /* The following test demonstrates that EM_SETTEXTEX supports RTF strings with a selection */
3903 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"TestSomeText"); /* TestItem1 */
3904 p = (char *)buf;
3905 es.dwCookie = (DWORD_PTR)&p;
3906 es.dwError = 0;
3907 es.pfnCallback = test_WM_SETTEXT_esCallback;
3908 memset(buf, 0, sizeof(buf));
3909 SendMessageA(hwndRichEdit, EM_STREAMOUT,
3910 (WPARAM)(SF_RTF), (LPARAM)&es);
3911 trace("EM_STREAMOUT produced:\n%s\n", (char *)buf);
3913 /* select some text */
3914 cr.cpMax = 1;
3915 cr.cpMin = 3;
3916 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3918 /* ST_SELECTION && !Unicode && \rtf */
3919 setText.codepage = CP_ACP;/* EM_STREAMOUT saved as ANSI string */
3920 getText.codepage = 1200; /* no constant for unicode */
3921 getText.cb = MAX_BUF_LEN;
3922 getText.flags = GT_DEFAULT;
3923 getText.lpDefaultChar = NULL;
3924 getText.lpUsedDefChar = NULL;
3926 setText.flags = ST_SELECTION;
3927 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)buf);
3928 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3929 ok_w3("Expected \"%s\" or \"%s\", got \"%s\"\n", TestItem1alt, TestItem1altn, buf);
3931 /* The following test demonstrates that EM_SETTEXTEX replacing a selection */
3932 setText.codepage = 1200; /* no constant for unicode */
3933 getText.codepage = CP_ACP;
3934 getText.cb = MAX_BUF_LEN;
3936 setText.flags = 0;
3937 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); /* TestItem1 */
3938 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3940 /* select some text */
3941 cr.cpMax = 1;
3942 cr.cpMin = 3;
3943 SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&cr);
3945 /* ST_SELECTION && !Unicode && !\rtf */
3946 setText.codepage = CP_ACP;
3947 getText.codepage = 1200; /* no constant for unicode */
3948 getText.cb = MAX_BUF_LEN;
3949 getText.flags = GT_DEFAULT;
3950 getText.lpDefaultChar = NULL;
3951 getText.lpUsedDefChar = NULL;
3953 setText.flags = ST_SELECTION;
3954 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)bufACP);
3955 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
3956 ok(lstrcmpW(buf, TestItem1alt) == 0,
3957 "EM_GETTEXTEX results not what was set by EM_SETTEXTEX when"
3958 " using ST_SELECTION and non-Unicode\n");
3960 /* Test setting text using rich text format */
3961 setText.flags = 0;
3962 setText.codepage = CP_ACP;
3963 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf richtext}");
3964 getText.codepage = CP_ACP;
3965 getText.cb = MAX_BUF_LEN;
3966 getText.flags = GT_DEFAULT;
3967 getText.lpDefaultChar = NULL;
3968 getText.lpUsedDefChar = NULL;
3969 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3970 ok(!strcmp(bufACP, "richtext"), "expected 'richtext' but got '%s'\n", bufACP);
3972 setText.flags = 0;
3973 setText.codepage = CP_ACP;
3974 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\urtf morerichtext}");
3975 getText.codepage = CP_ACP;
3976 getText.cb = MAX_BUF_LEN;
3977 getText.flags = GT_DEFAULT;
3978 getText.lpDefaultChar = NULL;
3979 getText.lpUsedDefChar = NULL;
3980 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)bufACP);
3981 ok(!strcmp(bufACP, "morerichtext"), "expected 'morerichtext' but got '%s'\n", bufACP);
3983 /* test for utf8 text with BOM */
3984 setText.flags = 0;
3985 setText.codepage = CP_ACP;
3986 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3987 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3988 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3989 result = strcmp(bufACP, "TestUTF8WithBOM");
3990 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
3992 setText.flags = 0;
3993 setText.codepage = CP_UTF8;
3994 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"\xef\xbb\xbfTestUTF8WithBOM");
3995 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
3996 ok(result == 15, "EM_SETTEXTEX: Test UTF8 with BOM returned %d, expected 15\n", result);
3997 result = strcmp(bufACP, "TestUTF8WithBOM");
3998 ok(result == 0, "EM_SETTEXTEX: Test UTF8 with BOM set wrong text: Result: %s\n", bufACP);
4000 /* Test multibyte character */
4001 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_JAPANESE)
4002 skip("Skip multibyte character tests on non-Japanese platform\n");
4003 else
4005 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4006 setText.flags = ST_SELECTION;
4007 setText.codepage = CP_ACP;
4008 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4009 todo_wine ok(result == 5, "EM_SETTEXTEX incorrectly returned %d, expected 5\n", result);
4010 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4011 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4012 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4013 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4015 setText.flags = ST_DEFAULT;
4016 setText.codepage = CP_ACP;
4017 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"abc\x8e\xf0");
4018 ok(result == 1, "EM_SETTEXTEX incorrectly returned %d, expected 1\n", result);
4019 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4020 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4021 ok(!strcmp(bufACP, "abc\x8e\xf0"),
4022 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4024 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4025 setText.flags = ST_SELECTION;
4026 setText.codepage = CP_ACP;
4027 result = SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)"{\\rtf abc\x8e\xf0}");
4028 todo_wine ok(result == 4, "EM_SETTEXTEX incorrectly returned %d, expected 4\n", result);
4029 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)bufACP);
4030 ok(result == 5, "WM_GETTEXT incorrectly returned %d, expected 5\n", result);
4031 todo_wine ok(!strcmp(bufACP, "abc\x8e\xf0"),
4032 "EM_SETTEXTEX: Test multibyte character set wrong text: Result: %s\n", bufACP);
4035 DestroyWindow(hwndRichEdit);
4038 static void test_EM_LIMITTEXT(void)
4040 int ret;
4042 HWND hwndRichEdit = new_richedit(NULL);
4044 /* The main purpose of this test is to demonstrate that the nonsense in MSDN
4045 * about setting the length to -1 for multiline edit controls doesn't happen.
4048 /* Don't check default gettextlimit case. That's done in other tests */
4050 /* Set textlimit to 100 */
4051 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 100, 0);
4052 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4053 ok (ret == 100,
4054 "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret);
4056 /* Set textlimit to 0 */
4057 SendMessageA(hwndRichEdit, EM_LIMITTEXT, 0, 0);
4058 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4059 ok (ret == 65536,
4060 "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret);
4062 /* Set textlimit to -1 */
4063 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -1, 0);
4064 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4065 ok (ret == -1,
4066 "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret);
4068 /* Set textlimit to -2 */
4069 SendMessageA(hwndRichEdit, EM_LIMITTEXT, -2, 0);
4070 ret = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4071 ok (ret == -2,
4072 "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret);
4074 DestroyWindow (hwndRichEdit);
4078 static void test_EM_EXLIMITTEXT(void)
4080 int i, selBegin, selEnd, len1, len2;
4081 int result;
4082 char text[1024 + 1];
4083 char buffer[1024 + 1];
4084 int textlimit = 0; /* multiple of 100 */
4085 HWND hwndRichEdit = new_richedit(NULL);
4087 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4088 ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */
4090 textlimit = 256000;
4091 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4092 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4093 /* set higher */
4094 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4096 textlimit = 1000;
4097 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4098 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4099 /* set lower */
4100 ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i);
4102 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 0);
4103 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4104 /* default for WParam = 0 */
4105 ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i);
4107 textlimit = sizeof(text)-1;
4108 memset(text, 'W', textlimit);
4109 text[sizeof(text)-1] = 0;
4110 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4111 /* maxed out text */
4112 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4114 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4115 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4116 len1 = selEnd - selBegin;
4118 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1);
4119 SendMessageA(hwndRichEdit, WM_CHAR, VK_BACK, 1);
4120 SendMessageA(hwndRichEdit, WM_KEYUP, VK_BACK, 1);
4121 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4122 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4123 len2 = selEnd - selBegin;
4125 ok(len1 != len2,
4126 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4127 len1,len2,i);
4129 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4130 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4131 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1);
4132 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4133 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4134 len1 = selEnd - selBegin;
4136 ok(len1 != len2,
4137 "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4138 len1,len2,i);
4140 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A', 1);
4141 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4142 SendMessageA(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */
4143 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4144 SendMessageA(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd);
4145 len2 = selEnd - selBegin;
4147 ok(len1 == len2,
4148 "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n",
4149 len1,len2,i);
4151 /* set text up to the limit, select all the text, then add a char */
4152 textlimit = 5;
4153 memset(text, 'W', textlimit);
4154 text[textlimit] = 0;
4155 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4156 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4157 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1);
4158 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 1);
4159 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4160 result = strcmp(buffer, "A");
4161 ok(0 == result, "got string = \"%s\"\n", buffer);
4163 /* WM_SETTEXT not limited */
4164 textlimit = 10;
4165 memset(text, 'W', textlimit);
4166 text[textlimit] = 0;
4167 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5);
4168 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
4169 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4170 i = strlen(buffer);
4171 ok(10 == i, "expected 10 chars\n");
4172 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4173 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4175 /* try inserting more text at end */
4176 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4177 ok(0 == i, "WM_CHAR wasn't processed\n");
4178 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4179 i = strlen(buffer);
4180 ok(10 == i, "expected 10 chars, got %i\n", i);
4181 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4182 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4184 /* try inserting text at beginning */
4185 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 0);
4186 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4187 ok(0 == i, "WM_CHAR wasn't processed\n");
4188 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4189 i = strlen(buffer);
4190 ok(10 == i, "expected 10 chars, got %i\n", i);
4191 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4192 ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i);
4194 /* WM_CHAR is limited */
4195 textlimit = 1;
4196 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit);
4197 SendMessageA(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */
4198 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4199 ok(0 == i, "WM_CHAR wasn't processed\n");
4200 i = SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4201 ok(0 == i, "WM_CHAR wasn't processed\n");
4202 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4203 i = strlen(buffer);
4204 ok(1 == i, "expected 1 chars, got %i instead\n", i);
4206 DestroyWindow(hwndRichEdit);
4209 static void test_EM_GETLIMITTEXT(void)
4211 int i;
4212 HWND hwndRichEdit = new_richedit(NULL);
4214 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4215 ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */
4217 SendMessageA(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000);
4218 i = SendMessageA(hwndRichEdit, EM_GETLIMITTEXT, 0, 0);
4219 ok(50000 == i, "expected: %d, actual: %d\n", 50000, i);
4221 DestroyWindow(hwndRichEdit);
4224 static void test_WM_SETFONT(void)
4226 /* There is no invalid input or error conditions for this function.
4227 * NULL wParam and lParam just fall back to their default values
4228 * It should be noted that even if you use a gibberish name for your fonts
4229 * here, it will still work because the name is stored. They will display as
4230 * System, but will report their name to be whatever they were created as */
4232 HWND hwndRichEdit = new_richedit(NULL);
4233 HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4234 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4235 FF_DONTCARE, "Marlett");
4236 HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4237 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4238 FF_DONTCARE, "MS Sans Serif");
4239 HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4240 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4241 FF_DONTCARE, "Courier");
4242 LOGFONTA sentLogFont;
4243 CHARFORMAT2A returnedCF2A;
4245 returnedCF2A.cbSize = sizeof(returnedCF2A);
4247 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"x");
4248 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1, MAKELPARAM(TRUE, 0));
4249 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4251 GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont);
4252 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4253 "EM_GETCHARFORMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n",
4254 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4256 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2, MAKELPARAM(TRUE, 0));
4257 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4258 GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont);
4259 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4260 "EM_GETCHARFORMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n",
4261 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4263 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3, MAKELPARAM(TRUE, 0));
4264 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4265 GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont);
4266 ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName),
4267 "EM_GETCHARFORMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n",
4268 sentLogFont.lfFaceName,returnedCF2A.szFaceName);
4270 /* This last test is special since we send in NULL. We clear the variables
4271 * and just compare to "System" instead of the sent in font name. */
4272 ZeroMemory(&returnedCF2A,sizeof(returnedCF2A));
4273 ZeroMemory(&sentLogFont,sizeof(sentLogFont));
4274 returnedCF2A.cbSize = sizeof(returnedCF2A);
4276 SendMessageA(hwndRichEdit, WM_SETFONT, 0, MAKELPARAM((WORD) TRUE, 0));
4277 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&returnedCF2A);
4278 GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont);
4279 ok (!strcmp("System",returnedCF2A.szFaceName),
4280 "EM_GETCHARFORMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName);
4282 DestroyWindow(hwndRichEdit);
4286 static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie,
4287 LPBYTE pbBuff,
4288 LONG cb,
4289 LONG *pcb)
4291 const char** str = (const char**)dwCookie;
4292 int size = strlen(*str);
4293 if(size > 3) /* let's make it piecemeal for fun */
4294 size = 3;
4295 *pcb = cb;
4296 if (*pcb > size) {
4297 *pcb = size;
4299 if (*pcb > 0) {
4300 memcpy(pbBuff, *str, *pcb);
4301 *str += *pcb;
4303 return 0;
4306 static void test_EM_GETMODIFY(void)
4308 HWND hwndRichEdit = new_richedit(NULL);
4309 LRESULT result;
4310 SETTEXTEX setText;
4311 WCHAR TestItem1[] = {'T', 'e', 's', 't',
4312 'S', 'o', 'm', 'e',
4313 'T', 'e', 'x', 't', 0};
4314 WCHAR TestItem2[] = {'T', 'e', 's', 't',
4315 'S', 'o', 'm', 'e',
4316 'O', 't', 'h', 'e', 'r',
4317 'T', 'e', 'x', 't', 0};
4318 const char* streamText = "hello world";
4319 CHARFORMAT2A cf2;
4320 PARAFORMAT2 pf2;
4321 EDITSTREAM es;
4323 HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET,
4324 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
4325 FF_DONTCARE, "Courier");
4327 setText.codepage = 1200; /* no constant for unicode */
4328 setText.flags = ST_KEEPUNDO;
4331 /* modify flag shouldn't be set when richedit is first created */
4332 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4333 ok (result == 0,
4334 "EM_GETMODIFY returned non-zero, instead of zero on create\n");
4336 /* setting modify flag should actually set it */
4337 SendMessageA(hwndRichEdit, EM_SETMODIFY, TRUE, 0);
4338 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4339 ok (result != 0,
4340 "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n");
4342 /* clearing modify flag should actually clear it */
4343 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4344 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4345 ok (result == 0,
4346 "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n");
4348 /* setting font doesn't change modify flag */
4349 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4350 SendMessageA(hwndRichEdit, WM_SETFONT, (WPARAM)testFont, MAKELPARAM(TRUE, 0));
4351 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4352 ok (result == 0,
4353 "EM_GETMODIFY returned non-zero, instead of zero on setting font\n");
4355 /* setting text should set modify flag */
4356 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4357 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4358 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4359 ok (result != 0,
4360 "EM_GETMODIFY returned zero, instead of non-zero on setting text\n");
4362 /* undo previous text doesn't reset modify flag */
4363 SendMessageA(hwndRichEdit, WM_UNDO, 0, 0);
4364 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4365 ok (result != 0,
4366 "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n");
4368 /* set text with no flag to keep undo stack should not set modify flag */
4369 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4370 setText.flags = 0;
4371 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4372 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4373 ok (result == 0,
4374 "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n");
4376 /* WM_SETTEXT doesn't modify */
4377 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4378 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2);
4379 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4380 ok (result == 0,
4381 "EM_GETMODIFY returned non-zero for WM_SETTEXT\n");
4383 /* clear the text */
4384 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4385 SendMessageA(hwndRichEdit, WM_CLEAR, 0, 0);
4386 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4387 ok (result == 0,
4388 "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n");
4390 /* replace text */
4391 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4392 SendMessageA(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1);
4393 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4394 SendMessageA(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2);
4395 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4396 ok (result != 0,
4397 "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n");
4399 /* copy/paste text 1 */
4400 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4401 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4402 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4403 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4404 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4405 ok (result != 0,
4406 "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n");
4408 /* copy/paste text 2 */
4409 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4410 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 2);
4411 SendMessageA(hwndRichEdit, WM_COPY, 0, 0);
4412 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 3);
4413 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
4414 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4415 ok (result != 0,
4416 "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n");
4418 /* press char */
4419 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4420 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 1);
4421 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4422 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4423 ok (result != 0,
4424 "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n");
4426 /* press del */
4427 SendMessageA(hwndRichEdit, WM_CHAR, 'A', 0);
4428 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4429 SendMessageA(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0);
4430 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4431 ok (result != 0,
4432 "EM_GETMODIFY returned zero, instead of non-zero for backspace\n");
4434 /* set char format */
4435 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4436 cf2.cbSize = sizeof(CHARFORMAT2A);
4437 SendMessageA(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
4438 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
4439 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
4440 SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4441 result = SendMessageA(hwndRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
4442 ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result);
4443 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4444 ok (result != 0,
4445 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n");
4447 /* set para format */
4448 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4449 pf2.cbSize = sizeof(PARAFORMAT2);
4450 SendMessageA(hwndRichEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
4451 pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask;
4452 pf2.wAlignment = PFA_RIGHT;
4453 SendMessageA(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
4454 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4455 ok (result == 0,
4456 "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n");
4458 /* EM_STREAM */
4459 SendMessageA(hwndRichEdit, EM_SETMODIFY, FALSE, 0);
4460 es.dwCookie = (DWORD_PTR)&streamText;
4461 es.dwError = 0;
4462 es.pfnCallback = test_EM_GETMODIFY_esCallback;
4463 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
4464 result = SendMessageA(hwndRichEdit, EM_GETMODIFY, 0, 0);
4465 ok (result != 0,
4466 "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n");
4468 DestroyWindow(hwndRichEdit);
4471 struct exsetsel_s {
4472 LONG min;
4473 LONG max;
4474 LRESULT expected_retval;
4475 int expected_getsel_start;
4476 int expected_getsel_end;
4477 BOOL todo;
4480 static const struct exsetsel_s exsetsel_tests[] = {
4481 /* sanity tests */
4482 {5, 10, 10, 5, 10 },
4483 {15, 17, 17, 15, 17 },
4484 /* test cpMax > strlen() */
4485 {0, 100, 18, 0, 18 },
4486 /* test cpMin < 0 && cpMax >= 0 after cpMax > strlen() */
4487 {-1, 1, 17, 17, 17 },
4488 /* test cpMin == cpMax */
4489 {5, 5, 5, 5, 5 },
4490 /* test cpMin < 0 && cpMax >= 0 (bug 4462) */
4491 {-1, 0, 5, 5, 5 },
4492 {-1, 17, 5, 5, 5 },
4493 {-1, 18, 5, 5, 5 },
4494 /* test cpMin < 0 && cpMax < 0 */
4495 {-1, -1, 17, 17, 17 },
4496 {-4, -5, 17, 17, 17 },
4497 /* test cpMin >=0 && cpMax < 0 (bug 6814) */
4498 {0, -1, 18, 0, 18 },
4499 {17, -5, 18, 17, 18 },
4500 {18, -3, 17, 17, 17 },
4501 /* test if cpMin > cpMax */
4502 {15, 19, 18, 15, 18 },
4503 {19, 15, 18, 15, 18 },
4504 /* cpMin == strlen() && cpMax > cpMin */
4505 {17, 18, 18, 17, 18 },
4506 {17, 50, 18, 17, 18 },
4509 static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4510 CHARRANGE cr;
4511 LRESULT result;
4512 int start, end;
4514 cr.cpMin = setsel->min;
4515 cr.cpMax = setsel->max;
4516 result = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr);
4518 ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4520 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4522 if (setsel->todo) {
4523 todo_wine {
4524 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);
4526 } else {
4527 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);
4531 static void test_EM_EXSETSEL(void)
4533 HWND hwndRichEdit = new_richedit(NULL);
4534 int i;
4535 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4537 /* sending some text to the window */
4538 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4539 /* 01234567890123456*/
4540 /* 10 */
4542 for (i = 0; i < num_tests; i++) {
4543 check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4546 DestroyWindow(hwndRichEdit);
4549 static void check_EM_SETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) {
4550 LRESULT result;
4551 int start, end;
4553 result = SendMessageA(hwnd, EM_SETSEL, setsel->min, setsel->max);
4555 ok(result == setsel->expected_retval, "EM_SETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result);
4557 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
4559 if (setsel->todo) {
4560 todo_wine {
4561 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);
4563 } else {
4564 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);
4568 static void test_EM_SETSEL(void)
4570 char buffA[32];
4571 HWND hwndRichEdit = new_richedit(NULL);
4572 int i;
4573 const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s);
4575 /* sending some text to the window */
4576 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4577 /* 01234567890123456*/
4578 /* 10 */
4580 for (i = 0; i < num_tests; i++) {
4581 check_EM_SETSEL(hwndRichEdit, &exsetsel_tests[i], i);
4584 SendMessageA(hwndRichEdit, EM_SETSEL, 17, 18);
4585 buffA[0] = 123;
4586 SendMessageA(hwndRichEdit, EM_GETSELTEXT, 0, (LPARAM)buffA);
4587 ok(buffA[0] == 0, "selection text %s\n", buffA);
4589 DestroyWindow(hwndRichEdit);
4592 static void test_EM_REPLACESEL(int redraw)
4594 HWND hwndRichEdit = new_richedit(NULL);
4595 char buffer[1024] = {0};
4596 int r;
4597 GETTEXTEX getText;
4598 CHARRANGE cr;
4599 CHAR rtfstream[] = "{\\rtf1 TestSomeText}";
4600 CHAR urtfstream[] = "{\\urtf1 TestSomeText}";
4602 /* sending some text to the window */
4603 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"testing selection");
4604 /* 01234567890123456*/
4605 /* 10 */
4607 /* FIXME add more tests */
4608 SendMessageA(hwndRichEdit, EM_SETSEL, 7, 17);
4609 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, 0);
4610 ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r);
4611 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4612 r = strcmp(buffer, "testing");
4613 ok(0 == r, "expected %d, got %d\n", 0, r);
4615 DestroyWindow(hwndRichEdit);
4617 hwndRichEdit = new_richedit(NULL);
4619 trace("Testing EM_REPLACESEL behavior with redraw=%d\n", redraw);
4620 SendMessageA(hwndRichEdit, WM_SETREDRAW, redraw, 0);
4622 /* Test behavior with carriage returns and newlines */
4623 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4624 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1");
4625 ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r);
4626 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4627 r = strcmp(buffer, "RichEdit1");
4628 ok(0 == r, "expected %d, got %d\n", 0, r);
4629 getText.cb = 1024;
4630 getText.codepage = CP_ACP;
4631 getText.flags = GT_DEFAULT;
4632 getText.lpDefaultChar = NULL;
4633 getText.lpUsedDefChar = NULL;
4634 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4635 ok(strcmp(buffer, "RichEdit1") == 0,
4636 "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n");
4638 /* Test number of lines reported after EM_REPLACESEL */
4639 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4640 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4642 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4643 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r");
4644 ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r);
4645 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4646 r = strcmp(buffer, "RichEdit1\r\n");
4647 ok(0 == r, "expected %d, got %d\n", 0, r);
4648 getText.cb = 1024;
4649 getText.codepage = CP_ACP;
4650 getText.flags = GT_DEFAULT;
4651 getText.lpDefaultChar = NULL;
4652 getText.lpUsedDefChar = NULL;
4653 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4654 ok(strcmp(buffer, "RichEdit1\r") == 0,
4655 "EM_GETTEXTEX returned incorrect string\n");
4657 /* Test number of lines reported after EM_REPLACESEL */
4658 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4659 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4661 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4662 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"RichEdit1\r\n");
4663 ok(r == 11, "EM_REPLACESEL returned %d, expected 11\n", r);
4665 /* Test number of lines reported after EM_REPLACESEL */
4666 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4667 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4669 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4670 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4671 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4672 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4674 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4675 r = strcmp(buffer, "RichEdit1\r\n");
4676 ok(0 == r, "expected %d, got %d\n", 0, r);
4677 getText.cb = 1024;
4678 getText.codepage = CP_ACP;
4679 getText.flags = GT_DEFAULT;
4680 getText.lpDefaultChar = NULL;
4681 getText.lpUsedDefChar = NULL;
4682 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4683 ok(strcmp(buffer, "RichEdit1\r") == 0,
4684 "EM_GETTEXTEX returned incorrect string\n");
4686 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4687 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4688 ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin);
4689 ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax);
4691 /* The following tests show that richedit should handle the special \r\r\n
4692 sequence by turning it into a single space on insertion. However,
4693 EM_REPLACESEL on WinXP returns the number of characters in the original
4694 string.
4697 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4698 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r");
4699 ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r);
4700 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4701 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4702 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4703 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4705 /* Test the actual string */
4706 getText.cb = 1024;
4707 getText.codepage = CP_ACP;
4708 getText.flags = GT_DEFAULT;
4709 getText.lpDefaultChar = NULL;
4710 getText.lpUsedDefChar = NULL;
4711 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4712 ok(strcmp(buffer, "\r\r") == 0,
4713 "EM_GETTEXTEX returned incorrect string\n");
4715 /* Test number of lines reported after EM_REPLACESEL */
4716 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4717 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4719 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4720 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n");
4721 ok(r == 3, "EM_REPLACESEL returned %d, expected 3\n", r);
4722 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4723 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4724 ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin);
4725 ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax);
4727 /* Test the actual string */
4728 getText.cb = 1024;
4729 getText.codepage = CP_ACP;
4730 getText.flags = GT_DEFAULT;
4731 getText.lpDefaultChar = NULL;
4732 getText.lpUsedDefChar = NULL;
4733 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4734 ok(strcmp(buffer, " ") == 0,
4735 "EM_GETTEXTEX returned incorrect string\n");
4737 /* Test number of lines reported after EM_REPLACESEL */
4738 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4739 ok(r == 1, "EM_GETLINECOUNT returned %d, expected 1\n", r);
4741 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4742 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\r\r\r\n\r\r\r");
4743 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4744 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4745 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4746 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4747 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4749 /* Test the actual string */
4750 getText.cb = 1024;
4751 getText.codepage = CP_ACP;
4752 getText.flags = GT_DEFAULT;
4753 getText.lpDefaultChar = NULL;
4754 getText.lpUsedDefChar = NULL;
4755 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4756 ok(strcmp(buffer, "\r\r\r \r\r\r") == 0,
4757 "EM_GETTEXTEX returned incorrect string\n");
4759 /* Test number of lines reported after EM_REPLACESEL */
4760 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4761 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4763 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4764 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\n");
4765 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4766 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4767 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4768 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4769 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4771 /* Test the actual string */
4772 getText.cb = 1024;
4773 getText.codepage = CP_ACP;
4774 getText.flags = GT_DEFAULT;
4775 getText.lpDefaultChar = NULL;
4776 getText.lpUsedDefChar = NULL;
4777 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4778 ok(strcmp(buffer, " \r") == 0,
4779 "EM_GETTEXTEX returned incorrect string\n");
4781 /* Test number of lines reported after EM_REPLACESEL */
4782 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4783 ok(r == 2, "EM_GETLINECOUNT returned %d, expected 2\n", r);
4785 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4786 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\r\r\n\r\r");
4787 ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4788 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4789 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4790 ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin);
4791 ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax);
4793 /* Test the actual string */
4794 getText.cb = 1024;
4795 getText.codepage = CP_ACP;
4796 getText.flags = GT_DEFAULT;
4797 getText.lpDefaultChar = NULL;
4798 getText.lpUsedDefChar = NULL;
4799 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4800 ok(strcmp(buffer, " \r\r") == 0,
4801 "EM_GETTEXTEX returned incorrect string\n");
4803 /* Test number of lines reported after EM_REPLACESEL */
4804 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4805 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4807 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4808 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\rX\r\n\r\r");
4809 ok(r == 6, "EM_REPLACESEL returned %d, expected 6\n", r);
4810 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4811 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4812 ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin);
4813 ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax);
4815 /* Test the actual string */
4816 getText.cb = 1024;
4817 getText.codepage = CP_ACP;
4818 getText.flags = GT_DEFAULT;
4819 getText.lpDefaultChar = NULL;
4820 getText.lpUsedDefChar = NULL;
4821 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4822 ok(strcmp(buffer, "\rX\r\r\r") == 0,
4823 "EM_GETTEXTEX returned incorrect string\n");
4825 /* Test number of lines reported after EM_REPLACESEL */
4826 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4827 ok(r == 5, "EM_GETLINECOUNT returned %d, expected 5\n", r);
4829 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4830 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n");
4831 ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r);
4832 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4833 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4834 ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin);
4835 ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax);
4837 /* Test the actual string */
4838 getText.cb = 1024;
4839 getText.codepage = CP_ACP;
4840 getText.flags = GT_DEFAULT;
4841 getText.lpDefaultChar = NULL;
4842 getText.lpUsedDefChar = NULL;
4843 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4844 ok(strcmp(buffer, "\r\r") == 0,
4845 "EM_GETTEXTEX returned incorrect string\n");
4847 /* Test number of lines reported after EM_REPLACESEL */
4848 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4849 ok(r == 3, "EM_GETLINECOUNT returned %d, expected 3\n", r);
4851 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4852 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"\n\n\n\n\r\r\r\r\n");
4853 ok(r == 9, "EM_REPLACESEL returned %d, expected 9\n", r);
4854 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4855 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4856 ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin);
4857 ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax);
4859 /* Test the actual string */
4860 getText.cb = 1024;
4861 getText.codepage = CP_ACP;
4862 getText.flags = GT_DEFAULT;
4863 getText.lpDefaultChar = NULL;
4864 getText.lpUsedDefChar = NULL;
4865 SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buffer);
4866 ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0,
4867 "EM_GETTEXTEX returned incorrect string\n");
4869 /* Test number of lines reported after EM_REPLACESEL */
4870 r = SendMessageA(hwndRichEdit, EM_GETLINECOUNT, 0, 0);
4871 ok(r == 7, "EM_GETLINECOUNT returned %d, expected 7\n", r);
4873 /* Test with multibyte character */
4874 if (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_JAPANESE)
4875 skip("Skip multibyte character tests on non-Japanese platform\n");
4876 else
4878 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4879 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"abc\x8e\xf0");
4880 todo_wine ok(r == 5, "EM_REPLACESEL returned %d, expected 5\n", r);
4881 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4882 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
4883 ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
4884 ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
4885 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4886 ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
4887 ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
4889 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4890 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)"{\\rtf abc\x8e\xf0}");
4891 todo_wine ok(r == 4, "EM_REPLACESEL returned %d, expected 4\n", r);
4892 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4893 ok(r == 0, "EM_EXGETSEL returned %d, expected 0\n", r);
4894 todo_wine ok(cr.cpMin == 4, "EM_EXGETSEL returned cpMin=%d, expected 4\n", cr.cpMin);
4895 todo_wine ok(cr.cpMax == 4, "EM_EXGETSEL returned cpMax=%d, expected 4\n", cr.cpMax);
4896 r = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4897 todo_wine ok(!strcmp(buffer, "abc\x8e\xf0"), "WM_GETTEXT returned incorrect string\n");
4898 todo_wine ok(r == 5, "WM_GETTEXT returned %d, expected 5\n", r);
4901 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4902 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
4903 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
4904 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4905 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4906 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
4907 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
4908 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4909 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
4911 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
4912 r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)urtfstream);
4913 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
4914 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4915 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4916 todo_wine ok(cr.cpMin == 12, "EM_EXGETSEL returned cpMin=%d, expected 12\n", cr.cpMin);
4917 todo_wine ok(cr.cpMax == 12, "EM_EXGETSEL returned cpMax=%d, expected 12\n", cr.cpMax);
4918 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4919 todo_wine ok(!strcmp(buffer, "TestSomeText"), "WM_GETTEXT returned incorrect string\n");
4921 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"Wine");
4922 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
4923 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
4924 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
4925 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4926 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4927 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
4928 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
4929 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4930 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
4932 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"{\\rtf1 Wine}");
4933 SendMessageA(hwndRichEdit, EM_SETSEL, 1, 2);
4934 todo_wine r = SendMessageA(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM)rtfstream);
4935 todo_wine ok(r == 12, "EM_REPLACESEL returned %d, expected 12\n", r);
4936 r = SendMessageA(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr);
4937 ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r);
4938 todo_wine ok(cr.cpMin == 13, "EM_EXGETSEL returned cpMin=%d, expected 13\n", cr.cpMin);
4939 todo_wine ok(cr.cpMax == 13, "EM_EXGETSEL returned cpMax=%d, expected 13\n", cr.cpMax);
4940 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4941 todo_wine ok(!strcmp(buffer, "WTestSomeTextne"), "WM_GETTEXT returned incorrect string\n");
4943 if (!redraw)
4944 /* This is needed to avoid interferring with keybd_event calls
4945 * on other tests that simulate keyboard events. */
4946 SendMessageA(hwndRichEdit, WM_SETREDRAW, TRUE, 0);
4948 DestroyWindow(hwndRichEdit);
4951 /* Native riched20 inspects the keyboard state (e.g. GetKeyState)
4952 * to test the state of the modifiers (Ctrl/Alt/Shift).
4954 * Therefore Ctrl-<key> keystrokes need to be simulated with
4955 * keybd_event or by using SetKeyboardState to set the modifiers
4956 * and SendMessage to simulate the keystrokes.
4958 static LRESULT send_ctrl_key(HWND hwnd, UINT key)
4960 LRESULT result;
4961 hold_key(VK_CONTROL);
4962 result = SendMessageA(hwnd, WM_KEYDOWN, key, 1);
4963 release_key(VK_CONTROL);
4964 return result;
4967 static void test_WM_PASTE(void)
4969 int result;
4970 char buffer[1024] = {0};
4971 const char* text1 = "testing paste\r";
4972 const char* text1_step1 = "testing paste\r\ntesting paste\r\n";
4973 const char* text1_after = "testing paste\r\n";
4974 const char* text2 = "testing paste\r\rtesting paste";
4975 const char* text2_after = "testing paste\r\n\r\ntesting paste";
4976 const char* text3 = "testing paste\r\npaste\r\ntesting paste";
4977 HWND hwndRichEdit = new_richedit(NULL);
4979 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
4980 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 14);
4982 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
4983 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
4984 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
4985 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4986 /* Pasted text should be visible at this step */
4987 result = strcmp(text1_step1, buffer);
4988 ok(result == 0,
4989 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4991 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
4992 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
4993 /* Text should be the same as before (except for \r -> \r\n conversion) */
4994 result = strcmp(text1_after, buffer);
4995 ok(result == 0,
4996 "test paste: strcmp = %i, text='%s'\n", result, buffer);
4998 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text2);
4999 SendMessageA(hwndRichEdit, EM_SETSEL, 8, 13);
5000 send_ctrl_key(hwndRichEdit, 'C'); /* Copy */
5001 SendMessageA(hwndRichEdit, EM_SETSEL, 14, 14);
5002 send_ctrl_key(hwndRichEdit, 'V'); /* Paste */
5003 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5004 /* Pasted text should be visible at this step */
5005 result = strcmp(text3, buffer);
5006 ok(result == 0,
5007 "test paste: strcmp = %i\n", result);
5008 send_ctrl_key(hwndRichEdit, 'Z'); /* Undo */
5009 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5010 /* Text should be the same as before (except for \r -> \r\n conversion) */
5011 result = strcmp(text2_after, buffer);
5012 ok(result == 0,
5013 "test paste: strcmp = %i\n", result);
5014 send_ctrl_key(hwndRichEdit, 'Y'); /* Redo */
5015 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5016 /* Text should revert to post-paste state */
5017 result = strcmp(buffer,text3);
5018 ok(result == 0,
5019 "test paste: strcmp = %i\n", result);
5021 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5022 /* Send WM_CHAR to simulate Ctrl-V */
5023 SendMessageA(hwndRichEdit, WM_CHAR, 22,
5024 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5025 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5026 /* Shouldn't paste because pasting is handled by WM_KEYDOWN */
5027 result = strcmp(buffer,"");
5028 ok(result == 0,
5029 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5031 /* Send keystrokes with WM_KEYDOWN after setting the modifiers
5032 * with SetKeyboard state. */
5034 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5035 /* Simulates paste (Ctrl-V) */
5036 hold_key(VK_CONTROL);
5037 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'V',
5038 (MapVirtualKeyA('V', MAPVK_VK_TO_VSC) << 16) | 1);
5039 release_key(VK_CONTROL);
5040 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5041 result = strcmp(buffer,"paste");
5042 ok(result == 0,
5043 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5045 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text1);
5046 SendMessageA(hwndRichEdit, EM_SETSEL, 0, 7);
5047 /* Simulates copy (Ctrl-C) */
5048 hold_key(VK_CONTROL);
5049 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'C',
5050 (MapVirtualKeyA('C', MAPVK_VK_TO_VSC) << 16) | 1);
5051 release_key(VK_CONTROL);
5052 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5053 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5054 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5055 result = strcmp(buffer,"testing");
5056 ok(result == 0,
5057 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5059 /* Cut with WM_KEYDOWN to simulate Ctrl-X */
5060 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"cut");
5061 /* Simulates select all (Ctrl-A) */
5062 hold_key(VK_CONTROL);
5063 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'A',
5064 (MapVirtualKeyA('A', MAPVK_VK_TO_VSC) << 16) | 1);
5065 /* Simulates select cut (Ctrl-X) */
5066 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'X',
5067 (MapVirtualKeyA('X', MAPVK_VK_TO_VSC) << 16) | 1);
5068 release_key(VK_CONTROL);
5069 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5070 result = strcmp(buffer,"");
5071 ok(result == 0,
5072 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5073 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, 0);
5074 SendMessageA(hwndRichEdit, WM_PASTE, 0, 0);
5075 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5076 result = strcmp(buffer,"cut\r\n");
5077 ok(result == 0,
5078 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5079 /* Simulates undo (Ctrl-Z) */
5080 hold_key(VK_CONTROL);
5081 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Z',
5082 (MapVirtualKeyA('Z', MAPVK_VK_TO_VSC) << 16) | 1);
5083 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5084 result = strcmp(buffer,"");
5085 ok(result == 0,
5086 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5087 /* Simulates redo (Ctrl-Y) */
5088 SendMessageA(hwndRichEdit, WM_KEYDOWN, 'Y',
5089 (MapVirtualKeyA('Y', MAPVK_VK_TO_VSC) << 16) | 1);
5090 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5091 result = strcmp(buffer,"cut\r\n");
5092 ok(result == 0,
5093 "test paste: strcmp = %i, actual = '%s'\n", result, buffer);
5094 release_key(VK_CONTROL);
5096 DestroyWindow(hwndRichEdit);
5099 static void test_EM_FORMATRANGE(void)
5101 int r, i, tpp_x, tpp_y;
5102 HDC hdc;
5103 HWND hwndRichEdit = new_richedit(NULL);
5104 FORMATRANGE fr;
5105 BOOL skip_non_english;
5106 static const struct {
5107 const char *string; /* The string */
5108 int first; /* First 'pagebreak', 0 for don't care */
5109 int second; /* Second 'pagebreak', 0 for don't care */
5110 } fmtstrings[] = {
5111 {"WINE wine", 0, 0},
5112 {"WINE wineWine", 0, 0},
5113 {"WINE\r\nwine\r\nwine", 5, 10},
5114 {"WINE\r\nWINEwine\r\nWINEwine", 5, 14},
5115 {"WINE\r\n\r\nwine\r\nwine", 5, 6}
5118 skip_non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
5119 if (skip_non_english)
5120 skip("Skipping some tests on non-English platform\n");
5122 hdc = GetDC(hwndRichEdit);
5123 ok(hdc != NULL, "Could not get HDC\n");
5125 /* Calculate the twips per pixel */
5126 tpp_x = 1440 / GetDeviceCaps(hdc, LOGPIXELSX);
5127 tpp_y = 1440 / GetDeviceCaps(hdc, LOGPIXELSY);
5129 /* Test the simple case where all the text fits in the page rect. */
5130 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
5131 fr.hdc = fr.hdcTarget = hdc;
5132 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5133 fr.rc.right = fr.rcPage.right = 500 * tpp_x;
5134 fr.rc.bottom = fr.rcPage.bottom = 500 * tpp_y;
5135 fr.chrg.cpMin = 0;
5136 fr.chrg.cpMax = -1;
5137 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5138 todo_wine ok(r == 2, "r=%d expected r=2\n", r);
5140 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"ab");
5141 fr.rc.bottom = fr.rcPage.bottom;
5142 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, (LPARAM)&fr);
5143 todo_wine ok(r == 3, "r=%d expected r=3\n", r);
5145 SendMessageA(hwndRichEdit, EM_FORMATRANGE, FALSE, 0);
5147 for (i = 0; i < sizeof(fmtstrings)/sizeof(fmtstrings[0]); i++)
5149 GETTEXTLENGTHEX gtl;
5150 SIZE stringsize;
5151 int len;
5153 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)fmtstrings[i].string);
5155 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5156 gtl.codepage = CP_ACP;
5157 len = SendMessageA(hwndRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5159 /* Get some size information for the string */
5160 GetTextExtentPoint32A(hdc, fmtstrings[i].string, strlen(fmtstrings[i].string), &stringsize);
5162 /* Define the box to be half the width needed and a bit larger than the height.
5163 * Changes to the width means we have at least 2 pages. Changes to the height
5164 * is done so we can check the changing of fr.rc.bottom.
5166 fr.hdc = fr.hdcTarget = hdc;
5167 fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0;
5168 fr.rc.right = fr.rcPage.right = (stringsize.cx / 2) * tpp_x;
5169 fr.rc.bottom = fr.rcPage.bottom = (stringsize.cy + 10) * tpp_y;
5171 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5172 todo_wine {
5173 ok(r == len, "Expected %d, got %d\n", len, r);
5176 /* We know that the page can't hold the full string. See how many characters
5177 * are on the first one
5179 fr.chrg.cpMin = 0;
5180 fr.chrg.cpMax = -1;
5181 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5182 todo_wine {
5183 if (! skip_non_english)
5184 ok(fr.rc.bottom == (stringsize.cy * tpp_y), "Expected bottom to be %d, got %d\n", (stringsize.cy * tpp_y), fr.rc.bottom);
5186 if (fmtstrings[i].first)
5187 todo_wine {
5188 ok(r == fmtstrings[i].first, "Expected %d, got %d\n", fmtstrings[i].first, r);
5190 else
5191 ok(r < len, "Expected < %d, got %d\n", len, r);
5193 /* Do another page */
5194 fr.chrg.cpMin = r;
5195 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
5196 if (fmtstrings[i].second)
5197 todo_wine {
5198 ok(r == fmtstrings[i].second, "Expected %d, got %d\n", fmtstrings[i].second, r);
5200 else if (! skip_non_english)
5201 ok (r < len, "Expected < %d, got %d\n", len, r);
5203 /* There is at least on more page, but we don't care */
5205 r = SendMessageA(hwndRichEdit, EM_FORMATRANGE, TRUE, 0);
5206 todo_wine {
5207 ok(r == len, "Expected %d, got %d\n", len, r);
5211 ReleaseDC(NULL, hdc);
5212 DestroyWindow(hwndRichEdit);
5215 static int nCallbackCount = 0;
5217 static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff,
5218 LONG cb, LONG* pcb)
5220 const char text[] = {'t','e','s','t'};
5222 if (sizeof(text) <= cb)
5224 if ((int)dwCookie != nCallbackCount)
5226 *pcb = 0;
5227 return 0;
5230 memcpy (pbBuff, text, sizeof(text));
5231 *pcb = sizeof(text);
5233 nCallbackCount++;
5235 return 0;
5237 else
5238 return 1; /* indicates callback failed */
5241 static DWORD CALLBACK test_EM_STREAMIN_esCallback(DWORD_PTR dwCookie,
5242 LPBYTE pbBuff,
5243 LONG cb,
5244 LONG *pcb)
5246 const char** str = (const char**)dwCookie;
5247 int size = strlen(*str);
5248 *pcb = cb;
5249 if (*pcb > size) {
5250 *pcb = size;
5252 if (*pcb > 0) {
5253 memcpy(pbBuff, *str, *pcb);
5254 *str += *pcb;
5256 return 0;
5259 static DWORD CALLBACK test_EM_STREAMIN_esCallback_UTF8Split(DWORD_PTR dwCookie,
5260 LPBYTE pbBuff,
5261 LONG cb,
5262 LONG *pcb)
5264 DWORD *phase = (DWORD *)dwCookie;
5266 if(*phase == 0){
5267 static const char first[] = "\xef\xbb\xbf\xc3\x96\xc3";
5268 *pcb = sizeof(first) - 1;
5269 memcpy(pbBuff, first, *pcb);
5270 }else if(*phase == 1){
5271 static const char second[] = "\x8f\xc3\x8b";
5272 *pcb = sizeof(second) - 1;
5273 memcpy(pbBuff, second, *pcb);
5274 }else
5275 *pcb = 0;
5277 ++*phase;
5279 return 0;
5282 struct StringWithLength {
5283 int length;
5284 char *buffer;
5287 /* This callback is used to handled the null characters in a string. */
5288 static DWORD CALLBACK test_EM_STREAMIN_esCallback2(DWORD_PTR dwCookie,
5289 LPBYTE pbBuff,
5290 LONG cb,
5291 LONG *pcb)
5293 struct StringWithLength* str = (struct StringWithLength*)dwCookie;
5294 int size = str->length;
5295 *pcb = cb;
5296 if (*pcb > size) {
5297 *pcb = size;
5299 if (*pcb > 0) {
5300 memcpy(pbBuff, str->buffer, *pcb);
5301 str->buffer += *pcb;
5302 str->length -= *pcb;
5304 return 0;
5307 static void test_EM_STREAMIN(void)
5309 HWND hwndRichEdit = new_richedit(NULL);
5310 DWORD phase;
5311 LRESULT result;
5312 EDITSTREAM es;
5313 char buffer[1024] = {0}, tmp[16];
5314 CHARRANGE range;
5316 const char * streamText0 = "{\\rtf1 TestSomeText}";
5317 const char * streamText0a = "{\\rtf1 TestSomeText\\par}";
5318 const char * streamText0b = "{\\rtf1 TestSomeText\\par\\par}";
5319 const char * ptr;
5321 const char * streamText1 =
5322 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
5323 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
5324 "}\r\n";
5326 /* In richedit 2.0 mode, this should NOT be accepted, unlike 1.0 */
5327 const char * streamText2 =
5328 "{{\\colortbl;\\red0\\green255\\blue102;\\red255\\green255\\blue255;"
5329 "\\red170\\green255\\blue255;\\red255\\green238\\blue0;\\red51\\green255"
5330 "\\blue221;\\red238\\green238\\blue238;}\\tx0 \\tx424 \\tx848 \\tx1272 "
5331 "\\tx1696 \\tx2120 \\tx2544 \\tx2968 \\tx3392 \\tx3816 \\tx4240 \\tx4664 "
5332 "\\tx5088 \\tx5512 \\tx5936 \\tx6360 \\tx6784 \\tx7208 \\tx7632 \\tx8056 "
5333 "\\tx8480 \\tx8904 \\tx9328 \\tx9752 \\tx10176 \\tx10600 \\tx11024 "
5334 "\\tx11448 \\tx11872 \\tx12296 \\tx12720 \\tx13144 \\cf2 RichEdit1\\line }";
5336 const char * streamText3 = "RichEdit1";
5338 const char * streamTextUTF8BOM = "\xef\xbb\xbfTestUTF8WithBOM";
5340 const char * streamText4 =
5341 "This text just needs to be long enough to cause run to be split onto "
5342 "two separate lines and make sure the null terminating character is "
5343 "handled properly.\0";
5345 const WCHAR UTF8Split_exp[4] = {0xd6, 0xcf, 0xcb, 0};
5347 int length4 = strlen(streamText4) + 1;
5348 struct StringWithLength cookieForStream4 = {
5349 length4,
5350 (char *)streamText4,
5353 const WCHAR streamText5[] = { 'T', 'e', 's', 't', 'S', 'o', 'm', 'e', 'T', 'e', 'x', 't' };
5354 int length5 = sizeof(streamText5) / sizeof(WCHAR);
5355 struct StringWithLength cookieForStream5 = {
5356 sizeof(streamText5),
5357 (char *)streamText5,
5360 /* Minimal test without \par at the end */
5361 es.dwCookie = (DWORD_PTR)&streamText0;
5362 es.dwError = 0;
5363 es.pfnCallback = test_EM_STREAMIN_esCallback;
5364 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5365 ok(result == 12, "got %ld, expected %d\n", result, 12);
5367 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5368 ok (result == 12,
5369 "EM_STREAMIN: Test 0 returned %ld, expected 12\n", result);
5370 result = strcmp (buffer,"TestSomeText");
5371 ok (result == 0,
5372 "EM_STREAMIN: Test 0 set wrong text: Result: %s\n",buffer);
5373 ok(es.dwError == 0, "EM_STREAMIN: Test 0 set error %d, expected %d\n", es.dwError, 0);
5375 /* Native richedit 2.0 ignores last \par */
5376 ptr = streamText0a;
5377 es.dwCookie = (DWORD_PTR)&ptr;
5378 es.dwError = 0;
5379 es.pfnCallback = test_EM_STREAMIN_esCallback;
5380 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5381 ok(result == 12, "got %ld, expected %d\n", result, 12);
5383 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5384 ok (result == 12,
5385 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5386 result = strcmp (buffer,"TestSomeText");
5387 ok (result == 0,
5388 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5389 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5391 /* Native richedit 2.0 ignores last \par, next-to-last \par appears */
5392 es.dwCookie = (DWORD_PTR)&streamText0b;
5393 es.dwError = 0;
5394 es.pfnCallback = test_EM_STREAMIN_esCallback;
5395 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5396 ok(result == 13, "got %ld, expected %d\n", result, 13);
5398 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5399 ok (result == 14,
5400 "EM_STREAMIN: Test 0-b returned %ld, expected 14\n", result);
5401 result = strcmp (buffer,"TestSomeText\r\n");
5402 ok (result == 0,
5403 "EM_STREAMIN: Test 0-b set wrong text: Result: %s\n",buffer);
5404 ok(es.dwError == 0, "EM_STREAMIN: Test 0-b set error %d, expected %d\n", es.dwError, 0);
5406 /* Show that when using SFF_SELECTION the last \par is not ignored. */
5407 ptr = streamText0a;
5408 es.dwCookie = (DWORD_PTR)&ptr;
5409 es.dwError = 0;
5410 es.pfnCallback = test_EM_STREAMIN_esCallback;
5411 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5412 ok(result == 12, "got %ld, expected %d\n", result, 12);
5414 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5415 ok (result == 12,
5416 "EM_STREAMIN: Test 0-a returned %ld, expected 12\n", result);
5417 result = strcmp (buffer,"TestSomeText");
5418 ok (result == 0,
5419 "EM_STREAMIN: Test 0-a set wrong text: Result: %s\n",buffer);
5420 ok(es.dwError == 0, "EM_STREAMIN: Test 0-a set error %d, expected %d\n", es.dwError, 0);
5422 range.cpMin = 0;
5423 range.cpMax = -1;
5424 result = SendMessageA(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM)&range);
5425 ok (result == 13, "got %ld\n", result);
5427 ptr = streamText0a;
5428 es.dwCookie = (DWORD_PTR)&ptr;
5429 es.dwError = 0;
5430 es.pfnCallback = test_EM_STREAMIN_esCallback;
5432 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SFF_SELECTION | SF_RTF, (LPARAM)&es);
5433 ok(result == 13, "got %ld, expected 13\n", result);
5435 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5436 ok (result == 14,
5437 "EM_STREAMIN: Test SFF_SELECTION 0-a returned %ld, expected 14\n", result);
5438 result = strcmp (buffer,"TestSomeText\r\n");
5439 ok (result == 0,
5440 "EM_STREAMIN: Test SFF_SELECTION 0-a set wrong text: Result: %s\n",buffer);
5441 ok(es.dwError == 0, "EM_STREAMIN: Test SFF_SELECTION 0-a set error %d, expected %d\n", es.dwError, 0);
5443 es.dwCookie = (DWORD_PTR)&streamText1;
5444 es.dwError = 0;
5445 es.pfnCallback = test_EM_STREAMIN_esCallback;
5446 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5447 ok(result == 12, "got %ld, expected %d\n", result, 12);
5449 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5450 ok (result == 12,
5451 "EM_STREAMIN: Test 1 returned %ld, expected 12\n", result);
5452 result = strcmp (buffer,"TestSomeText");
5453 ok (result == 0,
5454 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5455 ok(es.dwError == 0, "EM_STREAMIN: Test 1 set error %d, expected %d\n", es.dwError, 0);
5457 es.dwCookie = (DWORD_PTR)&streamText2;
5458 es.dwError = 0;
5459 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5460 ok(result == 0, "got %ld, expected %d\n", result, 0);
5462 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5463 ok (result == 0,
5464 "EM_STREAMIN: Test 2 returned %ld, expected 0\n", result);
5465 ok(!buffer[0], "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5466 ok(es.dwError == -16, "EM_STREAMIN: Test 2 set error %d, expected %d\n", es.dwError, -16);
5468 es.dwCookie = (DWORD_PTR)&streamText3;
5469 es.dwError = 0;
5470 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
5471 ok(result == 0, "got %ld, expected %d\n", result, 0);
5473 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5474 ok (result == 0,
5475 "EM_STREAMIN: Test 3 returned %ld, expected 0\n", result);
5476 ok(!buffer[0], "EM_STREAMIN: Test 3 set wrong text: Result: %s\n",buffer);
5477 ok(es.dwError == -16, "EM_STREAMIN: Test 3 set error %d, expected %d\n", es.dwError, -16);
5479 es.dwCookie = (DWORD_PTR)&streamTextUTF8BOM;
5480 es.dwError = 0;
5481 es.pfnCallback = test_EM_STREAMIN_esCallback;
5482 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5483 ok(result == 18, "got %ld, expected %d\n", result, 18);
5485 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5486 ok(result == 15,
5487 "EM_STREAMIN: Test UTF8WithBOM returned %ld, expected 15\n", result);
5488 result = strcmp (buffer,"TestUTF8WithBOM");
5489 ok(result == 0,
5490 "EM_STREAMIN: Test UTF8WithBOM set wrong text: Result: %s\n",buffer);
5491 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8WithBOM set error %d, expected %d\n", es.dwError, 0);
5493 phase = 0;
5494 es.dwCookie = (DWORD_PTR)&phase;
5495 es.dwError = 0;
5496 es.pfnCallback = test_EM_STREAMIN_esCallback_UTF8Split;
5497 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5498 ok(result == 8, "got %ld\n", result);
5500 WideCharToMultiByte(CP_ACP, 0, UTF8Split_exp, -1, tmp, sizeof(tmp), NULL, NULL);
5502 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5503 ok(result == 3,
5504 "EM_STREAMIN: Test UTF8Split returned %ld\n", result);
5505 result = memcmp (buffer, tmp, 3);
5506 ok(result == 0,
5507 "EM_STREAMIN: Test UTF8Split set wrong text: Result: %s\n",buffer);
5508 ok(es.dwError == 0, "EM_STREAMIN: Test UTF8Split set error %d, expected %d\n", es.dwError, 0);
5510 es.dwCookie = (DWORD_PTR)&cookieForStream4;
5511 es.dwError = 0;
5512 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5513 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5514 ok(result == length4, "got %ld, expected %d\n", result, length4);
5516 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5517 ok (result == length4,
5518 "EM_STREAMIN: Test 4 returned %ld, expected %d\n", result, length4);
5519 ok(es.dwError == 0, "EM_STREAMIN: Test 4 set error %d, expected %d\n", es.dwError, 0);
5521 es.dwCookie = (DWORD_PTR)&cookieForStream5;
5522 es.dwError = 0;
5523 es.pfnCallback = test_EM_STREAMIN_esCallback2;
5524 result = SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM)&es);
5525 ok(result == sizeof(streamText5), "got %ld, expected %u\n", result, (UINT)sizeof(streamText5));
5527 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5528 ok (result == length5,
5529 "EM_STREAMIN: Test 5 returned %ld, expected %d\n", result, length5);
5530 ok(es.dwError == 0, "EM_STREAMIN: Test 5 set error %d, expected %d\n", es.dwError, 0);
5532 DestroyWindow(hwndRichEdit);
5535 static void test_EM_StreamIn_Undo(void)
5537 /* The purpose of this test is to determine when a EM_StreamIn should be
5538 * undoable. This is important because WM_PASTE currently uses StreamIn and
5539 * pasting should always be undoable but streaming isn't always.
5541 * cases to test:
5542 * StreamIn plain text without SFF_SELECTION.
5543 * StreamIn plain text with SFF_SELECTION set but a zero-length selection
5544 * StreamIn plain text with SFF_SELECTION and a valid, normal selection
5545 * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to)
5546 * Feel free to add tests for other text modes or StreamIn things.
5550 HWND hwndRichEdit = new_richedit(NULL);
5551 LRESULT result;
5552 EDITSTREAM es;
5553 char buffer[1024] = {0};
5554 const char randomtext[] = "Some text";
5556 es.pfnCallback = EditStreamCallback;
5558 /* StreamIn, no SFF_SELECTION */
5559 es.dwCookie = nCallbackCount;
5560 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5561 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5562 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5563 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT, (LPARAM)&es);
5564 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5565 result = strcmp (buffer,"test");
5566 ok (result == 0,
5567 "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer);
5569 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5570 ok (result == FALSE,
5571 "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n");
5573 /* StreamIn, SFF_SELECTION, but nothing selected */
5574 es.dwCookie = nCallbackCount;
5575 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5576 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5577 SendMessageA(hwndRichEdit, EM_SETSEL,0,0);
5578 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5579 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5580 result = strcmp (buffer,"testSome text");
5581 ok (result == 0,
5582 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5584 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5585 ok (result == TRUE,
5586 "EM_STREAMIN with SFF_SELECTION but no selection set "
5587 "should create an undo\n");
5589 /* StreamIn, SFF_SELECTION, with a selection */
5590 es.dwCookie = nCallbackCount;
5591 SendMessageA(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0);
5592 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)randomtext);
5593 SendMessageA(hwndRichEdit, EM_SETSEL,4,5);
5594 SendMessageA(hwndRichEdit, EM_STREAMIN, SF_TEXT|SFF_SELECTION, (LPARAM)&es);
5595 SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buffer);
5596 result = strcmp (buffer,"Sometesttext");
5597 ok (result == 0,
5598 "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer);
5600 result = SendMessageA(hwndRichEdit, EM_CANUNDO, 0, 0);
5601 ok (result == TRUE,
5602 "EM_STREAMIN with SFF_SELECTION and selection set "
5603 "should create an undo\n");
5605 DestroyWindow(hwndRichEdit);
5608 static BOOL is_em_settextex_supported(HWND hwnd)
5610 SETTEXTEX stex = { ST_DEFAULT, CP_ACP };
5611 return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0;
5614 static void test_unicode_conversions(void)
5616 static const WCHAR tW[] = {'t',0};
5617 static const WCHAR teW[] = {'t','e',0};
5618 static const WCHAR textW[] = {'t','e','s','t',0};
5619 static const char textA[] = "test";
5620 char bufA[64];
5621 WCHAR bufW[64];
5622 HWND hwnd;
5623 int em_settextex_supported, ret;
5625 #define set_textA(hwnd, wm_set_text, txt) \
5626 do { \
5627 SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \
5628 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5629 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5630 ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5631 ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \
5632 } while(0)
5633 #define expect_textA(hwnd, wm_get_text, txt) \
5634 do { \
5635 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5636 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5637 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5638 memset(bufA, 0xAA, sizeof(bufA)); \
5639 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5640 ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \
5641 ret = lstrcmpA(bufA, txt); \
5642 ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \
5643 } while(0)
5645 #define set_textW(hwnd, wm_set_text, txt) \
5646 do { \
5647 SETTEXTEX stex = { ST_DEFAULT, 1200 }; \
5648 WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \
5649 assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \
5650 ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \
5651 ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \
5652 } while(0)
5653 #define expect_textW(hwnd, wm_get_text, txt) \
5654 do { \
5655 GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \
5656 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5657 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5658 memset(bufW, 0xAA, sizeof(bufW)); \
5659 ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \
5660 ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \
5661 ret = lstrcmpW(bufW, txt); \
5662 ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \
5663 } while(0)
5664 #define expect_empty(hwnd, wm_get_text) \
5665 do { \
5666 GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \
5667 WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)&gtex; \
5668 assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \
5669 memset(bufA, 0xAA, sizeof(bufA)); \
5670 ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \
5671 ok(!ret, "empty richedit should return 0, got %d\n", ret); \
5672 ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \
5673 } while(0)
5675 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5676 0, 0, 200, 60, 0, 0, 0, 0);
5677 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5679 ret = IsWindowUnicode(hwnd);
5680 ok(ret, "RichEdit20W should be unicode under NT\n");
5682 /* EM_SETTEXTEX is supported starting from version 3.0 */
5683 em_settextex_supported = is_em_settextex_supported(hwnd);
5684 trace("EM_SETTEXTEX is %ssupported on this platform\n",
5685 em_settextex_supported ? "" : "NOT ");
5687 expect_empty(hwnd, WM_GETTEXT);
5688 expect_empty(hwnd, EM_GETTEXTEX);
5690 ret = SendMessageA(hwnd, WM_CHAR, textW[0], 0);
5691 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5692 expect_textA(hwnd, WM_GETTEXT, "t");
5693 expect_textA(hwnd, EM_GETTEXTEX, "t");
5694 expect_textW(hwnd, EM_GETTEXTEX, tW);
5696 ret = SendMessageA(hwnd, WM_CHAR, textA[1], 0);
5697 ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret);
5698 expect_textA(hwnd, WM_GETTEXT, "te");
5699 expect_textA(hwnd, EM_GETTEXTEX, "te");
5700 expect_textW(hwnd, EM_GETTEXTEX, teW);
5702 set_textA(hwnd, WM_SETTEXT, NULL);
5703 expect_empty(hwnd, WM_GETTEXT);
5704 expect_empty(hwnd, EM_GETTEXTEX);
5706 set_textA(hwnd, WM_SETTEXT, textA);
5707 expect_textA(hwnd, WM_GETTEXT, textA);
5708 expect_textA(hwnd, EM_GETTEXTEX, textA);
5709 expect_textW(hwnd, EM_GETTEXTEX, textW);
5711 if (em_settextex_supported)
5713 set_textA(hwnd, EM_SETTEXTEX, textA);
5714 expect_textA(hwnd, WM_GETTEXT, textA);
5715 expect_textA(hwnd, EM_GETTEXTEX, textA);
5716 expect_textW(hwnd, EM_GETTEXTEX, textW);
5719 set_textW(hwnd, WM_SETTEXT, textW);
5720 expect_textW(hwnd, WM_GETTEXT, textW);
5721 expect_textA(hwnd, WM_GETTEXT, textA);
5722 expect_textW(hwnd, EM_GETTEXTEX, textW);
5723 expect_textA(hwnd, EM_GETTEXTEX, textA);
5725 if (em_settextex_supported)
5727 set_textW(hwnd, EM_SETTEXTEX, textW);
5728 expect_textW(hwnd, WM_GETTEXT, textW);
5729 expect_textA(hwnd, WM_GETTEXT, textA);
5730 expect_textW(hwnd, EM_GETTEXTEX, textW);
5731 expect_textA(hwnd, EM_GETTEXTEX, textA);
5733 DestroyWindow(hwnd);
5735 hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP,
5736 0, 0, 200, 60, 0, 0, 0, 0);
5737 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5739 ret = IsWindowUnicode(hwnd);
5740 ok(!ret, "RichEdit20A should NOT be unicode\n");
5742 set_textA(hwnd, WM_SETTEXT, textA);
5743 expect_textA(hwnd, WM_GETTEXT, textA);
5744 expect_textA(hwnd, EM_GETTEXTEX, textA);
5745 expect_textW(hwnd, EM_GETTEXTEX, textW);
5747 if (em_settextex_supported)
5749 set_textA(hwnd, EM_SETTEXTEX, textA);
5750 expect_textA(hwnd, WM_GETTEXT, textA);
5751 expect_textA(hwnd, EM_GETTEXTEX, textA);
5752 expect_textW(hwnd, EM_GETTEXTEX, textW);
5755 set_textW(hwnd, WM_SETTEXT, textW);
5756 expect_textW(hwnd, WM_GETTEXT, textW);
5757 expect_textA(hwnd, WM_GETTEXT, textA);
5758 expect_textW(hwnd, EM_GETTEXTEX, textW);
5759 expect_textA(hwnd, EM_GETTEXTEX, textA);
5761 if (em_settextex_supported)
5763 set_textW(hwnd, EM_SETTEXTEX, textW);
5764 expect_textW(hwnd, WM_GETTEXT, textW);
5765 expect_textA(hwnd, WM_GETTEXT, textA);
5766 expect_textW(hwnd, EM_GETTEXTEX, textW);
5767 expect_textA(hwnd, EM_GETTEXTEX, textA);
5769 DestroyWindow(hwnd);
5772 static void test_WM_CHAR(void)
5774 HWND hwnd;
5775 int ret;
5776 const char * char_list = "abc\rabc\r";
5777 const char * expected_content_single = "abcabc";
5778 const char * expected_content_multi = "abc\r\nabc\r\n";
5779 char buffer[64] = {0};
5780 const char * p;
5782 /* single-line control must IGNORE carriage returns */
5783 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5784 0, 0, 200, 60, 0, 0, 0, 0);
5785 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5787 p = char_list;
5788 while (*p != '\0') {
5789 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5790 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5791 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5792 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5793 p++;
5796 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5797 ret = strcmp(buffer, expected_content_single);
5798 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5800 DestroyWindow(hwnd);
5802 /* multi-line control inserts CR normally */
5803 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
5804 0, 0, 200, 60, 0, 0, 0, 0);
5805 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5807 p = char_list;
5808 while (*p != '\0') {
5809 SendMessageA(hwnd, WM_KEYDOWN, *p, 1);
5810 ret = SendMessageA(hwnd, WM_CHAR, *p, 1);
5811 ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret);
5812 SendMessageA(hwnd, WM_KEYUP, *p, 1);
5813 p++;
5816 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5817 ret = strcmp(buffer, expected_content_multi);
5818 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5820 DestroyWindow(hwnd);
5823 static void test_EM_GETTEXTLENGTHEX(void)
5825 HWND hwnd;
5826 GETTEXTLENGTHEX gtl;
5827 int ret;
5828 const char * base_string = "base string";
5829 const char * test_string = "a\nb\n\n\r\n";
5830 const char * test_string_after = "a";
5831 const char * test_string_2 = "a\rtest\rstring";
5832 char buffer[64] = {0};
5834 /* single line */
5835 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP,
5836 0, 0, 200, 60, 0, 0, 0, 0);
5837 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5839 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5840 gtl.codepage = CP_ACP;
5841 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5842 ok(ret == 0, "ret %d\n",ret);
5844 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5845 gtl.codepage = CP_ACP;
5846 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5847 ok(ret == 0, "ret %d\n",ret);
5849 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5851 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5852 gtl.codepage = CP_ACP;
5853 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5854 ok(ret == strlen(base_string), "ret %d\n",ret);
5856 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5857 gtl.codepage = CP_ACP;
5858 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5859 ok(ret == strlen(base_string), "ret %d\n",ret);
5861 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5863 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5864 gtl.codepage = CP_ACP;
5865 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5866 ok(ret == 1, "ret %d\n",ret);
5868 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5869 gtl.codepage = CP_ACP;
5870 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5871 ok(ret == 1, "ret %d\n",ret);
5873 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
5874 ret = strcmp(buffer, test_string_after);
5875 ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n");
5877 DestroyWindow(hwnd);
5879 /* multi line */
5880 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE,
5881 0, 0, 200, 60, 0, 0, 0, 0);
5882 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
5884 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5885 gtl.codepage = CP_ACP;
5886 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5887 ok(ret == 0, "ret %d\n",ret);
5889 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5890 gtl.codepage = CP_ACP;
5891 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5892 ok(ret == 0, "ret %d\n",ret);
5894 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)base_string);
5896 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5897 gtl.codepage = CP_ACP;
5898 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5899 ok(ret == strlen(base_string), "ret %d\n",ret);
5901 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5902 gtl.codepage = CP_ACP;
5903 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5904 ok(ret == strlen(base_string), "ret %d\n",ret);
5906 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5908 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5909 gtl.codepage = CP_ACP;
5910 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5911 ok(ret == strlen(test_string_2) + 2, "ret %d\n",ret);
5913 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5914 gtl.codepage = CP_ACP;
5915 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5916 ok(ret == strlen(test_string_2), "ret %d\n",ret);
5918 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string);
5920 gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF;
5921 gtl.codepage = CP_ACP;
5922 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5923 ok(ret == 10, "ret %d\n",ret);
5925 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5926 gtl.codepage = CP_ACP;
5927 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5928 ok(ret == 6, "ret %d\n",ret);
5930 /* Unicode/NUMCHARS/NUMBYTES */
5931 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)test_string_2);
5933 gtl.flags = GTL_DEFAULT;
5934 gtl.codepage = 1200;
5935 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5936 ok(ret == lstrlenA(test_string_2),
5937 "GTL_DEFAULT gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5939 gtl.flags = GTL_NUMCHARS;
5940 gtl.codepage = 1200;
5941 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5942 ok(ret == lstrlenA(test_string_2),
5943 "GTL_NUMCHARS gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5945 gtl.flags = GTL_NUMBYTES;
5946 gtl.codepage = 1200;
5947 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5948 ok(ret == lstrlenA(test_string_2)*2,
5949 "GTL_NUMBYTES gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5951 gtl.flags = GTL_PRECISE;
5952 gtl.codepage = 1200;
5953 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5954 ok(ret == lstrlenA(test_string_2)*2,
5955 "GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2)*2);
5957 gtl.flags = GTL_NUMCHARS | GTL_PRECISE;
5958 gtl.codepage = 1200;
5959 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5960 ok(ret == lstrlenA(test_string_2),
5961 "GTL_NUMCHAR | GTL_PRECISE gave %i, expected %i\n", ret, lstrlenA(test_string_2));
5963 gtl.flags = GTL_NUMCHARS | GTL_NUMBYTES;
5964 gtl.codepage = 1200;
5965 ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
5966 ok(ret == E_INVALIDARG,
5967 "GTL_NUMCHARS | GTL_NUMBYTES gave %i, expected %i\n", ret, E_INVALIDARG);
5969 DestroyWindow(hwnd);
5973 /* globals that parent and child access when checking event masks & notifications */
5974 static HWND eventMaskEditHwnd = 0;
5975 static int queriedEventMask;
5976 static int watchForEventMask = 0;
5978 /* parent proc that queries the edit's event mask when it gets a WM_COMMAND */
5979 static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5981 if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16)))
5983 queriedEventMask = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
5985 return DefWindowProcA(hwnd, message, wParam, lParam);
5988 /* test event masks in combination with WM_COMMAND */
5989 static void test_eventMask(void)
5991 HWND parent;
5992 int ret, style;
5993 WNDCLASSA cls;
5994 const char text[] = "foo bar\n";
5995 int eventMask;
5997 /* register class to capture WM_COMMAND */
5998 cls.style = 0;
5999 cls.lpfnWndProc = ParentMsgCheckProcA;
6000 cls.cbClsExtra = 0;
6001 cls.cbWndExtra = 0;
6002 cls.hInstance = GetModuleHandleA(0);
6003 cls.hIcon = 0;
6004 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6005 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6006 cls.lpszMenuName = NULL;
6007 cls.lpszClassName = "EventMaskParentClass";
6008 if(!RegisterClassA(&cls)) assert(0);
6010 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6011 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6012 ok (parent != 0, "Failed to create parent window\n");
6014 eventMaskEditHwnd = new_richedit(parent);
6015 ok(eventMaskEditHwnd != 0, "Failed to create edit window\n");
6017 eventMask = ENM_CHANGE | ENM_UPDATE;
6018 ret = SendMessageA(eventMaskEditHwnd, EM_SETEVENTMASK, 0, eventMask);
6019 ok(ret == ENM_NONE, "wrong event mask\n");
6020 ret = SendMessageA(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0);
6021 ok(ret == eventMask, "failed to set event mask\n");
6023 /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */
6024 queriedEventMask = 0; /* initialize to something other than we expect */
6025 watchForEventMask = EN_CHANGE;
6026 ret = SendMessageA(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM)text);
6027 ok(ret == TRUE, "failed to set text\n");
6028 /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE
6029 notification in response to WM_SETTEXT */
6030 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6031 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6033 /* check to see if EN_CHANGE is sent when redraw is turned off */
6034 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6035 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6036 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, FALSE, 0);
6037 /* redraw is disabled by making the window invisible. */
6038 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6039 queriedEventMask = 0; /* initialize to something other than we expect */
6040 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6041 ok(queriedEventMask == (eventMask & ~ENM_CHANGE),
6042 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6043 SendMessageA(eventMaskEditHwnd, WM_SETREDRAW, TRUE, 0);
6044 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6046 /* check to see if EN_UPDATE is sent when the editor isn't visible */
6047 SendMessageA(eventMaskEditHwnd, WM_CLEAR, 0, 0);
6048 style = GetWindowLongA(eventMaskEditHwnd, GWL_STYLE);
6049 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style & ~WS_VISIBLE);
6050 ok(!IsWindowVisible(eventMaskEditHwnd), "Window shouldn't be visible.\n");
6051 watchForEventMask = EN_UPDATE;
6052 queriedEventMask = 0; /* initialize to something other than we expect */
6053 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6054 ok(queriedEventMask == 0,
6055 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6056 SetWindowLongA(eventMaskEditHwnd, GWL_STYLE, style);
6057 ok(IsWindowVisible(eventMaskEditHwnd), "Window should be visible.\n");
6058 queriedEventMask = 0; /* initialize to something other than we expect */
6059 SendMessageA(eventMaskEditHwnd, EM_REPLACESEL, 0, (LPARAM)text);
6060 ok(queriedEventMask == eventMask,
6061 "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask);
6064 DestroyWindow(parent);
6067 static int received_WM_NOTIFY = 0;
6068 static int modify_at_WM_NOTIFY = 0;
6069 static BOOL filter_on_WM_NOTIFY = FALSE;
6070 static HWND hwndRichedit_WM_NOTIFY;
6072 static LRESULT WINAPI WM_NOTIFY_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6074 if(message == WM_NOTIFY)
6076 received_WM_NOTIFY = 1;
6077 modify_at_WM_NOTIFY = SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETMODIFY, 0, 0);
6078 if (filter_on_WM_NOTIFY) return TRUE;
6080 return DefWindowProcA(hwnd, message, wParam, lParam);
6083 static void test_WM_NOTIFY(void)
6085 HWND parent;
6086 WNDCLASSA cls;
6087 CHARFORMAT2A cf2;
6088 int sel_start, sel_end;
6090 /* register class to capture WM_NOTIFY */
6091 cls.style = 0;
6092 cls.lpfnWndProc = WM_NOTIFY_ParentMsgCheckProcA;
6093 cls.cbClsExtra = 0;
6094 cls.cbWndExtra = 0;
6095 cls.hInstance = GetModuleHandleA(0);
6096 cls.hIcon = 0;
6097 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6098 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6099 cls.lpszMenuName = NULL;
6100 cls.lpszClassName = "WM_NOTIFY_ParentClass";
6101 if(!RegisterClassA(&cls)) assert(0);
6103 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6104 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6105 ok (parent != 0, "Failed to create parent window\n");
6107 hwndRichedit_WM_NOTIFY = new_richedit(parent);
6108 ok(hwndRichedit_WM_NOTIFY != 0, "Failed to create edit window\n");
6110 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_SELCHANGE);
6112 /* Notifications for selection change should only be sent when selection
6113 actually changes. EM_SETCHARFORMAT is one message that calls
6114 ME_CommitUndo, which should check whether message should be sent */
6115 received_WM_NOTIFY = 0;
6116 cf2.cbSize = sizeof(CHARFORMAT2A);
6117 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
6118 cf2.dwMask = CFM_ITALIC | cf2.dwMask;
6119 cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects;
6120 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6121 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6123 /* WM_SETTEXT should NOT cause a WM_NOTIFY to be sent when selection is
6124 already at 0. */
6125 received_WM_NOTIFY = 0;
6126 modify_at_WM_NOTIFY = 0;
6127 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6128 ok(received_WM_NOTIFY == 0, "Unexpected WM_NOTIFY was sent!\n");
6129 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6131 received_WM_NOTIFY = 0;
6132 modify_at_WM_NOTIFY = 0;
6133 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 4, 4);
6134 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6136 received_WM_NOTIFY = 0;
6137 modify_at_WM_NOTIFY = 0;
6138 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETTEXT, 0, (LPARAM)"sometext");
6139 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6140 ok(modify_at_WM_NOTIFY == 0, "WM_NOTIFY callback saw text flagged as modified!\n");
6142 /* Test for WM_NOTIFY messages with redraw disabled. */
6143 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6144 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, FALSE, 0);
6145 received_WM_NOTIFY = 0;
6146 SendMessageA(hwndRichedit_WM_NOTIFY, EM_REPLACESEL, FALSE, (LPARAM)"inserted");
6147 ok(received_WM_NOTIFY == 1, "Expected WM_NOTIFY was NOT sent!\n");
6148 SendMessageA(hwndRichedit_WM_NOTIFY, WM_SETREDRAW, TRUE, 0);
6150 /* Test filtering key events. */
6151 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETSEL, 0, 0);
6152 SendMessageA(hwndRichedit_WM_NOTIFY, EM_SETEVENTMASK, 0, ENM_KEYEVENTS);
6153 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6154 received_WM_NOTIFY = 0;
6155 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6156 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6157 ok(sel_start == 1 && sel_end == 1,
6158 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6159 filter_on_WM_NOTIFY = TRUE;
6160 received_WM_NOTIFY = 0;
6161 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6162 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6163 ok(sel_start == 1 && sel_end == 1,
6164 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6166 /* test with owner set to NULL */
6167 SetWindowLongPtrA(hwndRichedit_WM_NOTIFY, GWLP_HWNDPARENT, 0);
6168 SendMessageA(hwndRichedit_WM_NOTIFY, WM_KEYDOWN, VK_RIGHT, 0);
6169 SendMessageA(hwndRichedit_WM_NOTIFY, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6170 ok(sel_start == 1 && sel_end == 1,
6171 "selections is incorrectly at (%d,%d)\n", sel_start, sel_end);
6173 DestroyWindow(hwndRichedit_WM_NOTIFY);
6174 DestroyWindow(parent);
6177 static ENLINK enlink;
6178 #define CURSOR_CLIENT_X 5
6179 #define CURSOR_CLIENT_Y 5
6180 #define WP_PARENT 1
6181 #define WP_CHILD 2
6183 static LRESULT WINAPI EN_LINK_ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6185 if(message == WM_NOTIFY && ((NMHDR*)lParam)->code == EN_LINK)
6187 enlink = *(ENLINK*)lParam;
6189 return DefWindowProcA(hwnd, message, wParam, lParam);
6192 static void link_notify_test(const char *desc, int i, HWND hwnd, HWND parent,
6193 UINT msg, WPARAM wParam, LPARAM lParam, BOOL notifies)
6195 ENLINK junk_enlink;
6197 switch (msg)
6199 case WM_LBUTTONDBLCLK:
6200 case WM_LBUTTONDOWN:
6201 case WM_LBUTTONUP:
6202 case WM_MOUSEHOVER:
6203 case WM_MOUSEMOVE:
6204 case WM_MOUSEWHEEL:
6205 case WM_RBUTTONDBLCLK:
6206 case WM_RBUTTONDOWN:
6207 case WM_RBUTTONUP:
6208 lParam = MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y);
6209 break;
6210 case WM_SETCURSOR:
6211 if (wParam == WP_PARENT)
6212 wParam = (WPARAM)parent;
6213 else if (wParam == WP_CHILD)
6214 wParam = (WPARAM)hwnd;
6215 break;
6218 memset(&junk_enlink, 0x23, sizeof(junk_enlink));
6219 enlink = junk_enlink;
6221 SendMessageA(hwnd, msg, wParam, lParam);
6223 if (notifies)
6225 ok(enlink.nmhdr.hwndFrom == hwnd,
6226 "%s test %i: Expected hwnd %p got %p\n", desc, i, hwnd, enlink.nmhdr.hwndFrom);
6227 ok(enlink.nmhdr.idFrom == 0,
6228 "%s test %i: Expected idFrom 0 got 0x%lx\n", desc, i, enlink.nmhdr.idFrom);
6229 ok(enlink.msg == msg,
6230 "%s test %i: Expected msg 0x%x got 0x%x\n", desc, i, msg, enlink.msg);
6231 if (msg == WM_SETCURSOR)
6233 ok(enlink.wParam == 0,
6234 "%s test %i: Expected wParam 0 got 0x%lx\n", desc, i, enlink.wParam);
6236 else
6238 ok(enlink.wParam == wParam,
6239 "%s test %i: Expected wParam 0x%lx got 0x%lx\n", desc, i, wParam, enlink.wParam);
6241 ok(enlink.lParam == MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y),
6242 "%s test %i: Expected lParam 0x%lx got 0x%lx\n",
6243 desc, i, MAKELPARAM(CURSOR_CLIENT_X, CURSOR_CLIENT_Y), enlink.lParam);
6244 ok(enlink.chrg.cpMin == 0 && enlink.chrg.cpMax == 31,
6245 "%s test %i: Expected link range [0,31) got [%i,%i)\n", desc, i, enlink.chrg.cpMin, enlink.chrg.cpMax);
6247 else
6249 ok(memcmp(&enlink, &junk_enlink, sizeof(enlink)) == 0,
6250 "%s test %i: Expected enlink to remain unmodified\n", desc, i);
6254 static void test_EN_LINK(void)
6256 HWND hwnd, parent;
6257 WNDCLASSA cls;
6258 CHARFORMAT2A cf2;
6259 POINT orig_cursor_pos;
6260 POINT cursor_screen_pos = {CURSOR_CLIENT_X, CURSOR_CLIENT_Y};
6261 int i;
6263 static const struct
6265 UINT msg;
6266 WPARAM wParam;
6267 LPARAM lParam;
6268 BOOL notifies;
6270 link_notify_tests[] =
6272 /* hold down the left button and try some messages */
6273 { WM_LBUTTONDOWN, 0, 0, TRUE }, /* 0 */
6274 { EM_LINESCROLL, 0, 1, FALSE },
6275 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6276 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6277 { WM_MOUSEHOVER, 0, 0, FALSE },
6278 { WM_MOUSEMOVE, 0, 0, FALSE },
6279 { WM_MOUSEWHEEL, 0, 0, FALSE },
6280 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6281 { WM_RBUTTONDOWN, 0, 0, TRUE },
6282 { WM_RBUTTONUP, 0, 0, TRUE },
6283 { WM_SETCURSOR, 0, 0, FALSE },
6284 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6285 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6286 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6287 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6288 { WM_LBUTTONUP, 0, 0, TRUE },
6289 /* hold down the right button and try some messages */
6290 { WM_RBUTTONDOWN, 0, 0, TRUE }, /* 16 */
6291 { EM_LINESCROLL, 0, 1, FALSE },
6292 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6293 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6294 { WM_LBUTTONDOWN, 0, 0, TRUE },
6295 { WM_LBUTTONUP, 0, 0, TRUE },
6296 { WM_MOUSEHOVER, 0, 0, FALSE },
6297 { WM_MOUSEMOVE, 0, 0, TRUE },
6298 { WM_MOUSEWHEEL, 0, 0, FALSE },
6299 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6300 { WM_SETCURSOR, 0, 0, FALSE },
6301 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6302 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6303 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6304 { WM_VSCROLL, SB_BOTTOM, 0, FALSE },
6305 { WM_RBUTTONUP, 0, 0, TRUE },
6306 /* try the messages with both buttons released */
6307 { EM_LINESCROLL, 0, 1, FALSE }, /* 32 */
6308 { EM_SCROLL, SB_BOTTOM, 0, FALSE },
6309 { WM_LBUTTONDBLCLK, 0, 0, TRUE },
6310 { WM_LBUTTONDOWN, 0, 0, TRUE },
6311 { WM_LBUTTONUP, 0, 0, TRUE },
6312 { WM_MOUSEHOVER, 0, 0, FALSE },
6313 { WM_MOUSEMOVE, 0, 0, TRUE },
6314 { WM_MOUSEWHEEL, 0, 0, FALSE },
6315 { WM_RBUTTONDBLCLK, 0, 0, TRUE },
6316 { WM_RBUTTONDOWN, 0, 0, TRUE },
6317 { WM_RBUTTONUP, 0, 0, TRUE },
6318 { WM_SETCURSOR, 0, 0, FALSE },
6319 { WM_SETCURSOR, WP_CHILD, 0, TRUE },
6320 { WM_SETCURSOR, WP_CHILD, 1, TRUE },
6321 { WM_SETCURSOR, WP_PARENT, 0, FALSE },
6322 { WM_VSCROLL, SB_BOTTOM, 0, FALSE }
6325 /* register class to capture WM_NOTIFY */
6326 cls.style = 0;
6327 cls.lpfnWndProc = EN_LINK_ParentMsgCheckProcA;
6328 cls.cbClsExtra = 0;
6329 cls.cbWndExtra = 0;
6330 cls.hInstance = GetModuleHandleA(0);
6331 cls.hIcon = 0;
6332 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
6333 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
6334 cls.lpszMenuName = NULL;
6335 cls.lpszClassName = "EN_LINK_ParentClass";
6336 if(!RegisterClassA(&cls)) assert(0);
6338 parent = CreateWindowA(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE,
6339 0, 0, 200, 60, NULL, NULL, NULL, NULL);
6340 ok(parent != 0, "Failed to create parent window\n");
6342 hwnd = new_richedit(parent);
6343 ok(hwnd != 0, "Failed to create edit window\n");
6345 SendMessageA(hwnd, EM_SETEVENTMASK, 0, ENM_LINK);
6347 cf2.cbSize = sizeof(CHARFORMAT2A);
6348 cf2.dwMask = CFM_LINK;
6349 cf2.dwEffects = CFE_LINK;
6350 SendMessageA(hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
6351 /* mixing letters and numbers causes runs to be split */
6352 SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"link text with at least 2 runs");
6354 GetCursorPos(&orig_cursor_pos);
6355 SetCursorPos(0, 0);
6357 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6359 link_notify_test("cursor position simulated", i, hwnd, parent,
6360 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6361 link_notify_tests[i].msg == WM_SETCURSOR ? FALSE : link_notify_tests[i].notifies);
6364 ClientToScreen(hwnd, &cursor_screen_pos);
6365 SetCursorPos(cursor_screen_pos.x, cursor_screen_pos.y);
6367 for (i = 0; i < sizeof(link_notify_tests)/sizeof(link_notify_tests[0]); i++)
6369 link_notify_test("cursor position set", i, hwnd, parent,
6370 link_notify_tests[i].msg, link_notify_tests[i].wParam, link_notify_tests[i].lParam,
6371 link_notify_tests[i].notifies);
6374 SetCursorPos(orig_cursor_pos.x, orig_cursor_pos.y);
6375 DestroyWindow(hwnd);
6376 DestroyWindow(parent);
6379 static void test_undo_coalescing(void)
6381 HWND hwnd;
6382 int result;
6383 char buffer[64] = {0};
6385 /* multi-line control inserts CR normally */
6386 hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE,
6387 0, 0, 200, 60, 0, 0, 0, 0);
6388 ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError());
6390 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6391 ok (result == FALSE, "Can undo after window creation.\n");
6392 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6393 ok (result == FALSE, "Undo operation successful with nothing to undo.\n");
6394 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6395 ok (result == FALSE, "Can redo after window creation.\n");
6396 result = SendMessageA(hwnd, EM_REDO, 0, 0);
6397 ok (result == FALSE, "Redo operation successful with nothing undone.\n");
6399 /* Test the effect of arrows keys during typing on undo transactions*/
6400 simulate_typing_characters(hwnd, "one two three");
6401 SendMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 1);
6402 SendMessageA(hwnd, WM_KEYUP, VK_RIGHT, 1);
6403 simulate_typing_characters(hwnd, " four five six");
6405 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6406 ok (result == FALSE, "Can redo before anything is undone.\n");
6407 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6408 ok (result == TRUE, "Cannot undo typed characters.\n");
6409 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6410 ok (result == TRUE, "EM_UNDO Failed to undo typed characters.\n");
6411 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6412 ok (result == TRUE, "Cannot redo after undo.\n");
6413 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6414 result = strcmp(buffer, "one two three");
6415 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6417 result = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
6418 ok (result == TRUE, "Cannot undo typed characters.\n");
6419 result = SendMessageA(hwnd, WM_UNDO, 0, 0);
6420 ok (result == TRUE, "Failed to undo typed characters.\n");
6421 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6422 result = strcmp(buffer, "");
6423 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6425 /* Test the effect of focus changes during typing on undo transactions*/
6426 simulate_typing_characters(hwnd, "one two three");
6427 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6428 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6429 SendMessageA(hwnd, WM_KILLFOCUS, 0, 0);
6430 SendMessageA(hwnd, WM_SETFOCUS, 0, 0);
6431 simulate_typing_characters(hwnd, " four five six");
6432 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6433 ok (result == TRUE, "Failed to undo typed characters.\n");
6434 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6435 result = strcmp(buffer, "one two three");
6436 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6438 /* Test the effect of the back key during typing on undo transactions */
6439 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6440 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6441 ok (result == TRUE, "Failed to clear the text.\n");
6442 simulate_typing_characters(hwnd, "one two threa");
6443 result = SendMessageA(hwnd, EM_CANREDO, 0, 0);
6444 ok (result == FALSE, "Redo buffer should have been cleared by typing.\n");
6445 SendMessageA(hwnd, WM_KEYDOWN, VK_BACK, 1);
6446 SendMessageA(hwnd, WM_KEYUP, VK_BACK, 1);
6447 simulate_typing_characters(hwnd, "e four five six");
6448 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6449 ok (result == TRUE, "Failed to undo typed characters.\n");
6450 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6451 result = strcmp(buffer, "");
6452 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6454 /* Test the effect of the delete key during typing on undo transactions */
6455 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6456 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"abcd");
6457 ok(result == TRUE, "Failed to set the text.\n");
6458 SendMessageA(hwnd, EM_SETSEL, 1, 1);
6459 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6460 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6461 SendMessageA(hwnd, WM_KEYDOWN, VK_DELETE, 1);
6462 SendMessageA(hwnd, WM_KEYUP, VK_DELETE, 1);
6463 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6464 ok (result == TRUE, "Failed to undo typed characters.\n");
6465 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6466 result = strcmp(buffer, "acd");
6467 ok (result == 0, "expected '%s' but got '%s'\n", "acd", buffer);
6468 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6469 ok (result == TRUE, "Failed to undo typed characters.\n");
6470 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6471 result = strcmp(buffer, "abcd");
6472 ok (result == 0, "expected '%s' but got '%s'\n", "abcd", buffer);
6474 /* Test the effect of EM_STOPGROUPTYPING on undo transactions*/
6475 SendMessageA(hwnd, EM_EMPTYUNDOBUFFER, 0, 0);
6476 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"");
6477 ok (result == TRUE, "Failed to clear the text.\n");
6478 simulate_typing_characters(hwnd, "one two three");
6479 result = SendMessageA(hwnd, EM_STOPGROUPTYPING, 0, 0);
6480 ok (result == 0, "expected %d but got %d\n", 0, result);
6481 simulate_typing_characters(hwnd, " four five six");
6482 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6483 ok (result == TRUE, "Failed to undo typed characters.\n");
6484 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6485 result = strcmp(buffer, "one two three");
6486 ok (result == 0, "expected '%s' but got '%s'\n", "one two three", buffer);
6487 result = SendMessageA(hwnd, EM_UNDO, 0, 0);
6488 ok (result == TRUE, "Failed to undo typed characters.\n");
6489 SendMessageA(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
6490 result = strcmp(buffer, "");
6491 ok (result == 0, "expected '%s' but got '%s'\n", "", buffer);
6493 DestroyWindow(hwnd);
6496 static LONG CALLBACK customWordBreakProc(WCHAR *text, int pos, int bytes, int code)
6498 int length;
6500 /* MSDN lied, length is actually the number of bytes. */
6501 length = bytes / sizeof(WCHAR);
6502 switch(code)
6504 case WB_ISDELIMITER:
6505 return text[pos] == 'X';
6506 case WB_LEFT:
6507 case WB_MOVEWORDLEFT:
6508 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6509 return pos-1;
6510 return min(customWordBreakProc(text, pos, bytes, WB_LEFTBREAK)-1, 0);
6511 case WB_LEFTBREAK:
6512 pos--;
6513 while (pos > 0 && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6514 pos--;
6515 return pos;
6516 case WB_RIGHT:
6517 case WB_MOVEWORDRIGHT:
6518 if (customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6519 return pos+1;
6520 return min(customWordBreakProc(text, pos, bytes, WB_RIGHTBREAK)+1, length);
6521 case WB_RIGHTBREAK:
6522 pos++;
6523 while (pos < length && !customWordBreakProc(text, pos, bytes, WB_ISDELIMITER))
6524 pos++;
6525 return pos;
6526 default:
6527 ok(FALSE, "Unexpected code %d\n", code);
6528 break;
6530 return 0;
6533 static void test_word_movement(void)
6535 HWND hwnd;
6536 int result;
6537 int sel_start, sel_end;
6538 const WCHAR textW[] = {'o','n','e',' ','t','w','o','X','t','h','r','e','e',0};
6540 /* multi-line control inserts CR normally */
6541 hwnd = new_richedit(NULL);
6543 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one two three");
6544 ok (result == TRUE, "Failed to clear the text.\n");
6545 SendMessageA(hwnd, EM_SETSEL, 0, 0);
6546 /* |one two three */
6548 send_ctrl_key(hwnd, VK_RIGHT);
6549 /* one |two three */
6550 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6551 ok(sel_start == sel_end, "Selection should be empty\n");
6552 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6554 send_ctrl_key(hwnd, VK_RIGHT);
6555 /* one two |three */
6556 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6557 ok(sel_start == sel_end, "Selection should be empty\n");
6558 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6560 send_ctrl_key(hwnd, VK_LEFT);
6561 /* one |two three */
6562 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6563 ok(sel_start == sel_end, "Selection should be empty\n");
6564 ok(sel_start == 4, "Cursor is at %d instead of %d\n", sel_start, 4);
6566 send_ctrl_key(hwnd, VK_LEFT);
6567 /* |one two three */
6568 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6569 ok(sel_start == sel_end, "Selection should be empty\n");
6570 ok(sel_start == 0, "Cursor is at %d instead of %d\n", sel_start, 0);
6572 SendMessageA(hwnd, EM_SETSEL, 8, 8);
6573 /* one two | three */
6574 send_ctrl_key(hwnd, VK_RIGHT);
6575 /* one two |three */
6576 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6577 ok(sel_start == sel_end, "Selection should be empty\n");
6578 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6580 SendMessageA(hwnd, EM_SETSEL, 11, 11);
6581 /* one two th|ree */
6582 send_ctrl_key(hwnd, VK_LEFT);
6583 /* one two |three */
6584 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6585 ok(sel_start == sel_end, "Selection should be empty\n");
6586 ok(sel_start == 9, "Cursor is at %d instead of %d\n", sel_start, 9);
6588 /* Test with a custom word break procedure that uses X as the delimiter. */
6589 result = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)"one twoXthree");
6590 ok (result == TRUE, "Failed to clear the text.\n");
6591 SendMessageA(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6592 /* |one twoXthree */
6593 send_ctrl_key(hwnd, VK_RIGHT);
6594 /* one twoX|three */
6595 SendMessageA(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6596 ok(sel_start == sel_end, "Selection should be empty\n");
6597 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6599 DestroyWindow(hwnd);
6601 /* Make sure the behaviour is the same with a unicode richedit window,
6602 * and using unicode functions. */
6604 hwnd = CreateWindowW(RICHEDIT_CLASS20W, NULL,
6605 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6606 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6608 /* Test with a custom word break procedure that uses X as the delimiter. */
6609 result = SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
6610 ok (result == TRUE, "Failed to clear the text.\n");
6611 SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)customWordBreakProc);
6612 /* |one twoXthree */
6613 send_ctrl_key(hwnd, VK_RIGHT);
6614 /* one twoX|three */
6615 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
6616 ok(sel_start == sel_end, "Selection should be empty\n");
6617 ok(sel_start == 8, "Cursor is at %d instead of %d\n", sel_start, 8);
6619 DestroyWindow(hwnd);
6622 static void test_EM_CHARFROMPOS(void)
6624 HWND hwnd;
6625 int result;
6626 RECT rcClient;
6627 POINTL point;
6628 point.x = 0;
6629 point.y = 40;
6631 /* multi-line control inserts CR normally */
6632 hwnd = new_richedit(NULL);
6633 result = SendMessageA(hwnd, WM_SETTEXT, 0,
6634 (LPARAM)"one two three four five six seven\reight");
6635 ok(result == 1, "Expected 1, got %d\n", result);
6636 GetClientRect(hwnd, &rcClient);
6638 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6639 ok(result == 34, "expected character index of 34 but got %d\n", result);
6641 /* Test with points outside the bounds of the richedit control. */
6642 point.x = -1;
6643 point.y = 40;
6644 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6645 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6647 point.x = 1000;
6648 point.y = 0;
6649 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6650 todo_wine ok(result == 33, "expected character index of 33 but got %d\n", result);
6652 point.x = 1000;
6653 point.y = 36;
6654 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6655 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6657 point.x = 1000;
6658 point.y = -1;
6659 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6660 todo_wine ok(result == 0, "expected character index of 0 but got %d\n", result);
6662 point.x = 1000;
6663 point.y = rcClient.bottom + 1;
6664 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6665 todo_wine ok(result == 34, "expected character index of 34 but got %d\n", result);
6667 point.x = 1000;
6668 point.y = rcClient.bottom;
6669 result = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6670 todo_wine ok(result == 39, "expected character index of 39 but got %d\n", result);
6672 DestroyWindow(hwnd);
6675 static void test_word_wrap(void)
6677 HWND hwnd;
6678 POINTL point = {0, 60}; /* This point must be below the first line */
6679 const char *text = "Must be long enough to test line wrapping";
6680 DWORD dwCommonStyle = WS_VISIBLE|WS_POPUP|WS_VSCROLL|ES_MULTILINE;
6681 int res, pos, lines;
6683 /* Test the effect of WS_HSCROLL and ES_AUTOHSCROLL styles on wrapping
6684 * when specified on window creation and set later. */
6685 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6686 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6687 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6688 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6689 ok(res, "WM_SETTEXT failed.\n");
6690 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6691 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6692 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6693 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6695 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL);
6696 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6697 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6698 DestroyWindow(hwnd);
6700 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|WS_HSCROLL,
6701 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6702 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6704 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6705 ok(res, "WM_SETTEXT failed.\n");
6706 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6707 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6708 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6709 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6711 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6712 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6713 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6714 DestroyWindow(hwnd);
6716 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle|ES_AUTOHSCROLL,
6717 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6718 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6719 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6720 ok(res, "WM_SETTEXT failed.\n");
6721 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6722 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6724 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6725 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6726 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6727 DestroyWindow(hwnd);
6729 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL,
6730 dwCommonStyle|WS_HSCROLL|ES_AUTOHSCROLL,
6731 0, 0, 200, 80, NULL, NULL, hmoduleRichEdit, NULL);
6732 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6733 res = SendMessageA(hwnd, WM_SETTEXT, 0, (LPARAM)text);
6734 ok(res, "WM_SETTEXT failed.\n");
6735 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6736 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6738 SetWindowLongW(hwnd, GWL_STYLE, dwCommonStyle);
6739 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6740 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6742 /* Test the effect of EM_SETTARGETDEVICE on word wrap. */
6743 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 1);
6744 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6745 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6746 ok(!pos, "pos=%d indicating word wrap when none is expected.\n", pos);
6748 res = SendMessageA(hwnd, EM_SETTARGETDEVICE, 0, 0);
6749 ok(res, "EM_SETTARGETDEVICE failed (returned %d).\n", res);
6750 pos = SendMessageA(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&point);
6751 ok(pos, "pos=%d indicating no word wrap when it is expected.\n", pos);
6752 DestroyWindow(hwnd);
6754 /* Test to see if wrapping happens with redraw disabled. */
6755 hwnd = CreateWindowA(RICHEDIT_CLASS20A, NULL, dwCommonStyle,
6756 0, 0, 400, 80, NULL, NULL, hmoduleRichEdit, NULL);
6757 ok(hwnd != NULL, "error: %d\n", (int) GetLastError());
6758 SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
6759 res = SendMessageA(hwnd, EM_REPLACESEL, FALSE, (LPARAM)text);
6760 ok(res, "EM_REPLACESEL failed.\n");
6761 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6762 ok(lines == 1, "Line wasn't expected to wrap (lines=%d).\n", lines);
6763 MoveWindow(hwnd, 0, 0, 200, 80, FALSE);
6764 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6765 ok(lines > 1, "Line was expected to wrap (lines=%d).\n", lines);
6767 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6768 DestroyWindow(hwnd);
6771 static void test_autoscroll(void)
6773 HWND hwnd = new_richedit(NULL);
6774 int lines, ret, redraw;
6775 POINT pt;
6777 for (redraw = 0; redraw <= 1; redraw++) {
6778 trace("testing with WM_SETREDRAW=%d\n", redraw);
6779 SendMessageA(hwnd, WM_SETREDRAW, redraw, 0);
6780 SendMessageA(hwnd, EM_REPLACESEL, 0, (LPARAM)"1\n2\n3\n4\n5\n6\n7\n8");
6781 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6782 ok(lines == 8, "%d lines instead of 8\n", lines);
6783 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6784 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6785 ok(pt.y != 0, "Didn't scroll down after replacing text.\n");
6786 ret = GetWindowLongA(hwnd, GWL_STYLE);
6787 ok(ret & WS_VSCROLL, "Scrollbar was not shown yet (style=%x).\n", (UINT)ret);
6789 SendMessageA(hwnd, WM_SETTEXT, 0, 0);
6790 lines = SendMessageA(hwnd, EM_GETLINECOUNT, 0, 0);
6791 ok(lines == 1, "%d lines instead of 1\n", lines);
6792 ret = SendMessageA(hwnd, EM_GETSCROLLPOS, 0, (LPARAM)&pt);
6793 ok(ret == 1, "EM_GETSCROLLPOS returned %d instead of 1\n", ret);
6794 ok(pt.y == 0, "y scroll position is %d after clearing text.\n", pt.y);
6795 ret = GetWindowLongA(hwnd, GWL_STYLE);
6796 ok(!(ret & WS_VSCROLL), "Scrollbar is still shown (style=%x).\n", (UINT)ret);
6799 SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
6800 DestroyWindow(hwnd);
6802 /* The WS_VSCROLL and WS_HSCROLL styles implicitly set
6803 * auto vertical/horizontal scrolling options. */
6804 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6805 WS_POPUP|ES_MULTILINE|WS_VSCROLL|WS_HSCROLL,
6806 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6807 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6808 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6809 ok(ret & ECO_AUTOVSCROLL, "ECO_AUTOVSCROLL isn't set.\n");
6810 ok(ret & ECO_AUTOHSCROLL, "ECO_AUTOHSCROLL isn't set.\n");
6811 ret = GetWindowLongA(hwnd, GWL_STYLE);
6812 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6813 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6814 DestroyWindow(hwnd);
6816 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6817 WS_POPUP|ES_MULTILINE,
6818 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6819 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6820 ret = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6821 ok(!(ret & ECO_AUTOVSCROLL), "ECO_AUTOVSCROLL is set.\n");
6822 ok(!(ret & ECO_AUTOHSCROLL), "ECO_AUTOHSCROLL is set.\n");
6823 ret = GetWindowLongA(hwnd, GWL_STYLE);
6824 ok(!(ret & ES_AUTOVSCROLL), "ES_AUTOVSCROLL is set.\n");
6825 ok(!(ret & ES_AUTOHSCROLL), "ES_AUTOHSCROLL is set.\n");
6826 DestroyWindow(hwnd);
6830 static void test_format_rect(void)
6832 HWND hwnd;
6833 RECT rc, expected, clientRect;
6834 int n;
6835 DWORD options;
6837 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
6838 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
6839 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
6840 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
6842 GetClientRect(hwnd, &clientRect);
6844 expected = clientRect;
6845 expected.left += 1;
6846 expected.right -= 1;
6847 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6848 ok(rc.top == expected.top && rc.left == expected.left &&
6849 rc.bottom == expected.bottom && rc.right == expected.right,
6850 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6851 rc.top, rc.left, rc.bottom, rc.right,
6852 expected.top, expected.left, expected.bottom, expected.right);
6854 for (n = -3; n <= 3; n++)
6856 rc = clientRect;
6857 rc.top += n;
6858 rc.left += n;
6859 rc.bottom -= n;
6860 rc.right -= n;
6861 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6863 expected = rc;
6864 expected.top = max(0, rc.top);
6865 expected.left = max(0, rc.left);
6866 expected.bottom = min(clientRect.bottom, rc.bottom);
6867 expected.right = min(clientRect.right, rc.right);
6868 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6869 ok(rc.top == expected.top && rc.left == expected.left &&
6870 rc.bottom == expected.bottom && rc.right == expected.right,
6871 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6872 n, rc.top, rc.left, rc.bottom, rc.right,
6873 expected.top, expected.left, expected.bottom, expected.right);
6876 rc = clientRect;
6877 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6878 expected = clientRect;
6879 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6880 ok(rc.top == expected.top && rc.left == expected.left &&
6881 rc.bottom == expected.bottom && rc.right == expected.right,
6882 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6883 rc.top, rc.left, rc.bottom, rc.right,
6884 expected.top, expected.left, expected.bottom, expected.right);
6886 /* Adding the selectionbar adds the selectionbar width to the left side. */
6887 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
6888 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6889 ok(options & ECO_SELECTIONBAR, "EM_SETOPTIONS failed to add selectionbar.\n");
6890 expected.left += 8; /* selection bar width */
6891 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6892 ok(rc.top == expected.top && rc.left == expected.left &&
6893 rc.bottom == expected.bottom && rc.right == expected.right,
6894 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6895 rc.top, rc.left, rc.bottom, rc.right,
6896 expected.top, expected.left, expected.bottom, expected.right);
6898 rc = clientRect;
6899 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6900 expected = clientRect;
6901 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6902 ok(rc.top == expected.top && rc.left == expected.left &&
6903 rc.bottom == expected.bottom && rc.right == expected.right,
6904 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6905 rc.top, rc.left, rc.bottom, rc.right,
6906 expected.top, expected.left, expected.bottom, expected.right);
6908 /* Removing the selectionbar subtracts the selectionbar width from the left side,
6909 * even if the left side is already 0. */
6910 SendMessageA(hwnd, EM_SETOPTIONS, ECOOP_AND, ~ECO_SELECTIONBAR);
6911 options = SendMessageA(hwnd, EM_GETOPTIONS, 0, 0);
6912 ok(!(options & ECO_SELECTIONBAR), "EM_SETOPTIONS failed to remove selectionbar.\n");
6913 expected.left -= 8; /* selection bar width */
6914 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6915 ok(rc.top == expected.top && rc.left == expected.left &&
6916 rc.bottom == expected.bottom && rc.right == expected.right,
6917 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6918 rc.top, rc.left, rc.bottom, rc.right,
6919 expected.top, expected.left, expected.bottom, expected.right);
6921 /* Set the absolute value of the formatting rectangle. */
6922 rc = clientRect;
6923 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
6924 expected = clientRect;
6925 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6926 ok(rc.top == expected.top && rc.left == expected.left &&
6927 rc.bottom == expected.bottom && rc.right == expected.right,
6928 "[n=%d] rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6929 n, rc.top, rc.left, rc.bottom, rc.right,
6930 expected.top, expected.left, expected.bottom, expected.right);
6932 /* MSDN documents the EM_SETRECT message as using the rectangle provided in
6933 * LPARAM as being a relative offset when the WPARAM value is 1, but these
6934 * tests show that this isn't true. */
6935 rc.top = 15;
6936 rc.left = 15;
6937 rc.bottom = clientRect.bottom - 15;
6938 rc.right = clientRect.right - 15;
6939 expected = rc;
6940 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6941 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6942 ok(rc.top == expected.top && rc.left == expected.left &&
6943 rc.bottom == expected.bottom && rc.right == expected.right,
6944 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6945 rc.top, rc.left, rc.bottom, rc.right,
6946 expected.top, expected.left, expected.bottom, expected.right);
6948 /* For some reason it does not limit the values to the client rect with
6949 * a WPARAM value of 1. */
6950 rc.top = -15;
6951 rc.left = -15;
6952 rc.bottom = clientRect.bottom + 15;
6953 rc.right = clientRect.right + 15;
6954 expected = rc;
6955 SendMessageA(hwnd, EM_SETRECT, 1, (LPARAM)&rc);
6956 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6957 ok(rc.top == expected.top && rc.left == expected.left &&
6958 rc.bottom == expected.bottom && rc.right == expected.right,
6959 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6960 rc.top, rc.left, rc.bottom, rc.right,
6961 expected.top, expected.left, expected.bottom, expected.right);
6963 /* Reset to default rect and check how the format rect adjusts to window
6964 * resize and how it copes with very small windows */
6965 SendMessageA(hwnd, EM_SETRECT, 0, 0);
6967 MoveWindow(hwnd, 0, 0, 100, 30, FALSE);
6968 GetClientRect(hwnd, &clientRect);
6970 expected = clientRect;
6971 expected.left += 1;
6972 expected.right -= 1;
6973 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6974 ok(rc.top == expected.top && rc.left == expected.left &&
6975 rc.bottom == expected.bottom && rc.right == expected.right,
6976 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6977 rc.top, rc.left, rc.bottom, rc.right,
6978 expected.top, expected.left, expected.bottom, expected.right);
6980 MoveWindow(hwnd, 0, 0, 0, 30, FALSE);
6981 GetClientRect(hwnd, &clientRect);
6983 expected = clientRect;
6984 expected.left += 1;
6985 expected.right -= 1;
6986 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
6987 ok(rc.top == expected.top && rc.left == expected.left &&
6988 rc.bottom == expected.bottom && rc.right == expected.right,
6989 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
6990 rc.top, rc.left, rc.bottom, rc.right,
6991 expected.top, expected.left, expected.bottom, expected.right);
6993 MoveWindow(hwnd, 0, 0, 100, 0, FALSE);
6994 GetClientRect(hwnd, &clientRect);
6996 expected = clientRect;
6997 expected.left += 1;
6998 expected.right -= 1;
6999 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7000 ok(rc.top == expected.top && rc.left == expected.left &&
7001 rc.bottom == expected.bottom && rc.right == expected.right,
7002 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7003 rc.top, rc.left, rc.bottom, rc.right,
7004 expected.top, expected.left, expected.bottom, expected.right);
7006 DestroyWindow(hwnd);
7008 /* The extended window style affects the formatting rectangle. */
7009 hwnd = CreateWindowExA(WS_EX_CLIENTEDGE, RICHEDIT_CLASS20A, NULL,
7010 ES_MULTILINE|WS_POPUP|WS_HSCROLL|WS_VSCROLL|WS_VISIBLE,
7011 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7012 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7014 GetClientRect(hwnd, &clientRect);
7016 expected = clientRect;
7017 expected.left += 1;
7018 expected.top += 1;
7019 expected.right -= 1;
7020 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7021 ok(rc.top == expected.top && rc.left == expected.left &&
7022 rc.bottom == expected.bottom && rc.right == expected.right,
7023 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7024 rc.top, rc.left, rc.bottom, rc.right,
7025 expected.top, expected.left, expected.bottom, expected.right);
7027 rc = clientRect;
7028 rc.top += 5;
7029 rc.left += 5;
7030 rc.bottom -= 5;
7031 rc.right -= 5;
7032 expected = rc;
7033 expected.top -= 1;
7034 expected.left -= 1;
7035 expected.right += 1;
7036 SendMessageA(hwnd, EM_SETRECT, 0, (LPARAM)&rc);
7037 SendMessageA(hwnd, EM_GETRECT, 0, (LPARAM)&rc);
7038 ok(rc.top == expected.top && rc.left == expected.left &&
7039 rc.bottom == expected.bottom && rc.right == expected.right,
7040 "rect a(t=%d, l=%d, b=%d, r=%d) != e(t=%d, l=%d, b=%d, r=%d)\n",
7041 rc.top, rc.left, rc.bottom, rc.right,
7042 expected.top, expected.left, expected.bottom, expected.right);
7044 DestroyWindow(hwnd);
7047 static void test_WM_GETDLGCODE(void)
7049 HWND hwnd;
7050 UINT res, expected;
7051 MSG msg;
7053 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7055 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7056 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7057 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7058 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7059 msg.hwnd = hwnd;
7060 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, 0);
7061 expected = expected | DLGC_WANTMESSAGE;
7062 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7063 res, expected);
7064 DestroyWindow(hwnd);
7066 msg.message = WM_KEYDOWN;
7067 msg.wParam = VK_RETURN;
7068 msg.lParam = (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7069 msg.pt.x = 0;
7070 msg.pt.y = 0;
7071 msg.time = GetTickCount();
7073 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7074 ES_MULTILINE|ES_WANTRETURN|WS_POPUP,
7075 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7076 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7077 msg.hwnd = hwnd;
7078 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7079 expected = expected | DLGC_WANTMESSAGE;
7080 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7081 res, expected);
7082 DestroyWindow(hwnd);
7084 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7085 ES_MULTILINE|WS_POPUP,
7086 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7087 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7088 msg.hwnd = hwnd;
7089 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7090 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7091 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7092 res, expected);
7093 DestroyWindow(hwnd);
7095 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7096 ES_WANTRETURN|WS_POPUP,
7097 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7098 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7099 msg.hwnd = hwnd;
7100 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7101 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7102 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7103 res, expected);
7104 DestroyWindow(hwnd);
7106 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7107 WS_POPUP,
7108 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7109 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7110 msg.hwnd = hwnd;
7111 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7112 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7113 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7114 res, expected);
7115 DestroyWindow(hwnd);
7117 msg.wParam = VK_TAB;
7118 msg.lParam = (MapVirtualKeyA(VK_TAB, MAPVK_VK_TO_VSC) << 16) | 0x0001;
7120 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7121 ES_MULTILINE|WS_POPUP,
7122 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7123 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7124 msg.hwnd = hwnd;
7125 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7126 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7127 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7128 res, expected);
7129 DestroyWindow(hwnd);
7131 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7132 WS_POPUP,
7133 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7134 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7135 msg.hwnd = hwnd;
7136 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7137 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7138 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7139 res, expected);
7140 DestroyWindow(hwnd);
7142 hold_key(VK_CONTROL);
7144 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7145 ES_MULTILINE|WS_POPUP,
7146 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7147 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7148 msg.hwnd = hwnd;
7149 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7150 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7151 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7152 res, expected);
7153 DestroyWindow(hwnd);
7155 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7156 WS_POPUP,
7157 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7158 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7159 msg.hwnd = hwnd;
7160 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7161 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7162 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7163 res, expected);
7164 DestroyWindow(hwnd);
7166 release_key(VK_CONTROL);
7168 msg.wParam = 'a';
7169 msg.lParam = (MapVirtualKeyA('a', MAPVK_VK_TO_VSC) << 16) | 0x0001;
7171 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7172 ES_MULTILINE|WS_POPUP,
7173 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7174 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7175 msg.hwnd = hwnd;
7176 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7177 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7178 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7179 res, expected);
7180 DestroyWindow(hwnd);
7182 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7183 WS_POPUP,
7184 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7185 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7186 msg.hwnd = hwnd;
7187 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7188 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7189 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7190 res, expected);
7191 DestroyWindow(hwnd);
7193 msg.message = WM_CHAR;
7195 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7196 ES_MULTILINE|WS_POPUP,
7197 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7198 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7199 msg.hwnd = hwnd;
7200 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7201 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL|DLGC_WANTMESSAGE;
7202 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7203 res, expected);
7204 DestroyWindow(hwnd);
7206 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7207 WS_POPUP,
7208 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7209 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7210 msg.hwnd = hwnd;
7211 res = SendMessageA(hwnd, WM_GETDLGCODE, VK_RETURN, (LPARAM)&msg);
7212 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS|DLGC_HASSETSEL;
7213 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7214 res, expected);
7215 DestroyWindow(hwnd);
7217 hwnd = CreateWindowExA(0, RICHEDIT_CLASS20A, NULL,
7218 WS_POPUP|ES_SAVESEL,
7219 0, 0, 200, 60, NULL, NULL, hmoduleRichEdit, NULL);
7220 ok(hwnd != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7221 res = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
7222 expected = DLGC_WANTCHARS|DLGC_WANTTAB|DLGC_WANTARROWS;
7223 ok(res == expected, "WM_GETDLGCODE returned %x but expected %x\n",
7224 res, expected);
7225 DestroyWindow(hwnd);
7228 static void test_zoom(void)
7230 HWND hwnd;
7231 UINT ret;
7232 RECT rc;
7233 POINT pt;
7234 int numerator, denominator;
7236 hwnd = new_richedit(NULL);
7237 GetClientRect(hwnd, &rc);
7238 pt.x = (rc.right - rc.left) / 2;
7239 pt.y = (rc.bottom - rc.top) / 2;
7240 ClientToScreen(hwnd, &pt);
7242 /* Test initial zoom value */
7243 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7244 ok(numerator == 0, "Numerator should be initialized to 0 (got %d).\n", numerator);
7245 ok(denominator == 0, "Denominator should be initialized to 0 (got %d).\n", denominator);
7246 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7248 /* test scroll wheel */
7249 hold_key(VK_CONTROL);
7250 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7251 MAKELPARAM(pt.x, pt.y));
7252 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7253 release_key(VK_CONTROL);
7255 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7256 ok(numerator == 110, "incorrect numerator is %d\n", numerator);
7257 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7258 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7260 /* Test how much the mouse wheel can zoom in and out. */
7261 ret = SendMessageA(hwnd, EM_SETZOOM, 490, 100);
7262 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7264 hold_key(VK_CONTROL);
7265 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7266 MAKELPARAM(pt.x, pt.y));
7267 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7268 release_key(VK_CONTROL);
7270 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7271 ok(numerator == 500, "incorrect numerator is %d\n", numerator);
7272 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7273 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7275 ret = SendMessageA(hwnd, EM_SETZOOM, 491, 100);
7276 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7278 hold_key(VK_CONTROL);
7279 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7280 MAKELPARAM(pt.x, pt.y));
7281 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7282 release_key(VK_CONTROL);
7284 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7285 ok(numerator == 491, "incorrect numerator is %d\n", numerator);
7286 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7287 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7289 ret = SendMessageA(hwnd, EM_SETZOOM, 20, 100);
7290 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7292 hold_key(VK_CONTROL);
7293 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7294 MAKELPARAM(pt.x, pt.y));
7295 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7296 release_key(VK_CONTROL);
7298 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7299 ok(numerator == 10, "incorrect numerator is %d\n", numerator);
7300 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7301 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7303 ret = SendMessageA(hwnd, EM_SETZOOM, 19, 100);
7304 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7306 hold_key(VK_CONTROL);
7307 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, -WHEEL_DELTA),
7308 MAKELPARAM(pt.x, pt.y));
7309 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7310 release_key(VK_CONTROL);
7312 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7313 ok(numerator == 19, "incorrect numerator is %d\n", numerator);
7314 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7315 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7317 /* Test how WM_SCROLLWHEEL treats our custom denominator. */
7318 ret = SendMessageA(hwnd, EM_SETZOOM, 50, 13);
7319 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7321 hold_key(VK_CONTROL);
7322 ret = SendMessageA(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(MK_CONTROL, WHEEL_DELTA),
7323 MAKELPARAM(pt.x, pt.y));
7324 ok(!ret, "WM_MOUSEWHEEL failed (%d).\n", ret);
7325 release_key(VK_CONTROL);
7327 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7328 ok(numerator == 394, "incorrect numerator is %d\n", numerator);
7329 ok(denominator == 100, "incorrect denominator is %d\n", denominator);
7330 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7332 /* Test bounds checking on EM_SETZOOM */
7333 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 127);
7334 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7336 ret = SendMessageA(hwnd, EM_SETZOOM, 127, 2);
7337 ok(ret == TRUE, "EM_SETZOOM rejected valid values (%d).\n", ret);
7339 ret = SendMessageA(hwnd, EM_SETZOOM, 2, 128);
7340 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7342 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7343 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7344 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7345 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7347 ret = SendMessageA(hwnd, EM_SETZOOM, 128, 2);
7348 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7350 /* See if negative numbers are accepted. */
7351 ret = SendMessageA(hwnd, EM_SETZOOM, -100, -100);
7352 ok(ret == FALSE, "EM_SETZOOM accepted invalid values (%d).\n", ret);
7354 /* See if negative numbers are accepted. */
7355 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 100);
7356 ok(ret == FALSE, "EM_SETZOOM failed (%d).\n", ret);
7358 ret = SendMessageA(hwnd, EM_GETZOOM, (WPARAM)&numerator, (LPARAM)&denominator);
7359 ok(numerator == 127, "incorrect numerator is %d\n", numerator);
7360 ok(denominator == 2, "incorrect denominator is %d\n", denominator);
7361 ok(ret == TRUE, "EM_GETZOOM failed (%d).\n", ret);
7363 /* Reset the zoom value */
7364 ret = SendMessageA(hwnd, EM_SETZOOM, 0, 0);
7365 ok(ret == TRUE, "EM_SETZOOM failed (%d).\n", ret);
7367 DestroyWindow(hwnd);
7370 struct dialog_mode_messages
7372 int wm_getdefid, wm_close, wm_nextdlgctl;
7375 static struct dialog_mode_messages dm_messages;
7377 #define test_dm_messages(wmclose, wmgetdefid, wmnextdlgctl) \
7378 ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE message, " \
7379 "got %d\n", wmclose, dm_messages.wm_close); \
7380 ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID message, " \
7381 "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
7382 ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL message, " \
7383 "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
7385 static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
7387 switch (iMsg)
7389 case DM_GETDEFID:
7390 dm_messages.wm_getdefid++;
7391 return MAKELONG(ID_RICHEDITTESTDBUTTON, DC_HASDEFID);
7392 case WM_NEXTDLGCTL:
7393 dm_messages.wm_nextdlgctl++;
7394 break;
7395 case WM_CLOSE:
7396 dm_messages.wm_close++;
7397 break;
7400 return DefWindowProcA(hwnd, iMsg, wParam, lParam);
7403 static void test_dialogmode(void)
7405 HWND hwRichEdit, hwParent, hwButton;
7406 MSG msg= {0};
7407 int lcount, r;
7408 WNDCLASSA cls;
7410 cls.style = 0;
7411 cls.lpfnWndProc = dialog_mode_wnd_proc;
7412 cls.cbClsExtra = 0;
7413 cls.cbWndExtra = 0;
7414 cls.hInstance = GetModuleHandleA(0);
7415 cls.hIcon = 0;
7416 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
7417 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
7418 cls.lpszMenuName = NULL;
7419 cls.lpszClassName = "DialogModeParentClass";
7420 if(!RegisterClassA(&cls)) assert(0);
7422 hwParent = CreateWindowA("DialogModeParentClass", NULL, WS_OVERLAPPEDWINDOW,
7423 CW_USEDEFAULT, 0, 200, 120, NULL, NULL, GetModuleHandleA(0), NULL);
7425 /* Test richedit(ES_MULTILINE) */
7427 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7429 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7430 ok(0 == r, "expected 0, got %d\n", r);
7431 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7432 ok(2 == lcount, "expected 2, got %d\n", lcount);
7434 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, 0);
7435 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7437 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7438 ok(0 == r, "expected 0, got %d\n", r);
7439 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7440 ok(3 == lcount, "expected 3, got %d\n", lcount);
7442 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7443 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7444 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7445 ok(0 == r, "expected 0, got %d\n", r);
7446 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7447 ok(3 == lcount, "expected 3, got %d\n", lcount);
7449 DestroyWindow(hwRichEdit);
7451 /* Test standalone richedit(ES_MULTILINE) */
7453 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, NULL);
7455 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7456 ok(0 == r, "expected 0, got %d\n", r);
7457 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7458 ok(2 == lcount, "expected 2, got %d\n", lcount);
7460 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7461 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7463 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7464 ok(0 == r, "expected 0, got %d\n", r);
7465 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7466 ok(2 == lcount, "expected 2, got %d\n", lcount);
7468 DestroyWindow(hwRichEdit);
7470 /* Check a destination for messages */
7472 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7474 SetWindowLongA(hwRichEdit, GWL_STYLE, GetWindowLongA(hwRichEdit, GWL_STYLE)& ~WS_POPUP);
7475 SetParent( hwRichEdit, NULL);
7477 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7478 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7480 memset(&dm_messages, 0, sizeof(dm_messages));
7481 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7482 ok(0 == r, "expected 0, got %d\n", r);
7483 test_dm_messages(0, 1, 0);
7485 memset(&dm_messages, 0, sizeof(dm_messages));
7486 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7487 ok(0 == r, "expected 0, got %d\n", r);
7488 test_dm_messages(0, 0, 1);
7490 DestroyWindow(hwRichEdit);
7492 /* Check messages from richedit(ES_MULTILINE) */
7494 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE, hwParent);
7496 memset(&dm_messages, 0, sizeof(dm_messages));
7497 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7498 ok(0 == r, "expected 0, got %d\n", r);
7499 test_dm_messages(0, 0, 0);
7501 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7502 ok(2 == lcount, "expected 2, got %d\n", lcount);
7504 memset(&dm_messages, 0, sizeof(dm_messages));
7505 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7506 ok(0 == r, "expected 0, got %d\n", r);
7507 test_dm_messages(0, 0, 0);
7509 memset(&dm_messages, 0, sizeof(dm_messages));
7510 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7511 ok(0 == r, "expected 0, got %d\n", r);
7512 test_dm_messages(0, 0, 0);
7514 memset(&dm_messages, 0, sizeof(dm_messages));
7515 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7516 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7517 test_dm_messages(0, 0, 0);
7519 memset(&dm_messages, 0, sizeof(dm_messages));
7520 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7521 ok(0 == r, "expected 0, got %d\n", r);
7522 test_dm_messages(0, 1, 0);
7524 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7525 ok(2 == lcount, "expected 2, got %d\n", lcount);
7527 memset(&dm_messages, 0, sizeof(dm_messages));
7528 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7529 ok(0 == r, "expected 0, got %d\n", r);
7530 test_dm_messages(0, 0, 0);
7532 memset(&dm_messages, 0, sizeof(dm_messages));
7533 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7534 ok(0 == r, "expected 0, got %d\n", r);
7535 test_dm_messages(0, 0, 1);
7537 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7538 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7539 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7541 memset(&dm_messages, 0, sizeof(dm_messages));
7542 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7543 ok(0 == r, "expected 0, got %d\n", r);
7544 test_dm_messages(0, 1, 1);
7546 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7547 ok(2 == lcount, "expected 2, got %d\n", lcount);
7549 DestroyWindow(hwButton);
7550 DestroyWindow(hwRichEdit);
7552 /* Check messages from richedit(ES_MULTILINE|ES_WANTRETURN) */
7554 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_MULTILINE|ES_WANTRETURN, hwParent);
7556 memset(&dm_messages, 0, sizeof(dm_messages));
7557 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7558 ok(0 == r, "expected 0, got %d\n", r);
7559 test_dm_messages(0, 0, 0);
7561 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7562 ok(2 == lcount, "expected 2, got %d\n", lcount);
7564 memset(&dm_messages, 0, sizeof(dm_messages));
7565 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7566 ok(0 == r, "expected 0, got %d\n", r);
7567 test_dm_messages(0, 0, 0);
7569 memset(&dm_messages, 0, sizeof(dm_messages));
7570 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7571 ok(0 == r, "expected 0, got %d\n", r);
7572 test_dm_messages(0, 0, 0);
7574 memset(&dm_messages, 0, sizeof(dm_messages));
7575 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7576 ok(0x8f == r, "expected 0x8f, got 0x%x\n", r);
7577 test_dm_messages(0, 0, 0);
7579 memset(&dm_messages, 0, sizeof(dm_messages));
7580 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7581 ok(0 == r, "expected 0, got %d\n", r);
7582 test_dm_messages(0, 0, 0);
7584 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7585 ok(3 == lcount, "expected 3, got %d\n", lcount);
7587 memset(&dm_messages, 0, sizeof(dm_messages));
7588 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7589 ok(0 == r, "expected 0, got %d\n", r);
7590 test_dm_messages(0, 0, 0);
7592 memset(&dm_messages, 0, sizeof(dm_messages));
7593 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7594 ok(0 == r, "expected 0, got %d\n", r);
7595 test_dm_messages(0, 0, 1);
7597 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7598 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7599 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7601 memset(&dm_messages, 0, sizeof(dm_messages));
7602 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7603 ok(0 == r, "expected 0, got %d\n", r);
7604 test_dm_messages(0, 0, 0);
7606 lcount = SendMessageA(hwRichEdit, EM_GETLINECOUNT, 0, 0);
7607 ok(4 == lcount, "expected 4, got %d\n", lcount);
7609 DestroyWindow(hwButton);
7610 DestroyWindow(hwRichEdit);
7612 /* Check messages from richedit(0) */
7614 hwRichEdit = new_window(RICHEDIT_CLASS20A, 0, hwParent);
7616 memset(&dm_messages, 0, sizeof(dm_messages));
7617 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7618 ok(0 == r, "expected 0, got %d\n", r);
7619 test_dm_messages(0, 0, 0);
7621 memset(&dm_messages, 0, sizeof(dm_messages));
7622 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7623 ok(0 == r, "expected 0, got %d\n", r);
7624 test_dm_messages(0, 0, 0);
7626 memset(&dm_messages, 0, sizeof(dm_messages));
7627 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7628 ok(0 == r, "expected 0, got %d\n", r);
7629 test_dm_messages(0, 0, 0);
7631 memset(&dm_messages, 0, sizeof(dm_messages));
7632 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7633 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7634 test_dm_messages(0, 0, 0);
7636 memset(&dm_messages, 0, sizeof(dm_messages));
7637 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7638 ok(0 == r, "expected 0, got %d\n", r);
7639 test_dm_messages(0, 1, 0);
7641 memset(&dm_messages, 0, sizeof(dm_messages));
7642 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
7643 ok(0 == r, "expected 0, got %d\n", r);
7644 test_dm_messages(0, 0, 0);
7646 memset(&dm_messages, 0, sizeof(dm_messages));
7647 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
7648 ok(0 == r, "expected 0, got %d\n", r);
7649 test_dm_messages(0, 0, 1);
7651 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7652 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7653 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7655 memset(&dm_messages, 0, sizeof(dm_messages));
7656 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7657 ok(0 == r, "expected 0, got %d\n", r);
7658 test_dm_messages(0, 1, 1);
7660 DestroyWindow(hwRichEdit);
7662 /* Check messages from richedit(ES_WANTRETURN) */
7664 hwRichEdit = new_window(RICHEDIT_CLASS20A, ES_WANTRETURN, hwParent);
7666 memset(&dm_messages, 0, sizeof(dm_messages));
7667 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7668 ok(0 == r, "expected 0, got %d\n", r);
7669 test_dm_messages(0, 0, 0);
7671 memset(&dm_messages, 0, sizeof(dm_messages));
7672 r = SendMessageA(hwRichEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
7673 ok(0x8b == r, "expected 0x8b, got 0x%x\n", r);
7674 test_dm_messages(0, 0, 0);
7676 memset(&dm_messages, 0, sizeof(dm_messages));
7677 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7678 ok(0 == r, "expected 0, got %d\n", r);
7679 test_dm_messages(0, 0, 0);
7681 hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
7682 100, 100, 50, 20, hwParent, (HMENU)ID_RICHEDITTESTDBUTTON, GetModuleHandleA(0), NULL);
7683 ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
7685 memset(&dm_messages, 0, sizeof(dm_messages));
7686 r = SendMessageA(hwRichEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
7687 ok(0 == r, "expected 0, got %d\n", r);
7688 test_dm_messages(0, 0, 0);
7690 DestroyWindow(hwRichEdit);
7691 DestroyWindow(hwParent);
7694 static void test_EM_FINDWORDBREAK_W(void)
7696 static const struct {
7697 WCHAR c;
7698 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7699 } delimiter_tests[] = {
7700 {0x0a, FALSE}, /* newline */
7701 {0x0b, FALSE}, /* vertical tab */
7702 {0x0c, FALSE}, /* form feed */
7703 {0x0d, FALSE}, /* carriage return */
7704 {0x20, TRUE}, /* space */
7705 {0x61, FALSE}, /* capital letter a */
7706 {0xa0, FALSE}, /* no-break space */
7707 {0x2000, FALSE}, /* en quad */
7708 {0x3000, FALSE}, /* Ideographic space */
7709 {0x1100, FALSE}, /* Hangul Choseong Kiyeok (G sound) Ordinary Letter*/
7710 {0x11ff, FALSE}, /* Hangul Jongseoung Kiyeok-Hieuh (Hard N sound) Ordinary Letter*/
7711 {0x115f, FALSE}, /* Hangul Choseong Filler (no sound, used with two letter Hangul words) Ordinary Letter */
7712 {0xac00, FALSE}, /* Hangul character GA*/
7713 {0xd7af, FALSE}, /* End of Hangul character chart */
7714 {0xf020, TRUE}, /* MS private for CP_SYMBOL round trip?, see kb897872 */
7715 {0xff20, FALSE}, /* fullwidth commercial @ */
7716 {WCH_EMBEDDING, FALSE}, /* object replacement character*/
7718 int i;
7719 HWND hwndRichEdit = new_richeditW(NULL);
7720 ok(IsWindowUnicode(hwndRichEdit), "window should be unicode\n");
7721 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7723 WCHAR wbuf[2];
7724 int result;
7726 wbuf[0] = delimiter_tests[i].c;
7727 wbuf[1] = 0;
7728 SendMessageW(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)wbuf);
7729 result = SendMessageW(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER,0);
7730 if (wbuf[0] == 0x20 || wbuf[0] == 0xf020)
7731 todo_wine
7732 ok(result == delimiter_tests[i].isdelimiter,
7733 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7734 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7735 else
7736 ok(result == delimiter_tests[i].isdelimiter,
7737 "wanted ISDELIMITER_W(0x%x) %d, got %d\n",
7738 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7740 DestroyWindow(hwndRichEdit);
7743 static void test_EM_FINDWORDBREAK_A(void)
7745 static const struct {
7746 WCHAR c;
7747 BOOL isdelimiter; /* expected result of WB_ISDELIMITER */
7748 } delimiter_tests[] = {
7749 {0x0a, FALSE}, /* newline */
7750 {0x0b, FALSE}, /* vertical tab */
7751 {0x0c, FALSE}, /* form feed */
7752 {0x0d, FALSE}, /* carriage return */
7753 {0x20, TRUE}, /* space */
7754 {0x61, FALSE}, /* capital letter a */
7756 int i;
7757 HWND hwndRichEdit = new_richedit(NULL);
7759 ok(!IsWindowUnicode(hwndRichEdit), "window should not be unicode\n");
7760 for (i = 0; i < sizeof(delimiter_tests)/sizeof(delimiter_tests[0]); i++)
7762 int result;
7763 char buf[2];
7764 buf[0] = delimiter_tests[i].c;
7765 buf[1] = 0;
7766 SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)buf);
7767 result = SendMessageA(hwndRichEdit, EM_FINDWORDBREAK, WB_ISDELIMITER, 0);
7768 if (buf[0] == 0x20)
7769 todo_wine
7770 ok(result == delimiter_tests[i].isdelimiter,
7771 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7772 delimiter_tests[i].c, delimiter_tests[i].isdelimiter,result);
7773 else
7774 ok(result == delimiter_tests[i].isdelimiter,
7775 "wanted ISDELIMITER_A(0x%x) %d, got %d\n",
7776 delimiter_tests[i].c, delimiter_tests[i].isdelimiter, result);
7778 DestroyWindow(hwndRichEdit);
7782 * This test attempts to show the effect of enter on a richedit
7783 * control v1.0 inserts CRLF whereas for higher versions it only
7784 * inserts CR. If shows that EM_GETTEXTEX with GT_USECRLF == WM_GETTEXT
7785 * and also shows that GT_USECRLF has no effect in richedit 1.0, but
7786 * does for higher. The same test is cloned in riched32 and riched20.
7788 static void test_enter(void)
7790 static const struct {
7791 const char *initialtext;
7792 const int cursor;
7793 const char *expectedwmtext;
7794 const char *expectedemtext;
7795 const char *expectedemtextcrlf;
7796 } testenteritems[] = {
7797 { "aaabbb\r\n", 3, "aaa\r\nbbb\r\n", "aaa\rbbb\r", "aaa\r\nbbb\r\n"},
7798 { "aaabbb\r\n", 6, "aaabbb\r\n\r\n", "aaabbb\r\r", "aaabbb\r\n\r\n"},
7799 { "aa\rabbb\r\n", 7, "aa\r\nabbb\r\n\r\n", "aa\rabbb\r\r", "aa\r\nabbb\r\n\r\n"},
7800 { "aa\rabbb\r\n", 3, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"},
7801 { "aa\rabbb\r\n", 2, "aa\r\n\r\nabbb\r\n", "aa\r\rabbb\r", "aa\r\n\r\nabbb\r\n"}
7804 char expectedbuf[1024];
7805 char resultbuf[1024];
7806 HWND hwndRichEdit = new_richedit(NULL);
7807 UINT i,j;
7809 for (i = 0; i < sizeof(testenteritems)/sizeof(testenteritems[0]); i++) {
7811 char buf[1024] = {0};
7812 LRESULT result;
7813 GETTEXTEX getText;
7814 const char *expected;
7816 /* Set the text to the initial text */
7817 result = SendMessageA(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)testenteritems[i].initialtext);
7818 ok (result == 1, "[%d] WM_SETTEXT returned %ld instead of 1\n", i, result);
7820 /* Send Enter */
7821 SendMessageA(hwndRichEdit, EM_SETSEL, testenteritems[i].cursor, testenteritems[i].cursor);
7822 simulate_typing_characters(hwndRichEdit, "\r");
7824 /* 1. Retrieve with WM_GETTEXT */
7825 buf[0] = 0x00;
7826 result = SendMessageA(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM)buf);
7827 expected = testenteritems[i].expectedwmtext;
7829 resultbuf[0]=0x00;
7830 for (j = 0; j < (UINT)result; j++)
7831 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7832 expectedbuf[0] = '\0';
7833 for (j = 0; j < strlen(expected); j++)
7834 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7836 result = strcmp(expected, buf);
7837 ok (result == 0,
7838 "[%d] WM_GETTEXT unexpected '%s' expected '%s'\n",
7839 i, resultbuf, expectedbuf);
7841 /* 2. Retrieve with EM_GETTEXTEX, GT_DEFAULT */
7842 getText.cb = sizeof(buf);
7843 getText.flags = GT_DEFAULT;
7844 getText.codepage = CP_ACP;
7845 getText.lpDefaultChar = NULL;
7846 getText.lpUsedDefChar = NULL;
7847 buf[0] = 0x00;
7848 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7849 expected = testenteritems[i].expectedemtext;
7851 resultbuf[0]=0x00;
7852 for (j = 0; j < (UINT)result; j++)
7853 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7854 expectedbuf[0] = '\0';
7855 for (j = 0; j < strlen(expected); j++)
7856 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7858 result = strcmp(expected, buf);
7859 ok (result == 0,
7860 "[%d] EM_GETTEXTEX, GT_DEFAULT unexpected '%s', expected '%s'\n",
7861 i, resultbuf, expectedbuf);
7863 /* 3. Retrieve with EM_GETTEXTEX, GT_USECRLF */
7864 getText.cb = sizeof(buf);
7865 getText.flags = GT_USECRLF;
7866 getText.codepage = CP_ACP;
7867 getText.lpDefaultChar = NULL;
7868 getText.lpUsedDefChar = NULL;
7869 buf[0] = 0x00;
7870 result = SendMessageA(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM)buf);
7871 expected = testenteritems[i].expectedemtextcrlf;
7873 resultbuf[0]=0x00;
7874 for (j = 0; j < (UINT)result; j++)
7875 sprintf(resultbuf+strlen(resultbuf), "%02x", buf[j] & 0xFF);
7876 expectedbuf[0] = '\0';
7877 for (j = 0; j < strlen(expected); j++)
7878 sprintf(expectedbuf+strlen(expectedbuf), "%02x", expected[j] & 0xFF);
7880 result = strcmp(expected, buf);
7881 ok (result == 0,
7882 "[%d] EM_GETTEXTEX, GT_USECRLF unexpected '%s', expected '%s'\n",
7883 i, resultbuf, expectedbuf);
7886 DestroyWindow(hwndRichEdit);
7889 static void test_WM_CREATE(void)
7891 static const WCHAR titleW[] = {'l','i','n','e','1','\n','l','i','n','e','2',0};
7892 static const char title[] = "line1\nline2";
7894 HWND rich_edit;
7895 LRESULT res;
7896 char buf[64];
7897 int len;
7899 rich_edit = CreateWindowA(RICHEDIT_CLASS20A, title, WS_POPUP|WS_VISIBLE,
7900 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7901 ok(rich_edit != NULL, "class: %s, error: %d\n", RICHEDIT_CLASS20A, (int) GetLastError());
7903 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7904 ok(len == 5, "GetWindowText returned %d\n", len);
7905 ok(!strcmp(buf, "line1"), "buf = %s\n", buf);
7907 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7908 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7910 DestroyWindow(rich_edit);
7912 rich_edit = CreateWindowW(RICHEDIT_CLASS20W, titleW, WS_POPUP|WS_VISIBLE|ES_MULTILINE,
7913 0, 0, 200, 80, NULL, NULL, NULL, NULL);
7914 ok(rich_edit != NULL, "class: %s, error: %d\n", wine_dbgstr_w(RICHEDIT_CLASS20W), (int) GetLastError());
7916 len = GetWindowTextA(rich_edit, buf, sizeof(buf));
7917 ok(len == 12, "GetWindowText returned %d\n", len);
7918 ok(!strcmp(buf, "line1\r\nline2"), "buf = %s\n", buf);
7920 res = SendMessageA(rich_edit, EM_GETSEL, 0, 0);
7921 ok(res == 0, "SendMessage(EM_GETSEL) returned %lx\n", res);
7923 DestroyWindow(rich_edit);
7926 /*******************************************************************
7927 * Test that after deleting all of the text, the first paragraph
7928 * format reverts to the default.
7930 static void test_reset_default_para_fmt( void )
7932 HWND richedit = new_richeditW( NULL );
7933 PARAFORMAT2 fmt;
7934 WORD def_align, new_align;
7936 memset( &fmt, 0, sizeof(fmt) );
7937 fmt.cbSize = sizeof(PARAFORMAT2);
7938 fmt.dwMask = -1;
7939 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7940 def_align = fmt.wAlignment;
7941 new_align = (def_align == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
7943 simulate_typing_characters( richedit, "123" );
7945 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7946 fmt.dwMask = PFM_ALIGNMENT;
7947 fmt.wAlignment = new_align;
7948 SendMessageA( richedit, EM_SETPARAFORMAT, 0, (LPARAM)&fmt );
7950 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7951 ok( fmt.wAlignment == new_align, "got %d expect %d\n", fmt.wAlignment, new_align );
7953 SendMessageA( richedit, EM_SETSEL, 0, -1 );
7954 SendMessageA( richedit, WM_CUT, 0, 0 );
7956 SendMessageA( richedit, EM_GETPARAFORMAT, 0, (LPARAM)&fmt );
7957 ok( fmt.wAlignment == def_align, "got %d exppect %d\n", fmt.wAlignment, def_align );
7959 DestroyWindow( richedit );
7962 static void test_EM_SETREADONLY(void)
7964 HWND richedit = new_richeditW(NULL);
7965 DWORD dwStyle;
7966 LRESULT res;
7968 res = SendMessageA(richedit, EM_SETREADONLY, TRUE, 0);
7969 ok(res == 1, "EM_SETREADONLY\n");
7970 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7971 ok(dwStyle & ES_READONLY, "got wrong value: 0x%x\n", dwStyle);
7973 res = SendMessageA(richedit, EM_SETREADONLY, FALSE, 0);
7974 ok(res == 1, "EM_SETREADONLY\n");
7975 dwStyle = GetWindowLongA(richedit, GWL_STYLE);
7976 ok(!(dwStyle & ES_READONLY), "got wrong value: 0x%x\n", dwStyle);
7978 DestroyWindow(richedit);
7981 static inline LONG twips2points(LONG value)
7983 return value / 20;
7986 #define TEST_EM_SETFONTSIZE(hwnd,size,expected_size,expected_res,expected_undo) \
7987 _test_font_size(__LINE__,hwnd,size,expected_size,expected_res,expected_undo)
7988 static void _test_font_size(unsigned line, HWND hwnd, LONG size, LONG expected_size,
7989 LRESULT expected_res, BOOL expected_undo)
7991 CHARFORMAT2A cf;
7992 LRESULT res;
7993 BOOL isundo;
7995 cf.cbSize = sizeof(cf);
7996 cf.dwMask = CFM_SIZE;
7998 res = SendMessageA(hwnd, EM_SETFONTSIZE, size, 0);
7999 SendMessageA(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
8000 isundo = SendMessageA(hwnd, EM_CANUNDO, 0, 0);
8001 ok_(__FILE__,line)(res == expected_res, "EM_SETFONTSIZE unexpected return value: %lx.\n", res);
8002 ok_(__FILE__,line)(twips2points(cf.yHeight) == expected_size, "got wrong font size: %d, expected: %d\n",
8003 twips2points(cf.yHeight), expected_size);
8004 ok_(__FILE__,line)(isundo == expected_undo, "get wrong undo mark: %d, expected: %d.\n",
8005 isundo, expected_undo);
8008 static void test_EM_SETFONTSIZE(void)
8010 HWND richedit = new_richedit(NULL);
8011 CHAR text[] = "wine";
8012 CHARFORMAT2A tmp_cf;
8013 LONG default_size;
8015 tmp_cf.cbSize = sizeof(tmp_cf);
8016 tmp_cf.dwMask = CFM_SIZE;
8017 tmp_cf.yHeight = 9 * 20.0;
8018 SendMessageA(richedit, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8020 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)text);
8022 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8023 /* without selection */
8024 TEST_EM_SETFONTSIZE(richedit, 1, 10, TRUE, FALSE); /* 9 + 1 -> 10 */
8025 SendMessageA(richedit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&tmp_cf);
8026 default_size = twips2points(tmp_cf.yHeight);
8027 ok(default_size == 9, "Default font size should not be changed.\n");
8028 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8030 SendMessageA(richedit, EM_SETSEL, 0, 2);
8032 TEST_EM_SETFONTSIZE(richedit, 0, 9, TRUE, TRUE); /* 9 + 0 -> 9 */
8034 SendMessageA(richedit, EM_SETMODIFY, FALSE, 0);
8035 TEST_EM_SETFONTSIZE(richedit, 3, 12, TRUE, TRUE); /* 9 + 3 -> 12 */
8036 ok(SendMessageA(richedit, EM_SETMODIFY, 0, 0) == FALSE, "Modify flag should not be changed.\n");
8038 TEST_EM_SETFONTSIZE(richedit, 1, 14, TRUE, TRUE); /* 12 + 1 + 1 -> 14 */
8039 TEST_EM_SETFONTSIZE(richedit, -1, 12, TRUE, TRUE); /* 14 - 1 - 1 -> 12 */
8040 TEST_EM_SETFONTSIZE(richedit, 4, 16, TRUE, TRUE); /* 12 + 4 -> 16 */
8041 TEST_EM_SETFONTSIZE(richedit, 3, 20, TRUE, TRUE); /* 16 + 3 + 1 -> 20 */
8042 TEST_EM_SETFONTSIZE(richedit, 0, 20, TRUE, TRUE); /* 20 + 0 -> 20 */
8043 TEST_EM_SETFONTSIZE(richedit, 8, 28, TRUE, TRUE); /* 20 + 8 -> 28 */
8044 TEST_EM_SETFONTSIZE(richedit, 0, 28, TRUE, TRUE); /* 28 + 0 -> 28 */
8045 TEST_EM_SETFONTSIZE(richedit, 1, 36, TRUE, TRUE); /* 28 + 1 -> 36 */
8046 TEST_EM_SETFONTSIZE(richedit, 0, 36, TRUE, TRUE); /* 36 + 0 -> 36 */
8047 TEST_EM_SETFONTSIZE(richedit, 1, 48, TRUE, TRUE); /* 36 + 1 -> 48 */
8048 TEST_EM_SETFONTSIZE(richedit, 0, 48, TRUE, TRUE); /* 48 + 0 -> 48 */
8049 TEST_EM_SETFONTSIZE(richedit, 1, 72, TRUE, TRUE); /* 48 + 1 -> 72 */
8050 TEST_EM_SETFONTSIZE(richedit, 0, 72, TRUE, TRUE); /* 72 + 0 -> 72 */
8051 TEST_EM_SETFONTSIZE(richedit, 1, 80, TRUE, TRUE); /* 72 + 1 -> 80 */
8052 TEST_EM_SETFONTSIZE(richedit, 0, 80, TRUE, TRUE); /* 80 + 0 -> 80 */
8053 TEST_EM_SETFONTSIZE(richedit, 1, 90, TRUE, TRUE); /* 80 + 1 -> 90 */
8054 TEST_EM_SETFONTSIZE(richedit, 0, 90, TRUE, TRUE); /* 90 + 0 -> 90 */
8055 TEST_EM_SETFONTSIZE(richedit, 1, 100, TRUE, TRUE); /* 90 + 1 -> 100 */
8056 TEST_EM_SETFONTSIZE(richedit, 25, 130, TRUE, TRUE); /* 100 + 25 -> 130 */
8057 TEST_EM_SETFONTSIZE(richedit, -1, 120, TRUE, TRUE); /* 130 - 1 -> 120 */
8058 TEST_EM_SETFONTSIZE(richedit, -35, 80, TRUE, TRUE); /* 120 - 35 -> 80 */
8059 TEST_EM_SETFONTSIZE(richedit, -7, 72, TRUE, TRUE); /* 80 - 7 -> 72 */
8060 TEST_EM_SETFONTSIZE(richedit, -42, 28, TRUE, TRUE); /* 72 - 42 -> 28 */
8061 TEST_EM_SETFONTSIZE(richedit, -16, 12, TRUE, TRUE); /* 28 - 16 -> 12 */
8062 TEST_EM_SETFONTSIZE(richedit, -3, 9, TRUE, TRUE); /* 12 - 3 -> 9 */
8063 TEST_EM_SETFONTSIZE(richedit, -8, 1, TRUE, TRUE); /* 9 - 8 -> 1 */
8064 TEST_EM_SETFONTSIZE(richedit, -111, 1, TRUE, TRUE); /* 1 - 111 -> 1 */
8065 TEST_EM_SETFONTSIZE(richedit, 10086, 1638, TRUE, TRUE); /* 1 + 10086 -> 1638 */
8067 /* return FALSE when richedit is TM_PLAINTEXT mode */
8068 SendMessageA(richedit, WM_SETTEXT, 0, (LPARAM)"");
8069 SendMessageA(richedit, EM_SETTEXTMODE, (WPARAM)TM_PLAINTEXT, 0);
8070 TEST_EM_SETFONTSIZE(richedit, 0, 9, FALSE, FALSE);
8072 DestroyWindow(richedit);
8075 static void test_alignment_style(void)
8077 HWND richedit = NULL;
8078 PARAFORMAT2 pf;
8079 DWORD align_style[] = {ES_LEFT, ES_CENTER, ES_RIGHT, ES_RIGHT | ES_CENTER,
8080 ES_LEFT | ES_CENTER, ES_LEFT | ES_RIGHT,
8081 ES_LEFT | ES_RIGHT | ES_CENTER};
8082 DWORD align_mask[] = {PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_CENTER, PFA_CENTER,
8083 PFA_RIGHT, PFA_CENTER};
8084 const char * streamtext =
8085 "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang12298{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 System;}}\r\n"
8086 "\\viewkind4\\uc1\\pard\\f0\\fs17 TestSomeText\\par\r\n"
8087 "}\r\n";
8088 EDITSTREAM es;
8089 int i;
8091 for (i = 0; i < sizeof(align_style) / sizeof(align_style[0]); i++)
8093 DWORD dwStyle, new_align;
8095 richedit = new_windowW(RICHEDIT_CLASS20W, align_style[i], NULL);
8096 memset(&pf, 0, sizeof(pf));
8097 pf.cbSize = sizeof(PARAFORMAT2);
8098 pf.dwMask = -1;
8100 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8101 ok(pf.wAlignment == align_mask[i], "(i = %d) got %d expected %d\n",
8102 i, pf.wAlignment, align_mask[i]);
8103 dwStyle = GetWindowLongW(richedit, GWL_STYLE);
8104 ok((i ? (dwStyle & align_style[i]) : (!(dwStyle & 0x0000000f))) ,
8105 "(i = %d) didn't set right align style: 0x%x\n", i, dwStyle);
8108 /* Based on test_reset_default_para_fmt() */
8109 new_align = (align_mask[i] == PFA_LEFT) ? PFA_RIGHT : PFA_LEFT;
8110 simulate_typing_characters(richedit, "123");
8112 SendMessageW(richedit, EM_SETSEL, 0, -1);
8113 pf.dwMask = PFM_ALIGNMENT;
8114 pf.wAlignment = new_align;
8115 SendMessageW(richedit, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
8117 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8118 ok(pf.wAlignment == new_align, "got %d expect %d\n", pf.wAlignment, new_align);
8120 SendMessageW(richedit, EM_SETSEL, 0, -1);
8121 SendMessageW(richedit, WM_CUT, 0, 0);
8123 SendMessageW(richedit, EM_GETPARAFORMAT, 0, (LPARAM)&pf);
8124 ok(pf.wAlignment == align_mask[i], "got %d exppect %d\n", pf.wAlignment, align_mask[i]);
8126 DestroyWindow(richedit);
8129 /* test with EM_STREAMIN */
8130 richedit = new_windowW(RICHEDIT_CLASS20W, ES_CENTER, NULL);
8131 simulate_typing_characters(richedit, "abc");
8132 es.dwCookie = (DWORD_PTR)&streamtext;
8133 es.dwError = 0;
8134 es.pfnCallback = test_EM_STREAMIN_esCallback;
8135 SendMessageW(richedit, EM_STREAMIN, SF_RTF, (LPARAM)&es);
8136 SendMessageW(richedit, EM_SETSEL, 0, -1);
8137 memset(&pf, 0, sizeof(pf));
8138 pf.cbSize = sizeof(PARAFORMAT2);
8139 pf.dwMask = -1;
8140 SendMessageW(richedit, EM_GETPARAFORMAT, SCF_SELECTION, (LPARAM)&pf);
8141 ok(pf.wAlignment == ES_CENTER, "got %d expected ES_CENTER\n", pf.wAlignment);
8142 DestroyWindow(richedit);
8145 START_TEST( editor )
8147 BOOL ret;
8148 /* Must explicitly LoadLibrary(). The test has no references to functions in
8149 * RICHED20.DLL, so the linker doesn't actually link to it. */
8150 hmoduleRichEdit = LoadLibraryA("riched20.dll");
8151 ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError());
8153 test_WM_CHAR();
8154 test_EM_FINDTEXT(FALSE);
8155 test_EM_FINDTEXT(TRUE);
8156 test_EM_GETLINE();
8157 test_EM_POSFROMCHAR();
8158 test_EM_SCROLLCARET();
8159 test_EM_SCROLL();
8160 test_scrollbar_visibility();
8161 test_WM_SETTEXT();
8162 test_EM_LINELENGTH();
8163 test_EM_SETCHARFORMAT();
8164 test_EM_SETTEXTMODE();
8165 test_TM_PLAINTEXT();
8166 test_EM_SETOPTIONS();
8167 test_WM_GETTEXT();
8168 test_EM_GETTEXTRANGE();
8169 test_EM_GETSELTEXT();
8170 test_EM_SETUNDOLIMIT();
8171 test_ES_PASSWORD();
8172 test_EM_SETTEXTEX();
8173 test_EM_LIMITTEXT();
8174 test_EM_EXLIMITTEXT();
8175 test_EM_GETLIMITTEXT();
8176 test_WM_SETFONT();
8177 test_EM_GETMODIFY();
8178 test_EM_SETSEL();
8179 test_EM_EXSETSEL();
8180 test_WM_PASTE();
8181 test_EM_STREAMIN();
8182 test_EM_STREAMOUT();
8183 test_EM_STREAMOUT_FONTTBL();
8184 test_EM_StreamIn_Undo();
8185 test_EM_FORMATRANGE();
8186 test_unicode_conversions();
8187 test_EM_GETTEXTLENGTHEX();
8188 test_EM_REPLACESEL(1);
8189 test_EM_REPLACESEL(0);
8190 test_WM_NOTIFY();
8191 test_EN_LINK();
8192 test_EM_AUTOURLDETECT();
8193 test_eventMask();
8194 test_undo_coalescing();
8195 test_word_movement();
8196 test_EM_CHARFROMPOS();
8197 test_SETPARAFORMAT();
8198 test_word_wrap();
8199 test_autoscroll();
8200 test_format_rect();
8201 test_WM_GETDLGCODE();
8202 test_zoom();
8203 test_dialogmode();
8204 test_EM_FINDWORDBREAK_W();
8205 test_EM_FINDWORDBREAK_A();
8206 test_enter();
8207 test_WM_CREATE();
8208 test_reset_default_para_fmt();
8209 test_EM_SETREADONLY();
8210 test_EM_SETFONTSIZE();
8211 test_alignment_style();
8213 /* Set the environment variable WINETEST_RICHED20 to keep windows
8214 * responsive and open for 30 seconds. This is useful for debugging.
8216 if (getenv( "WINETEST_RICHED20" )) {
8217 keep_responsive(30);
8220 OleFlushClipboard();
8221 ret = FreeLibrary(hmoduleRichEdit);
8222 ok(ret, "error: %d\n", (int) GetLastError());